Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Exposing Bindings as Properties of a Control

, 19 Jun 2009
Rate this:
Please Sign up or sign in to vote.
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">
    <!--<span class="code-comment"> ItemsSource property set in code-behind --></span>
    <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: bindingproperties.zip.

Regards, Colin E.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Colin Eberhardt
Architect Scott Logic
United Kingdom United Kingdom
I am CTO at ShinobiControls, a team of iOS developers who are carefully crafting iOS charts, grids and controls for making your applications awesome.
 
I am a Technical Architect for Visiblox which have developed the world's fastest WPF / Silverlight and WP7 charts.
 
I am also a Technical Evangelist at Scott Logic, a provider of bespoke financial software and consultancy for the retail and investment banking, stockbroking, asset management and hedge fund communities.
 
Visit my blog - Colin Eberhardt's Adventures in .NET.
 
Follow me on Twitter - @ColinEberhardt
 
-
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralNicely done Colin PinmvpPete O'Hanlon19-Jun-09 11:11 
GeneralRe: Nicely done Colin PinmemberColin Eberhardt19-Jun-09 21:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 20 Jun 2009
Article Copyright 2009 by Colin Eberhardt
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid