Archives

Archives / 2010 / April
  • Synchronize Data between a Silverlight ListBox and a User Control

    One of the great things about XAML is the powerful data-binding capabilities. If you load up a list box with a collection of objects, you can display detail data about each object without writing any C# or VB.NET code. Take a look at Figure 1 that shows a collection of Product objects in a list box. When you click on a list box you bind the current Product object selected in the list box to a set of controls in a user control with just a very simple Binding statement in XAML.

    Figure 1 
    Figure 1: Synchronizing a ListBox to a User Control is easy with Data Binding

    Product and Products Classes

    To illustrate this data binding feature I am going to just create some local data instead of using a WCF service. The code below shows a Product class that has three properties, namely, ProductId, ProductName and Price. This class also has a constructor that takes 3 parameters and allows us to set the 3 properties in an instance of our Product class.

    C#
    public class Product
    {
      public Product(int productId, string productName, decimal price)
      {
        ProductId = productId;
        ProductName = productName;
        Price = price;
      }

      public int ProductId { get; set; }
      public string ProductName { get; set; }
      public decimal Price { get; set; }
    }

    VB
    Public Class Product
      Public Sub New(ByVal _productId As Integer, _
                     ByVal _productName As String, _
                     ByVal _price As Decimal)
        ProductId = _productId
        ProductName = _productName
        Price = _price
      End Sub

      Private mProductId As Integer
      Private mProductName As String
      Private mPrice As Decimal

      Public Property ProductId() As Integer
        Get
          Return mProductId
        End Get
        Set(ByVal value As Integer)
          mProductId = value
        End Set
      End Property

      Public Property ProductName() As String
        Get
          Return mProductName
        End Get
        Set(ByVal value As String)
          mProductName = value
        End Set
      End Property

      Public Property Price() As Decimal
        Get
          Return mPrice
        End Get
        Set(ByVal value As Decimal)
          mPrice = value
        End Set
      End Property
    End Class

    To fill up a list box you need a collection class of Product objects. The code below creates a generic collection class of Product objects. In the constructor of the Products class I have hard-coded five product objects and added them to the collection. In a real-world application you would get your data through a call to service to fill the list box, but for simplicity and just to illustrate the data binding, I am going to just hard code the data.

    C#
    public class Products : List<Product>
    {
      public Products()
      {
        this.Add(new Product(1, "Microsoft VS.NET 2008", 1000));
        this.Add(new Product(2, "Microsoft VS.NET 2010", 1000));
        this.Add(new Product(3, "Microsoft Silverlight 4", 1000));
        this.Add(new Product(4, "Fundamentals of N-Tier eBook", 20));
        this.Add(new Product(5, "ASP.NET Security eBook", 20));
      }
    }

    VB
    Public Class Products
      Inherits List(Of Product)

      Public Sub New()
        Me.Add(New Product(1, "Microsoft VS.NET 2008", 1000))
        Me.Add(New Product(2, "Microsoft VS.NET 2010", 1000))
        Me.Add(New Product(3, "Microsoft Silverlight 4", 1000))
        Me.Add(New Product(4, "Fundamentals of N-Tier eBook", 20))
        Me.Add(New Product(5, "ASP.NET Security eBook", 20))
      End Sub
    End Class

    The Product Detail User Control

    Below is a user control (named ucProduct) that is used to display the product detail information seen in the bottom portion of Figure 1. This is very basic XAML that just creates a text block and a text box control for each of the three properties in the Product class. Notice the {Binding Path=[PropertyName]} on each of the text box controls. This means that if the DataContext property of this user control is set to an instance of a Product class, then the data in the properties of that Product object will be displayed in each of the text boxes.

    <UserControl x:Class="SL_SyncListBoxAndUserControl_CS.ucProduct"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      HorizontalAlignment="Left"
      VerticalAlignment="Top">
      <Grid Margin="4">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="Auto" />
          <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition MinWidth="120" />
          <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0"
                   Grid.Column="0"
                   Text="Product Id" />
        <TextBox Grid.Row="0"
                 Grid.Column="1"
                 Text="{Binding Path=ProductId}" />
        <TextBlock Grid.Row="1"
                   Grid.Column="0"
                   Text="Product Name" />
        <TextBox Grid.Row="1"
                 Grid.Column="1"
                 Text="{Binding Path=ProductName}" />
        <TextBlock Grid.Row="2"
                   Grid.Column="0"
                   Text="Price" />
        <TextBox Grid.Row="2"
                 Grid.Column="1"
                 Text="{Binding Path=Price}" />
      </Grid>
    </UserControl>

    Synchronize ListBox with User Control

    You are now ready to fill the list box with the collection class of Product objects and then bind the SelectedItem of the list box to the Product detail user control. The XAML below is the complete code for Figure 1.

    <UserControl x:Class="SL_SyncListBoxAndUserControl_CS.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:src="clr-namespace:SL_SyncListBoxAndUserControl_CS"
      VerticalAlignment="Top"
      HorizontalAlignment="Left">
      <UserControl.Resources>
        <src:Products x:Key="productCollection" />
      </UserControl.Resources>
      <Grid x:Name="LayoutRoot"
            Margin="4"
            Background="White">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <ListBox x:Name="lstData"
                 Grid.Row="0"
                 BorderBrush="Black"
                 BorderThickness="1"
                 ItemsSource="{Binding
                      Source={StaticResource productCollection}}"
                 DisplayMemberPath="ProductName" />
        <src:ucProduct x:Name="prodDetail"
                       Grid.Row="1"
                       DataContext="{Binding ElementName=lstData,
                                             Path=SelectedItem}" />
      </Grid>
    </UserControl>

    The first step to making this happen is to reference the Silverlight project (SL_SyncListBoxAndUserControl_CS) where the Product and Products classes are located. I added this namespace and assigned it a namespace prefix of “src” as shown in the line below:

    xmlns:src="clr-namespace:SL_SyncListBoxAndUserControl_CS"

    Next, to use the data from an instance of the Products collection, you create a UserControl.Resources section in the XAML and add a tag that creates an instance of the Products class and assigns it a key of “productCollection”.

      <UserControl.Resources>
        <src:Products x:Key="productCollection" />
      </UserControl.Resources>

    Next, you bind the list box to this productCollection object using the ItemsSource property. You bind the ItemsSource of the list box to the static resource named productCollection. You can then set the DisplayMemberPath attribute of the list box to any property of the Product class that you want. In the XAML below I used the ProductName property.

    <ListBox x:Name="lstData"
             ItemsSource="{Binding
                Source={StaticResource productCollection}}"
             DisplayMemberPath="ProductName" />

    You now need to create an instance of the ucProduct user contol below the list box. You do this by once again referencing the “src” namespace and typing in the name of the user control. You then set the DataContext property on this user control to a binding. The binding uses the ElementName attribute to bind to the list box name, in this case “lstData”. The Path of the data is SelectedItem. These two attributes together tell Silverlight to bind the DataContext to the selected item of the list box. That selected item is a Product object. So, once this is bound, the bindings on each text box in the user control are updated and display the current product information.

    <src:ucProduct x:Name="prodDetail"
                   DataContext="{Binding ElementName=lstData,
                                         Path=SelectedItem}" />

    Summary

    Once you understand the basics of data binding in XAML, you eliminate a lot code that is otherwise needed to move data into controls and out of controls back into an object. Connecting two controls together is easy by just binding using the ElementName and Path properties of the Binding markup extension. Another good tip out of this blog is use user controls and set the DataContext of the user control to have all of the data on the user control update through the bindings.

    NOTE: You can download the complete sample code (in both VB and C#) at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "SL – Synchronize List Box Data with User Control" from the drop-down.

    Good Luck with your Coding,
    Paul Sheriff

    ** SPECIAL OFFER FOR MY BLOG READERS **
    Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".

    Read more...