Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Exposing Bindings as Properties of a Control

0.00/5 (No votes)
19 Jun 2009 1  
Demonstrates how to create versatile controls which expose bindings for the elements which they contain internally

card

I must admit that the title of this post is not entirely clear, however I couldn't find a way to sum up the content in one short sentence, so we'll dive straight into an example. Let’s say for example, you have developed a funky little business-card as illustrated above, using the simple XAML below:

<Border BorderBrush="LightGray" Background="White"  

	BorderThickness="1.5" CornerRadius="20"

        Width="220" Height="120">
    <Border Margin="5" BorderBrush="LightGray" 

    BorderThickness="1.5" CornerRadius="15">
        <Border.Background>
            <ImageBrush>
                <ImageBrush.ImageSource>
                    <BitmapImage UriSource="back.jpg" />
                </ImageBrush.ImageSource>
            </ImageBrush>
        </Border.Background>
 
        <StackPanel Orientation="Vertical" Margin="10">
            <TextBlock Text="{Binding Name}"

                       FontSize="15" FontWeight="Bold" />
            <Rectangle Stroke="DarkGray" HorizontalAlignment="Stretch"

                       StrokeThickness="0.5" Fill="Black" Height="1"/>
            <TextBlock Margin="0,10,0,0"  Text="{Binding Company}"/>
            <StackPanel Orientation="Horizontal" >
                <TextBlock Text="e: " FontSize="10"/>
                <TextBlock Text="{Binding Email}" FontSize="10"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="w: " FontSize="10"/>
                <HyperlinkButton Content="{Binding Web}" FontSize="10"

                             NavigateUri="{Binding Web}"/>
            </StackPanel>
        </StackPanel>
    </Border>
</Border>

The above XAML creates a simple visual layout that binds to the Name, Email, Web and Company properties of the data object which is presented to the inherited DataContext. Nice and simple.

Now, let’s say you want to re-use this XAML markup in a number of places across your application. The obvious solution is to package it within a UserControl. However, elsewhere within the application, contact details are stored in different types, with different properties, perhaps the property Web is used in some places, whereas the property URL is used elsewhere. If our UserControl simply contains the XAML markup given above, the binding paths are hard-coded and inflexible. So, how do we package the above markup for re-use whilst maintaining flexibility?

In UI frameworks which do not support databinding, the obvious choice would be to define an interface IContactDetails, which defines a compile-time contract that clients of the control must implement on their business objects. This would allow us to enforce the presence of properties with specific names. However, with WPF / Silverlight, the binding framework removes the need for controls to demand the presence of a certain interface and life is now much better as a result!

So … what we really want here is to allow the clients of our ContactDetails user control to provide bindings for the various framework elements within the user control itself.

Bindings can be constructed in code-behind by invoking the SetBinding method defined on DependencyObject as shown in the example below:

// create a binding with a given source and property-path
Binding binding = new Binding();
binding.Source = myClass;
binding.Path = new PropertyPath("WindowTitle");
// bind the button's Content property
button.SetBinding(Button.ContentProperty, binding);

Therefore, if we were to expose the various Bindings as properties of the control (hence this blog post’s title), we could set the name, email, web elements binding accordingly using the Setbinding method.

The first time I tried this, I added a NameBinding dependency property of type Binding to my UserControl, so that the client XAML looked as follows:

<local:ContactDetailsControl

    NameBinding="{Binding Name}"/>

However, this didn't quite have the desired effect. When the XAML reader processes the above markup, it creates the Binding object that we are after, however it does not set the NameBinding property value to this binding. Instead, it uses it to bind the NameBinding property.

The solution to the problem is to make NameBinding a standard CLR property. This time, when the XAML reader encounters the above markup, it once again creates the Binding object, but since NameBinding is not a dependency property, it does not use it to bind the property, it simply sets the property value.

The code-behind of our ContactDetails user control looks like this:

public partial class ContactDetailsControl : UserControl
{
    public Binding NameBinding { get; set; }
    public Binding CompanyBinding { get; set; }
    public Binding WebBinding { get; set; }
    public Binding EmailBinding { get; set; }
 
    public ContactDetailsControl()
    {
        InitializeComponent();
    }
 
    private void Border_Loaded(object sender, RoutedEventArgs e)
    {
        nameText.SetBinding(TextBlock.TextProperty, NameBinding);
        webText.SetBinding(HyperlinkButton.ContentProperty, WebBinding);
        webText.SetBinding(HyperlinkButton.NavigateUriProperty, WebBinding);
        companyText.SetBinding(TextBlock.TextProperty, CompanyBinding);
        emailText.SetBinding(TextBlock.TextProperty, EmailBinding);
    }
}

The four bindings are exposed as CLR properties. When the UI is Loaded, we use them to bind the various elements within the control. The net result is that we now have a re-useable control whilst maintaining the flexibility provided by the binding framework, as illustrated by its usage below:

<local:ContactDetailsControl

    NameBinding="{Binding FullName}"

    WebBinding="{Binding Website}"

    CompanyBinding="{Binding Company}"

    EmailBinding="{Binding Email}"/>

Now for another example and a bit more fun …

(CodeProject does not support embedded Silverlight content .... see it in action on my blog.)

The above Silverlight application shows a number of particles exhibiting Brownian motion.

This application creates 50 instances of a Particle object within an ObservableCollection:

class Particle : INotifyPropertyChanged
{
    public double XLocation { ... }
    public double YLocation { ... }
    public double Excitement { ... }
}

The particles are rendered by a ScatterControl, which is a UserControl:

<Border BorderBrush="LightGray" BorderThickness="1">
    <!-- ItemsSource property set in code-behind -->
    <local:ScatterControl x:Name="scatterControl"

                      XValueBinding="{Binding Path=XLocation}"

                      YValueBinding="{Binding Path=YLocation}"

                      FillBinding="{Binding Path=Excitement,
                            Converter={StaticResource FillConverter}}"/>
</Border>

This control has an ItemsSource property of type IEnumerable, and exposes three other properties of type Binding.

The markup for this control is given below:

<UserControl ... >
 
    <ItemsControl x:Name="itemsControl">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas>
                    <Ellipse Width="10" Height="10" Fill="Red"

                             Loaded="Ellipse_Loaded"/>
                </Canvas>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>    
</UserControl>

The particles are rendered using an ItemsControl, which creates an instance of an Ellipse for each bound particle and places it within a Canvas (as defined by the ItemsControl.ItemsPanel property). As each ellipse is loaded, we set the bindings in code-behind:

private void Ellipse_Loaded(object sender, RoutedEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    ellipse.SetBinding(Canvas., XValueBinding);
    ellipse.SetBinding(Canvas., YValueBinding);
    ellipse.SetBinding(Ellipse.FillProperty, FillBinding);
}

Again, by exposing Bindings as properties, this user control is just as flexible as if its markup was included in the page directly. This example also illustrates another interesting point, the Bindings that the client provided have been re-used for each Ellipse that was created by the ItemsControl. This is made possible because when SetBinding is invoked on our Ellipse, the Binding is not associated with the Ellipse, rather, it is used to create an instance of a BindingExpression. Binding is used to declare a binding, whereas BindingExpression is the implementation, i.e., it does the heavy-lifting of copying data to-and-from source to target (and vice-versa).

You can download the full source for both examples here.

Regards,
Colin E.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here