Saturday, July 04, 2009   Search 
Links

 

Disciple
 Ask Dr. WPF Minimize
Author: Dr. WPF Created: 8/13/2007 5:15 PM
Do you have questions about Windows Presentation Foundation that might have broad appeal? Ask Dr. WPF!

Sorry about the down time!
By Dr. WPF on 6/29/2009 7:52 PM

Warning:  There is nothing technical in this post and definitely nothing about WPF.  It is just an explanation and request for web hosting recommendations.

If you tried to reach my blog today and received an error, I apologize.  Evidently, my hosting service (normally I'd include a link to DailyRazor.com, but I don't want to send them any business!), has decided that my site is too popular.  This is the third time they have shut down my domain because it had more than 200 concurrent connections.

Okay... I know some of you are wondering how I possibly get that much traffic, as delinquent as I am lately in posting new stuff.  I apologize for that too! 

Anyway, for whatever reason (I like to think it's because there's a decent amount of good WPF information here), this site gets quite a few visitors each day.  Every once in a while, too many of these connections occur at once.  My host told me today that if it happens again, they are shutting my account down for good.  I am VERY disappointed about this, and needless to say, I will be seeking a new host as soon as my schedule allows.

In the meantime, if anyone has a recommendation for a company that offers Windows-based hosting, please let me know.  I don't make any money off of this site and I prefer not to host ads, so I am hoping for something affordable... preferably someone who offers phone support!

Thanks in advance for any recommendations!  And if you just want to rant about DailyRazor, feel free to post a comment below with the title "DailyRazor Sucks!"  But please don't everyone post a comment at once... that'll get my site turned off for sure! 

Comments (9)

ItemsControl: 'L' is for Lookless (a.k.a., "Styles, Templates, and Triggers... Oh My!")
By Dr. WPF on 5/12/2009 1:04 PM

As we continue our series on ItemsControl, it probably makes sense to take a small detour and look at WPF controls in general. In this post, we will examine how WPF controls get their visual representation through styles and templates. Then we will look at how these techniques specifically apply to several ItemsControl classes.

This article is fairly long and covers a lot of really important information, including the following:

It is definitely worth taking your time to understand these concepts. Feel free to break the article up into more manageable pieces if you are time-constrained.

 I would also encourage you to keep kaxaml up and running as you learn about styles and templates. I think you'll find that it makes the content more meaningful when you directly interact with the samples. As such, this article contains a lot of kaxamples, which is my term for kaxaml-ready samples.  When you see the kaxaml symbol, you know you can cut and paste the included markup snippet directly into kaxaml to interact with it live. Then you are free to tweak the markup and make a few cool new styles and templates of your own!

The Lookless Control Model

When developers first start learning WPF, they are often very surprised to learn that a WPF control does not include a hardcoded visual representation. Rather, the default visual representation is specified in XAML in a completely separate theme assembly from the class that implements the control's logic. This separation of code-based logic from visual representation (the control's look and feel) is what makes the WPF control model so powerful. We refer to this approach as a "lookless control model".

Why, you may be asking, is such a model better than the traditional approach of defining the visuals as part of the control? I'll give you two big reasons: design and reusability.

If you've studied WPF or Silverlight much, you have certainly heard about the new developer/designer workflow that these technologies enable. Namely, developers and designers can work side by side to simultaneously create powerful and visually stunning applications, with each person doing what they do best. A developer can implement an application's logic at the same time that a designer creates the application's look and feel. To a great degree, it is the lookless control model that enables this new workflow.

It turns out that left-brained developers don't always come up with the most eye-pleasing visual designs, as can be seen by looking at the "battleship gray" user interfaces coming out of the Windows Forms world for the past decade. Now contrast these apps with the vibrant Flash-based web applications created in the same timeframe. The big difference is that most of the cool Flash applications were created by right-brained designers who take aesthetics very seriously. (Yes, I'm stereotyping with the whole left-brained/right-brained thing... some stereotypes are just true.)

By introducing a lookless control model where the visual representation of the control is specified apart from the control's code, we are able to put the visual design into the hands of a qualified UX designer, where it belongs.

The second big reason for separating a control's visuals from its code implementation is reusability. In the past, if you wanted to have two buttons with different visual appearances, you had to create two separate button classes with the correct rendering code baked into each class. Both classes contained the same basic button logic. They both provided a "click" event in response to the appropriate mouse or keyboard user interactions. The only difference between the button classes was in the rendering logic.

By moving the visual representation into XAML and out of the control class, we allow a single Button class to be reused wherever we need to support a Click interaction. The button can have whatever visual representation the designer wants to create. The developer no longer needs to be involved in the process of redefining a button's visual representation. 

An example of this reusability can be seen in the following image (borrowed from my Observable Dictionary Sample) depicting a ListView in which each row contains a style name and a button. The only difference between each button is the "Style" used to define the button's look and feel.

Introduction to Styles

We have already seen an example of how one might style and template an item container via the ItemContainerStyle property (see 'I' is for Item Container). Now it's time to actually dig into the nitty gritty details of control styling and templating. If you are already familiar with styling and templating, you can skip ahead to the section entitled Templating an Items Control. (Of course, you might consider continuing on through the following sections as a refresher course and it's possible you might even learn something new. )

