Powered by

US - English
NEW! Silverlight 5 is available Learn More

Silverlight Data Binding

By Microsoft Silverlight Team|December 1, 2010|Level 300 : Intermediate

Contents

Overview

Estimated Time: 60 minutes

Data binding is a key technology in Silverlight that allows data to be presented to end users and then processed. In this lab exercise you'll learn different data binding techniques that can be used to perform one way and two way bindings and see how data can be accessed directly without having to go through controls in the user interface.You'll also work with new binding properties available in Silverlight 4 and learn how to write a custom value converter. The application that you'll build in the lab exercises is shown next:

You Will Benefit from this Lab if:

  • You need to bind data to a Silverlight user interface
  • You would like to learn how to visually bind data to controls in Visual Studio 2010

You Will Learn:

  • How to use the INotifyPropertyChanged interface
  • The role of DataContext in Silverlight applications
  • How to bind data to controls using the Visual Studio 2010 designer
  • How to access data using two way bindings
  • How to use the StringFormat property
  • How to create and use a value converter

Business Requirements for the Silverlight application include:

  • Create a new Silverlight project
  • Create data entity classes that implement INotifyPropertyChanged
  • Create a user interface capable of displaying and filtering customers
  • Bind data entity objects to Silverlight controls
  • Use the StringFormat property to control how data is output in the user interface
  • Create a value converter and use it within the user interface to show "gold" customers

Exercise 1: Create Data Entity Classes and Implement INotifyPropertyChanged

