Home > Silverlight, Windows Phone 7 > Silverlight for Windows Phone 7: How to bind UserControl to itself

Silverlight for Windows Phone 7: How to bind UserControl to itself

     Very often I want my UserControl to be bound to itself. It allows to have more control under data presentation and formatting. To bind the UserControl to itself declaratively we need to add DataContext initialization to the xaml-file:

<UserControl x:Class="SomeNamespace.ProductDispalyControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="480" d:DesignWidth="480"
    DataContext = "{Binding RelativeSource={RelativeSource Self}}">

    <StackPanel x:Name="LayoutRoot">
        <TextBlock Text="{Binding ProductName}" />
        <TextBlock Text="{Binding ProductPrice}" />
    </StackPanel>

</UserControl>

This works great, if we provide the UserControl with an ability to receive somehow data, which should be rendered. Let’s take a look at example:

/// <summary>
/// Represents data to render
/// </summary>
public class Product
{
    public string Name  { get; set; }
    public double Price { get; set; }
}

/// <summary>
/// Renders information about product
/// </summary>
public partial class ProductDispalyControl : UserControl
{
    protected Product _product = null;

    public Product CurrentProduct
    {
        get { return _product; }
        set { _product = value; }
    }

    public string ProductName
    {
        get { return string.Format("<{0}>", CurrentProduct.Name); }
    }

    public string ProductPrice
    {
        get { return string.Format("${0}", CurrentProduct.Price); }
    }

    public ProductDispalyControl(Product product)
    {
        _product = product;
        InitializeComponent();
    }
    public ProductDispalyControl()
    {
        InitializeComponent();
    }
}

Product (data to render) can be passed into the UserControl through the constructor, if the control is created dynamically, for example,

public ParentControl()
    {
        InitializeComponent();

        ProductDispalyControl productCtrl = new ProductDispalyControl(product); // passing data through the constructor
        placeHolder.Children.Add(productCtrl);
    }

or can be set through the property CurrentProduct inside the constructor of parent-control right after the UserControl is created, if the control is declared in xaml-file of parent control

public ParentControl()
{
    InitializeComponent();
    productCtrl.CurrentProduct = Product; // passing data through the property
}

If you are devoted to declarative way, it’s not convenient every time to add some additional initialization code in code-behind. But in the same time we can’t bind DataContext property of our UserControl to the data in xaml-file, because we need the UserControl to be bound to itself. Of course, you still can try binding CurrentProduct to the data. For this you need to transform CurrentProduct property to DependencyProperty to support binding. However, in some cases it doesn’t help, for example,

<ItemsControl ItemsSource="{Binding Path=ProductCollection}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
      	    <someNamespacePrefix:ProductDispalyControl />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

In this case every Product from ProductCollection will be passed to the certain ProductDispalyControl through DataContext property by default, however it conflicts with our main idea to have the UserControl bound to itself. To overcome this problem I’ve developed a simple class

namespace MyControls
{
    public class BoundToSelfControl : UserControl
    {
        protected object _source = null;

        public BoundToSelfControl() : base()
        {
            Loaded += new RoutedEventHandler(BoundToSelfControl_Loaded);
        }

        public virtual void OnLoad()
        {
            // NOTE: When we navigate from the current PhoneApplicationPage,
            // the state of the page is persisted (including the states of controls the page contains).
            // It's so called Tombstoning. If we get back to the persisted page,
            // BoundToSelfControl's Loaded-handler is called again (as opposed to the control constructor).
            // When it happens, DataContext already refers to the control itself, and there is no need to do anything else.
            // Of course, it's topical for windows phone 7 only
            if (_source == null)
            {
                _source = DataContext; // save data passed through DataContext in the field _source
                DataContext = this;    // bind to itself
            }
        }

        void BoundToSelfControl_Loaded(object sender, RoutedEventArgs e)
        {
            OnLoad();
        }
    }
}

In a Loaded-handler BoundToSelfControl gets Product passed through DataContext and saves it in the field _source, then binds itself to itself. Now we need to derive ProductDispalyControl from BoundToSelfControl:

public partial class ProductDispalyControl : BoundToSelfControl
{
    protected Product _product = null;

    public Product CurrentProduct
    {
        get
        {
            if (_product == null)
                _product = (Product)_source;
            return _product;
        }
    }

    public string ProductName
    {
        get { return string.Format("<{0}>", CurrentProduct.Name); }
    }

    public string ProductPrice
    {
        get { return string.Format("${0}", CurrentProduct.Price); }
    }

    public ProductDispalyControl()
    {
        InitializeComponent();
    }
}

The xaml-file of ProductDispalyControl will be the following:

<myControls:BoundToSelfControl x:Class="SomeNamespace.ProductDispalyControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:myControls="clr-namespace:MyControls"
    d:DesignHeight="480" d:DesignWidth="480"
    DataContext = "{Binding RelativeSource={RelativeSource Self}}">

    <StackPanel x:Name="LayoutRoot">
        <TextBlock Text="{Binding ProductName}" />
        <TextBlock Text="{Binding ProductPrice}" />
    </StackPanel>

</myControls:BoundToSelfControl>

Do not forget to add xmlns:myControls=”clr-namespace:MyControls” to declaration. After this an auto generated part of ProductDispalyControl will be successfully created as well.

I use BoundToSelfControl every time when I create a new UserControl. I hope it will be useful for someone else.

ps I utilize BoundToSelfControl in an Silverlight Windows Phone 7 application, but I think it will work with usual Silverlight with no problems.

See Continuation of the topic

Related posts:
 
  1. June 15th, 2011 at 22:41 | #1

    IJWTS wow! Why can’t I think of thigns like that?

  2. August 7th, 2011 at 07:44 | #2

    In my opinion this is not a good solution. First of all you might end up binding loops (not sure how SL handles those). Secondly you will most likely have difficult time using expression blend with design time data. Also you are basically duplicating Product properties in the ProductDisplay control. And if you decide to have some business logic (validation, saving of the data) then what is the layer where you put it?

    If you use MVVM (pretty standard design pattern with WPF and Silverlight) you would have something like ProductViewModel and you would instance of that class into the datacontext. Then bind to it and use e.g. value converters or data templates to change how the data looks (I’m not sure if WP7 SL support string formatting conversions out of the box). Expression blend with design time data would work. Your data would be separated from the the view.

    I have hard time seeing benefits for this kind of “bind to itself” functionality over standard MVVM solution.

  3. Administrator
    August 7th, 2011 at 23:42 | #3

    @Toni
    Hello!
    I think you are partially right. I’ve never used Expression Blend and know nothing how user control bound to itself (hereafter, UCBTI) will behave in Expression Blend. Also I use this approach only for displaying data, without data feedback (no validation/saving), however I believe such functionality can be implemented, probably, it’s not trivial, but possible. I just didn’t need it.
    I think the approach doesn’t contradict MVVM, we set DataContext of the UCBTI to ProductViewModel (or its property) as usual, but UCBTI operates bound data in its own way. The main difference is that we don’t need to produce a huge amount of Converters. All formating is encapsulated inside UCBTI.
    Thanks!

  1. No trackbacks yet.