Windows Phone Data Binding and Templates

.Data Template to Format Data

Data Template, Own Work

Data Binding

"Silverlight data binding connects features of the user interface to the properties of objects.".

  • Data binding is a way to take data from a source and associate with a user interface control. In Silverlight, the source data must be in the form of objects in order to perform the binding.
    • Silverlight is NOT able to connect directly to databases (no ADO.net support). Instead a web service is required to receive data from a database.
    • The Silverlight binding model only works with objects. For example, if the data stream is xml from a web service, it needs to be converted into objects if it is to be used in data binding.
    • Silverlight data binding connects features of the user interface to the properties of objects. The object properties can be either text or graphical. For example, you could bind a numeric value to the height of a rectangle in a bar graph.
    • Data binding uses "Binding Expressions" which are Binding Markup Extensions (they decide at run-time how to populate the value). An example of a simple binding expression is:

         <TextBox Text="{Binding Path=Name}" />

      The "Path=" syntax is option, so the same expression can be written as:

         <TextBox Text="{Binding Name}" />

    • Some of the properties of the Binding Markup Extension include:
      1. Path - Specifies the path to the binding source property. The "Path=" may be omitted if the name of the path is specified immediately after the Binding keyword.
      2. Converter - Specifies the converter object that is called by the binding engine.
      3. ElementName - Specifies a data source by referencing another element. The named element must exist in the same XAML name scope as does the object where the binding is applied.
      4. FallbackValue - Specifies a value to display when the source path cannot be resolved.
      5. Mode - Specifies the binding mode, as one of the following strings: OneTime, OneWay, or TwoWay.
      6. StringFormat - Specifies the String format to use for display.

        Note: Two properties not listed above are (Source and RelativeSource). They can be used to specify a binding source, however this is rarely done. Instead a DataContext is usually set to specify the binding source. Also, the properties (Source, RelativeSource, and ElementName) each specify a binding source. These properties are mutually exclusive, setting more than one of these properties causes an exception when the binding is applied to a property.

    • Data binding always connects two properties together: the source property (data object) with the target property (UI control). There are three required parts to data binding:
      1. Target Object Property - of the UI control, also called the "the Dependency Property" (eg TextBox=Text property; ListBox=ItemSource property).
      2. Source Object Property - which is the data object's property which contains the data. Source object can be any CLR object.
      3. Binding Expression - which creates the run-time communication between the data source and UI control.
    • Typically a Data Context is used to set the data source (instead of using the Source or RelativeSource of the binding markup extension). The DataContext is inheritable. Setting the data context on a parent element, will pass down the data context to all of its children. A child element can override this behavior by setting the Source property on its binding object or by setting its DataContext, which will then apply to all its children.
    • In XAML, DataContext is most typically set to as a binding declaration. Binding can also be created in the C# code by setting the data source with the Source (directly on control) or DataContext (on parent control) properties.
    • Below is a simple code example of data binding using DataContext to identify the data source. The data object uses the traditional way of specifying a backing field for the "Name" property, and the newer Auto-Implemented Property way for specifying the "City" property.


Output from Sample Code Below
.Output from Data Binding Sample Code

Screen Output, Own Work

MainPage.xaml

<UserControl x:Class="DataBindingDemo.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Height="23"
                   HorizontalAlignment="Left"
                   Margin="23,49,0,0"
                   Name="WinnerLine"
                   Text="The winner is: "
                   VerticalAlignment="Top"
                   Width="118"
                   FontSize="14" />
       
        <TextBlock Height="23"
                   HorizontalAlignment="Left"
                   Margin="137,49,0,0"
                   Name="WinnerName"
                   VerticalAlignment="Top"
                   Width="50"
                   Text="{Binding Name}"
                   FontSize="14" />

        <TextBlock Height="23"
                   Margin="193,49,0,0"
                   Name="WinnerCity"
                   VerticalAlignment="Top"
                   Text="{Binding City}"
                   FontSize="14"
                   HorizontalAlignment="Left"
                   Width="149" />

    </Grid>
</UserControl>

MainPage.xaml.cs

using System.Windows.Controls;

namespace DataBindingDemo
{
    public partial class MainPage : UserControl
    {
        // Create an Instance of the Data Object
        Person winner = new Person("Cathy", "Champaign");

        public MainPage()
        {
            InitializeComponent();           
           
            // Set DataContext
            this.DataContext = winner;
        }
    }

    public class Person
    {
        // Traditional Property for Name
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        // Auto-Implemented Property for City
        public string City { get; set; }