A WPF style consists of a collection of property values that are applied to a framework element either explicitly (by setting its Style property) or implicitly (based on a resource lookup using the element's default style key). These property values are applied using Setter objects. Thus, a style can be thought of as a collection of Setter objects. Here is a very simple example:

<Style TargetType="{x:Type Rectangle}">
  <Setter Property="Width" Value="50" />
  <Setter Property="Height" Value="50" />
</Style>

In this case, a style is created for a Rectangle. There are two setters in the style. These set the Width and Height properties of the Rectangle to 50.

So how do you actually use the style? Typically, you will give it a resource key and add it to a resource dictionary somewhere in your element hierarchy. Then you can use the resource key to apply the style to specific elements in the subtree. Here is a kaxample:

Kaxample: Try pasting the following markup snippet into kaxaml. 

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid.Resources>
    <Style x:Key="MyRectangleStyle"
        TargetType="{x:Type Rectangle}">
      <Setter Property="Width" Value="50" />
      <Setter Property="Height" Value="50" />
    </Style>
  </Grid.Resources>
  <StackPanel Orientation="Horizontal">
    <Rectangle Style="{StaticResource MyRectangleStyle}" Fill="Red" />
    <Rectangle Style="{StaticResource MyRectangleStyle}" Fill="Green" />
    <Rectangle Style="{StaticResource MyRectangleStyle}" Fill="Blue" />
    <Rectangle Style="{StaticResource MyRectangleStyle}" Fill="Black" />
  </StackPanel>
</Grid>

In the above example, the style is explicitly applied to each Rectangle because we have set the Style property on the Rectangle elements. If we want the style to be applied implicitly to every Rectangle in the subtree, we can simply remove the x:Key attribute from the style declaration and then not specify the Style property on the Rectangle elements, as shown in this kaxample:

Kaxample: Try pasting the following markup snippet into kaxaml. 
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid.Resources>
    <Style TargetType="{x:Type Rectangle}">
      <Setter Property="Width" Value="50" />
      <Setter Property="Height" Value="50" />
    </Style>
  </Grid.Resources>
  <StackPanel Orientation="Horizontal">
    <Rectangle Fill="Red" />
    <Rectangle Fill="Green" />
    <Rectangle Fill="Blue" />
    <Rectangle Fill="Black" />
  </StackPanel>
</Grid>

You may be curious as to why the above style is applied to all Rectangle elements in the subtree. The answer is hidden in the framework's parsing routine for resource dictionaries. Each element added to a resource dictionary must have a key. It is noteworthy that the style above is conspicuously missing an x:Key attribute. It turns out that the XAML parser has special handling for Style objects that are added to a resource dictionary. If no key is specified, the parser uses the style's TargetType as its key. So the above style declaration is actually equivalent to the following:

<Style x:Key="{x:Type Rectangle}" TargetType="{x:Type Rectangle}">
  <Setter Property="Width" Value="50" />
  <Setter Property="Height" Value="50" />
</Style>

There is a convention in WPF that an element's type is used as its default style key. As mentioned earlier, if the Style property is not explicitly set on a framework element, a resource lookup takes place using this default style key (the element type) to find an appropriate style. Hence, in the example above, our style is applied to each Rectangle as a result of its default style key.

Now let's take a look at a styling feature that even some of the most experienced designers may not know about... 

Our style above can only be applied to Rectangle elements. What if we want to create a more generic style that can be applied to any framework element? It's actually possible to create such a generic style by simply not setting the TargetType property. Of course, this creates some ambiguity for the framework because without knowing the object type, WPF doesn't necessarily know how to interpret the Property member of a Setter. To remove this ambiguity, we must specify the object type in each setter, as shown here:

<Style x:Key="SharedStyle">
  <Setter Property="FrameworkElement.Width" Value="50" />
  <Setter Property="FrameworkElement.Height" Value="50" />
  <Setter Property="Button.IsDefault" Value="True" />
</Style>

Now the style can be applied to elements of differing types. In fact, generic styles actually give us the ability to set properties that are not shared by all of the elements to which the styles are applied. For example, the style above can be applied to both a Rectangle and a Button even though the style contains a Setter for the Button.IsDefault property, as shown in this kaxample:

Kaxample: Try pasting the following markup snippet into kaxaml. 
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid.Resources>
    <Style x:Key="SharedStyle">
      <Setter Property="FrameworkElement.Width" Value="50" />
      <Setter Property="FrameworkElement.Height" Value="50" />
      <Setter Property="Button.IsDefault" Value="True" />
    </Style>
  </Grid.Resources>
  <StackPanel Orientation="Horizontal">
    <Rectangle Style="{StaticResource SharedStyle}" Fill="Red" />
    <Button Style="{StaticResource SharedStyle}">Click Me</Button>
  </StackPanel>
</Grid>

There is a lot more that could be covered in this section on element styling (including things like "based on" styles, overriding style properties, declaring styles at the application and theme levels, etc), but this is an introductory look so let's move on now to control templates...

Introduction to Control Templates

We first introduced the concept of templates in 'D' is for DataTemplate. At that time, we learned that a template is just a tree of visual elements (along with some resources and triggers) used to define the look and feel of a member of the logical tree. As it builds the logical tree, the framework watches for controls and data items that have corresponding templates. When such an element is encountered, the appropriate template is "inflated" into the actual visuals that represent the logical item and those visuals are inserted into the visual tree.

We've already learned that a DataTemplate is used to declare the visual representation of a data item that appears within an application's logical tree. In 'P' is for Panel, we learned that an ItemsPanelTemplate is used to declare the items host used within an ItemsControl. (Note that an ItemsPanelTemplate is a special template whose visual tree must consist of only a single Panel element.) In this section, we will look at a third type of template... a ControlTemplate.

As the name implies, a ControlTemplate is used to declare the visual representation for a control. All native WPF controls have default control templates for each Windows theme. These templates give controls their default visual appearance. 

It should come as no surprise that the visual representation of a control like a Button is composed of several other WPF elements, including a ContentPresenter and an element to render the outer and inner lines that give the Button a 3-dimensional appearance. These subelements which make up the control's appearance are part of its ControlTemplate. 

The nice thing about the lookless control model is that we have full control over the visual appearance of all WPF controls. We can either accept a control's default appearance or we can change the control's appearance by defining our own ControlTemplate. Below is an example of a ControlTemplate for a ListBox:

<ControlTemplate x:Key="MyListBoxTemplate" TargetType="{x:Type ListBox}">
  <Border Background="White" BorderBrush="Black"
      BorderThickness="1" CornerRadius="6">
    <ScrollViewer Margin="4">
      <ItemsPresenter />
    </ScrollViewer>
  </Border>
</ControlTemplate>

This template defines a visual tree for the ListBox that consists of a Border, a ScrollViewer, and an ItemsPresenter. The logic behind the ListBox is still defined within the ListBox class, but the appearance is defined by us.

Note that similar to a style, the ControlTemplate class exposes a TargetType property. This property must always be set. If you ever notice your template is not being applied as expected, be sure to verify that the TargetType matches the control type.

Now to apply our custom template to a ListBox, we simply set the Template property on the ListBox, as shown here:

<ListBox Width="80" Height="200"
    Template="{StaticResource MyListBoxTemplate}">
  <Rectangle Width="50" Height="50" Fill="Red" />
  <Rectangle Width="50" Height="50" Fill="Green" />
  <Rectangle Width="50" Height="50" Fill="Blue" />
  <Rectangle Width="50" Height="50" Fill="Black" />
</ListBox>

Setting the Template in a Style

In the previous example, we assigned a ControlTemplate to a ListBox by setting the Template property directly on the ListBox. This is not the most common scenario, though. Styles and templates were designed to go hand-in-hand. As such, templates are very often included as part of a style.

Remember that a style consists of a collection of Setter objects. For a control style, one of these Setter objects usually sets the Template property on the control, as shown in the following ListBox style:

<Style TargetType="{x:Type ListBox}">
  <Setter Property="Background" Value="LightGray" />
  <Setter Property="BorderThickness" Value="2" />
  <Setter Property="HorizontalContentAlignment" Value="Center" />
  <Setter Property="Padding" Value="5" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ListBox}">
        <Border Background="White" BorderBrush="Black"
            BorderThickness="1" CornerRadius="6">
          <ScrollViewer Margin="4">
            <ItemsPresenter />
          </ScrollViewer>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Suppose we add the above style to our application's resources. Note that this style is using the exact same control template as we presented earlier. And since we've only set the TargetType on this style, all ListBox elements in our application will implicitly use our control template. So now we can add the following ListBox to our UI:

<ListBox Width="100" Height="200">
  <Rectangle Width="50" Height="50" Fill="Red" />
  <Rectangle Width="50" Height="50" Fill="Green" />
  <Rectangle Width="50" Height="50" Fill="Blue" />
  <Rectangle Width="50" Height="50" Fill="Black" />
</ListBox>
 

That looks pretty good, don't you think? Well, upon closer inspection of our style, you might notice that a few things are not quite right. (Hint: Look at the first few Setter objects in the style.)

Clearly, we want our template and style to play nicely together. If we use a Setter in the style to set a property, we want the template to leverage that property. The problem in our example is that several property values (Background, BorderBrush, BorderThickness, Margin, etc) are hard coded on elements in our template. Other properties of the control are simply not bound to anything in the template. This clearly isn't what we want. To fix these issues, we need to use something called a TemplateBinding within the template.

A TemplateBinding is a lightweight binding object used to link the value of a property on an element in the control template to the value of a property on the actual control that is being templated. In our ListBox example, we can add a few such bindings, as shown below, to achieve the desired result:

<Style TargetType="{x:Type ListBox}">
  <Setter Property="Background" Value="LightGray" />
  <Setter Property="BorderThickness" Value="2" />
  <Setter Property="HorizontalContentAlignment" Value="Center" />
  <Setter Property="Padding" Value="5" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ListBox}">
        <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            CornerRadius="6">
          <ScrollViewer Margin="{TemplateBinding Padding}">
            <ItemsPresenter />
          </ScrollViewer>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

 

 

Now all of the properties being set within the style are connected to elements within the template. Thus, those properties are truly reflected in the control's visual representation.

Introduction to Triggers

So far we've seen how to provide the look for a lookless control. Now let's peek at the other half of the user experience... the feel. A control gets its look from the visuals. It gets its feel from how those visuals respond to state changes or user interactions. This is where triggers come into play.

