What's New in Silverlight 5 - XAML Changes



Silverlight5.png

Introduction


In this article, we'll have a brief discussion of the new XAML features of Silverlight 5.

Overview

Silverlight 5 has the following improvements in the XAML stack:

  • Implicit Data Templates
  • Ancestor RelativeSource
  • Binding in Styles
  • Markup Extensions
  • XAML Debugging

Let's see them in action one by one.

Implicit Data Templates

Silverlight 5 has received one of many great features of data templates in WPF, Implicit Data Templates. Instead of explicitly attaching the data template to every control, you can set a data type (through the DataType property) that the data template will apply to and then the data template will be applied automatically to any control that's trying to display that data type.

Keep in mind that an implicit data template applies only to controls that...
  • Are trying to display the data type specified
  • Have a templatable content (e.g. collection controls)
  • Defined in the scope of the template

So the implicit data template won't apply to any control that doesn't meet those requirements.

Implementation:

Let's see implicit data templates in action. In the following scenario we have a list box that uses a data template to display some books and it gets populated through the code:

<ListBox x:Name="books">
<ListBox.ItemTemplate>
<
DataTemplate>
<
StackPanel>
<
TextBlock Text="{Binding Title}" FontWeight="Bold" />
<TextBlock Text="{Binding Author}" />
<TextBlock Text="{Binding Price, StringFormat='C'}" />
</StackPanel>
</
DataTemplate>
</
ListBox.ItemTemplate>
</
ListBox>

We could refactor this code by making the data template implicit, and this can be done by moving the data template from the list box to application resources (for instance) and specifying the data type, and then the data template will be applied automatically to each templatable-content control trying to display the data type specified:

<Application.Resources>
<
DataTemplate DataType="loc:Book">
<StackPanel>
<
TextBlock Text="{Binding Title}" FontWeight="Bold" />
<TextBlock Text="{Binding Author}" />
<TextBlock Text="{Binding Price, StringFormat='C'}" />
</StackPanel>
</
DataTemplate>
</
Application.Resources>
 
<ListBox x:Name="books" />

Clear and simple, right?

Now let's do something more interesting to see the real power of implicit data templates. Instead of having a simple data template, we'll have two templates for the two types of books, once for the paper books, and the other for the audio books:

<DataTemplate DataType="loc:PaperBook">
<StackPanel>
<
TextBlock Text="{Binding Title}" FontWeight="Bold" />
<TextBlock Text="{Binding Author}" />
<TextBlock Text="{Binding Price, StringFormat='C'}" />
<TextBlock Text="{Binding Isbn}" />
<TextBlock Text="{Binding Pages}" />
</StackPanel>
</
DataTemplate>

<DataTemplate DataType="loc:AudioBook">
<StackPanel>
<
TextBlock Text="{Binding Title}" FontWeight="Bold" />
<TextBlock Text="{Binding Author}" />
<TextBlock Text="{Binding Price, StringFormat='C'}" />
<TextBlock Text="{Binding Duration}" />
</StackPanel>
</
DataTemplate>

As you see, each data template will be applied to a templatable-content control that tries to display the data type it specifies. In addition, both the data templates would be applied to a collection control that tries to display a collection of both PaperBook and AudioBook.

Ancestor RelativeSource

This is another feature of XAML that Silverlight 5 has gotten from WPF. It allows you to bind to a property in a parent control. This is especially useful in the situation where you are in a data template and wish to bind to a property in a control outside the template higher in the render tree.

Implementation:

You use {Binding.RelativeSource} to specify the source in the tree.

  • Use the AncestorType property to specify the type of the parent control that you wish to bind to.
  • Use AncestorLevel to specify how far is the parent control of the type specified from the current control.

The following TextBlock controls all bind to the same Tag property found in the root StackPanel:

<StackPanel Tag="Hello, World">

<TextBlock Text="{Binding Tag,
RelativeSource={RelativeSource AncestorType=StackPanel}}" />

<TextBlock Text="{Binding Tag,
RelativeSource={RelativeSource AncestorType=StackPanel, AncestorLevel=1}}" />

<StackPanel>

<TextBlock Text="{Binding Tag,
RelativeSource={RelativeSource AncestorType=StackPanel, AncestorLevel=2}}" />

<Grid>

<TextBlock Text="{Binding Tag,
RelativeSource={RelativeSource AncestorType=StackPanel, AncestorLevel=2}}" />

</Grid>

</StackPanel>

</StackPanel>

And here's a more complex example. In the following example we change the color of a control inside an item template based on whether the item is selected or not. For this to work we bind to the IsSelected property of the ListBoxItem control (that represents an item on the list box) and we use a type converter to return a color based on a Boolean value:

<ListBox x:Name="books">
<ListBox.ItemTemplate>
<
DataTemplate>
<
StackPanel>
<
TextBlock Text="{Binding Title}" FontWeight="Bold"
Foreground="{Binding IsSelected, Converter={StaticResource conv},
RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
/>
<TextBlock Text="{Binding Author}" />
<TextBlock Text="{Binding Price, StringFormat='C'}" />
</StackPanel>
</
DataTemplate>
</
ListBox>

Style Binding

This is another feature of XAML that Silverlight 5 has gotten from WPF. It allows you to bind directly in style setters, and that would allow changing styles automatically at runtime by changing source objects.