Watch Video

  1. Create a new Silverlight Application project named DataBinding in Visual Studio 2010 (the project can be saved anywhere you'd like):
  2. Right-click on the ClientBin folder in the DataBinding.Web project and select Add → New Folder. Name the folder Images.
  3. Copy the blue.png and GoldStar.png files from the lab's Starting Point folder into the Images folder using the Add → Existing Item option in Visual Studio.
  4. Locate the DataBinding project in the Solution Explorer and add the following classes into it by right-clicking on the project and selecting Add → Class:
    Class Name
    Customer
    State
    CustomerContainer
  5. Add the following properties into the State class (add them as standard .NET properties):
    Property Type
    Name String
    Abbreviation String
  6. Open the Customer class and import the System.ComponentModel namespace.
  7. Implement the INotifyPropertyChanged interface on the Customer class (if you need assistance with this step please refer to the Completed folder and view the Customer class in the lab solution):
    Language Code
    C# After adding the interface to the Customer class right-click it and select Implement Interface → Implement Interface from the menu.
    Visual Basic After adding the interface to the Customer class hit [Enter] to implement the interface.

    The INotifyPropertyChanged interface is a key part of the data binding infrastructure available in Silverlight. It contains a single event named PropertyChanged that is used to notify objects when a particular property value changes.

  8. Add the following OnPropertyChanged method into the Customer class to handle raising the PropertyChanged event:

    C#

    protected void OnPropertyChanged(string propName)
    {
       if (PropertyChanged != null)
       {
           PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
    

    Visual Basic

    Protected Overridable Sub OnPropertyChanged(ByVal propName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
    End Sub
    
  9. Add a Name property and associated field into the Customer class that raises the PropertyChanged event in its set block as shown next:

    C#

    string _Name;
    public string Name
    {
        get { return _Name; }
        set
        {
          if (_Name != value)
          {
             _Name = value;
             OnPropertyChanged("Name");
          }
        }
    }
    

    Visual Basic

    Private _Name As String
    Public Property Name() As String
    Get
        Return _Name
    End Get
    Set(ByVal value As String)
       If _Name IsNot value Then
         _Name = value
         OnPropertyChanged("Name")
       End If
     End Set
    End Property
    

    If you're using Visual Basic ensure that IsNot is used for comparing reference types and <> is used for comparing value types when creating the properties that follow.

  10. Using the same pattern shown in the previous step, add the following properties and associated fields into the Customer class. Ensure that OnPropertyChanged is called in each set block and that the property name is passed as a parameter to the method:

    A code snippet file is available in the lab's Starting Point folder that can be used to simplify the process of creating properties that call OnPropertyChanged. Use the Code Snippet Manager to import the appropriate snippet file (C# or VB) if you'd like to use the snippet. Once imported, the shortcut for the snippet is mvvmInpc.

    Property Type
    City String
    State String
    ImageUrl String
    Birthday DateTime
    IsGold Boolean
  11. Open the CustomerContainer class and import the System.ComponentModel, System.Linq and System.Collections.ObjectModel namespaces.
  12. Implement INotifyPropertyChanged on the CustomerContainer class and add an OnPropertyChanged method into it to raise the event.

    Anytime INotifyPropertyChanged must be implemented on multiple classes it's often more efficient to create a base class that implements the interface and provides the OnPropertyChanged method. Classes needing to implement the interface can then derive from the base class which provides better code-use and simplified maintenance. If time permits, create a base class that implements INotifyPropertyChanged and contains the OnPropertyChanged method and then derive Customer and CustomerContainer from it.

  13. Add the following properties into the CustomerContainer class. Ensure that each property's set block makes a call to OnPropertyChanged and passes the appropriate property name as a parameter. Follow the pattern shown earlier with the Name property in the Customer class.

    A code snippet file is available in the Starting Point folder that can be used to simplify the process of creating properties that call OnPropertyChanged. Use the Code Snippet Manager to import the appropriate snippet file (C# or VB) if you'd like to use the snippet. Once imported, the shortcut for the snippet is mvvmInpc.

    Property Type
    States ObservableCollection of State
    Customers ObservableCollection of Customer
    FilteredCustomers ObservableCollection of Customer
    CurrentCustomer Customer
    CurrentState State
  14. Add the following method into the CustomerContainer class to filter Customer objects based upon a State:

    C#

    private void FilterCustomersByState()
    {
         if (CurrentState != null)
         {
            if (CurrentState.Name != "View All")
             {
                var customers = Customers.Where(c => c.State== CurrentState.Name);
                FilteredCustomers = new ObservableCollection<Customer>(customers);
             }      
             else
             {
                 FilteredCustomers = Customers;
             }
          }
    }
    

    Visual Basic

    Private Sub FilterCustomersByState()
        If CurrentState IsNot Nothing Then
     If CurrentState.Name <> "View All" Then
           Dim customers=Me.Customers.Where(Function(c) c.State=CurrentState.Name)                                       
        FilteredCustomers = New ObservableCollection(Of Customer)(customers)
     Else
         FilteredCustomers = Customers
     End If
       End If
    End Sub
    
  15. Within the set block of the CurrentState property add a call to FilterCustomersByState(). It should look like the following code once completed:

    C#

    set
    {
        if (_CurrentState != value)
        {
            _CurrentState = value;
            OnPropertyChanged("CurrentState");
            FilterCustomersByState();
        }
    }
    

    Visual Basic

    Set(ByVal value As State)
        If _CurrentState IsNot value Then
           _CurrentState = value
      OnPropertyChanged("CurrentState")
           FilterCustomersByState()
        End If
    End Set
    
  16. Add the following constant into the CustomerContainer class:

    C#

    const string IMAGE = "Images/blue.png";
    

    Visual Basic

    Const IMAGE As String = "Images/blue.png"
    
  17. Add an empty constructor into CustomerContainer and add the following code into it to create State objects:

    C#

    States = new ObservableCollection<State>
    {
       new State{Name="Arizona",Abbreviation="AZ"},
       new State{Name="California",Abbreviation="CA"},
       new State{Name="Nevada",Abbreviation="NV"},
       new State{Name="View All"}
     };
    

    Visual Basic

    States = New ObservableCollection (Of State)() From {
             New State With {.Name="Arizona", .Abbreviation="AZ"},
             New State With {.Name="California", .Abbreviation="CA"},
             New State With {.Name="Nevada", .Abbreviation="NV"},
             New State With {.Name="View All"}}
    
  18. Add the following code into the constructor to create instances of the Customer class and assign them to the Customers property:

    This code can be cut-and-paste from the CustomerContainer class in the lab solution available in the Completed folder.

    C#

    Customers = new ObservableCollection<Customer>
    {
        new Customer{Name="John Doe",City="Phoenix", State="Arizona",IsGold=true,
                     Birthday=new DateTime(1950,5,10),ImageUrl=IMAGE},
        new Customer{Name="Jane Doe",City="Tempe", State="Arizona",
                     Birthday=new DateTime(1970,4,13),ImageUrl=IMAGE},
        new Customer{Name="Johnny Doe",City="San Diego",State="California",
                     Birthday=new DateTime(1980,8,26),ImageUrl=IMAGE},
        new Customer{Name="James Doe",City="Las Vegas",State="Nevada",IsGold=true, 
                     Birthday=new DateTime(1956,8,30),ImageUrl=IMAGE},
        new Customer{Name="Gina Doe",City="Anaheim",State="California",
                     Birthday=new DateTime(1984,2,28),ImageUrl=IMAGE}
    };
    FilteredCustomers = Customers;
    

    Visual Basic

    Customers = New ObservableCollection(Of Customer) 
               From {
               New Customer With {.Name = "John Doe", .City = "Phoenix", _
                .State = "Arizona", .IsGold = True, _
                .Birthday = New Date(1950, 5, 10), .ImageUrl = IMAGE},
               New Customer With {.Name = "Jane Doe", .City = "Tempe", _
                 .State =  "Arizona", .Birthday = New Date(1970, 4, 13), _
                 .ImageUrl = IMAGE},
               New Customer With {.Name = "Johnny Doe", .City = "San Diego", _
                 .State = "California", .Birthday = New Date(1980, 8, 26), _
                .ImageUrl = IMAGE},
               New Customer With {.Name = "James Doe", .City = "Las Vegas", _
                .State = "Nevada", .IsGold = True, _
                .Birthday = New Date(1956, 8, 30), .ImageUrl = IMAGE},
               New Customer With {.Name = "Gina Doe", .City = "Anaheim", _
                .State = "California", .Birthday = New Date(1984, 2, 28), _
                .ImageUrl = IMAGE}}
      FilteredCustomers = Customers
    

    Data is being added directly into the CustomerContainer class since the focus of this lab is on data binding. In a real-world application data would be retrieved from a Web Service or RESTful service. Additional labs in this series are available that cover retrieving data from distributed sources using WCF and WCF RIA Services.

  19. Build the project and ensure that no compilation errors occur before continuing.

Exercise 2: Creating a User Interface and Binding Data to Controls

  1. Open MainPage.xaml and change the DesignHeight and DesignWidth attributes on the UserControl element to 500 in the XAML
  2. Add Height and Width attributes to the UserControl element and give them a value of 500 to fix the size of the user interface.
  3. Add 6 rows and 2 columns using the designer as shown next:

    Click on the Grid control in the Visual Studio designer and then add rows and columns by clicking within the blue regions to the left and top of the interface.

  4. Create the following customer information screen by dragging the appropriate controls from the ToolBox onto the Visual Studio designer:

    You'll need 6 TextBlock controls, a ComboBox control, a ListBox control, 3 TextBox controls and a Button. Place the controls in the appropriate rows and columns of the Grid.

  5. Give the TextBlock with the text [Output TextBlock] in the designer a name of OutputTextBlock and remove the value from the Text property.
  6. Within the MainPage.xaml.cs constructor create a new instance of the CustomerContainer class and assign it to the LayoutRoot's DataContext property(the grid has a name of LayoutRoot):

    C#

    LayoutRoot.DataContext = new CustomerContainer();
    

    Visual Basic

    LayoutRoot.DataContext = New CustomerContainer()
    
  7. Switch back to MainPage.xaml, highlight the ComboBox control and view its properties in the Properties window
  8. Click the ItemsSource property and try to visually bind it to the CustomerContainer object's States property. Notice that none of the custom properties appear in the data binding window (see Figure 5). This is due to the DataContext being assigned at runtime rather than at design-time. Design-time data is important when you'd like to see data in the designer while building your application.
  9. Remove the line of code you added into the MainPage.xaml.cs constructor. The next steps will demonstrate how to bind objects declaratively to provide a better design-time experience.
  10. Add the following XML namespace prefix definition on the UserControl element in the XAML (use the XAML code editor):

    XAML

    xmlns:data="clr-namespace:DataBinding"
    
  11. Add the following code immediately below the UserControl element (immediately above the existing Grid):
    <UserControl.Resources>
        <data:CustomerContainer x:Key="CustomerContainerObject" />
    </UserControl.Resources>
    

    This will create a new instance of the CustomerContainer object at runtime and assign it to the CustomerContainerObject key.

  12. Locate the Grid control named LayoutRoot in the XAML and add the following DataContext attribute to it:

    XAML

    DataContext="{Binding Source={StaticResource CustomerContainerObject}}"
    

    This code binds the CustomerContainerObject key (which represents an instance of the CustomerContainer object) to the DataContext declaratively. This type of binding will execute in design-mode and at runtime.

  13. Build the solution so that the following data binding steps work properly.
  14. Bind the ComboBox control's ItemsSource property to the CustomerContainer object's States property using the Properties window as shown next:

    The CustomerContainer object assigned to the DataContext is automatically detected as the Source.

  15. Change the ComboBox control's DisplayMemberPath property to a value of Name so that the ComboBox shows the Name property of the State class.
  16. Run the solution and notice that once the Silverlight interface loads the ComboBox displays a list of states:
  17. Bind the ComboBox control's SelectedItem to the CurrentState property:
  18. Bind the ListBox control's ItemsSource to FilteredCustomers and its SelectedItem property to CurrentCustomer using the same technique shown in the previous step.
  19. Once the bindings are in place for the ListBox you'll see the text "DataBinding.Customer" appear multiple times in the designer as shown in the following figure since it doesn't know what Customer object property to bind to at this point:
  20. To fix the issue, add the following ItemTemplate within the ListBox control using the XAML editor (add this XAML between the ListBox control's begin and end tags):

    XAML

    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Width="350">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width=".20*" />
                    <ColumnDefinition Width=".30*" />
                    <ColumnDefinition Width=".20*" />
                    <ColumnDefinition Width=".30*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30" />
                </Grid.RowDefinitions>
                <Image Grid.Column="0" Source="{Binding Path=ImageUrl}" />
                <TextBlock Grid.Column="1" Text="{Binding Path=Name}" 
                  FontSize="12" Foreground="Navy" />
                <TextBlock Grid.Column="2" Text="{Binding Path=City}" 
                  FontSize="12" Foreground="Red" />
                <TextBlock Grid.Column="3" Text="{Binding Path=State}" 
                  FontSize="12" Foreground="Green" />                       
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
    
  21. Bind the Name TextBox control's Text property to CurrentCustomer.Name:
  22. Bind the City and Birthday TextBox controls to the associated properties on CurrentCustomer as shown in the previous step.
  23. Double-click the Button control to create an event handler and add the following code within the event handler to write out a message to the OutputTextBlock control:

    C#

    var customers = LayoutRoot.DataContext as CustomerContainer;
    var name = customers.CurrentCustomer.Name;
    OutputTextBlock.Text = name + " updated!";
    

    Visual Basic

    Dim customers = CType(LayoutRoot.DataContext, CustomerContainer)
    Dim name = customers.CurrentCustomer.Name
    OutputTextBlock.Text = name + " updated!"
    

    This code accesses the LayoutRoot object's DataContext and casts it to a CustomerConatiner type so that you can access the CurrentCustomer object's Name property value.

  24. Run the application and notice that all customers show in the ListBox.
  25. Select a state from the ComboBox to filter the customers. Click on a customer within the ListBox and note that the appropriate values show in the TextBox controls.
  26. The Birthday TextBox currently shows the date and time. To only show the date select the TextBox back in the Visual Studio designer and go to the Text property's data binding window. Click on the Options section and select the {0:d} format from the String Format drop-down as shown next:

    The StringFormat property provides a way to apply a format code to data as it is bound to a control. Standard .NET format codes used to format dates, times, decimals, currencies, and more can be used.

  27. Take a moment to examine the data binding syntax for the Birthday TextBox in the XAML editor and notice that a StringFormat property has been applied to the binding and that the Mode of the TextBox (and the other TextBox controls) is set to TwoWay. TwoWay bindings automatically push data from controls back to the bound property providing a powerful way to interact with data without having to know control names.

    XAML

    Text="{Binding Path=CurrentCustomer.Birthday, Mode=TwoWay, 
           StringFormat=\{0:d\}}"
    
  28. Run the application again and notice that the BirthdayTextBox only shows the date as a result of applying the d format code.
  29. To finish the application you need to show a gold star image if a customer's IsGold property is true. To accomplish this task drag an Image control onto the design surface and position it as shown by the highlighted control below:
  30. Assign the Image control's Source property a value of Images/GoldStar.png.
  31. The Image control should only show if a customer's IsGold property is true. To hide the Image control when IsGold is false you'll need to create a value converter to convert a Boolean value to a Visibility value.
  32. Add a new folder named Converters into the DataBinding project.
  33. Add a new class named BoolToVisibilityConverter into the Converters folder and import the System.Windows.Data namespace.
  34. Implement the IValueConverter interface on the BoolToVisibilityConverter class and add the following code within the Convert() method:

    C#

    return ((bool)value == true) → Visibility.Visible : Visibility.Collapsed;
    

    Visual Basic

    Return If(CBool(value) = True, Visibility.Visible, Visibility.Collapsed)
    
  35. Go to MainPage.xaml and add the following XML namespace prefix on the UserControl element:

    XAML

    xmlns:converters="clr-namespace:DataBinding.Converters"
    
  36. Add the following within the UserControl.Resources element to register the converter and make it available to use within the XAML:

    XAML

    <converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
    
  37. Add the following Visibility attribute on the Image control within the XAML to associate the converter with the IsGold property (ensure the attribute value doesn't wrap):

    XAML

    Visibility="{Binding CurrentCustomer.IsGold,Converter={StaticResource 
      BoolToVisibilityConverter},FallbackValue=Collapsed}"
    

    When the screen first loads CurrentCustomer will be null so the converter will never be called. To account for this the FallbackValue property is used to define that the default Visibility is Collapsed which will hide the image. FallbackValue is used whenever a binding can't be resolved.

  38. Run the application and select the first customer in the ListBox. Notice that the gold star shows since the customer's IsGold property has a value of true. Select other customers and notice that the gold star disappears for some of them.

Summary

In this exercise you created data entity objects that implemented INotifyPropertyChanged and used Silverlight data binding features to bind object properties to controls. You also used the StringFormat property to format data and a value converter to hide a control based upon a property value. Specific requirements satisfied in this lab include:

  • Create a new Silverlight project
  • Create data entity classes that implement INotifyPropertyChanged
  • Create a user interface capable of displaying and filtering customers
  • Bind data entity objects to Silverlight controls
  • Use the StringFormat property to control how data is output in the user interface
  • Create a value converter and use it within the user interface to show "gold" customers
Microsoft Silverlight Team

By Microsoft Silverlight Team, Silverlight is a powerful development platform for creating engaging, interactive user experiences for Web, desktop, and mobile applications when online or offline.

Comments (0)