2
votes

In the following XAML UserControl I am binding a few items to properties in the UserControl's linked class.

<UserControl x:Class="Kiosk.EventSelectButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Kiosk"
    Height="130" Width="130">
    <Grid>
        <Button 
            Style="{DynamicResource DarkButton130x130}" 
            HorizontalAlignment="Left" 
            VerticalAlignment="Center">
            <Grid Margin="0,0,0,0" Height="118" Width="118">
                <Image VerticalAlignment="Top" HorizontalAlignment="Center" Source="image/select_button_arrows.png" />
                <Image x:Name="EventImageComponent" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{DynamicResource KioskStandardDropShadow}" Source="{Binding Path=EventImage}" />
                <TextBlock x:Name="SelectTextBlock" Text="{Binding Path=SelectText}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,-2,0,0" FontSize="10pt" Foreground="#5aaff5" />
                <TextBlock x:Name="LabelTextBlock" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,0,0" FontSize="14pt" FontWeight="Bold" Text="{Binding Path=Label}"/>
            </Grid>
        </Button>
    </Grid>
</UserControl>

In the linked class' contstructor I'm applying the DataContext of the items to this, as you can see below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Kiosk
{
    /// <summary>
    /// Interaction logic for EventSelectButton.xaml
    /// </summary>
    public partial class EventSelectButton : UserControl
    {

        public String ValueContainer;

        private String _EventImage;

        public String EventImage
        {
            get
            {
                return _EventImage;
            }
            set
            {
                _EventImage = value;
            }
        }

        private String _Label;

        public String Label
        {
            get
            {
                return _Label;
            }
            set
            {
                _Label = value;
            }
        }

        private String _SelectText;

        public String SelectText 
        {
            get 
            { 
                return _SelectText; 
            }
            set 
            { 
                _SelectText = value; 
            }
        }

        public EventSelectButton()
        {
            InitializeComponent();
            LabelTextBlock.DataContext = this;
            SelectTextBlock.DataContext = this;
            EventImageComponent.DataContext = this;
        }
    }
}

Edit

Although this works as intended, I'm interested to know if there is a simpler way of doing this. (edit, lessons learned.) This won't actually work beyond the initialisation, the public properties will be set, however because the class doesn't use DependentProperties or alternatively, implement INotifyPropertyChanged, binding will not work as expected. (end edit)

For example,

  • Can I set the DataContext of these items in the XAML to this (as the EventSelectButton instance), and if so, how?
  • Alternatively, is it possible to inherit the DataContext from the UserControl parent, thus making the Binding Paths simpler.

The only alternatives I've found so far are more verbose, e.g. using the RelativeSource binding method to locate the EventSelectButton Ancestor.

So please, let me know any ways I can improve this binding expression, and any comments on best practices for binding within a UserComponent are much appreciated.

1

1 Answers

2
votes

One way is to do the following:

  1. Name your UserControl in your XAML.
  2. Bind the DataContext of the root element (i.e. Grid) to the UserControl.

Like this:

<UserControl x:Name="uc">
  <Grid DataContext="{Binding ElementName=uc}">
  .
  .
  .
  </Grid>
</UserControl>

Now, you'll ask, why not just set the DataContext of the UserControl itself? Well, this just ensures that setting the DataContext of an instance of the UserControl will still work without affecting the bindings in the UserControl's visual tree. So something like the one below will still work fine.

<Window>
  <uc:EventSelectButton DataContext="{Binding SomeDataContext}" Width="{Binding SomeDataContextWidth}"/>
</Window>

EDIT

To make the solution complete requires the properties in the UserControl to be changed to use DependencyProperty objects instead. Below are the updates to the codes:

XAML:

<UserControl x:Class="Kiosk.EventSelectButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Kiosk"
    x:Name="root"             
    Height="130" Width="130">
    <Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=root}">
        <Button 
            Style="{DynamicResource DarkButton130x130}" 
            HorizontalAlignment="Left" 
            VerticalAlignment="Center">
            <Grid Margin="0,0,0,0" Height="118" Width="118" >
                <Image VerticalAlignment="Top" HorizontalAlignment="Center" Source="image/select_button_arrows.png" />
                <Image Source="{Binding EventImage}" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{DynamicResource KioskStandardDropShadow}"  />
                <TextBlock Text="{Binding SelectText}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,-2,0,0" FontSize="10pt" Foreground="#5aaff5" />
                <TextBlock Text="{Binding Label}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,0,0" FontSize="14pt" FontWeight="Bold" />
            </Grid>
        </Button>
    </Grid>
</UserControl>

Code-Behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Kiosk
{
    public partial class EventSelectButton : UserControl
    {
        public static readonly DependencyProperty EventImageProperty =
                DependencyProperty.Register(
                "EventImage",
                typeof(string),
                typeof(EventSelectButton));

        public String EventImage
        {
            get { return (string)GetValue(EventImageProperty); }
            set { SetValue(EventImageProperty, value); }
        }

        public static readonly DependencyProperty SelectTextProperty =
                DependencyProperty.Register(
                "SelectText",
                typeof(string),
                typeof(EventSelectButton));

        public String SelectText
        {
            get { return (string)GetValue(SelectTextProperty); }
            set { SetValue(SelectTextProperty, value); }
        }

        public static readonly DependencyProperty LabelProperty =
                DependencyProperty.Register(
                "Label",
                typeof(string),
                typeof(EventSelectButton));

        public String Label
        {
            get { return (string)GetValue(LabelProperty); }
            set { SetValue(LabelProperty, value); }
        }

        public EventSelectButton()
        {
            InitializeComponent();
        }
    }
}