Follow Me on Instagram Subscribe via RSS Feed

6th Day of Silverlight : Binding in Style Setters

December 18, 2011

So here we are rounding out the half way mark of our 12 Days of Silverlight series.  I’ve had a lot of fun writing the series so far and have had a lot of good feedback on it as well.  Looking at the date, it looks like I should get the series wrapped up right before Christmas.  Good timing… Smile

On the sixth day of Silverlight, the team delivered to me… binding in style setters.

This is an exciting little feature and one that I rolled out two implementations of the day SL5 released.  So why is it so exciting you ask?  Well this just happens to be the right blog to ask that particular question.

Styles in Silverlight 4

To get a better idea of the benefits of this new feature, let’s take a look at how styles worked in SL4.  Let’s take a very basic scenario and create the following UI in a new Silverlight application .

<UserControl.Resources>
    <Style TargetType="TextBox">
        <Setter Property="Background"
                Value="Red"/>
        <Setter Property="Foreground"
                Value="White"/>
    </Style>
    <Style TargetType="TextBlock">
        <Setter Property="Foreground"
                Value="Red"/>
        <Setter Property="FontSize"
                Value="20"/>
    </Style>
    <Style x:Key="Accent" TargetType="Grid">
        <Setter Property="Background"
                Value="Red"/>
    </Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <Grid Style="{StaticResource Accent}"/>
    <Grid Grid.Column="1">
        <StackPanel Orientation="Vertical"
                HorizontalAlignment="Center"
                VerticalAlignment="Center">
            <TextBlock Text="My TextBlock"/>
            <TextBox Text="My TextBox"
                Width="150"
                Height="25"
                Margin="0,20,0,0"/>
        </StackPanel>
    </Grid>
</Grid>

It’s not the world’s most complicated UI, but it will work for our case.  If you run the application, the results should look like this:

image

In a typical application scenario, your styles would all be pulled out into one or a more resource dictionaries for re-usability.  You might notice that our example is starting to look like a skin.  And in that skin is a great example of what the problem is.  Let’s say we want the user to have the option of dynamically changing the skin colors.  In previous versions of Silverlight, you could only accomplish this by actually reloading the UI.

When a UI element is created it gets a static copy of the style to be applied to it.  If you wanted to change a property in the style, as the red colors to blue, the updated style would not be reapplied to the element.  That made things, like user defined skins, quite troublesome to write.

Data Binding in Style Setters

In SL5, we have a new way of handling this with data binding in style setters.  The first thing to mention is that the data binding is no different in syntax or concept than any other binding in SL.  So it works exactly how you expect it to work.  The big difference is that by applying binding to the style setters, you make it possible to dynamically update your UI.  The best way to really see the power in this is to simply try it.  I mean what else is a blog post for any way?

So let’s set this up with a nice little skin class.  Our skin class is going to have a grand total of one property in it : AccentBrush.  This property is returning a brush instead of a color to keep the bindings simple.

public class MyStyle : INotifyPropertyChanged
{
    private SolidColorBrush _accentBrush;

    public SolidColorBrush AccentBrush
    {
        get { return _accentBrush; }
        set 
        {
            _accentBrush = value;
            NotifyPropertyChanged("AccentBrush");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
            PropertyChanged(this, 
              new PropertyChangedEventArgs(propertyName));
    }
}

Now let’s add a ComboBox to our UI to allow the user to switch the colors in our application.  To do this we will modify our XAML to look like this:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
        <StackPanel Orientation="Horizontal"
                Margin="0,20,0,20"
                HorizontalAlignment="Center"
                Grid.ColumnSpan="2"
                VerticalAlignment="Center">
            <TextBlock Text="Select Color : "
                       Margin="0,0,20,0"/>
            <ComboBox x:Name="cbAccent"
                      Width="150"
                      Height="25"
                      SelectionChanged="cbAccent_SelectionChanged"
            >
                <ComboBox.Items>
                    <ComboBoxItem IsSelected="True">
                        <System:String>Red</System:String>
                    </ComboBoxItem>
                    <ComboBoxItem>
                        <System:String>Green</System:String>
                    </ComboBoxItem>
                </ComboBox.Items>
            </ComboBox>
        </StackPanel>
            <Grid Style="{StaticResource Accent}"
          Grid.Row="1"/>
    <Grid Grid.Column="1" Grid.Row="1">
        <StackPanel Orientation="Vertical"
                HorizontalAlignment="Center"
                VerticalAlignment="Center">
            <TextBlock Text="My TextBlock"/>
            <TextBox Text="My TextBox"
                Width="150"
                Height="25"
                Margin="0,20,0,0"/>
        </StackPanel>
    </Grid>
</Grid>

Before we can code that SelectionChanged event handler, let’s make a few changes to our styles.   You might think the easiest way to test this is to set the DataContext of the MainPage to our skin class, basically using the skin as a view model.  In fact, that was going to be my first approach.  However, I thought it would be better to show you in a more real world approach.  So why not use it as a DataContext in a real app?   The answer to that is simple, it’s not data.  Save the DataContext for actual data and real view models so that you keep a clean separation in your code from UI and data/business logic.

So we will add a copy of our skin class to the resources section.  Then we will also make some changes to the rest of our styles to bind to that new skin.

<local:MyStyle x:Key="myStyle"/>
<Style TargetType="TextBox">
    <Setter Property="Background"
            Value="{Binding AccentBrush,
        Source={StaticResource myStyle}}"/>
    <Setter Property="Foreground"
            Value="White"/>