A trigger is a collection of Setter objects (or animation actions) that get applied only when a given condition is true. WPF contains three main types of triggers: Trigger (also referred to as a property trigger), DataTrigger, and EventTrigger. It's also possible to respond to multiple simultaneous conditions by using the related MultiTrigger and MultiDataTrigger objects.

There is a Triggers collection within each of the Style, ControlTemplate, and DataTemplate classes. This means that triggers can be used in both styles and templates, by adding them to the appropriate Triggers collection. (There is also a Triggers collection on FrameworkElement, but it can only contain event triggers... not property or data triggers.)

Let's take a look at a very simple example. Here is a style for a Button:

<Style TargetType="{x:Type Button}">
  <Style.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
      <Setter Property="Opacity" Value="0.7" />
      <Setter Property="TextBlock.FontWeight" Value="Bold" />
    </Trigger>
  </Style.Triggers>
</Style>

As you might expect, moving the mouse over a Button that is using this style causes the opacity of the Button to change to 0.7. It also changes the inherited FontWeight value for any contained TextBlock elements to Bold.

As the example above illustrates, triggers are fairly intuitive. You can pretty much understand how they work just by reading the markup. As such, we won't spend too much time explaining their usage. Instead, we will just look at some tips and tricks for leveraging triggers in different scenarios.

A data trigger allows you to trigger off of a property on your data item by leveraging a Binding. This is most often used in the context of a DataTemplate, but don't overlook the power of data triggers in other scenarios. For example, a data trigger is very handy for triggering off of a property on another object, as shown in this example:

<DataTemplate x:Key="MyItemTemplate">
  <Border x:Name="root" BorderThickness="2" CornerRadius="6">
    <TextBlock Margin="4" Text="{Binding XPath=@First}" />
  </Border>
  <DataTemplate.Triggers>
    <DataTrigger Value="True" Binding="{Binding Path=IsSelected,
        RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}">
      <Setter TargetName="root" Property="BorderBrush" Value="Pink" />
    </DataTrigger>
  </DataTemplate.Triggers>
</DataTemplate>

In this case, we are creating a DataTrigger that acts more like a property trigger by using a FindAncestor binding to get to a source object. Namely, we are triggering off of the IsSelected property on an ancestor ListBoxItem. This will allow us to draw a pink border around our template whenever it is selected. Now suppose our data item is a person and we only want a pink border when decorating a female. If the person is male, we instead want a blue border. We could achieve this by adding a second trigger, as shown in the following template:

<DataTemplate x:Key="MyItemTemplate">
  <Border x:Name="root" BorderThickness="2" CornerRadius="6">
    <TextBlock Margin="4" Text="{Binding XPath=@First}" />
  </Border>
  <DataTemplate.Triggers>
    <DataTrigger Value="True" Binding="{Binding Path=IsSelected,
        RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}">
      <Setter TargetName="root" Property="BorderBrush" Value="Pink" />
    </DataTrigger>
    <MultiDataTrigger>
      <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding XPath=@Gender}" Value="Male" />
        <Condition Value="True" Binding="{Binding Path=IsSelected,
            RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
      </MultiDataTrigger.Conditions>
      <Setter TargetName="root" Property="BorderBrush" Value="Blue" />
    </MultiDataTrigger>
  </DataTemplate.Triggers>
</DataTemplate>

Now we have added a MultiDataTrigger with two conditions. The first contains a Binding to a property on the data item that identifies the gender of the person. The second is the same binding we used earlier to get at the IsSelected property on the ancestor ListBoxItem. Now if the person is male and they are selected, the border will be blue.

The above example illustrates another important aspect of triggers and setters... namely that the order of triggers and setters is important. When multiple triggers evaluate to true, all of their setters are applied. More specifically, they are applied sequentially in the order that the triggers appear within the Triggers collection and in the order that the setters appear within each trigger.  Here's a simple way to think about this... if multiple setters target the same property, the last setter wins!

The final thing I will point out about this example is that it demonstrates how to combine both property triggers and data triggers by leveraging a MultiDataTrigger. At times, this can come in very handy. People tend to forget that a DataTrigger can always be used as a property trigger simply by setting the source of the binding appropriately.

There is one more type of trigger called an EventTrigger. An event trigger, as the name implies, can be used to start an action in response to an event. More specifically, an event trigger executes in response to a routed event. Here's a kaxample of a Button that uses an event trigger to begin an opacity animation when the Button is first loaded:

Kaxample: Try pasting the following markup snippet into kaxaml. 
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Button Opacity="0" Content="My Button">
    <Button.Triggers>
      <EventTrigger RoutedEvent="FrameworkElement.Loaded">
        <BeginStoryboard Name="MyBeginAction">
          <Storyboard>
            <DoubleAnimation Storyboard.TargetProperty="Opacity"
                Duration="0:0:1" BeginTime="0:0:0.25" To="1.0" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
    </Button.Triggers>
  </Button>
</Canvas>

In this example, the event trigger is added directly to the Triggers collection of the framework element (the Button), itself. Again, the FrameworkElement.Triggers collection can only contain event triggers. If you wish to use property or data triggers, they must be within the Triggers collection of a style or template.

Note that you cannot trigger off of a standard CLR event because the mechanism for supporting event triggers depends on the WPF event routing engine, which intelligently routes events to only the elements that have registered to receive them.

With a property or data trigger, the condition that causes the trigger to be applied is based on state. As such, the condition will be true for a period of time. As long as the condition evaluates to true, the setters are applied to their target properties. When the condition later becomes false, the setters are no longer applied and the target properties fall back to their untriggered values.

There is no such concept of state with an event trigger. An event is fleeting. The event fires and then it's gone. Since the condition that invokes an event trigger is instantaneous, an EventTrigger does not contain a collection of setters.  Rather, it contains actions which execute in response to the event. These actions allow you to control (start, stop, pause, resume, etc) the execution of storyboards. The example above uses a BeginStoryboard action in response to the FrameworkElement.Loaded event.

You may also want to set properties based on the firing of an event. Even though you don't have setters, you can still use an EventTrigger to set property values. You achieve this by leveraging an object animation with a DiscreteObjectKeyFrame, as shown in this kaxample:

Kaxample: Try pasting the following markup snippet into kaxaml. 
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <Grid.Triggers>
    <EventTrigger RoutedEvent="CheckBox.Checked">
      <EventTrigger.Actions>
        <BeginStoryboard>
          <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="tb"
                Storyboard.TargetProperty="Text" Duration="0:0:0.1">
              <DiscreteObjectKeyFrame Value="Thanks!&#013;That felt great!!" />
            </ObjectAnimationUsingKeyFrames>
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="CheckBox.Unchecked">
      <EventTrigger.Actions>
        <BeginStoryboard>
          <Storyboard>
            <ObjectAnimationUsingKeyFrames Duration="0:0:0.1"
                Storyboard.TargetName="tb"
                Storyboard.TargetProperty="Text">
              <DiscreteObjectKeyFrame Value="Oh yeah!&#013;I'm outta here!" />
            </ObjectAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Duration="0:0:0.1"
                Storyboard.TargetProperty="IsHitTestVisible">
              <DiscreteObjectKeyFrame>
                <DiscreteObjectKeyFrame.Value>
                  <sys:Boolean>False</sys:Boolean>
                </DiscreteObjectKeyFrame.Value>
              </DiscreteObjectKeyFrame>
            </ObjectAnimationUsingKeyFrames>
            <DoubleAnimation Duration="0:0:0.5" To="0"
                Storyboard.TargetProperty="Opacity"
                BeginTime="0:0:2" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger.Actions>
    </EventTrigger>
  </Grid.Triggers>
  <ToggleButton Width="100" Height="100">
    <TextBlock x:Name="tb" Text="Toggle Me, Please!"
        TextAlignment="Center" TextWrapping="Wrap" />
  </ToggleButton>
</Grid>

Sidenote: This trick of using a discrete key frame animation to set a property value is actually a very useful thing to have in your arsenal... especially if you work a lot in Silverlight. Silverlight does not support property or data triggers.  It does however support event triggers.  As such, it is pretty common to use event triggers along with storyboards and discrete key frame animations in Silverlight to effectively set properties in response to events or visual state changes.