        // Constructor
        public Person(string name, string city)
        {
            Name = name;
            City = "of " + city;

        }       
    }
}

.Binding Direction

Computer Clip Art, Public Domain

Binding Modes

"When Two-Way data binding is used, the user can modify and save changes to the data with the user interface.

  • The Mode property specifies the direction of the data flow during the binding. The binding mode can be set as:
    1. One Time - data binding happens between the source and target only once.
    2. One Way - default mode, changes to the source will propagate to the target.
    3. One Way to Source - reverse direction of One Way binding above.
    4. Two Way - bidirectional mode where both the source and target propagate changes when either one changes.
  • When Two-Way data binding is used, the user can modify and save changes to the data by using the graphical interface.

  • Using the previous code example, I added a TextBox bound to "winner.Name" and a button whose click event will display the value of "winner.name" in a message box. Without specifying "Mode=TwoWay" in the binding expression, the binding mode defaults to one way. So the winner's name will display in the TextBox, but changing the value in the TextBox does NOT update the property "winner.name".

            private void viewButton_Click(object sender, RoutedEventArgs e)
            {
                MessageBox.Show(winner.Name);
            }

  • By changing the binding mode to "TwoWay", the value entered in the TextBox now updates the "winner.Name" property, as shown in the following screen capture:
  • .Two-Way Binding

    Two-Way Binding, Own Work

    • However in the preceding example, the "winner.Name" does NOT get reflected back to the TextBlock control. It still indicates on the screen that the winner is Cathy, even after I have changed the "winner.name" value to "Kevin".

    • The INotifyPropertyChanged interface needs to be implemented on the data class in order for the data changes to be reflected back to all bound controls.

    • The following code shows the changes made to implement the INotifyPropertyChanged interface on the data class. Note the "PropertyChangedEventHandler" is implemented to reflect the changed value back to the bound controls:

          public class Person : INotifyPropertyChanged
          {
              // Traditional Property for Name
              private string name;
              public string Name
              {
                  get { return name; }
                  set {
                         if (value != name)
                         {
                            name = value;
                            OnPropertyChanged("Name");
                         }               
                      }
              }

              public event PropertyChangedEventHandler PropertyChanged;

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

    • Now the TextBlock value always displays the current of "winner.name" on the screen as changes to the value are reflected back by implementing the INotifyPropertyChanged interface on the data class:
    .Reflecting the Data Changes Back to Bound Controls

    Screen Output, Own Work


    Binding to Collections

    "The ObservableCollection class has the INotifyCollectionChanged interface built in."

  • When binding to a collection:
    • Use the ItemsSource property on the ItemsControl target (eg ListBox, ComboBox, etc). However, note that the binding is OneWay because the ItemsSource property defaults to OneWay binding.
    • You can enumerate over any collection that implements the IEnumerable interface.
    • However, to set up dynamic bindings so changes in the collection automatically update the UI, the collection must implement the interface.
    • The ObservableCollection class has the INotifyCollectionChanged interface built in.
    • Once the collection is bound, if you wish to sort, filter, or group the data, you need to implement the ICollectionView interface.


  • .Data Templates


    Data Templates

    "Controls which can contain only a single item are called “content controls” (derived from ContentControl base class). Controls which can contain a collection of items are called “items controls”(derived from ItemsControl base class). DataTemplates can be used with either content controls or with items controls."

    • The Data Template class is used to define a visual presentation for a data item ... just as the Control Template class is used to define the visual presentation of a control.
      • An ItemsControl displays a string representation of the data by default (ie uses ToString method). A DataTemplate can be used to format the appearance of each data item from the collection which is bound to the ItemsControl. A data template is defined as XAML code which formats each item in the bound collection.

      • A data template is bound to an ItemsControl by setting the ItemTemplate property of the control to a DataTemplate. See code below for an example.

      • Data templates can be defined inline with the control, or as a resource. Defining the data template as a resource will allow the template to be used on other ItemsControls.

      • The DataTemplate class has a DataType property which allows the template to be applied to all objects of the specified type. This is similar to how the TargetType property works on the Style class.

      • Below is a Silverlight application that uses a data template to render the ListBox data (MainPage.xaml) and global styles (App.xaml). The code for the app is listed below, the control is actually a Silverlight plugin, so it should work in the browser if you try it.

      Listbox Photo Viewer (Select a Pet)
      Get Microsoft Silverlight

    • Besides the use of a Data Template, the code below also contains the following:
      1. Binding an ObservableCollection to a ListBox.
      2. Use of a SelectionChanged event to display an image when the ListBox selection changes.
      3. Use of styles to set the property values on groups of controls.
      4. Inherited styles which are "BasedOn" other styles.
      5. StringFormat to format string values in the data template.

    ListBox Photo Viewer - App.xaml



    <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 x:Class="ListBoxBinding2.App"
                 >
        <Application.Resources>
            <Style x:Key="BaseWhiteStyle"
                   TargetType="TextBlock">
                <Setter Property="Foreground"
                        Value="White" />
            </Style>

            <Style x:Key="TitleStyle"
                   TargetType="TextBlock"
                   BasedOn="{StaticResource BaseWhiteStyle}">
                <Setter Property="HorizontalAlignment"
                        Value="Center" />
                <Setter Property="FontSize"
                        Value="18" />
                <Setter Property="FontWeight"
                        Value="Bold" />
            </Style>
        </Application.Resources>
    </Application>


    ListBox Photo Viewer - MainPage.xaml

    <UserControl x:Class="ListBoxBinding2.MainPage"
        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"
        mc:Ignorable="d"
        d:DesignHeight="400" d:DesignWidth="675">

        <Grid x:Name="LayoutRoot">
            <Grid.RowDefinitions>
                <RowDefinition Height="32" />
                <RowDefinition Height="360" />
            </Grid.RowDefinitions>
            <Grid.Background>
                <ImageBrush ImageSource="Images/board.jpg">               
                </ImageBrush>
            </Grid.Background>
           
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="185" />
                <ColumnDefinition Width="480" />
            </Grid.ColumnDefinitions>
                   
            <StackPanel Margin="5,0,0,0"
                        Grid.Row="1">
                <ListBox x:Name="PetsListBox"
                         Height="360"
                         Width="185"
                         Margin="5, 0, 0, 0"
                         SelectionChanged="PetsListBox_SelectionChanged">
                   <ListBox.ItemTemplate>
                      <DataTemplate>
                         <StackPanel>
                             <Image Source="{Binding ImagePath}" Height="20" Width="20" Margin="0,6,0,3"/>
                             <TextBlock Text="{Binding Name, StringFormat=Name: \{0\} }" Margin="25,0,0,0" />
                             <TextBlock Text="{Binding Bdate, StringFormat=Birthdate: \{0:00-00-0000\} }" Margin="25,0,0,0" />                        
                         </StackPanel>                           
                      </DataTemplate>
                   </ListBox.ItemTemplate>
                </ListBox>
            </StackPanel>

            <Image Margin="5,0,12,0"
                   x:Name="fullImage"
                   Width="480"
                   Height="360"
                   Stretch="UniformToFill"
                   Grid.Column="1"
                   Grid.Row="1" />
            <TextBlock Text="Pets"
                       Style="{StaticResource TitleStyle}"
                       HorizontalAlignment="Center"
                       Grid.Column="1"
                       Margin="6,0,164,0"
                       Grid.RowSpan="2">           
            </TextBlock>
        </Grid>
    </UserControl>


    ListBox Photo Viewer - MainPage.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using ListBoxBinding2.Classes;
    using System.Collections.ObjectModel;
    using System.Windows.Media.Imaging;

    namespace ListBoxBinding2
    {
        public partial class MainPage : UserControl
        {
            public MainPage()
            {
                InitializeComponent();
                this.Loaded += new RoutedEventHandler(MainPage_Loaded);
            }

            void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                ObservableCollection<PetEntity> pets = new ObservableCollection<PetEntity>();
                pets.Add(new PetEntity() { Name = "Holly", Bdate = 05012001, ImagePath = "Images/holly.png" });
                pets.Add(new PetEntity() { Name = "Shadow", Bdate = 11012000, ImagePath = "Images/shadow.png" });
                pets.Add(new PetEntity() { Name = "Tessa", Bdate = 02012007, ImagePath = "Images/tessa.png" });
                pets.Add(new PetEntity() { Name = "Tucker", Bdate = 11012000, ImagePath = "Images/tucker.png" });
                PetsListBox.ItemsSource = pets;
                PetsListBox.SelectedIndex = 0;
            }

            private void PetsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                PetEntity pickedPet = new PetEntity();
                pickedPet  = (PetEntity)this.PetsListBox.SelectedItem;

                if (pickedPet == null)
                    return;

                string imagePath = "L" + pickedPet.ImagePath;

                fullImage.Source = new BitmapImage(new Uri(imagePath, UriKind.Relative));
            }

        }
    }


    Reference Articles