Implementation:

In the following scenario, we have a class that contains brushes used in the application:

public class MyBrushes
{
    public SolidColorBrush MainBrush { get; set; }

    public MyBrushes()
    {
        MainBrush = new SolidColorBrush(Colors.Red);
    }
}


And we have a style that binds to that class and gets the main brush from there (as you can see, all binding features are available now in style setters):

<loc:MyBrushes x:Key="brushes" />

<Style TargetType="TextBlock">
<Style.Setters>
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter
Property="Foreground"
Value="{Binding MainBrush, Source={StaticResource brushes}}" />
</
Style.Setters>
</Style>

Finally, we can change the style automatically at runtime by changing the source brush using code like this:

MyBrushes brshes = Application.Current.Resources["brushes"] as MyBrushes;
brshes.MainBrush.Color = Colors.Red;


Markup Extensions

Markup extensions allow you to execute code at XAML parsing time, they are like {Binding}, {StaticResource}, {RelativeSource}, etc. The new feature of XAML in Silverlight 5 is the ability to create custom markup extensions. They provide more concise syntax, and they are easier and simpler than attached properties.

Implementation:

An example of a very simple markup extension is an extension that sums two numbers and returns the result to the control (the following TextBlock would have the text '3' at runtime):

<TextBlock Text="{my:SumExtension FirstNumber=1, SecondNumber=2}" />

So how to create such an extension? You can create markup extensions by implementing the generic IMarkupExtenstion interface (in System.Xaml namespace) that accepts a type parameter that specifies the return type from the extension (that must be a reference type). After that, you provide extension parameters as properties, and you implement ProvideValue() to do the work and return the results to the XAML.

The following code defines our summation extension:

public class SumExtension : IMarkupExtension<object>
{
    public int FirstNumber { get; set; }
    public int SecondNumber { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return (FirstNumber + SecondNumber).ToString();
    }
}


And here's a more complex example. In the books scenario, we have defined a markup extension that returns a collection of books based on the book type (paper/audio):

public class BookLocatorExtension : IMarkupExtension<object>
{
    public BookType Type { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Type == BookType.Paper)
            return BookData.PaperBooks;
        else
            return BookData.AudioBooks;
    }
}

public enum BookType { Paper, Audio }

And here's how we can use the extension:

<ListBox ItemsSource="{loc:BookLocator Type=Paper}" />
<ComboBox ItemsSource="{loc:BookLocator Type=Audio}" />

XAML Debugging

The last new XAML feature introduced in Silverlight 5 is the ability to debug data bindings. It is very useful when watching for binding errors and it works by placing a breakpoint inside the binding in XAML and watching the Locals window and other Visual Studio windows for binding details.

Keep in mind that XAML debugging works only in Internet Explorer 9.

Implementation:

In our books scenario, we have the following data form:

<UserControl.Resources>
<
loc:BookData x:Key="data" />
</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<
ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<
ListBox x:Name="booksList"
DataContext="{StaticResource data}"
ItemsSource="{Binding}" />
<StackPanel Grid.Column="1"
DataContext="{Binding SelectedItem, ElementName=booksList}">
<
TextBlock>Title</TextBlock>
<TextBox Text="{Binding Title, Mode=TwoWay}" />
<TextBlock>Author</TextBlock>
<TextBox Text="{Binding Author, Mode=TwoWay}" />
<TextBlock>Price</TextBlock>
<TextBox Text="{Binding Price, StringFormat='C', Mode=TwoWay}" />
</StackPanel>
</
Grid>

Now, put a breakpoint inside the binding in the list box and run the application to see what happens.

Silverlight5.1.png

When you run the application, Visual Studio stops on the breakpoint while it loads the list box with data. Looking at the Locals window we can see binding details:

Silverlight5.2.png

Here we have the details encapsulated in a BindingState, and the current state is UpdatingTarget means that it's a Pull operation where list box gets loaded with data.

We can see that we have 5 items in the list referred by FinalSource. In addition, we don't have any errors or validation errors yet.

Now stop the application, and let's try something else. Try inserting the breakpoint into the Price text box instead and run the application.

Silverlight5.3.png

As you can see, Visual Studio stops on both Push and Pull operations. The Pull operation happens when the text box is loaded with the data, and the Push operation happens when the value in the text box changes and gets updated to the source.

Now try writing some invalid data in the price text box (e.g. some letters) and watch the Locals Window:

Silverlight5.4.png

Now you watch out any error that might happen in binding.

The last thing to mention is that conditions and filters can be used in the breakpoints. As an example, you can use the following condition to tell Visual Studio to stop only on binding errors and not in every operation:

((System.Windows.Data.Debugging.BindingDebugState)BindingState).Error != null

Silverlight5.5.png

Summary

We had a discussion of the XAML stack in Silverlight 5 and we have talked about the 4 new features:

  • Implicit Data Templates:

    Allows you to automatically apply data templates to controls based on the data type the control uses.
     
  • Ancestor RelativeSource:

    Allows you to bind to a property in a parent control.
     
  • Binding in Styles:

    Allows you to use binding directly in style setters.
     
  • Markup Extensions:

    Allows you to execute code at XAML parsing time by using custom markup extensions.
     
  • XAML Debugging:

    Allows you debug data bindings.


Similar Articles