</Style>
<Style TargetType="TextBlock">
    <Setter Property="Foreground"
            Value="{Binding AccentBrush,
        Source={StaticResource myStyle}}"/>
    <Setter Property="FontSize"
            Value="20"/>
</Style>
<Style x:Key="Accent" TargetType="Grid">
    <Setter Property="Background"
            Value="{Binding AccentBrush,
        Source={StaticResource myStyle}}"/>
</Style>

We can see now that any place were we setting a red SolidColorBrush before is now binding to the AccentBrush property of our skin. With our skin in the resources, we can now attack our code behind.  Let’s look at the cbAccent_SelectionChanged event handler in MainPage.xaml.cs.  Our code here is pretty simple.  Depending on which color is selected from the ComboBox, we set the AccentBrush property to a SolidColorBrush of that color.  So our event handler should look something like this:

private void cbAccent_SelectionChanged(object sender, 
                 SelectionChangedEventArgs e)
{
    var myStyle = Resources["myStyle"] as MyStyle;

   var sel = (e.AddedItems[0] as ComboBoxItem).Content.ToString();

    switch(sel)
    {
        case "Red":
          myStyle.AccentBrush = new SolidColorBrush(Colors.Red);
          break;
        case "Green":
          myStyle.AccentBrush = new SolidColorBrush(Colors.Green);
          break;
    }
}

So now if we run our application, it should start off looking like this:

image

If we change the ComboBox to value from red to green our UI should automatically update for us.

image

Impact

I wanted to wrap this post up and talk about the potential impact that this could have on your applications.  At first glance, yes it makes a great update to the skinning scenario in Silverlight.  As our simplistic example demonstrated, it can allow you to easily handle user requested UI changes without some of the previous hurdles.

However, there is a more important concept here I want you to start thinking about.  When you are trying to write and maintain reusable code, managing your styles always seems to be troublesome.  Your start off with moving everything to styles. Then you pull your styles out to resource dictionaries.  Soon you are trying to maintain a series of resource dictionaries with different and overlapping naming conventions, etc.  It gets to be a real mess real quick.

What if you added one more abstract to the puzzle?  What if you defined a class or series of classes that defined your root elements, like accent colors?  Then you wouldn’t have to worry about changing accent colors in each of your dictionaries that might be using different names, etc.  By doing this it opens a lot of doors for you.  Now you can save your UI properties in a database or a serialized text file or any other mechanism.  You can load a UI based on user, location, current time, or any other aspect you can think of.  The possibilities begin to pile up rather quickly.

It’s just some food for thought.  We will most likely dig into this some more down the road.

Well that wraps up this episode of 12 Days of Silverlight.  You can download the source for this project here : DaysOfSilverlight_StyleBinding.zip.

I hope you are finding some good nuggets of info along the way.  See you soon…

Series Navigation<< 5th Day of Silverlight: Text Improvements7th Day of Silverlight : Ancestor Relative Source Binding >>

Comments (1)

Trackback URL | Comments RSS Feed

  1. bogdan says:

    Great tutorial,thank you!
    I really like your examples,keep them simple.

Leave a Reply