It's worth noting that both property and data triggers can be used to launch actions too. You simply need to set the EnterActions (which execute when the trigger condition becomes true) and/or the ExitActions (which execute when the trigger condition becomes false). This is very handy for dealing with scenarios where there is no routed event off of which to trigger but there is a property. Here is a kaxample:

Kaxample: Try pasting the following markup snippet into kaxaml. 
<StackPanel Orientation="Horizontal"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel.Resources>
    <Style TargetType="{x:Type TextBox}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type TextBoxBase}">
            <Grid>
              <Rectangle Margin="1" Fill="#FFF" />
              <Rectangle Name="DisabledBackground" Margin="1"
                  Fill="#CCC" RenderTransformOrigin="1,0.5">
                <Rectangle.RenderTransform>
                  <ScaleTransform ScaleX="0" />
                </Rectangle.RenderTransform>
              </Rectangle>
              <Border Name="Border" CornerRadius="2" Padding="2"
                  BorderThickness="1" BorderBrush="#888">
                <ScrollViewer Margin="0" x:Name="PART_ContentHost" />
              </Border>
            </Grid>
            <ControlTemplate.Triggers>
              <Trigger Property="IsEnabled" Value="False">
                <Setter TargetName="Border" Property="BorderBrush" Value="#AAA"/>
                <Setter Property="Foreground" Value="#888"/>
                <Trigger.EnterActions>
                  <BeginStoryboard>
                    <Storyboard TargetName="DisabledBackground"
                        TargetProperty="RenderTransform.ScaleX">
                      <DoubleAnimation To="1" Duration="0:0:0.7"
                          DecelerationRatio="0.5" />
                    </Storyboard>
                  </BeginStoryboard>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                  <BeginStoryboard>
                    <Storyboard TargetName="DisabledBackground"
                        TargetProperty="RenderTransform.ScaleX">
                      <DoubleAnimation To="0" Duration="0:0:0.7"
                          DecelerationRatio="0.5" />
                    </Storyboard>
                  </BeginStoryboard>
                </Trigger.ExitActions>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </StackPanel.Resources>
  <CheckBox Name="MyCheckBox" FontWeight="Bold" Margin="0,6,4,0"
      VerticalAlignment="Top" Content="Print To File:" />
  <TextBox VerticalAlignment="Top" Margin="0,3,0,0" Text="C:\MyFile.prn"
      Width="200" IsEnabled="{Binding IsChecked, ElementName=MyCheckBox}" />
</StackPanel>

In this example, when the TextBox becomes disabled, a dark background appears to slide shut over the control, letting the user know that they can no longer edit the control. When the control becomes enabled again, the dark background slides back out of the way. Since there is no event that fires on the TextBox when the control becomes disabled or enabled, we must instead leverage a property trigger's EnterActions and ExitActions to begin the animations.

Windows Theme Files

Hopefully styles, templates, and triggers are starting to make a lot of sense. To really appreciate how this lookless model works, I would encourage you to spend some time looking at the styles and templates for the native WPF controls. All of the default control styles and templates are available in the theme files which ship as part of the Windows SDK or with Blend.

When installing the SDK, you must select the option to install the .NET Framework 3.0 Samples. Then you will be able to access the theme files by looking in the Core directory within the following archive: %ProgramFiles%\Microsoft SDKs\Windows\v6.0 \Samples\WPFSamples.zip. The theme files also ship with Blend in the following directory: "%ProgramFiles%\Microsoft Expression\Blend <version>\SystemThemes". 

If you have not installed the Windows SDK or Blend, you can still view the theme styles and templates using .NET Reflector with the BamlViewer Add-In. Simply reflect upon the theme assembly of your choice. For example, you could load the Aero theme assembly from "%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.Aero". Once loaded, select the assembly and then select "BAML Viewer" from the Tools menu.

It's worth noting that Blend also includes a set of "Simple Styles", which are basically reference samples of how you might style and template the most common framework controls. These styles tend to be much simpler (hence the name) than the actual theme styles. See "Working with simple styles" in the Blend help file for more details.

Templating an ItemsControl

This is an ItemsControl series, so I guess we should spend a little time examining the lookless nature of a few ItemsControl classes. The remainder of this article covers several different aspects of ItemsControl templates.  As with the rest of this series, many of these concepts also apply to non-ItemsControl classes.

A Lookless Lesson Learned

There is a story I really like to tell when introducing the lookless control model to developers and designers...

The early WPF (well, Avalon) platform contained an ItemsControl that never actually made it into any released bits. It was called RadioButtonList. As you can probably guess from the name, this was simply a list of mutually exclusive options, each one selectable via a RadioButton control. At first blush, this probably sounds like a really useful control to have in the platform, right? That's what we all thought too. In fact we used it all over the place in demos and POCs. Then someone pointed out that the platform already contained a control for selecting an item from a list of mutually exclusive options... namely, a single-select ListBox!

Doh! Clearly, if they were going to sell this lookless story, they needed to live by it, so soon thereafter, a breaking change email announced the removal of the RadioButtonList control. Each instance was replaced with a styled ListBox.

The moral of the story is that the lookless concept requires a little getting used to. Clearly, even the architects of this new control model struggled a bit with it in the early days.

For your convenience, below is a kaxample that demonstrates my typical RadioButtonList style.  You will need to press F5 to execute this sample within kaxaml:

Kaxample: Try pasting the following markup snippet into kaxaml. 
<Window Title="RadioButtonList Sample" Width="500" Height="180"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Topmost="{Binding SelectedItem, ElementName=TopmostSelector}"
    WindowStyle="{Binding SelectedItem, ElementName=WindowStyleSelector}">
  <Window.Resources>
    <ObjectDataProvider x:Key="WindowStyles" MethodName="GetValues"
        ObjectType="{x:Type sys:Enum}" >
      <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="WindowStyle" />
      </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
      <Setter Property="BorderBrush" Value="{x:Null}" />
      <Setter Property="BorderThickness" Value="0" />
      <Setter Property="Tag" Value="Vertical" />
      <Setter Property="ItemsPanel">
        <Setter.Value>
          <ItemsPanelTemplate>
            <StackPanel Orientation="{Binding Tag,
                RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" />
          </ItemsPanelTemplate>
        </Setter.Value>
      </Setter>
      <Setter Property="ItemContainerStyle">
        <Setter.Value>
          <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Margin" Value="6,2" />
            <Setter Property="Template">
              <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                  <Border Background="Transparent">
                    <RadioButton Focusable="False"
                        IsHitTestVisible="False"
                        IsChecked="{TemplateBinding IsSelected}">
                      <ContentPresenter />
                    </RadioButton>
                  </Border>
                </ControlTemplate>
              </Setter.Value>
            </Setter>
          </Style>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <StackPanel Margin="10">
    <TextBlock FontWeight="Bold">Topmost:</TextBlock>
    <ListBox Name="TopmostSelector" SelectedIndex="1" Margin="10,0,0,20"
        Style="{StaticResource RadioButtonList}" Tag="Vertical">
      <sys:Boolean>True</sys:Boolean>  
      <sys:Boolean>False</sys:Boolean>  
    </ListBox>
    <TextBlock FontWeight="Bold">WindowStyle:</TextBlock>
    <ListBox Name="WindowStyleSelector" SelectedIndex="1" Margin="10,0,0,0"
        Style="{StaticResource RadioButtonList}" Tag="Horizontal"
        ItemsSource="{Binding Source={StaticResource WindowStyles}}" />
  </StackPanel>
</Window>

Notice that I leverage the Tag property on the ListBox and a property trigger to toggle between a vertical and horizontal layout for the radio buttons. This is merely for convenience. If you want a more readable solution, you can always declare your own attached property of type Orientation to trigger off of in the style.

