Follow Me on Instagram Subscribe via RSS Feed

2nd Day of Silverlight: DataContextChanged

December 12, 2011

This is a continuation of some of my favorite features in the new Silverlight 5 release.  Since it’s right before Christmas, I decided to do a 12 Days of Silverlight, so feel free to try and put the post to song if you would like (just make sure you YouTube it).  If you haven’t downloaded the new release, you can find it on the Silverlight download page. So….

On the second day of Silverlight, the team delivered to me… a DataContextChanged event.

Data binding in Silverlight is awesome.  There is just no other way to describe it.  That is with one exception, the lack of a DataContextChanged event.  Why is this event so important?  Well that is a good question and one we will look at as well as how to use the new event.

Silverlight does a great job on notifying your UI (or code) when a property within an object that supports INotifyPropertyChanged is changed.  That is what drives your data binding experience and allows all of the cool features to happen.  But what happens if your code needs to now when the DataContext itself has changed?  Maybe it needs to fire off some calculations, clean up some old code, etc.  In SL4 there was no way out-of-the-box to detect this type of change.  There are work-arounds out there, but the Silverlight team gave us a built-in solution in SL5.

The DataContextChanged event was added to the FrameworkElement object and you can find the official documentation on the MSDN site.  The event is fairly straight forward.  Let’s take a look at it in action.

Let’s begin with creating a new Silverlight 5 application.  Before we start building our UI, let’s create a data class for this application.  Add a new Person class to your project and add FirstName and LastName as a couple of properties.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

In our MainPage we will add a few elements.  We will add a ListBox on the left hand side and a custom UserControl on the right.  Let’s add a new UserControl to the project called Customer.xaml.  In the XAML, let’s add a few fields like such:

<Grid x:Name="LayoutRoot" 
        Background="Transparent" 
        Width="Auto">
    <Grid.ColumnDefinitions>    
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
        
    <TextBlock Text="First Name :"/>
    <TextBlock Text="Last Name :"
                Grid.Row="1"/>
        
    <TextBox Text="{Binding FirstName}"
                Width="100"
                Grid.Column="1"/>
    <TextBox Text="{Binding LastName}"
                Width="100"
                Grid.Column="1"
                Grid.Row="1"/>
</Grid>

The binding for the two text boxes are set to the properties of our new Person class.  Bet you can’t guess what type of object our DataContext is going to be.  Now we can create our XAML for the MainPage and make it look something like this:

<Grid x:Name="LayoutRoot" Background="LightGray"
        Width="500" Height="300">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
        
    <ListBox x:Name="lstPerson"
                Width="150"
                Margin="10,10,10,10">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding FirstName}"/>
                    <TextBlock Text="{Binding LastName}"
                                Margin="5,0,0,0"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <local:Customer 
      DataContext="{Binding ElementName=lstPerson, Path=SelectedItem}"
      Grid.Column="1"
      Height="Auto"
      Margin="10,10,10,0"/>
</Grid>

As you can see we set the DataContext of Customer to the SelectedItem of our ListBox.  Now we need to populate our ListBox with some data.  We will do that in the code behind of our MainPage:

public MainPage()
{
    InitializeComponent();

    lstPerson.ItemsSource = 
        new List<Person>()
            {
                new Person()
                    {
                        FirstName = "Homer",
                        LastName = "Simpson"
                    },
                new Person()
                    {
                        FirstName = "Marge",
                        LastName = "Simpson"
                    },
                new Person()
                    {
                        FirstName = "Bart",
                        LastName = "Simpson"
                    }
            };
}

image

If you run the application you will see we have a typical master/detail type of screen here.  Now let’s add a new ListBox the below our Customer instance.  In this ListBox we want to visualize a log of the user’s activities.  In particular, we want to know when the user switches to a new Person object.  That way we can track how long they spend with each customer.  So our new XAML looks like this:

<Grid x:Name="LayoutRoot" Background="LightGray"
        Width="500" Height="300">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
        
    <ListBox x:Name="lstPerson"
                Width="150"
                Margin="10,10,10,10">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding FirstName}"/>
                    <TextBlock Text="{Binding LastName}"
                                Margin="5,0,0,0"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <Grid Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <local:Customer 
        x:Name="customer"
      DataContext="{Binding ElementName=lstPerson, Path=SelectedItem}"
        Height="Auto"
        Width="Auto"
        Margin="10,10,10,0"/>
        <ListBox x:Name="lstLog"
                 Margin="10,10,10,10"
                    Grid.Row="1"/>
    </Grid>
</Grid>

We will store our messages and set our binding for the new ListBox in code behind (add to MainPage())

lstLog.ItemsSource = new ObservableCollection<string>();

Now that we have a mechanism to visualize our log, let’s use the new DataContextChanged property to implement the logging.  We are going to track when the DataContext is changed for our Customer object.  Each time the user selects a new Person out of our list we will generate a log entry.

So in our MainPage constructor we will add an event handler to the DataContextChanged event on Customer :

customer.DataContextChanged 
  += new DependencyPropertyChangedEventHandler
          (customer_DataContextChanged);

Then our handler will add a new log entry every time the event is fired:

void customer_DataContextChanged(object sender, 
             DependencyPropertyChangedEventArgs e)
{
    var log = lstLog.ItemsSource as ObservableCollection<string>;
    var per = e.OldValue == null ? null : (e.OldValue as Person);

    log.Add(String.Format("Switch from {0} to {1}",
        e.OldValue == null ? "[null]" 
                   : (e.OldValue as Person).FirstName,
        e.NewValue == null ? "[null]" 
                   : (e.NewValue as Person).FirstName
                ));
}

image

An important thing to note in the code, is that the DependencyPropertyChangedEventArgs parameter in the event has the OldValue and the NewValue of the DataContext.  We take advantage of this by noting which Person we are switching from and to.  This is obviously a simplistic example.  However, this is an important addition and comes in handy quite often.

You can download the source code here : DaysOfSilverlight_DataContextChanged.zip

Make sure you stop by tomorrow for the next post in the 12 Days of Silverlight series.

Series Navigation<< 1st Day of Silverlight: Native Windows3rd Day of Silverlight: Databinding Debugging >>

Leave a Reply