The Many Faces of a ListBox

We've already seen that a ListBox can take on many different looks. There is the default look, as determined by the theme's ListBox style and template. Then there is the look we just defined above for a list of radio buttons. 

Just for fun, let's review a few other styled ListBox examples. It should be clear by now that all of these different looks are enabled by this lookless control model which brings together the things covered in this article (styles, templates, and triggers) as well as several other concepts covered in previous articles, including item templates ('D' is for DataTemplate), container styles ('I' is for Item Container), and swappable items panels ('P' is for Panel).

As we've done in the past, let's borrow some images from the business modeling application used by the Microsoft Dynamics team. Here are a couple of more traditional styled ListBox examples, where the items are stacked vertically:

Naturally, that application also leverages a ListBox to present a list of radio buttons:

As described in 'P' is for Panel, the application uses a very slick custom panel within a ListBox to provide a pivot view for exploring entity relationships:

You may recall from the ICIQ Test ('B' is for Bet You Can't Find Them All) that the application also contains a custom TreeGraph control. Technically, this control also qualifies as a ListBox (it derives from the ListBox class and then adds additional functionality to support custom item containers):

I guess while we're on the topic of ListBox derivatives, it is worth noting that the native ListView control also derives from ListBox. So a ListView is actually a multi-columned ListBox:

Wow! Since the control, itself, is lookless, we could essentially go on forever coming up with new looks for a ListBox. We are limited only by our imaginations and styling skills!

Since we have this great power, it is worth also mentioning the great responsibility that comes with the lookless control model. In the end, the look and feel of our controls should be driven by the desire to create the best user experience possible for the scenario at hand. I've already seen too many videos playing inside rotating buttons. We must avoid the temptation to do things just because we can. (Hearken back to the early days of WYSIWYG word processors, where so many documents came out looking like ransom letters...  'nuff said!

The Items Panel

There is one element that you will almost always find within the template of an ItemsControl. It is called an ItemsPresenter. In 'P' is for Panel, we learned that an ItemsPresenter reserves real estate within the control's template for the items host (a.k.a., the items panel). By including this ItemsPresenter element within the control template and specifying the ItemsPanelTemplate separately, it is possible to have a swappable items panel without having to retemplate the entire ItemsControl. The ItemsPanelTemplate will be inflated within the ItemsPresenter to create a panel to host the items of the ItemsControl.

Of course, as we also learned in 'P' is for Panel, it is not absolutely necessary to include an ItemsPresenter in the control template. Another approach is to simply include a panel in the template and set its IsItemsHost property to true. The drawback here is that the items panel is no longer swappable via the ItemsPanelTemplate property. In fact, ItemsPanelTemplate is completely disregarded in this scenario. 

The ToolBar control is an example of one such ItemsControl that uses a hard coded items panel. If you examine the control template for ToolBar, you will see an element called ToolBarPanel that has IsItemsHost set to true. This was an intentional design decision for ToolBar. The control template basically tells us that a ToolBar cannot function without a ToolBarPanel. (The reason for this requirement is that a ToolBar has special support for overflow items... if the items cannot fit within the space available in the ToolBarPanel, they are arranged within a ToolBarOverflowPanel in a Popup control.)

I think its fine to derive custom ItemsControl classes like ToolBar that have special dependencies on known template elements. As a control developer, there are certain measures that you should take whenever you decide to go this route. First, you need to fully document the requirements of your control. By requiring specific elements in the template, you are reducing the lookless nature of your control. Designers will need to be aware of these limitations when they re-template your control.

Other Known Parts

While we're on this topic of required template elements, it's worth noting that an ItemsControl will sometimes have dependencies on other elements besides just the items panel. Let's consider the ComboBox control...

A ComboBox is a single-select ItemsControl whose full list of available items is usually presented in a "dropdown list" below a textual representation of the selected item. The template for a ComboBox consists of a TextBox and a Popup (as well as several other visual elements). As you might expect, the TextBox displays the selected item and the Popup contains the ItemsPresenter so it naturally presents the available items in the dropdown list.

The ComboBox class contains certain logic that is very specific to the Popup control. Again, any such dependency on a known template element (also referred to as a "template part") reduces the looklessness of the control, but that's okay as long as the requirement for the part is well documented. 

This naturally raises a question as to how the ComboBox can accurately identify the known part (the Popup) within its template and hook it up to the control's logic. One approach might be to walk the visual elements in the control's template and look for an element of type Popup. Of course, this might fail if a designer decides to re-template the control in a way that involves two Popup elements.

To deal with ambiguities like this, the framework uses a specific naming convention for the identification of known template parts. That convention is to name the element using the prefix "PART_". In the case of the ComboBox template, the expected name for the Popup is "PART_Popup". So within the template, you will see a Popup element like this:

<Popup Name="PART_Popup" AllowsTransparency="True" Placement="Bottom">
  ...
</Popup>

Alright, we have a well-known name for the Popup. Next we need to use this name to locate the element. But when and how do we perform this lookup? It turns out that the framework provides a method for this very purpose. By overriding OnApplyTemplate(), a control can be notified when it's template is inflated and loaded into the visual tree. The GetTemplateChild() method can then be used to look up a template element by name, as shown here:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    _popup = GetTemplateChild("PART_Popup") as Popup;
}

The use of known parts within a control template clearly adds complexity for design tools that need to support re-templating the control. As such, you may be wondering how the known template parts are made discoverable to tools like Blend and Cider. Well, the framework actually leverages a custom TemplatePart attribute to decorate control classes with known parts. For example, the ComboBox class is decorated with the following attributes:

[TemplatePart(Name="PART_EditableTextBox",Type=typeof(TextBox))]
[TemplatePart(Name="PART_Popup",Type=typeof(Popup))]
public class ComboBox : Selector
{
    ...
}

Note that although we've been looking at the ComboBox class in the above example, this convention of using known template parts is really applicable to all controls within the framework. Anytime a designer wishes to re-template a control, they should take some time to familiarize themself with the known parts. If they need to re-template a control for which they do not already have a sample template, they can leverage a tool like .NET Reflector to disassemble the control class and view the TemplatePart attributes.

Keeping It Loose

I'd like to offer some advice specifically for the authors of custom controls. A well-written, lookless control will provide what I refer to as a "loose" contract between the control and its template. This means that the control does not blow up (throw an exception) when elements are missing from its template. Rather, it knows how to degrade gracefully.

This loose contract approach is used for the native ItemsControl classes in WPF. For example, you can re-template a ListBox and simply leave out its ItemsPresenter. The ListBox class never assumes that an items panel is present. As such, when you use your template, you will note that your app runs fine. Of course, no items are displayed within the ListBox because there is no items panel in which to present them. The important thing is that no exceptions are thrown either. 

The same graceful scale back can be seen in the ComboBox control when you leave out a known template part like "PART_Popup". The ComboBox class never assumes that a Popup is present. Instead, the class always checks its internal Popup reference to make sure its non-null prior to accessing it.

Testing your control against templates with missing elements is part of supporting a truly lookless control model. Users of your control will really appreciate your diligence in this matter because it means their designers will have full freedom to re- template the control as they see fit.

To Derive or Not To Derive

We've already seen that the lookless model provides the opportunity to reuse a single control class with many different visual representations. There will certainly be times when you need more than just a new look and feel. If a class does not provide all the functionality you need, you may actually need to derive a new control by extending an existing class.

The ListView control is a good example of an ItemsControl which has been thusly extended. The ListView class derives from ListBox and adds support for a broader concept of "Views". (We won't go into whether ListView is a well-contrived control... that is a much bigger conversation and this post is already too long!)

The most common view used within a ListView is called a GridView. This view allows each item to be presented as a row within a grid (or table). The columns in the grid each have their own header. In simple scenarios, a ListView can be used as a lightweight data grid (which was important in the initial release of the platform because there was no native DataGrid class). You can see the ListView class in action in 'R' is for Rob has a Customer.

Extending functionality is just one reason you might choose to derive a new control. Another reason might be that the existing control is not entirely lookless. In this case, you may wish to derive a new class and override specific functionality within the base class.

Consider the TreeView class. In 'B' is for Bet You Can't Find Them All, we saw an example of a TreeView that had been re-templated to support a horizontal layout. This telescoping TreeView is displayed in the following image:

It turns out that creating this custom TreeView was not as simple as merely re-templating the TreeView class. The item container class, TreeViewItem, contains hardcoded support for keyboard navigation that assumes the tree expands vertically. (Pressing the Left and Right arrow keys on a TreeViewItem expands or collapses a branch in the tree, whereas pressing the Up and Down arrows navigates between nodes in the tree.) In order to create a horizontally expanding TreeView, it was necessary to swap the directional navigation keys. This required the derivation of a new TelescopingTreeView class from the native TreeView class as well as the derivation of a new TelescopingTreeViewItem class from the native TreeViewItem class. 

Hopefully this can be a lesson to us all. Clearly, this TelescopingTreeView required a lot more work than would have been necessary if a truly lookless base class existed. It would have been nice if the native TreeViewItem added support for keyboard navigation via attached key bindings rather than hardcoded keyboard behaviors. (Note to self: Do a post on attached key bindings someday.) I certainly try to keep things like this in mind whenever I'm overriding specific input methods like OnKeyDown().

Coming Up Next

Congratulations! You made it through the longest post in this series (thus far ). More importantly, you are now familiar with the lookless control model and the techniques for defining the look and feel of controls within this model. In the next post, 'N' is for Natural User Interface, we will learn that an infinite loop is not always a bad thing!

Comments (11)

Free WPF Training!
By Dr. WPF on 4/2/2009 5:14 AM

My retirement from WPF officially ended at 11:59 PM on April 1, 2009.  It's good to be back!  As it turns out, I'm just not that good at golf and I don't know what else a retired doctor does.  I better start working on my swing, I guess!

I am excited to see that Karl and Jaime have announced the dates and cities for their upcoming "WPF for LOB" training tour.  The training is FREE!  but you must register. 

Location

Dates

 Los Angeles, CA

 4/24 - 4/25

 London, UK

 5/15 - 5/16

 New York, NY

 5/29 - 5/30

 Phoenix, AZ

 6/5 - 6/6

 Chicago, IL

 6/12 - 6/13

[4/20/09 Updated the dates above to reflect the latest info from Jaime's blog.]

Check out Jaime's announcement for the full details and registration links.

Comments (4)

Hanging up the Stethoscope
By Dr. WPF on 4/1/2009 2:41 AM

There's no easy way to break this news, so I'll just say it... I'm moving on!

After 6+ great years working with this awesome platform, I've decided to pursue a new interest.  This new pursuit will leave me with little time to devote to WPF.  I know this news comes as a shock to many of you, but hopefully once you see my new interest, you will understand. 

Please know that I still have great fondness for the WPF platform.  My decision should not reflect negatively in any way on WPF!

Okay, if you must know what is pulling me away from WPF, here's a sneak peak at my next big thing!

All my best!
Dr. WPF

Comments (14)

Jaime's Mix09 Recap Series
By Dr. WPF on 3/30/2009 8:36 AM

Jaime Rodriguez has just released a fantastic 3-part series summarizing the big things to directly or indirectly come out of Mix09.  I highly recommend it for anyone working within WPF and/or Silverlight:

It's great to get a little insider insight into the thinking behind Microsoft's ongoing client strategy.

Comments (0)

Silverlight: How I Really Feel About the Iridescent Thong
By Dr. WPF on 3/21/2009 11:12 PM

First, I have to give huge props to Microsoft for throwing an excellent party in Vegas this past week!  The week was definitely packed with excitement and backed by great content.  Be sure to check out the recorded sessions if you were not able to attend Mix09.

This post represents my response to this year's big unverbalized question...

What about WPF?

Yes, this year's conference felt like it was all about Silverlight 3.  I think Microsoft would readily acknowledge that this was by design (no pun intended).  And this was actually fine with me, since the Silverlight platform has made such great strides over the past couple of years.  It certainly deserves the attention it is now getting.

I have a reputation for making fun of Silverlight from time to time.  Truth be told, I bare no ill will toward Silverlight at all.  I've even delivered Silverlight training on several occasions.  I think of her as a kid sister to WPF (which plays the role of big sister, in case you were curious about either platform's gender).  Privately I'm pulling for Silverlight to make good, but that doesn't stop me from publicly taking shots at her whenever the opportunity arises.  It's all in good fun... really!  

I'm not exactly sure what role I'd give myself in all of this.  Some might say that WPF and I are BFFs, but I'd argue against the latter F.  To me, "forever" is a word that has no place in technology.  When it comes to WPF, my devotion only goes as far as the platform presently supports.  I am her student; I am her practitioner; I am even her disciple; but make no mistake... if something better comes along, I will turn her out like a two-bit whor... rible analogy!  Let me change directions...

Based on what I see on the horizon right now, I think WPF and I still have a bright future together.  Silverlight gets more impressive with each release, but it has quite a ways to go before it reaches parity with WPF.  I just spent the better part of a conference watching web developers "ooh" and "ah" over features that I've been using in WPF for years now.  (Imagine how they'd feel if they had something as basic as a trigger!  See... there I go again...  I just can't help myself. )

Additionally, I'm a platform guy.  So much of what I do now requires a full trust environment with access to platform APIs.  Until Silverlight breaks out of the sandbox and allows me to harness platform-specific features (which is not likely in the near-term, since it is so focused on cross-platform reach and a small footprint), I think WPF and I will stay pretty tight.

Why bring all this up now? 

I've received a number of... shall we say... "concerned" inquiries lately about the shrewdness of adopting WPF for new or future projects while Microsoft has such an obvious slant toward Silverlight. 

My honest recommendation to anyone deciding between WPF and Silverlight is unchanged over the last couple of years.  The platform has to be chosen based solely on the project requirements.  If Silverlight meets the requirements of your project and you need cross-platform reach, then you should definitely target Silverlight.  End of story.

On the other hand, if you're developing a full trust, Windows application, you will likely be far more successful with the more mature (and in my humble opinion, more elegant) big sister, WPF.

Impending Doom

Some emails have expressed an explicit fear that WPF is about to be abandoned by Microsoft and then disappear completely.  Let me put my response to such concerns in terms that only a geek can truly appreciate... 

Don't Panic!

To allay your fears, let me point you toward two very big buoys floating in the waters around Redmond: Expression Blend and Visual Studio 2010.  The user interfaces these products are actively being built in WPF.  Blend always has been a WPF application.  For information on the upcoming Visual Studio 2010 UI, check out Jason Zander's blog.  Feel free to anchor yourselves to these buoys.  If these projects change direction, make sure you follow!  (Can you imagine a Visual Studio user interface written in Silverlight?  Even I think that would be awesome!)

I would also point out that many new features highlighted at Mix09 actually represent nice WPF advances too.  I know Silverlight received most of the glory, but new Blend 3 features like Sketch Flow, Behaviors, and the new Adobe importers are actually just as pertinent to WPF as they are to Silverlight.  And of course, there was the sneak peak at WPF 4.

The Big "If"

So what happens down the road "if" Silverlight does reach parity with WPF and it finally breaks out of the sandbox? 

Should that ever happen, my response to potentially losing WPF would basically be a shrug and a big, "So what?"  If Silverlight v.Next++ gives me everything I need for Windows client development and supports the same collaborative workflow as WPF, then why would I (or anyone) mourn the loss of WPF?  At that point, I'd be more concerned if WPF *was* still around.

Again, let me be clear...  I think this latter scenario is a big "If" and should things go in that direction, I don't think we're talking near-term.  Microsoft can move very fast when the correct motivations are in place, but even at full speed, I think this would be a multi-year scenario.

Again, my take is that nothing lasts forever in technology.  Both WPF and Silverlight will eventually be replaced by something bigger and better... or maybe smaller and better... who knows?  For now, I'm a WPF junkie and a Silverlight user.

Now the Important Stuff...

Here are the Sudoku puzzles I conquered on my return flight from Vegas: One, Two, and Three. (See JAGS 10K Instructions for mouse play.) 

Yep, three of them!  Usually, I only make it through a single puzzle, but I was unexpectedly bumped to first class on this flight where I basqued in the glory of geek icons such as Scorbs, The Gu, the WPF Wonder Man, and even Nate Dunlap!  Their collective auras must have increased my puzzle IQ.

Comments (10)

Tips and Tricks: Making Value Converters More Accessible in Markup
By Dr. WPF on 3/17/2009 1:29 PM

I recently had the pleasure of presenting some rather advanced WPF concepts (along with some cool tips and tricks) to a group of smart folks.  Unfortunately, I ran up against time constraints and was forced to drop the data binding portion of my talk.  I'm a little bummed about not being able to show one cool trick that I use a lot, so here it is...

Call me lazy (really, I'm okay with it), but I've never been wild about having to declare my value converters as resources before using them.  It's just one extra step:

<Window.Resources>
  <src:MyConverter x:Key="MyConverter" />
</Window.Resources>

And then later the converter must be specified using a StaticResource reference, which incurs some overhead (albeit small overhead, in the grand scheme of things) for the resource resolution:

<TextBlock Text="{Binding SomePath, Converter={StaticResource MyConverter}}" />

I often choose to skip the middleman.  Instead of using the StaticResource markup extension to look up a converter, I simply derive my value converter, itself, from MarkupExtension.  Then I return an instance of it in the ProvideValue() override.

I have to assume there are plenty of others using this approach too.  (It's clever, afterall, but that's about the extent of the hyperbole I'd grant it.)  If you're not one of them, I simply wanted to put it out there as a potential trick for your arsenal.

Below is a super simple example of a dummy converter (typically used to debug bindings) that does this:

public class DummyConverter : MarkupExtension, IValueConverter
{
    private static DummyConverter _converter = null;
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (_converter == null)
        {
            _converter = new DummyConverter();
        }
        return _converter;
    }

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        return value; // set breakpoint here to debug your binding
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        return value;
    }

    #endregion
}

The above class creates a singleton dummy converter that can be used across my app without any resource lookup.  The XAML usage is now simply this:

<TextBlock Text="{Binding SomePath, Converter={src:DummyConverter}}" />

This is much better, to my lazy way of thinking.

Comments (10)

JAGS 10K, Platinum Edition
By Dr. WPF on 2/12/2009 8:18 AM

There's still time to vote in the Mix 10K Challenge

Voting closes on Monday, February 16, 2009.  If you haven't yet voted, check it out.  You simply rate the entries on a scale of 1 to 5.  Feel free to start with my JAGS 10K entry (Just Another Game of Sudoku in 10K). 

A New Release of JAGS 10K is Now Available

On my recent return flight from San Antonio, I had the opportunity to tackle one of the toughest games of Sudoku yet.  Only 20 cells were given.  A typical "hard" puzzle gives you at least 25.

Naturally, I decided to attempt it within JAGS 10K (using the super secret method of loading a custom puzzle described in my previous post).  Afterall, every developer should eat his own dogfood!

This attempt revealed that the golden release of JAGS 10K is lacking in a very key feature.  Namely, there is no way to make visual cues within a cell.  Most Sudoku players rely on these marks when solving a puzzle on paper.  Without this ability on the computer, solving a tough puzzle is nearly impossible (unless you're Dr. Spencer Reid from CBS's Criminal Minds... yes, I'm a fan).

Clearly, I had some work to do!  As such, I took some time this past weekend to add the required new feature and I'm happy to say that the new release is now available for installation here.

Previous Features

All of the original features are still there.  You can play an auto-generated puzzle of a specific difficulty or you can load a puzzle from the web.

Mouse Play:  Some people commented on the lack of discoverability for mouse play.  Unfortunately, 10K does not afford you any room for including instructions within the app and the stock cursors just don't do it for this particular user interaction.  So here's the mouse play secret again... simply click on a cell and then drag down (or right) to increase the number and up (or left) to decrease it.  (Designers:  If it helps, I've been told by a few people that this is similar to how Blend's property editor works for numerical properties.)

New Features

In JAGS 10K, Version 2.0, you can now hold down the Ctrl key to reveal visual cues that can then be toggled on or off, as seen here:

When you release the Ctrl key, you will see only the markers that are toggled on:

 

Now you can truly solve any level of puzzle! 

To make it even better, I added a more accessible way to specify a custom puzzle.  Now you can simply pass the gameboard as a URL parameter.  This allows you to easily share a puzzle with your friends.  In fact, if you'd like to take a crack at it, here is that tough puzzle from my return flight:

http://drwpf.com/Apps/JAGS/JAGS10K.application?000200000002930000005000670000000058070000020560000000014000900000076100000003000

Enjoy! It took me nearly an hour to solve!  Maybe you can beat me.

I also fixed a bug or two in the app and I added a minimize button (per Mladen's suggestion in the comments for my official entry), so you can quickly hide the game if your boss walks in to your office!

Code Size

So I'm sure you're asking, "What did these enhancements do to the size of the code?"  As you might expect, it increased it quite a bit.  The size of the code actually went up by about 20 percent!

My first inclination was to simply release the updated version as "JAGS 12K".  But alas, if you can't play by the rules, you shouldn't play at all!  So I've spent the last several nights getting extremely creative in the code.  I'm happy to say that the code for this v.2 release is now once again 10K (exactly 10,240 bytes, to be precise).

Source Code

If you saw my original post, you know that it took a lot of work to get down to 10K in the first place.  Explaining my latest tricks would take too long, so I've decided to instead just post the code for anyone who is really that interested.  You can download the project hereCaution: Don't expect it to be very legible!

Comments (0)

JAGS 10K has been posted
By Dr. WPF on 2/5/2009 12:25 AM

My aforementioned Mix10K entry has now been posted.  Please check it out, give it a rating, and let me know what you think.

I have just uploaded several new games, including some from The New York Times, The LA Times, and The Hindu (in honor of my original introduction to Sudoku).  I even included the puzzles from my recent flight to San Antonio (just because I could ).

If you're not into the syndicated games, you can also play auto-generated games at the difficulty level you select.

Enjoy your game!

Secret Bonus Feature for Hardcore Sudoku Players

I allow you to specify a gameboard on the command line.  This requires you to locate the executable file, jags.exe, in the click once application cache (C:\Users\[ProfileName]\AppData\Local\Apps\2.0\[obscure path]\jags.exe).  Simply pass an 81-character string of digits on the command line. Each 9-digit substring represents a row of the puzzle. Zeros (0s) represent empty cells.

If you actually use this feature, you may indeed have a problem.  Consider joining Sudokuholics Anonymous.  Maybe I'll see you there!

Comments (2)

Size Matters
By Dr. WPF on 1/31/2009 3:12 PM

Ha!  I did get a blog entry posted in January.  (And I'm not dead, in case you were wondering... just swamped with work.  Only a few weeks until my current project is complete and then I should have time to post some new stuff!)

Anyway, I thought I'd write a little blurb about my recent entry into the Mix 10K Smart Coding Challenge.  I did not plan to participate in this contest because of other demands on my time. Then Wednesday night someone mentioned that there were only two days left.  I took a quick peek at some of the existing entries and I suddenly got the urge to give it a shot.  Not that I thought I could do better... (I know my limitations when it comes to design! )  Rather, it was the caliber of other participants that drew me in.  Plus, the idea of squishing coolness into a mere 10K of code just seemed like fun.  I just needed an idea!

That's when I saw one of my favorite Christmas stocking stuffers sitting on the corner of my desk...  Sit & Solve Sudoku.  The shape of this little book says it all!

I love a good puzzle!  I first discovered Sudoku a couple of years ago when spending a few weeks in India.  Now it's one of my favorite leisure activities when trapped on a plane.

Yes, I'm aware there are lots of online Sudoku games out there.  But I'll wager that very few, if any, are written using less than 10K of code!  So there was my challenge.  And I'm happy to say that I met that challenge. 

You should be able to check out my entry, JAGS 10K, in the contest gallery shortly.  I submitted it yesterday but it has yet to appear on the site. 

I'll add a link here when the app goes live.  In the meantime, below is a sneak peak:


 
I probably won't win any awards for this little game, but I'm pretty proud of it nonetheless!  I had a blast packing it full of features and whittling it down to size.

Introduction to JAGS 10K

First, the name JAGS 10K stands for "Just Another Game of Sudoku (in 10K)".  It is quite possibly the smallest, fully functional version of Sudoku ever created! (hyperbole intended! )

Instructions

If you've never played Sudoku before, the rules are quite simple... Fill in the grid such that every row, column, and 3x3 box contains every digit from 1 to 9. That's it!

Game Play

You can play JAGS 10K using the keyboard, the mouse, or both. For keyboard play, simply select a cell using the tab or arrow keys and press the desired number. Press 0 (zero) or Delete to clear a cell. For mouse play, simply click an empty cell and drag the mouse downward to cycle through the available digits. Release the mouse when you find the digit you desire.

The application will generate custom games for you, or it can obtain recently published games from the web. Just launch the app, choose your difficulty level, and enjoy!

Cutting it Down to Size

I thought it would be fun to share a few of the tricks I used to bring this application within the 10K limit.  You can think of the remainder of this post as my tips for the next 10K challenge.

First, I should point out that JAGS 10K is written in WPF (duh!).  But most of the tricks described here would work equally well in WPF or that other XAML-based platform.  (You know what I'm talking about, right?  That web platform.  What was it called again?  I'm sure you know what I mean, anyway...  that technology whose logo looks like a big blue iridescent thong!  Oh never mind.)

I should also point out that calling this contest a "smart" coding challenge is a bit of a misnomer.  I would call it a "clever" coding challenge, at best.  (But then they couldn't play off the "art" in smart, so that wouldn't fly.)  In any event, making an application run in 10K or less involves the intentional implementation of some very poor coding practices!  Using these tricks for a production application is not advisable.

First Things First... Make it Work

I began by simply writing the application the way I would any new WPF project.  I always start with the view model and an extremely simple view to prove out the view model.  This provides a very basic proof of concept app, without the eye candy (styling and visual appeal).

I was able to create a very simple Sudoku game in just a few short hours.  I used fully descriptive variable names during this phase (since it's rather important that one understand and easily follow the code they are creating).  I also used a brute force algorithm for generating new game boards.  I won't bore you with the details, but I will readily admit that it is inelegant and architected purely to keep the code simple.

Downsizing via the Obvious Stuff

Once I had the game working the way I wanted, it was time to cut it down to size.  At this point, it was about 60K.  Clearly, when trying to get under 10 kilobytes, every byte counts!  (Literally.)  There are some very obvious things you can do: remove all white space, rename all variables using 1 character names, drop all the "private" qualifiers, group similar variables and declare them using a comma-delimited list, etc.  After doing these things, I was down to about 20K.

Now for some of the less obvious tricks...

Use integers instead of enum values

The WPF and BlueThong platforms contain a lot of enums with long, descriptive value names.  Typing out these names uses up precious bytes.  In my code, I instead specified enum members via their explicit integral values, which are easily discovered using reflector.

For example, instead of using:

  ResizeMode=ResizeMode.CanResizeWithGrip;

You might use:

  ResizeMode=(ResizeMode)3;

Voila!  You've just saved 15 bytes!

CAUTION: This is one of those shortcuts that you would never want to do in real code!  If Microsoft decides to reorder enum values in a future update, the application will surely break.

Use a series of if statements instead of a switch statement

The following example combines the enum trick with the elimination of a switch statement to further reduce size (at the slight expense of performance).  When processing keyboard input, each KeyDown event provides the key that was pressed using the System.Windows.Input.Key enum.  Rather than writing typical code that looks like this:

  switch (e.Key)
  {
      case Key.Back:
      case Key.Delete:
          f();
          break;

      case Key.NumPad0:
      case Key.D0:
           b();
           break;
  }

I instead wrote code like this:

  int v=(int)e.Key;
  if(v==2||v==32)f();
  if(v==34||v==74)b();

Even in its most compact form, the switch statement above requires 94 bytes, whereas the less optimal series of if statements using known integral values only requires 56 bytes.  An instant savings of almost 40 bytes!

Reuse Global Variables

If you find yourself declaring a lot of similar variables in different methods, consider just declaring the variable once in the global scope of your class and reusing it in those various methods.

Remember that code may be more compact than markup

Instead of this:

  <ComboBox Name="c">
    <s:String>Easy</s:String>
    <s:String>Medium</s:String>
    <s:String>Hard</s:String>
  </ComboBox>

You can do this:

  <ComboBox Name="c"/>

With this in the code behind:

  c.ItemsSource=new string[]{"Easy","Medium","Hard"}

Another 40-byte savings!

Instantiate variables using the var keyword

Since this contest targets .NET 3.5 and the current releases of the .NET languages, there are some additional cool tricks at our disposal.  For example, instead of doing this:

  ListBoxItem x=new ListBoxItem();

You can do the following to save some bytes:

  var x=new ListBoxItem();

Use abbreviated property declarations

If you don't need to access their private backing fields, declare properties using abbreviated notation:

  public string T{get;set;}

Leverage an existing framework class for INotifyPropertyChanged support

Remember, its size that matters... not efficiency.  When I was looking for those last 100 bytes, I realized that a lot of space was wasted implementing the INotifyPropertyChanged interface on my view model class.

My original class began like this:

  public class P : INotifyPropertyChanged
  {
      public event PropertyChangedEventHandler PropertyChanged;
      void N(string n)
      {
          if (PropertyChanged != null)
              PropertyChanged(this, new PropertyChangedEventArgs(n));
      }

Clearly this contains long class names that are out of my control.  Even with the white space removed, the above support for INotifyPropertyChanged requires a full 193 bytes.

Knowing that the framework contains several classes that already implement INotifyPropertyChanged, I set out with reflector to find one that could be leveraged for my needs.  I decided GroupStyle was a good candidate, so I changed my class to derive from it, as shown here:

  public class P:GroupStyle
  {
      void N(string n){OnPropertyChanged(new PropertyChangedEventArgs(n));}

Using this simple trick, I now had full support for property change notifications for about half the asking price (96 bytes instead of 193).  Tricky, eh?

Leverage Resources

Watch for lengthy elements that appear multiple times in your markup and surface those as resources. For example, if you notice that you are applying the same drop shadow effect in multiple locations, declare it as a resource and simply use a StaticResource reference to reuse it.  (Okay, this is just good general advice for structuring resources, but it is even more important when you are up against a 10K size limit. )

Make a few hard choices about Form vs. Function

Visual coolness costs big bytes!  Markup-declared animations are very verbose.  Styles are really lengthy because of all their Setters.  The same is true for the Triggers collection on an element or within a style or template.

By the time I had developed the game engine and view model, I had already exhausted the majority of my 10K allotment.  I then had to choose very carefully how I would design the actual user interface.  I didn't have room for gratuitous animations or other flashiness.  In the end, I decided that my 10K app was more about function than form.  (This decision may cost me in the voting, but I'm okay with that.)  I was still able to pull off a nice look and feel (using a grand total of 10,195 bytes, or 9.96K) and I'm really happy with the overall product!  In fact, I think I'll bid you adieu now and go play another game of Sudoku!

Comments (5)


 Print   
Copyright 2009 by Dr. WPF   Terms Of Use  Privacy Statement