Welcome to MSDN Blogs Sign in | Join | Help

Data See, Data Do

Mike Hillberg's Blog on Wpf and Silverlight

Syndication

News

All postings are provided "As Is" with no warranties, and confers no rights. Opinions and views expressed here are not necessarily those of Microsoft Corporation.


A Custom Storyboard Component in Xaml

In WPF & Silverlight, a Storyboard is a collection of animations running in parallel.  Not everyone likes the name “Storyboard” though.  The idea behind the name was that that list of timelines running in parallel are like a list of plot lines in the plan for a movie.

In any case, just like you can package up a tree of elements into a re-usable UserControl component, it’s nice to be able to package up a tree of timelines into a re-usable Storyboard component.  Here’s an approach to do just that, using Xaml, and made simpler by using a new Xaml feature in WPF4.

For example, here’s a Storyboard that puts three animations on a Rectangle:

<Grid>

    <Grid.Resources>
        <BackEase Amplitude="0.5" x:Key="backEase" />
    </Grid.Resources>

    <Grid.Triggers>
        <EventTrigger RoutedEvent="Grid.Loaded">
            <BeginStoryboard>
                <Storyboard TargetName="Rectangle1">
                    <DoubleAnimation Storyboard.TargetProperty="Width"
                                     From="0"
                                     EasingFunction="{StaticResource backEase}"
                                     Duration="0:0:1" />

                    <DoubleAnimation Storyboard.TargetProperty="Height"
                                     From="0"
                                     EasingFunction="{StaticResource backEase}"
                                     Duration="0:0:1" />

                    <ColorAnimation Storyboard.TargetProperty="Fill.Color"
                                    From="LightGray"
                                    Duration="0:0:1" />
                </Storyboard>

            </BeginStoryboard>
        </EventTrigger>
    </Grid.Triggers>

    <Rectangle Name="Rectangle1" Width="100" Height="100" Fill="Red" />

</Grid>    

The goal is to package up this Storyboard into a re-usable component that I can use on any Rectangle that has a SolidColorBrush Fill.  To do that, I’ll just move that <Storyboard> markup into a separate Xaml file and parameterize it.

Unfortunately there’s no single menu item for this e.g. in Visual Studio, but it’s not hard to do.  What I do is right click on the project, “Add”, “User Control”, name it something like “MyRectangleStoryboard.xaml”, and get something like this:

<UserControl x:Class="CustomStoryboard.MyRectangleStoryboard"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <Grid>
            
    </Grid>
</UserControl>

… and this:

public partial class MyRectangleStoryboard : UserControl
{
    public MyRectangleStoryboard()
    {
        InitializeComponent();
    }
}

Now, just change “UserControl” to “Storyboard (and remove the <Grid> from the Xaml), and you’re all set:

<Storyboard x:Class="CustomStoryboard2.MyRectangleStoryboard"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    
</Storyboard> 
public partial class MyRectangleStoryboard : Storyboard
{
    public MyRectangleStoryboard()
    {
        InitializeComponent();
    }
} 

The goal is to use this component instead of the storyboard/animation markup:

<Grid>
    <Grid.Resources>
        <BackEase Amplitude="0.5" x:Key="backEase" />
    </Grid.Resources>

    <Grid.Triggers>
        <EventTrigger RoutedEvent="Grid.Loaded">
            <BeginStoryboard>
                <l:MyRectangleStoryboard TargetName="Rectangle1" Duration="0:0:1" EasingFunction="{StaticResource backEase}"/>
            </BeginStoryboard>
        </EventTrigger>
    </Grid.Triggers>

    <Rectangle Name="Rectangle1" Width="100" Height="100" Fill="Red" />

</Grid>

As for the implementation of the MyRectangleStoryboard here’s the markup, which is cut/paste from the original with a couple of modifications for parameterization (which I’ll explain):

<Storyboard x:Class="CustomStoryboard.MyRectangleStoryboard"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:l="clr-namespace:CustomStoryboard" >

    <DoubleAnimation Storyboard.TargetProperty="Width"
                     From="0"
                     Duration="{Binding Duration, Source={l:XRoot}}"
                     EasingFunction="{Binding EasingFunction, Source={l:XRoot}}" />

    <DoubleAnimation Storyboard.TargetProperty="Height"
                     From="0"
                     Duration="{Binding Duration, Source={l:XRoot}}"
                     EasingFunction="{Binding EasingFunction, Source={l:XRoot}}" />

    <ColorAnimation Storyboard.TargetProperty="Fill.Color"
                    From="LightGray"
                    Duration="{Binding Duration, Source={l:XRoot}}" />

    
</Storyboard>

Notice that this custom storyboard is parameterized for the setting of the Duration and the EasingFunction properties on the animations.  (Recall that these two properties were set when the MyRectangleStoryboard was used above).

Ordinarily, if you set the Duration on the storyboard it doesn’t inherit down to its children timelines, instead it clips its children timelines.  But I want the MyRectangleStoryboard.Duration to actually be the Double/ColorAnimation.Duration, so I bound the Animation.Duration properties to the root’s Duration property. 

The source of this binding is a custom “XRoot” markup extension that I wrote.  You could equivalently establish this binding in code, and in fact you pretty much have to today.  But in WPF4 (which is now in beta), you can write a custom MarkupExtension to access the root of a Xaml document, using the new IRootObjectProvider service.  Here’s the implementation:

public class XRoot : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var rootProvider = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
        if (rootProvider == null)
            return null;

        return rootProvider.RootObject;
    }
}

The other parameterization on this MyRectangleStoryboard component is the EasingFunction.  This is a custom property on MyRectangleStoryboard so that I can set it any time I use it.  The implementation is just a DependencyProperty:

public partial class MyRectangleStoryboard : Storyboard
{
    public MyRectangleStoryboard()
    {
        InitializeComponent();
    }

    public EasingFunctionBase EasingFunction
    {
        get { return (EasingFunctionBase)GetValue(EasingFunctionProperty); }
        set { SetValue(EasingFunctionProperty, value); }
    }

    public static readonly DependencyProperty EasingFunctionProperty =
        DependencyProperty.Register("EasingFunction", typeof(EasingFunctionBase), typeof(MyRectangleStoryboard));

}

So in the end there’s three takeaways from this:

  • You can create powerful custom animation sets by subclassing Storyboard.
  • Xaml is a useful component definition language, it’s not just for controls.
  • In WPF4, Xaml markup extensions can get explicit access to the lexical root of the Xaml document.

Posted Friday, November 20, 2009 3:19 PM by MikeHillberg | 0 Comments

TreeView and HierarchicalDataTemplate, Step-by-Step

I’ve never found TreeView to be terribly confusing by itself.  But usually I want to data bind a TreeView to a collection with some hierarchy, which leads me to HierarchicalDataTemplate, which didn’t always just write itself for me.  If you look at it in steps, though, there really is a pretty nice progression from ListBox to TreeView.  Like a lot of my posts, this goes way too deep if you just want to create a quick TreeView.  But it’s useful to look at it if you want the HierarchicalDataTemplate to just write itself …

 

First, start with the example of a simple ListBox:

 

<ListBox>

    <sys:String>Western Conference</sys:String>

    <sys:String>Eastern Conference</sys:String>

</ListBox>

clip_image001

 

We can add a trivial ItemTemplate to it:

 

<ListBox>

    <ListBox.ItemTemplate>

        <DataTemplate>

            <TextBlock Foreground="Red" Text="{Binding}" />

        </DataTemplate>

    </ListBox.ItemTemplate>

    <sys:String>Western Conference</sys:String>

    <sys:String>Eastern Conference</sys:String>

</ListBox>

clip_image002

 

… and now the list box items are red.

 

A ListBox is an ItemsControl, and what ItemsControls like to do is wrap their items in a container (more on that here).  So the above essentially becomes:

 

<ListBox>

   

    <ListBoxItem Content="Eastern Conference">

        <ListBoxItem.ContentTemplate>

            <DataTemplate x:Name="_template1">

                <TextBlock Foreground="Red" Text="{Binding}" />

                  </DataTemplate>

        </ListBoxItem.ContentTemplate>

    </ListBoxItem>

    <ListBoxItem Content="Western Conference"

                 ContentTemplate="{Binding   ElementName=_template1}" />

</ListBox>

clip_image003

 

That is, for each item in the ListBox, a ListBoxItem is created.  The ListBoxItem’s Content property is bound to the item, and the ListBoxItem’s ContentTemplate property is bound to the ListBox’s ItemTemplate.

 

On to TreeView …

 

A TreeView is also an ItemsControl; it generates TreeViewItem controls for its items.  And actually, if you just take the original example and change “ListBox” to “TreeView”, it looks almost the same:

 

<TreeView>

    <sys:String>Eastern Conference</sys:String>

    <sys:String>Western Conference</sys:String>

</TreeView>

clip_image004

 

You can similarly set an explicit item template:

 

<TreeView>

    <TreeView.ItemTemplate>

        <DataTemplate>

            <TextBlock Foreground='Red' Text='{Binding}' />

        </DataTemplate>

    </TreeView.ItemTemplate>

 

    <sys:String>Western Conference</sys:String>

    <sys:String>Eastern Conference</sys:String>

</TreeView>

clip_image005

 

The next step here caught be by surprise when I first saw it.  The TreeView is going to create a TreeViewItem for each of its items (the two strings).  A TreeViewItem is a HeaderedItemsControl, which is an ItemsControl (just like ListBox and TreeView), but also has a Header (and HeaderTemplate) property.  (The Header is the part of the tree view item that you always see, right next to the expand/collapse button.)  The Header/HeaderTemplate of a HeaderedItemsControl is analogous to the Content/ContentTemplate of a ContentControl.  So, whereas the ListBox items were bound to each ListBoxItem’s Content property, in the TreeView case the items are bound to the TreeViewItem’s Header property.  Similarly, the TreeView’s ItemTemplate property is bound to the TreeViewItem’s HeaderTemplate property.  And in the end we essentially have:

 

<TreeView>

   

    <TreeViewItem Header='Western Conference'>

       

        <TreeViewItem.HeaderTemplate>

            <DataTemplate x:Name='_template2'>

                <TextBlock Foreground='Red' Text='{Binding}' />

            </DataTemplate>

        </TreeViewItem.HeaderTemplate>

       

    </TreeViewItem>

 

    <TreeViewItem Header='Eastern Conference'

                  HeaderTemplate='{Binding ElementName=_template2}' />

</TreeView>

clip_image006

 

Now what we need is some hierarchy.  Those items are the MLS conferences, and there’s supposed to be teams in those conferences.  Here’s the full data:

 

var western = new Conference("Western")

{

    Teams =

    {

        new Team("Club Deportivo Chivas USA"),

        new Team("Colorado Rapids"),

        new Team("FC Dallas"),

        new Team("Houston Dynamo"),

        new Team("Los Angeles Galaxy"),

        new Team("Real Salt Lake"),

        new Team("San Jose Earthquakes"),

        new Team("Seattle Sounders FC"),

        new Team("Portland 2011"),

        new Team("Vancouver 2011")

    }

};

var eastern = new Conference("Eastern")

{

    Teams =

    {

        new Team("Chicago Fire"),

        new Team("Columbus Crew"),

        new Team("D.C. United"),

        new Team("Kansas City Wizards"),

        new Team("New York Red Bulls"),

        new Team("New England Revolution"),

        new Team("Toronto FC"),

        new Team("Philadelphia Union 2010")

    }

};

var league = new Collection<Conference>() { western, eastern };

DataContext = new

{

    WesternConference = western,

    EasternConference = eastern,

    League = league

};

 

(Note that the DataContext now has this sample data.)

 

And now we can show all the teams with some explicit hierarchy:

 

<TreeView>

    <TreeViewItem Header='Western Conference'

          ItemsSource="{Binding WesternConference.Teams}">

        <TreeViewItem.ItemTemplate>

            <DataTemplate>

                <!-- Team name -->

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

            </DataTemplate>

        </TreeViewItem.ItemTemplate>

    </TreeViewItem>

 

    <TreeViewItem Header='Eastern Conference'

          ItemsSource="{Binding EasternConference.Teams}">

        <TreeViewItem.ItemTemplate>

            <DataTemplate>

                <!-- Team name -->

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

            </DataTemplate>

        </TreeViewItem.ItemTemplate>

    </TreeViewItem>

</TreeView>

 

clip_image007

Of course, what you really want to do is bind the TreeView itself to the hierarchical collection (the League of the DataContext).  I.e.:

 

<TreeView ItemsSource="{Binding League}" />

clip_image008

 

 

As you can see, there’s two items, as you’d expect, just like a ListBox.  Also just like a ListBox, it doesn’t show anything except for the ToString() of the Conference object.  So we need to give it an ItemTemplate to show the conference Name:

 

<TreeView ItemsSource="{Binding League}">

    <TreeView.ItemTemplate>

        <DataTemplate>

            <TextBlock Foreground="Red" Text="{Binding Name}" />

        </DataTemplate>

    </TreeView.ItemTemplate>

</TreeView>

clip_image009

 

Now, recall that the TreeView here is creating two TreeViewItems, binding the TreeViewItem’s Header to the Conference object, and setting the TreeViewItem’s HeaderTemplate to the TreeView’s ItemTemplate.  Next question is, how do we get the TreeViewItem’s ItemsSource bound to Conference.Teams?  That’s where the HierarchicalDataTemplate comes in. 

 

A HierarchicalDataTemplate is a DataTemplate with some extra properties.  But if you don’t use the extra properties, it’s no different than DataTemplate.  For example, change the last markup from DataTemplate to HierarchicalDataTemplate, and nothing changes:

 

<TreeView ItemsSource="{Binding League}">

    <TreeView.ItemTemplate>

        <HierarchicalDataTemplate >

            <TextBlock Foreground="Red" Text="{Binding Name}" />

        </HierarchicalDataTemplate>

    </TreeView.ItemTemplate>

</TreeView>

clip_image010

 

But HierarchicalDataTemplate adds two key properties:  ItemsSource and ItemTemplate.  The ItemsSource gets mapped to the TreeViewItem.ItemsSource, and the ItemTemplate gets mapped to the TreeViewItem.ItemTemplate.  So now we can show the conferences and the teams:

 

<TreeView ItemsSource="{Binding League}">

   

    <!-- Conference teamplate -->

    <TreeView.ItemTemplate>

        <HierarchicalDataTemplate ItemsSource="{Binding Teams}">

            <TextBlock Foreground="Red" Text="{Binding Name}" />

           

            <!-- Team template -->

            <HierarchicalDataTemplate.ItemTemplate>

                <DataTemplate>

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

                </DataTemplate>

            </HierarchicalDataTemplate.ItemTemplate>

           

        </HierarchicalDataTemplate>

    </TreeView.ItemTemplate>

   

</TreeView>

clip_image011

 

And if you want to show one level deeper in the hierarchy, you can change that team DataTemplate to a HierarchicalDataTemplate:

 

<TreeView ItemsSource="{Binding League}">

   

    <!-- Conference template -->

    <TreeView.ItemTemplate>

        <HierarchicalDataTemplate ItemsSource="{Binding Teams}">

            <TextBlock Foreground="Red" Text="{Binding Name}" />

           

            <!-- Team template -->

            <HierarchicalDataTemplate.ItemTemplate>

                <HierarchicalDataTemplate ItemsSource="{Binding Players}">

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

 

                    <!-- Player template -->

                    <HierarchicalDataTemplate.ItemTemplate>

                        <DataTemplate>

                            <TextBlock Text="{Binding}" />

                        </DataTemplate>

                    </HierarchicalDataTemplate.ItemTemplate>

                   

                </HierarchicalDataTemplate>

            </HierarchicalDataTemplate.ItemTemplate>

           

        </HierarchicalDataTemplate>

    </TreeView.ItemTemplate>

   

</TreeView>

 

 

clip_image012

In the end, the bottom line that I keep in mind when I’m writing a HierarchicalDataTemplate, is that it’s the template for the TreeViewItem.Header, and it lets me set the TreeViewItem’s ItemsSource and ItemTemplate properties.

Posted Friday, October 30, 2009 12:40 PM by MikeHillberg | 0 Comments

Filed under: ,

Attachment(s): TreeViewStepByStep.zip

Why is it called a "DependencyProperty"?

When we create new classes and members we spend a lot of time and effort to make them as usable, understandable, and discoverable as possible.  We follow the .Net Design Guidelines in general, and in particular we constantly look at how this new class relates to other classes, future plans, etc.

 

So we went through all that when naming DependencyProperty and DependencyObject.  All told we probably spent hours just on the name.  What dependency properties (DPs) boil down to in the end is property calculation and dependency tracking.  Property calculation isn’t terribly distinctive, lots of properties do that, so the essence of a DP is really the dependency tracking, thus “Dependency” properties.

 

 

Here’s an example of that, actually one piece of sample code that shows several examples of dependency tracking:

 

<StackPanel TextBlock.FontSize="22" DataContext="Hello, world">

    <StackPanel.Resources>

 

        <Style TargetType="TextBlock">

            <Setter Property="FontWeight" Value="Bold" />

            <Style.Triggers>

                <Trigger Property="IsMouseOver" Value="True">

                    <Setter Property="Background" Value="Red" />

                </Trigger>

            </Style.Triggers>

        </Style>

 

    </StackPanel.Resources>

 

    <TextBlock Text="{Binding}" />

   

</StackPanel>

 

 

The properties of this TextBlock have quite a few dependencies:

·         TextBlock.Text is dependent on the Binding, and the Binding is dependent on the DataContext in this case.  The DataContext is inheriting down from the StackPanel, so the Text property is therefore also dependent on the shape of the tree; if the TextBlock is removed from the StackPanel, it’s Text property will update.

·         TextBlock.FontSize is dependent on the tree as well.  Here, you can see it’s inheriting from the StackPanel.

·         All of the TextBlock properties depend on TextBlock.Style.  For example, here TextBlock.FontWeight is coming from the Style.

·         Similarly, TextBlock.Background depends on the Style, but here it’s being set in trigger.  So TextBlock.Background in this case also depends on TextBlock.IsMouseOver.

 

Sometimes, if you write your own DP, you need to help with the dependency tracking.  You do this by calling InvalidateProperty when a property needs to be re-calculated, usually because you reference it in a CoerceValueCallback.

 

For example, here’s a DP Foo and a companion (read-only) DP named FooPlus1.  FooPlus1 just has a CoerceValueCallback that calculates “Foo+1”.  Foo therefore has a PropertyChangedCallback that invalidates FooPlus1 whenever Foo changes.

 

//

// Foo property

//

 

public int Foo

{

    get { return (int)GetValue(FooProperty); }

    set { SetValue(FooProperty, value); }

}

 

public static readonly DependencyProperty FooProperty =

    DependencyProperty.Register("Foo", typeof(int), typeof(Window1),

                                new PropertyMetadata(FooChangedCallback));

 

static void FooChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args)

{

    // Whenever Foo changes, we need to invalidate FooPlus1, so that

    // the DependencyProperty system knows to update it (call its

    // CoerceValueCallback again).

    (d as Window1).InvalidateProperty(Window1.FooPlus1Property);

}

 

 

//

// FooPlus1 Property

//

 

public int FooPlus1

{

    get { return (int)GetValue(FooPlus1Property); }

}

 

static readonly DependencyPropertyKey FooPlus1PropertyKey =

    DependencyProperty.RegisterReadOnly("FooPlus1", typeof(int), typeof(Window1),

                                new PropertyMetadata(0, null, CoerceFooPlus1Callback));

static readonly DependencyProperty FooPlus1Property = FooPlus1PropertyKey.DependencyProperty;

 

static object CoerceFooPlus1Callback(DependencyObject d, object baseValue)

{

    return (d as Window1).Foo + 1;

}

 

 

Posted Friday, March 27, 2009 10:04 AM by MikeHillberg | 5 Comments

Filed under:

Implementing SelectedValue with the Silverlight ComboBox

Here’s the scenario … You have a Customers collection and an Orders collection.  In the Orders collection, an Order has a CustomerID property; this is the key to an item in the Customers collection.  Your goal is a ComboBox that updates the CustomerID property of a Customer, but interacts with the end user in terms of customer names.  If that doesn’t make sense, code & pictures help …

 

Here’s how you can accomplish this in markup in WPF (the l:Collection here is just a Collection<object>):

 

<Grid>

    <Grid.Resources>

       

        <l:Collection x:Key="Customers"> <!-- Collection<object> -->

            <l:Customer ID="1" CustomerName="Wilma" />

            <l:Customer ID="2" CustomerName="Betty" />

            <l:Customer ID="3" CustomerName="Fred" />

            <l:Customer ID="4" CustomerName="Barney" />

        </l:Collection>

 

        <l:Collection x:Key="Orders"> <!-- Collection<object> -->

            <l:Order Description="Magic carpet" CustomerID="1" />

            <l:Order Description="Blue suede shoes" CustomerID="4" />

            <l:Order Description="Hanna Montana wig" CustomerID="1" />

        </l:Collection>

 

    </Grid.Resources>

 

    <StackPanel>

       

        <!-- Show a customer ID -->

        <StackPanel Orientation="Horizontal">

            <TextBlock Text="Customer ID:" Margin="5"/>

            <TextBlock Name="TextBlock1" Margin="5"  Text="1"/>

        </StackPanel>

 

        <!-- Pick the customer ID that will be shown above -->

        <ComboBox ItemsSource="{StaticResource Customers}"

                  SelectedValuePath="ID"

                  SelectedValue="{Binding Text, ElementName=TextBlock1}"

                  DisplayMemberPath="CustomerName" />

       

    </StackPanel>

 

 

</Grid>

 

More on that ComboBox after these pictures,

 

Initially we get this:

 

clip_image002

… and here I am opening the ComboBox, and changing the customer to Betty (her CustomerID is 2):

clip_image004

 

So that changed the “Customer ID” from 1 to 2, but I (the end user) interacted only with customer names via the ComboBox.

 

Now let’s analyze that ComboBox markup:

 

<ComboBox ItemsSource="{StaticResource Customers}"

          SelectedValuePath="ID"

          SelectedValue="{Binding Text, ElementName=TextBlock1}"

          DisplayMemberPath="CustomerName" />

 

The ItemsSource is set to the Customers list.  So when you open the ComboBox, you’ll see customers.  Specifically, for each customer in that list, you’ll see the Customer.CustomerName, because the DisplayMemberPath is set to the “CustomerName” property.

 

Now whatever is selected in the ComboBox, I want it to show up in TextBlock1.  If I just bind SelectedItem to TextBlock1, I’d be sending the whole Customer object over, but really I just want the TextBlock.Text to show the Customer.CustomerID.  So, instead of binding SelectedItem to the TextBlock, I bind SelectedValue.  SelectedValue behaves the same as SelectedItem, though, until you set a SelectedValuePath.  When you set SelectedValuePath, then SelectedValue becomes that path into the SelectedItem.  For example, if your SelectedItem is a Customer, and your SelectedValuePath is “ID”, then SelectedValue is going to be Customer.ID, which is just what we want.

 

(Note in this example I defaulted TextBlock1.Text to ‘1’.  Otherwise the SelectedValue binding gets confused.  Alternatively, I could have set the Binding.Mode to ‘OneWayToSource’.)

 

 

Next let’s do this with the Silverlight ComboBox, because it doesn’t have the SelectedValue or SelectedValuePath properties.  But we can still get it to work with a little bit of code. 

 

Update: The Silverlight 4 Beta does now have SelectedValue and SelectedValuePath properties.

 

Change the markup to this:

 

<ComboBox ItemsSource="{StaticResource Customers}"

          DisplayMemberPath="CustomerName"

          SelectionChanged="ComboBox_SelectionChanged" />

 

… and write a little code:

 

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

    var comboBox = sender as ComboBox;

    TextBlock1.Text = (comboBox.SelectedItem as Customer).ID.ToString();

}

 

 

 

It’d be nice if we could do this all in markup, though, and in fact there have been some posts on the Silverlight forums to do just that with a Binding value converter.  Also, I wish I could have this feature on a TextBlock too, not just on ComboBox.  So below is a handy value converter that can be used with a Binding.  (I created this on the Silverlight 3 beta, but most of this is applicable to Silverlight 2 as well.)  The idea of this is to mimic the ComboBox mechanism.  First, here’s an example of the converter being created:

 

<Grid.Resources>

    <l:Collection x:Key="Customers"> <!-- Collection<object> -->

      ...

    </l:Collection>

   

    <l:Collection x:Key="Orders"> <!-- Collection<object> -->

      ...

    </l:Collection>

 

    <l:ValueToItemConverter x:Key="CustomerID2Name"

                            ItemsSource="{StaticResource Customers}"

                            ValuePath="ID"

                            DisplayMemberPath="CustomerName" />

</Grid.Resources>

 

… notice that it looks much like the ComboBox.  The only thing it doesn’t have is a counterpart to the SelectedValue property, because the value is what comes into the IValueConverter methods.  Now let’s use it in a trivial master/detail:

 

<Grid >

    <Grid.ColumnDefinitions>

        <ColumnDefinition/>

        <ColumnDefinition/>

    </Grid.ColumnDefinitions>

 

    <!-- List of Orders -->

    <ListBox ItemsSource="{StaticResource Orders}" Name="ListBox1">

        <ListBox.ItemTemplate>

            <DataTemplate>

                <TextBlock Text="{Binding Description}" />

            </DataTemplate>

        </ListBox.ItemTemplate>

    </ListBox>

 

    <!-- Detail for the selected Order -->

    <StackPanel Grid.Column="1"

                DataContext="{Binding SelectedItem, ElementName=ListBox1}">

        <TextBlock Text="{Binding Description}" />

       

        <!-- Order.CustomerID convertered to a Customer.CustomerName -->

        <TextBlock Text="{Binding CustomerID,

                                  Converter={StaticResource CustomerID2Name}}" />

    </StackPanel>

</Grid>

 

… which gives us this (here I’ve selected the first order):

clip_image006

The key here is that in the detail, rather than showing CustomerID 1 from the Order, we see “Wilma”.

 

 

And finally, here’s an example using this value converter in the column of a DataGrid.  Again, the items are Orders, and the column shows a CustomerID as a CustomerName, using a TextBlock when the cell isn’t being edited, and as a ComboBox when it is. 

 

First, create a version of the value converter that doesn’t have DisplayMemberPath set (because ComboBox has its own DisplayMemberPath property):

 

<l:ValueToItemConverter x:Key="CustomerID2Customer"

                        ItemsSource="{StaticResource Customers}"

                        ValuePath="ID" />

 

… and then the DataGrid itself:

 

<d:DataGrid AutoGenerateColumns="False"

            ItemsSource="{StaticResource Orders}">

    <d:DataGrid.Columns>

        <d:DataGridTextColumn Binding="{Binding Description}" />

       

        <d:DataGridTemplateColumn>

           

            <d:DataGridTemplateColumn.CellTemplate>

                <DataTemplate>

                    <TextBlock Text="{Binding CustomerID,

                                              Converter={StaticResource CustomerID2Name}}"

                               Margin="4"/>

                </DataTemplate>

            </d:DataGridTemplateColumn.CellTemplate>

           

            <d:DataGridTemplateColumn.CellEditingTemplate>

                <DataTemplate>

                    <ComboBox

                         DisplayMemberPath="CustomerName"

                         ItemsSource="{StaticResource Customers}"

                         SelectedItem="{Binding CustomerID, Mode=TwoWay,

                                          Converter={StaticResource CustomerID2Customer}}" />

                </DataTemplate>

            </d:DataGridTemplateColumn.CellEditingTemplate>

           

        </d:DataGridTemplateColumn>

    </d:DataGrid.Columns>

</d:DataGrid>

 

 

 

Posted Thursday, March 26, 2009 10:53 PM by MikeHillberg | 9 Comments

Filed under:

Attachment(s): SelectedValue.zip

An ICommand with IsEnabled

ICommand is a simple interface with three members – Execute, CanExecute, and CanExecuteChanged (more on those here).  You can write your own implementations of that interface, one for each command, but that gets a bit heavyweight.  So there are several implementations of ICommand that are pluggable and re-usable, like DelegateCommand, RelayCommand, and RoutedCommand.

 

One tricky part of implementing ICommand is the CanExecuteChanged event, because you have to know when to raise it.  (The easy alternative is to raise it whenever the CommandManager.RequerySuggested event is raised, but you need to use that with caution, because it can happen frequently and get expensive.) 

 

Really, though, CanExecute and CanExecuteChanged look like a typical Foo/FooChanged property/event pair that we use in data binding.  The reason that CanExecute isn’t actually a property, though, is that it takes an optional parameter.  But if you’re not using that parameter, a property would work just as well.

 

So I’ve been playing with an ICommand implementation that takes a delegate for the Execute method (just like DelegateCommand and RelayCommand), but also has an IsEnabled property that wraps CanExecute/CanExecuteChanged.  And since it’s all a DependencyObject, you can put a binding on IsEnabled.

 

I wasn’t sure what to call it though.  The key is that it has an IsEnabled property, but DelegateCommandWithIsEnabled didn’t sound good.  And it turns out that Enabled-able isn’t a word.  The closest thing I could come up with that had “able” in it was AbilityCommand.  Good enough for a blog post.

 

Anyway, here’s what the public members look like:

 

public class AbilityCommand : DependencyObject, ICommand

{

    public AbilityCommand(

        Action<object> executeDelegate,

        Binding isEnabledBinding );

 

    public AbilityCommand( Action<object> executeDelegate )

        : this( executeDelegate, null )  {  }

   

    public bool IsEnabled {get; set; }

    public static readonly DependencyProperty IsEnabledProperty ...;

 

}

 

Note that you can set IsEnabled however you want, including with a binding, and in fact the constructor lets you pass in the Binding to use.  AbilityCommand’s ICommand.CanExecute simply returns IsEnabled.  And any time IsEnabled’s value changes, AbilityCommand raises ICommand.CanExecuteChanged.

 

And here it is in practice.  In this snippet, the (View)Model object is the first page of a wizard-type view (with a “Next” button on it).  The SubmitCommand property binds the command’s IsEnabled to the object’s IsValid property:

 

public class FirstPage : INotifyPropertyChanged

{

    public FirstPage()

    {

        SubmitCommand = new AbilityCommand(

            (x) => OnSubmit(),

            new Binding("IsValid") { Source=this } );

 

    }

    public ICommand SubmitCommand { get; private set; }

 

    private void OnSubmit()

    {

        // ...

    }

 

    // INotifyPropertyChanged fires for this IsValid property if

    // either the "FirstProperty" or "SecondProperty" property is updated

    public bool IsValid

    {

        get

        {

            return !String.IsNullOrEmpty(FirstProperty)

                && !String.IsNullOrEmpty(SecondProperty);

        }

    }

 

 

 

 

And finally, here’s the whole AbilityCommand:

 

public class AbilityCommand : DependencyObject, ICommand

{

    Action<object> _executeDelegate;

 

    public AbilityCommand( Action<object> executeDelegate )

        : this( executeDelegate, null )  {  }

   

    public AbilityCommand(

        Action<object> executeDelegate,

        Binding isEnabledBinding )

    {

        _executeDelegate = executeDelegate;

 

        if( isEnabledBinding != null )

            BindingOperations.SetBinding(this, IsEnabledProperty, isEnabledBinding);

    }

 

    void ICommand.Execute(object parameter)

    {

        _executeDelegate(parameter);

    }

 

    bool ICommand.CanExecute(object parameter)

    {

        return IsEnabled;

       

    }

    public event EventHandler CanExecuteChanged;

 

    private void RaiseCanExecuteChanged()

    {

        if( CanExecuteChanged != null )

            CanExecuteChanged( this, new EventArgs() );

    }

 

    public bool IsEnabled

    {

        get { return (bool)GetValue(IsEnabledProperty); }

        set { SetValue(IsEnabledProperty, value); }

    }

    public static readonly DependencyProperty IsEnabledProperty =

        DependencyProperty.Register("IsEnabled", typeof(bool), typeof(AbilityCommand),

                            new PropertyMetadata(OnIsEnabledChanged));

 

    static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)

    {

        (d as AbilityCommand).RaiseCanExecuteChanged();

    }

}

 

 

Posted Monday, March 23, 2009 10:13 PM by MikeHillberg | 4 Comments

Filed under:

ICommand is like a chocolate cake

(This has been updated with some information about the origin of a routed command’s route, and of focus scopes.)

 

ICommand in WPF is a pretty simple thing at its core.  But it gets more interesting and complicated as you build up functionality on top of it, and integrate it into the higher layers of the UI.  So it’s either like a layer cake, or layers of an onion.  But onion is an outside-in metaphor, and layer cake is a bottom-up metaphor, and ICommand is easier to think of bottom up, so I’m declaring ICommand to be a layer cake.

 

For example, commands provide a mechanism to abstract input (so “navigate back” means “navigate back”, whether it came from the keyboard’s back button or the mouse’s X1 button or anywhere else).  And commands provide a mechanism for the View to update the Model in a Model/View separated application.  And commands provide a way to search the element tree for a command handler, as well as a way for a command handler to say that it doesn’t want to be executed at the moment.

 

The mechanism for all those scenarios is all very similar, but the scenarios themselves seem so different that it all can get confusing.  So here’s a step-by-step description of how it all fits together …

 

 

Start with ICommand

 

ICommand itself is very straightforward:

 

public interface ICommand

{

    void Execute(object parameter);

 

    bool CanExecute(object parameter);

    event EventHandler CanExecuteChanged;

}

 

Given an instance of an ICommand, you just call Execute, and it does whatever it’s supposed to do.  Except you shouldn’t call it if it’s CanExecute is false.  If you want to know when CanExecute might be willing to give you a different answer, listen to the CanExecuteChanged event.

 

For example, here’s a super simple command:

 

public class HelloWorldCommand : ICommand

{

    public void Execute(object parameter)

    {

        Debug.WriteLine("Hello, world");

    }

 

    public bool CanExecute(object parameter)

    {

        return true;

    }

    public event EventHandler CanExecuteChanged;

}

 

If you use that like this:

 

new HelloWorldCommand().Execute(null);

 

… you’ll see “Hello, world” in the debug output window.

 

You can make it more interesting with a parameter and by checking CanExecute.  First change the command like this:

 

public class HelloWorldCommand : ICommand

{

    public void Execute(object parameter)

    {

        Debug.WriteLine(parameter);

    }

 

    public bool CanExecute(object parameter)

    {

        return parameter != null;

    }

    public event EventHandler CanExecuteChanged;

}

 

… and call it like this:

 

var hwc = new HelloWorldCommand();

if (hwc.CanExecute(this))

    hwc.Execute(this);

 

… and in my case I see “CommandCake.Window1” in the debugger output window.

 

Piece o’ cake.

 

 

Button (heart) ICommand

 

Once you have an ICommand instance handy, you can give it to a Button (on the Button.Command property), and Button knows what to do with it.  As the simplest example, you can do this with the previous command:

 

<Grid>

  <Grid.Resources>

    <local:HelloWorldCommand x:Key="hwc"/>

  </Grid.Resources>

 

  <Button Command="{StaticResource hwc}">

    Click

  </Button>

</Grid>

 

But if you do that, you’ll notice that the Button is disabled.  That’s because Button knows to call CanExecute, but we haven’t specified a parameter, and recall from above that if you pass null as an argument to CanExecute it returns false.   So Button has a CommandParameter property that lets you specify what will be passed to CanExecute and Execute:

 

<Grid>

  <Grid.Resources>

    <local:HelloWorldCommand x:Key="hwc"/>

  </Grid.Resources>

 

  <Button CommandParameter="Hello, world"  Command="{StaticResource hwc}"  >

    Click

  </Button>

</Grid>

 

Now the button is enabled, and if you click on it, you’ll see “Hello, world” in the debug output window.

 

This actually isn’t just a Button feature, it’s actually in the base class ButtonBase.  And MenuItem is a ButtonBase.  So MenuItem (heart) ICommand too.

 

 

Update the Model from the View

 

Now we’ve got enough for the classic Model/View usage of ICommand.  In the Model/View practice, your View is a bunch of elements, which is data-bound to your Model, which has your actual content.  The View can modify the model with two-way bindings and with commands.

 

First, before showing an example of this, let’s make it easier to implement ICommand, by introducing a helper class (a more complete DelegateCommand helper can be found in Prism).  This just creates an ICommand instance that takes a delegate which will be called by ICommand.Execute:

 

public class SimpleDelegateCommand : ICommand

{

    Action<object> _executeDelegate;

 

    public SimpleDelegateCommand(Action<object> executeDelegate)

    {

        _executeDelegate = executeDelegate;

    }

 

    public void Execute(object parameter)

    {

        _executeDelegate(parameter);

    }

 

    public bool CanExecute(object parameter) { return true; }

    public event EventHandler CanExecuteChanged;

}

 

 

Now let’s define a Model of a simple Debug writer:

 

public class DebugWriter

{

    ICommand _indentCommand =    new SimpleDelegateCommand( (x) => Debug.Indent() );

    ICommand _unindentCommand =  new SimpleDelegateCommand( (x) => Debug.Unindent() );

    ICommand _writeLineCommand = new SimpleDelegateCommand( (x) => Debug.WriteLine(x) );

 

    public ICommand IndentCommand { get { return _indentCommand; } }

    public ICommand UnindentCommand { get { return _unindentCommand; } }

    public ICommand WriteLineCommand { get { return _writeLineCommand; } }

 

    public int IndentSize

    {

        get { return Debug.IndentSize; }

        set { Debug.IndentSize = value; }

    }

}

 

 

… and use it from a View:

 

<StackPanel>

  <StackPanel.DataContext>

    <local:DebugWriter />

  </StackPanel.DataContext>

 

  <Grid>

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="Auto"/><ColumnDefinition />

    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>

      <RowDefinition/><RowDefinition/>

    </Grid.RowDefinitions>

 

    <TextBlock Margin="3">Indent size:</TextBlock>

    <TextBlock Margin="4" Grid.Column="1" Text="{Binding IndentSize}" />

 

    <TextBlock Grid.Row="1" Margin="3">Output string:</TextBlock>

    <TextBox Text="s" Grid.Row="1" Grid.Column="1" Name="OutputString" />

   

  </Grid>

 

  <Button Command="{Binding IndentCommand}">Indent</Button>

  <Button Command="{Binding UnindentCommand}">Unindent</Button>

  <Button CommandParameter="{Binding Text,ElementName=OutputString}"

          Command="{Binding WriteLineCommand}">WriteLine</Button>

   

</StackPanel>

 

Notice here that the Button bound to the WriteLine command (the third Button) has its CommandParameter bound to a TextBox.Text.

 

 

Routed commands: an ICommand.Execute implementation that searches for an execute handler

 

For the above example I used the SimpleDelegateCommand as my implementation of ICommand, which maps ICommand.Execute to a delegate call. 

 

WPF similarly has a built-in ICommand implementation called RoutedCommand.  You don’t give a delegate directly to the RoutedCommand, though.  Instead, the RoutedCommand walks up the tree, looking for a delegate.  It’s similar to a routed event, in fact it’s implemented as a routed event internally.  So you can put your delegate anywhere higher in the tree, in the form of an event handler, using the CommandBinding class, and it will be called by Execute.  You specify your delegate with a CommandBinding object.

 

So I add this to my Window1.xaml.cs:

 

public static RoutedCommand HelloWorldRoutedCommand = new RoutedCommand();

 

… and this to my Xaml:

 

<Window.CommandBindings>

    <CommandBinding

          Command="{x:Static local:Window1.HelloWorldRoutedCommand}"

          Executed="CommandBinding_Executed" />

</Window.CommandBindings>

 

<Grid>

    ...

    <Button Command="{x:Static local:Window1.HelloWorldRoutedCommand}">Hello, world </Button>

    ...

</Grid>

 

… and then when I click the Button, my CommandBinding_Executed method gets called:

 

private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)

{

    if( e.Source is Button )

        Debug.WriteLine((e.Source as Button).Content);

    else

        Debug.WriteLine("Hello, world");

}

 

… and once again I see “Hello, world” in the debug output.

 

Note that the event handler of the CommandBinding.Executed event can look at the Source property of the event args to see where the RoutedCommand started.  You can also get the command itself, and the CommandParameter from the args.

 

In this example I put the CommandBinding in the tree ancestry of the Button.  Alternatively, you can also register your CommandBinding globally, using the CommandManager.RegisterClassCommandBinding method.  That CommandBinding’s CanExecute then gets called no matter where in the tree the routed command’s route is starting.

 

RoutedCommand also has support for CanExecute and CanExecuteChanged.  That’s the most complicated part of routed commands, so I’m saving that for the end.

 

 

Mapping input to an ICommand (keyboard accelerators)

 

Beyond Button and MenuItem, there’s another way to get an ICommand to Execute, this one based on user input.  As an example, with the following Xaml, pressing <Control>H on the keyboard executes our HelloWorldCommand, again showing “Hello, world” in the debugger:

 

<Grid>

    <Grid.Resources>

        <local:HelloWorldCommand x:Key="hwc"/>

    </Grid.Resources>

 

    <Grid.InputBindings>

        <KeyBinding Gesture="Control+H" Command="{StaticResource hwc}" CommandParameter="Hello, world"/>

    </Grid.InputBindings>

    ...

 

 

Note that this key binding only takes effect if the keyboard focus is currently somewhere under that Grid, e.g. on a TextBox; otherwise the KeyBinding doesn’t see the <Control>H.

 

RoutedCommand also has an InputGestures property on it, where you can set the default gestures for a command.  So you can get this same behavior for the HelloWorldRoutedCommand we created earlier by defining it like this:

 

public static RoutedCommand HelloWorldRoutedCommand = new RoutedCommand()

    {

        InputGestures = { new KeyGesture(Key.H, ModifierKeys.Control) }

    };

 

… and then it will Execute no matter where your keyboard focus is.

 

 

Built-in routed commands in WPF

 

Anyone can create a RoutedCommand.  But WPF has a set of them built-in.  These are all defined as static fields in the ApplicationCommands, EditingCommands, MediaCommands, and NavigationCommands classes.  E.g. NavigationCommands has the BrowseBack command, ApplicationCommands has cut/copy/paste, EditingCommands has ToggleBold, MoveRightByWord, etc.  The Windows WM_APPCOMMAND commands get converted into these built-in routed commands automatically by the WPF input system. 

 

So, for example, this markup:

 

<Window.CommandBindings>

 

    <CommandBinding Command="{x:Static NavigationCommands.BrowseBack}"

                    Executed="BrowseBackExecuted" />

 

</Window.CommandBindings>

 

… will cause the BrowseBackExecuted method to be called when the NavigationCommands.BrowseBack command is executed, for example if you click the mouse X1 button (this is the button that makes a web browser navigate back).

 

 

From whence the command routes

 

If you call RoutedCommand’s ICommand.Execute, the route will start from the element that currently has keyboard focus.  But RoutedCommand itself has a special overload of Execute that lets you pick where the route starts:

 

public class RoutedCommand

{

    ...

    public void Execute( Object parameter, IInputElement target);

    ...

}

 

And in fact, RoutedCommand’s implementation of ICommand.Execute just calls this Execute, looking roughly like this:

 

void ICommand.Execute(object parameter)

{

    this.Execute(parameter, Keyboard.FocusedElement );

}

 

 

Button (and MenuItem) actually understands RoutedCommand in particular, in addition to ICommand in general.  So if Button.Command is a RoutedCommand, Button by default calls this RoutedCommand.Execute method, passing itself as the target.  You can override this behavior, though, with the Button.CommandTarget property.  For example, here is a Button that sends the Cut command to a TextBox:

 

<Button Command="{x:Static ApplicationCommands.Cut}"

        CommandTarget="{Binding ElementName=TextBox1}">Click</Button>

<TextBox Name="TextBox1" />

 

This gets more complicated if you’re implementing a toolbar.  Say you have a toolbar with cut/copy/paste buttons.  You want that toolbar to work against whatever the currently focused text box is, so you don’t want to have to keep setting CommandTarget.  E.g., in this:

 

<StackPanel>

    <ToolBar>

        <Button Command="{x:Static ApplicationCommands.Cut}">Cut</Button>

        <Button Command="{x:Static ApplicationCommands.Copy}">Copy</Button>

        <Button Command="{x:Static ApplicationCommands.Paste}">Paste</Button>

    </ToolBar>

    <TextBox />

    <TextBox />

</StackPanel>

 

… the Cut/Copy/Paste should route to whichever TextBox has focus.

 

It doesn’t look like this should work, but it actually does, and here’s why … ToolBar is a “focus scope”.  Focus scopes is a whole other post, but the important thing here is what happens with a routed command that’s executing and looking for a CommandBinding.  As the execute request bubbles up to the ToolBar, it sees that it’s leaving a focus scope.  At that point, rather than just continuing to walk up the visual tree, it goes next to the focused element in that focus scope.  In this example, that means that when you click on the Cut button, the search for a handler starts at the Button, then the ToolBar, then the currently-focused TextBox, which is what we want.

 

Menu is also a focus scope, so what works for ToolBar works for Menu as well.

 

 

CanExecute and CanExecuteChanged for routed commands

 

Just like RoutedCommand allows you to define a command that walks up the tree, looking for someone to handle Execute, you also want to find someone to handle CanExecute.  So RoutedCommand’s CanExecute implementation does that, and you can listen for it on CommandBinding, as you’d expect, e.g.:

 

<CommandBinding

    Command="{x:Static local:Window1.HelloWorldRoutedCommand}"

    CanExecute="CommandBinding_CanExecute"

    Executed="CommandBinding_Executed" />

 

But here’s the trick:  RoutedCommand similarly needs to raise CanExecuteChanged.  But how does it know when to raise that event, when it doesn’t know what you’re going to do in CommandBinding_CanExecute? 

 

The solution in WPF is a global event named CommandManager.RequerySuggested.  This event is fired whenever the state of the routed command world might have changed.  In fact, the implementation of ICommand.CanExecuteChanged in RoutedCommand is just a forwarder:

 

public event EventHandler CanExecuteChanged

{

    add { CommandManager.RequerySuggested += value; }

    remove { CommandManager.RequerySuggested -= value; }

}

 

Next problem:  When should the RequerySuggested event fire?  You make that happen by calling CommandManager.InvalidateRequerySuggested.  But usually you don’t have to call it, it’s called automatically in several places, mostly during keyboard/mouse input.

 

That all creates a couple of interesting implications. 

 

First of all, if you’re using routed commands, you’ve got some work taking place on every keystroke.  On the one hand, even for a fast typist (I clocked in the other day at 88 words/minute on the high-difficulty test!), user input is very infrequent in CPU timeframes.  On the other hand, it still takes time to find those CanExecute handlers, and if those handlers do anything non-trivial, and you have a lot of them, it can add up.  We’ve seen that be a problem in some larger applications.  You can mitigate that by reducing use of routed commands (just use ICommand instead), and by keeping the CanExecute implementations fast.

 

The second interesting implication is that CommandManager.RequerySuggested is a static (global) event.  Usually such events can lead to leaks, because they hold the event handler delegate forever.  But RequerySuggested instead only keeps weak references to its handler delegates.  But now you’ve got a new problem; now the delegate can be garbage collected.  You probably don’t have to worry about that, because most applications don’t listen to RequerySuggested; it’s really the Button doing this on your behalf when you set Button.Command.  But Button (and MenuItem) deal with this problem by keeping their own strong reference on that delegate, so that it doesn’t get collected.

 

 

In summary

 

So the key points to remember here:

·         ICommand is a simple definition with Execute, CanExecute, and CanExecuteChanged.

·         You can point a Button  or MenuItem at any ICommand with the Command property.  The Button/MouseItem will then Execute that command if you click it, will disable itself if CanExecute is false, and will automatically listen for CanExecuteChanged.

·         Routed commands are an ICommand implementation that searches the tree (usually starting with the focused element) for a CommandBinding that provides Execute/CanExecute handlers.

·         You can also invoke an ICommand from input, e.g. using a KeyBinding.

·         WPF pre-defines a set of routed commands in ApplicationCommands, EditingCommands, NavigationCommands, and MediaCommands.

·         Be aware that routed commands can impact perf if you use a lot of them and/or your CanExecute handlers do non-trivial work.

 

 

 

Posted Friday, March 20, 2009 3:48 PM by MikeHillberg | 5 Comments

Filed under:

A master/detail view with navigation

I don’t remember what got me thinking about it, but somewhere along the line I wanted a master/detail view with a navigation bar.  E.g., when you change selection in the master view, you can navigate back to the previous selection. 

Here’s an example (here my selection started on “Wilma”, then I changed it to “Fred”, which is why the Back button of the navigation bar is enabled):

image

It looks like a Frame, and it is, but I just used the Frame for its navigation UI and its journaling functionality; I didn’t put anything (visible anyway) into the Content of the Frame.

Here’s the basic markup for the above Window sample:

<StackPanel>

   

    <navbar:NavigationBar

        JournaledValue="{Binding SelectedItem, ElementName=_listBox, Mode=TwoWay}"

        NavigationUIVisibility="Visible"/>

 

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="Auto" />

            <ColumnDefinition Width="*" />

        </Grid.ColumnDefinitions>

 

        <!-- Master view -->

        <ListBox ItemsSource="{Binding}" Name='_listBox' Margin="5">

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <TextBlock Text="{Binding FirstName}" Margin="2"/>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

 

        <!-- Details view (more on this later) -->

        ...

 

    </Grid>

</StackPanel>

What does this do?  The NavigationBar control has the navigation UI (forward/backward button).  It also journals whatever you put in the JournaledValue property.  That means:

  • If you change NavigationBar.JournaledValue, the old value will be added to the navigation service’s “BackStack”, and the Back button will become enabled.
  • If you then click on the Back button, NavigationBar.JournaledValue will be restored to its previous value.

So the way the properties are bound in this example, if the ListBox.SelectedItem changes, the old value gets journaled by the NavigationBar.  If you then click the Back button, the ListBox.SelectedItem gets restored to that previous value.  And of course, in the full example, the details view is similarly bound to the ListBox.SelectedItem, so it updates as well.

The NavigationBar control just has a couple of properties and a default ControlTemplate.  The properties are JournaledValue (which drives, and is driven by, the navigation bar UI), and NavigationUIVisibility (Automatic, Visible, or Hidden).  Here’s the control code:

public class NavigationBar : Control

{

    static NavigationBar()

    {

        DefaultStyleKeyProperty.OverrideMetadata(

                               typeof(NavigationBar),

                               new FrameworkPropertyMetadata(typeof(NavigationBar)));

    }

 

    // The JournaledValue property is the value that controls the navigation bar UI.

 

    public object JournaledValue

    {

        get { return (object)GetValue(JournaledValueProperty); }

        set { SetValue(JournaledValueProperty, value); }

    }

    public static readonly DependencyProperty JournaledValueProperty =

        DependencyProperty.Register("JournaledValue", typeof(object), typeof(NavigationBar));

 

 

 

    // The NavigationUIVisibility property is aliased to the

    // Frame.NavigationUIVisibility property.

 

    public NavigationUIVisibility NavigationUIVisibility

    {

        get { return (NavigationUIVisibility)GetValue(NavigationUIVisibilityProperty); }

        set { SetValue(NavigationUIVisibilityProperty, value); }

    }

    public static readonly DependencyProperty NavigationUIVisibilityProperty =

        Frame.NavigationUIVisibilityProperty.AddOwner(typeof(NavigationBar));

 

 

}

The NavigationBar’s ControlTemplate simplify forwards everything to a Frame.  Frame has the UI for the navigation controls, and journals its Content property.  So for journaling, I just bound the NavigationBar.JournaledValue to Frame.Content, and Frame takes care of the rest.  (Note that NavigationBar.NavigationUIVisibility is also bound to the corresponding property on Frame.)  The only trick is, I didn’t want the JournaledValue to actually get displayed by the Frame, so I put an empty ContentTemplate on it.  This leads to:

<ControlTemplate TargetType="{x:Type local:NavigationBar}">

 

    <Frame NavigationUIVisibility

              ='{TemplateBinding local:NavigationBar.NavigationUIVisibility}'

           Content='{Binding JournaledValue,

                             RelativeSource={RelativeSource TemplatedParent},

                             Mode=TwoWay}'

           Background="{TemplateBinding Background}"

           BorderBrush="{TemplateBinding BorderBrush}"

           BorderThickness="{TemplateBinding BorderThickness}" >

 

       <!-- Don't show the frame content, we're just using Frame

            for it's navigation UI and journaling -->

 

        <Frame.ContentTemplate>

            <DataTemplate />

        </Frame.ContentTemplate>

 

    </Frame>

 

</ControlTemplate>

 

That’s it for the NavigationBar control.  Here’s the rest of the sample app too.  For the details view, I used an ItemsControl to list the “fields” of the selected object, where each field is a HeaderedContentControl.  To get the right look, I then templated HeaderedContentControl.  Easier to show the markup.  First, the sample data:

 

<Window.DataContext>

    <x:Array Type="local:Person">

        <local:Person FirstName="Wilma" LastName="Flintstone" Age="41" />

        <local:Person FirstName="Fred" LastName="Flintstone" Age="42" />

        <local:Person FirstName="Betty" LastName="Rubble" Age="36" />

        <local:Person FirstName="Barney" LastName="Rubble" Age="35" />

    </x:Array>

</Window.DataContext>

… and then the details view:

<!-- Details view -->

<ItemsControl

         DataContext="{Binding SelectedItem, ElementName=_detailsView}"

         Grid.IsSharedSizeScope="True"

         Grid.Column="1"

         Margin="5" >

 

    <!-- For the selected item, show the first name, last name, and age -->

    <HeaderedContentControl Header="First name:" Content="{Binding FirstName}" />

    <HeaderedContentControl Header="Last name:" Content="{Binding LastName}" />

    <HeaderedContentControl Header="Age:" Content="{Binding Age}" />

 

    <!-- Create a HeaderedContentControl template to display each item

         as "Label: Value", e.g. "Age: 42" -->

    <ItemsControl.Resources>

        <Style TargetType="HeaderedContentControl">

            <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="HeaderedContentControl">

                        <Grid>

                            <Grid.ColumnDefinitions>

                                <ColumnDefinition SharedSizeGroup="FirstColumn"

                                                  Width="Auto" />

                                <ColumnDefinition SharedSizeGroup="SecondColumn" />

                            </Grid.ColumnDefinitions>

 

                            <TextBlock Text="{TemplateBinding Header}" Grid.Column="0"

                                       Margin="0,0,5,0" FontWeight="Bold"/>

                            <TextBlock Text="{TemplateBinding Content}" Grid.Column="1"

                                       Margin="0,0,5,0"/>

                        </Grid>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>

        </Style>

    </ItemsControl.Resources>

 

</ItemsControl>

 

Posted Friday, January 09, 2009 10:50 AM by MikeHillberg | 3 Comments

Filed under:

A Comparable DataTrigger

Property triggers today only check for equality.  We’d like to add support for other comparison operators, but that hasn’t happened yet.  But I needed them for a project, and wrote a workaround for it.  It’s a bit hacky in a couple of places, but if you can get past that, it’s a handy way to simplify some coding.

 

Here’s a sample of what I ended up with:

 

<DataTrigger Binding="{l:ComparisonBinding Age, LT, 65}" Value="{x:Null}" >

 

The basics:

·         You have to set the DataTrigger.Value to null.  That’s the main hack.

·         The supported comparison operators are GT, GTE, LT, LTE, and EQ.

·         The comparand (“65” in the above example) is converted from string to the type of the target value (presumably Age is an int in the above example), using Compare.ChangeType or the target’s TypeConverter.

 

That’s all there is to use it.  You have to remember to set DataTrigger.Value to null, otherwise it’s relatively straightforward.

 

And here’s the implementation:

 

//

// ComparisonBinding is a Binding that should be used in a DataTrigger.Binding.

// It supports a comparison operator and a comparand, so that you can use it as a

// conditional DataTrigger.  The trick is to set {x:Null} as the DataTrigger.Value.

// E.g.:

//

//  <DataTrigger Value={x:Null}

//               Binding={h:ComparisonBinding Width, EQ, 100}"

//

// The operator can be EQ, LT, LTE, GT, GTE.

//

 

public class ComparisonBinding : Binding

{

    // Default constructor

 

    public ComparisonBinding()

        : this(null, ComparisonOperators.EQ, null)

    {

    }

 

    // Construction with an operator & comparand

 

    public ComparisonBinding(string path, ComparisonOperators op, object comparand)

        : base(path)

    {

        RelativeSource = RelativeSource.Self;

        Comparand = comparand;

        Operator = op;

        Converter = new ComparisonConverter( this );

    }

 

    // Operator and comparand

 

    public ComparisonOperators Operator { get; set; }

    public object Comparand { get; set; }

 

}

 

// Supported types of comparisons

 

public enum ComparisonOperators

{

    EQ = 0,

    GT,

    GTE,

    LT,

    LTE

}

 

//

// Thie IValueConverter is used by the StyleBinding to

// implement the logical comparisson.  ConvertBack isn't supported.

// Convert returns null if the condition is met, non-null otherwise.

//

 

internal class ComparisonConverter : IValueConverter

{

    // Keep a back reference to the StyleBinding

    ComparisonBinding _styleBinding;

 

    // Return this if the condition isn't met

    static object _notNull = new Object();

 

    // In construction, get a reference to the StyleBinding

    public ComparisonConverter(ComparisonBinding styleBinding)

    {

        _styleBinding = styleBinding;

    }

 

 

    //

    //  IValueConverter.Convert

    //

    //  Return null of the condition is met, non-null if not.

    //

 

    public object Convert(

        object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

    {

        // Simple check for null

 

        if (value == null || _styleBinding.Comparand == null)

        {

            return ReturnHelper( value == _styleBinding.Comparand );

        }

 

        // Convert the comparand so that it matches the value

 

        object convertedComparand = _styleBinding.Comparand;

        try

        {

            // Only support simple conversions in here. 

            convertedComparand = System.Convert.ChangeType(_styleBinding.Comparand, value.GetType());

        }

        catch (InvalidCastException)

        {

            // If Convert.ChangeType didn't work, try a type converter

            TypeConverter typeConverter = TypeDescriptor.GetConverter(value);

            if (typeConverter != null)

            {

                if (typeConverter.CanConvertFrom(_styleBinding.Comparand.GetType()))

                {

                    convertedComparand = typeConverter.ConvertFrom(_styleBinding.Comparand);

                }

            }

        }

 

        // Simple check for the equality case

 

        if (_styleBinding.Operator == ComparisonOperators.EQ)

        {

            // Actually, equality is a little more interesting, so put it in

            // a helper routine

 

            return ReturnHelper(

                        CheckEquals(value.GetType(), value, convertedComparand) );

        }

 

        // For anything other than Equals, we need IComparable

 

        if (!(value is IComparable) || !(convertedComparand is IComparable))

        {

            Trace(value, "One of the values was not an IComparable");

            return ReturnHelper(false);

        }

 

        // Compare the values

 

        int comparison = (value as IComparable).CompareTo(convertedComparand);

 

        // And return the comparisson result

 

        switch (_styleBinding.Operator)

        {

            case ComparisonOperators.GT:

                return ReturnHelper( comparison > 0 );

 

            case ComparisonOperators.GTE:

                return ReturnHelper( comparison >= 0 );

 

            case ComparisonOperators.LT:

                return ReturnHelper( comparison < 0 );

 

            case ComparisonOperators.LTE:

                return ReturnHelper( comparison <= 0 );

        }

 

        return _notNull;

    }

 

    //

    // This helper produces the return value; null if the values

    // match, non-null otherwise.

    //

 

    object ReturnHelper(bool result)

    {

        return result ? null : _notNull;

    }

 

    //

    // Trace output to the debugger

    //

 

    void Trace(object value, string message)

    {

        if (Debugger.IsAttached)

        {

            Debug.WriteLine("StyleBinding couldn't convert '"

                             + value.GetType()

                             + "' to '"

                             + _styleBinding.Comparand.GetType()

                             + "'");

            Debug.WriteLine("(" + message + ")");

        }

    }

 

    //

    // Check for equality of two values

    //

 

    private bool CheckEquals(Type type, object value1, object value2)

    {

        if (type.IsValueType || type == typeof(string))

        {

            return Object.Equals(value1, value2);

        }

 

        else

        {

            return Object.ReferenceEquals(value1, value2);

        }

    }

 

    //

    //  IValueConverter.ConvertBack isn't supported.

    //

 

    public object ConvertBack(

        object value,

        Type targetType,

        object parameter,

        System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

}

 

 

Posted Tuesday, September 30, 2008 12:04 AM by MikeHillberg | 6 Comments

Compact CheckBox Sample

I was creating a view of an object that had a bunch of boolean properties, but I wanted to keep the visual representation small.  So I created a look for a compact CheckBox that I liked enough to post.

As an example scenario, say I’m visualizing a the attributes of a file (hidden, system, archive, and read-only).  I could do it with a TextBlock and some CheckBoxes easy enough, to create a look like this:

 

image

 

… but I want it more compact.  I want just a letter for each attribute, and I just want the letter to change when it’s checked.  That got me this:

 

image

 

Here, the light gray italicized boxes are un-checked, and the un-italicized black letters with a gray background are checked.  (So before capturing this screenshot, I had checked the read-only and archive bits on the “readme.txt” file.)   This image doesn’t show it, but there’s also a tooltip for each CheckBox that shows the full name of the attribute.

 

Here’s the ControlTemplate for the CheckBox:

 

<ControlTemplate TargetType="CheckBox">

  <Border BorderThickness="{TemplateBinding BorderThickness}"

          BorderBrush="Gray" Background="LightGray" Padding="2"

          Name="_border" >

    <Grid Margin="1">

      <TextBlock Text="{TemplateBinding Content}"

                 FontStyle="Italic"

                 Foreground="LightGray"/>

      <TextBlock Text="{TemplateBinding Content}"

                 Name="_checkedTextBlock" />

    </Grid>

  </Border>

 

  <ControlTemplate.Triggers>

    <Trigger Property="IsChecked" Value="False">

      <Setter TargetName="_checkedTextBlock"

              Property="Opacity" Value="0" />

      <Setter TargetName="_border"

              Property="Background" Value="Transparent" />

    </Trigger>

  </ControlTemplate.Triggers>

</ControlTemplate>

 

The interesting thing here is that I created two TextBlocks to show the content of the CheckBox (e.g. the “R” for the read-only attribute CheckBox).  The first one shows the content in “unchecked” form, and the second one shows it in “checked” form.  The Trigger then picks which one shows up.  I had started with a single TextBlock, and had the Trigger set the FontStyle and Foreground.  The problem with this is that the italicized text is slightly wider than the normal text, and that caused the checkboxes to jiggle a little bit when you click on them.  There’s probably other ways to solve this, but a simple solution was to have both TextBlocks there and taking up space in layout, and then let the Trigger decide which one wins for render.

 

And here’s a full template with sample usage, again with some of the more interesting parts highlighted.  (I changed the trigger so that instead of a Setter to update the Background when IsChecked changes, it animates the change.)

 

<Page

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

 

  <Grid>

   

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="Auto" />

    </Grid.ColumnDefinitions>

 

    <Grid.Resources>

     

      <!-- Implicit style to be applied to all CheckBoxs in this Window

           (this sets the new template) -->

 

      <Style TargetType="CheckBox">

       

        <Setter Property="BorderThickness" Value="1,0,0,0" />

        <Setter Property="Template">

          <Setter.Value>

           

            <ControlTemplate TargetType="CheckBox">

              <Border BorderThickness="{TemplateBinding BorderThickness}"

                      BorderBrush="Gray" Background="LightGray" Padding="2"

                      Name="_border" >

                <Grid Margin="1">

                  <TextBlock Text="{TemplateBinding Content}"

                             FontStyle="Italic"

                             Foreground="LightGray"/>

                  <TextBlock Text="{TemplateBinding Content}"

                             Name="_checkedTextBlock" />

                </Grid>

              </Border>

 

              <ControlTemplate.Triggers>

                <Trigger Property="IsChecked" Value="False">

                 

                  <Setter TargetName="_checkedTextBlock"

                          Property="Opacity" Value="0" />

                 

                  <!-- Instead of a Setter to update the background color,

                       use Trigger.EnterActions/ExitActions to animate it

                  <Setter TargetName="_border"

                          Property="Background" Value="Transparent" /> -->

 

                  <Trigger.EnterActions>

                    <BeginStoryboard>

                      <Storyboard TargetName="_border" TargetProperty="Background.Color">

                        <ColorAnimation Duration="0:0:0.5" To="Transparent" />

                      </Storyboard>

                    </BeginStoryboard>

                  </Trigger.EnterActions>

 

                  <Trigger.ExitActions>

                    <BeginStoryboard>

                      <Storyboard TargetName="_border" TargetProperty="Background.Color">

                        <ColorAnimation Duration="0:0:0.5" />

                      </Storyboard>

                    </BeginStoryboard>

                  </Trigger.ExitActions>

                 

                </Trigger>

              </ControlTemplate.Triggers>

            </ControlTemplate>

           

          </Setter.Value>

        </Setter>

      </Style>

 

      <!-- This DataTemplate is used to display attributes for one file -->

     

      <DataTemplate x:Key="FileTemplate">

        <Border BorderThickness="1" BorderBrush="Black"

                Margin="4" CornerRadius="5,5,0,0" Background="#FF83AFEA">

          <StackPanel >

           

            <!-- Show the file name -->

            <Border Padding="2,2,2,0" >

              <TextBlock Text="{Binding XPath=@Name}" HorizontalAlignment="Center" Margin="1"/>

            </Border>

 

            <!--  Show the attributes in a row -->

            <StackPanel Orientation="Horizontal" Background="White">

              <CheckBox BorderThickness="0" ToolTip="Hidden"

                        IsChecked="{Binding XPath=@Hidden, Mode=TwoWay}">

                H</CheckBox>

              <CheckBox ToolTip="System"

                        IsChecked="{Binding XPath=@System, Mode=TwoWay}">

                S</CheckBox>

              <CheckBox ToolTip="Read only"

                        IsChecked="{Binding XPath=@ReadOnly, Mode=TwoWay}">

                R</CheckBox>

              <CheckBox BorderThickness="1,0,1,0" ToolTip="Archive"

                        IsChecked="{Binding XPath=@Archive, Mode=TwoWay}">

                A</CheckBox>

            </StackPanel>

           

          </StackPanel>

        </Border>

      </DataTemplate>

 

      <!-- Sample data -->

 

      <XmlDataProvider x:Key="SampleData" XPath="Files">

        <x:XData >

          <Files xmlns="">

            <File Name="autoexec.bat" Archive="False" System="False"

                  ReadOnly="True" Hidden="True" />

            <File Name="readme.txt" Archive="False" System="True"

                  ReadOnly="False" Hidden="False" />

          </Files>

        </x:XData>

      </XmlDataProvider>

 

    </Grid.Resources>

 

    <!-- To test it out, show the sample data -->

   

    <ItemsControl ItemsSource="{Binding XPath=File, Source={StaticResource SampleData}}"

                  ItemTemplate="{StaticResource FileTemplate}" />

 

  </Grid>

</Page>

 

 

 

Posted Friday, June 13, 2008 3:17 PM by MikeHillberg | 1 Comments

Trying out Binding.StringFormat

 

StringFormat is a new property in .Net 3.5 SP1, which is currently in Beta.  See Scott’s blog for more info on the beta.

 

 

When you bind data into a property on an element, it’s automatically type converted for you.  For example, this markup:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

   

    <TextBlock Text="{Binding}" /> <!-- Simply bind to the DataContext -->

   

</StackPanel>

 

… shows a TextBlock with the text “123”.

 

If you want to have more control over the conversion of the value during binding, you can write a value converter for it.  For example, with this code:

 

public class TestConverter : IValueConverter

{

 

    public object Convert(object value, Type targetType, object parameter,

                          System.Globalization.CultureInfo culture)

    {

        return String.Format(culture, "Cost: {0:C}", value);

    }

 

    public object ConvertBack(object value, Type targetType, object parameter,

                          System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

}

 

… and this markup:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.Resources>

        <local:TestConverter x:Key="testConverter" />

    </StackPanel.Resources>

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

 

    <TextBlock Text="{Binding Converter={StaticResource testConverter}}" />

   

</StackPanel>

 

… my TextBlock shows “Cost: $123.00”.

 

 

The New StringFormat Property

 

That’s great, but it’s too bad that you have to write a value converter to get that.  To make it simpler, we’ve added the StringFormat property to Binding, which causes the value to be run through, not surprisingly, the String.Format method.

 

So, the above can now be accomplished with this markup, and no code:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

 

    <TextBlock Text="{Binding StringFormat=Cost: {0:C}}" />

   

</StackPanel>

 

 

If you just want to add the format specifier, e.g. just “{0:C}” rather than “Cost: {0:C}”, there’s nothing unusual if you’re in code, but in Xaml the syntax gets confused with the markup extension syntax.  That is, a property value that starts with a “{“ (as the first character) is interpreted to be a markup extension, such as {Binding}, {DynamicResource}, {x:Null}, etc.  The way to really have a “{“ as the first character of a property value is to escape it with a “{}”, so the above example becomes:

 

<TextBlock Text="{Binding StringFormat={}{0:C}}" />    

 

 

Not Just on Bindings

 

StringFormat shows up on some controls too.  For example, just like ContentControl (the base class of Button) lets you template your content with the ContentTemplate property, you can now format your content with the ContentFormatString property, such as:

 

<Button ContentStringFormat="{}{0:C}">

    <sys:Int32>123</sys:Int32>

</Button>

 

The ContentStringFormat property is available on ContentControl, ContentPresenter, and TabControl.

 

There’s an analogous HeaderStringFormat for HeaderedContentControl, GridViewColumn, GroupStyle, and HeaderedItemsControl.  Again, these are all cases where there is an analogous HeaderTemplate property.

 

Finally, there’s an ItemStringFormat on ItemsControl (the base class for ListBox) that allows you to format items.  For example, this produces a ListBox with two entries, “1,234.00” and “5,6789.00”:

 

<ListBox ItemStringFormat="{}{0:N2}">

    <sys:Int32>1234</sys:Int32>

    <sys:Int32>5678</sys:Int32>

</ListBox>

 

 

 

Multi Bindings

 

All of the examples so far reference the bound value with some form of “{0}”, which is the zero-th index into the array of sources.  With a MultiBinding, you can actually get more than one input.  For example, the following has a TextBlock that composes the value in two other TextBlocks (in a real-world example, those would probably be TextBox’s for user input):

 

<TextBlock Name="First">Fred</TextBlock>

<TextBlock Name="Last">Flintstone</TextBlock>

 

<TextBlock >

    <TextBlock.Text>

        <MultiBinding StringFormat="Name: {1}, {0}">

            <Binding ElementName="First" Path="Text"/>

            <Binding ElementName="Last" Path="Text"/>

        </MultiBinding>

    </TextBlock.Text>

</TextBlock>

 

This produces “Name: Flintstone, Fred”.

 

 

Culture

 

All of the examples so far have been in US English (“en-US”).  But you can pick the culture for StringFormat, the same way that you pick culture for the Binding.Converter property.  That is, you can set it explicitly on the Binding, as in:

 

<TextBlock Text="{Binding ConverterCulture='en-US', StringFormat='{}{0:N2}' }" />

 

… which gives me “123.00” (note the period for the decimal separator), or as in:

 

<TextBlock Text="{Binding ConverterCulture='fr-FR', StringFormat='{}{0:N2}' }" />

 

… which gives me “123,00” (note the comma for the decimal separator).

 

Or you can specify it on the element in the Language property, as in:

 

<StackPanel Language="fr-FR">

    <TextBlock Text="{Binding StringFormat='{}{0:N2}' }" />

</StackPanel>

 

As an aside, note that the xml:lang attribute is mapped to the Language property.  So the above is equivalent to:

 

<StackPanel xml:lang="fr-FR">

    <TextBlock Text="{Binding StringFormat='{}{0:N2}' }" />

</StackPanel>

 

 

More Samples

 

Finally, see Lester’s post for more StringFormat examples.

 

 

Posted Thursday, May 29, 2008 2:15 PM by MikeHillberg | 1 Comments

Filed under:

Of logical and visual trees in WPF

This post is about the “logical tree” in WPF, and how it differs from the visual tree.  For the most part you don’t need to understand this.  But if you want to understand some of the nit-like details of property inheritance, {DynamicResource} references, and ElementName bindings, this may be interesting.

A long time ago, I was trying to arrange all my files in a directory hierarchy (on DOS).  I had a directory for different project files, a directory for design documents, etc.  Then I hit the classic problem that reminded me of the 3 drawer metal filing cabinet – where do you put a project design document?  So I was delighted when Windows95 came out and had shortcut files.  A shortcut (similar to symbolic links) is a file that points to another file.  (One way to create a shortcut in Windows is to drag a file from one folder to another, using the right mouse button.)  So now I could put my project design document in my project directory, then create a shortcut to it in my design directory.  My document was in both directories.  It was cross-categorized.  My row had two key fields.  I was happy probably to a fault.

 

I flashed back to this early in WPF, with a hierarchy of another sort.  In this case, the first hierarchy is the visual tree.  The visual tree makes great sense to some developers, not as much to others.  So we created a cross-categorization called the logical tree to help things to be more … logical.  The rest of this post compares the two, and explains the motivation for their existence.

 

 

The visual tree always there

 

The visual tree in WPF is the core of all things rendering.  An element doesn’t show up on the screen until it’s in a rendered visual tree.  The visual tree is made up, not surprisingly, of Visual objects; Visual is an ancestor class for e.g. controls – like Button and ListBox – and panels – like Grid, StackPanel, and Canvas.

 

For example, in:

 

<Grid>

    <Button>Click</Button>

</Grid>

 

… the Grid is the visual parent of the Button, plain as day.

 

The visual tree affects all things visual, including input.  For example, setting the opacity to 50% on a visual parent makes all its children 50% as well (if you also set the child’s opacity to 50%, it is a cumulative 75% transparent).  Similarly, setting a transform on the parent, transforms the child as well.  Hit-testing goes through the visual tree.  Disabling the parent (setting IsEnabled to false) disables visual descendents, etc.

 

Now look at another example, change the button’s content to a StackPanel:

 

<Grid>

    <Button>

        <StackPanel>

            <Image />

            <TextBlock>Clack</TextBlock>

        </StackPanel>

    </Button>

</Grid>

 

… and again the Button’s visual parent is the Grid.  But the StackPanel’s visual parent isn’t the Button; it’s a ContentPresenter.  Plain as night.

 

The reason for this is that the Button has a template that describes how the Button really is rendered; that template defines the visual tree under the Button.  To make that more explicit, here’s an example of the above that actually shows the template, and therefore what the visual tree will be:

 

<Button>

    <StackPanel>

        <Image />

        <TextBlock>Clack</TextBlock>

    </StackPanel>

 

    <Button.Template>

        <ControlTemplate TargetType='Button'>

            <ContentPresenter Content='{TemplateBinding Content}' />

        </ControlTemplate>

    </Button.Template>

 

</Button>

 

The visual tree in this case, from Button on down, is Button : ContentPresenter : StackPanel : Image/TextBlock.

 

 

Here’s another interesting example, this time of elements embedded in text flow:

 

<RichTextBox>

    <FlowDocument>

        <Paragraph>

            Hello <Rectangle Width='10' Height='10' Fill='Red' />

        </Paragraph>

    </FlowDocument>

</RichTextBox>

 

In this case, you would expect the Rectangle’s “parent” to be the Paragraph.  But Paragraph, like Bold and Italic, isn’t a visual.  So the Rectangle’s visual parent is actually the RichTextBox.

 

 

Enter the logical tree

 

So we set out to make expectations come true – the StackPanel’s “parent” above should be the Button, and the Rectangle’s should be the Paragraph.  To do that, we created some supplementary helper references, like my shortcut files.  We gave elements the option to have a second parent, in addition to the visual parent, called the logical parent. 

 

The StackPanel’s logical parent above is the Button, and the Rectangle’s logical parent is the Paragraph.  Since that is the “parent” we thought that most developers & designers would interact with, we gave it the better name in the APIs; the logical parent is simply the Parent property on elements, whereas to get to the visual parent you have to call VisualTreeHelper.GetParent static method.

 

Often, an element’s logical parent is the same as its visual parent.  This is the case with elements in a Panel, such as a Grid; the Panel.Children property is a UIElementCollection type, and UIElementCollection has a feature that all items get the Panel as both the logical parent and the visual parent.  Other times, such as for the content of a Button (or any ContentControl), the two parents are different.

 

 

When does the logical tree matter?

 

Before going into more detail, let’s talk about where the logical tree is used.  The logical parent is relevant for several tree-based features:

 

·         The Parent property

 

As I just said above, the FrameworkElement.Parent property returns the logical parent.  So StackPanel.Parent above returns the Button, and Rectangle.Parent returns the Paragraph. 

 

·         Property inheritance

 

Inheritable properties, such as FontFamily and DataContext, come from an element’s logical parent (or logical ancestor).  The exception is that if an element has a visual parent but not a logical parent, the visual parent will be used.

 

·         {DynamicResource} references

 

If a property has a {DynamicResource} set on it, it will search the .Resources of logical ancestors.  Just like property inheritance, though, if there’s only a visual parent, that link is followed instead.

 

·         Name

 

When looking up a name, such as in {Binding ElementName=Foo}, the search walks up the ancestry looking for a name scope, again just as it does for inheritable properties.

 

·         Routed events

 

When an event is routing up the tree, such as the MouseLeftButtonDownEvent, the event goes up both the visual parent and the logical parent, if they’re different.

 

 

Let’s look at another example, taking an earlier example and adding an inheritable FontWeight property:

 

<Button FontWeight='bold'>

    <StackPanel>

        <Image />

        <TextBlock>Clack</TextBlock>

    </StackPanel>

 

    <Button.Template>

        <ControlTemplate TargetType='Button'>

            <ContentPresenter Content='{TemplateBinding Content}' TextBlock.FontWeight='normal'/>

        </ControlTemplate>

    </Button.Template>

 

</Button>

 

Is the FontWeight on the TextBlock bold (inherited from the Button) or normal (inherited from the ContentPresenter)?  The StackPanel’s visual parent is the ContentPresenter, and its logical parent is the Button.  So in searching for an inheritable property we walk up from the TextBlock to the StackPanel, from there to the Button, and find the property value.  So the TextBlock is bold.

 

 

Logical tree mechanics

 

An element doesn’t actually pick its logical parent; instead, a parent “adopts” children.  For example, when you set a property value to the ContentControl.Content property, ContentControl takes that value as its logical child.  To adopt a new logical child, an element simply calls AddLogicalChild.  Similarly, to remove a logical child, an element calls RemoveLogicalChild.  Note that you can’t adopt a child that already has a logical parent.

 

There is also actually an API to walk the logical tree – LogicalTreeHelper.GetParent and LogicalTreeHelper.GetChildren.

 

 

Logical tree in WPF controls

 

WPF controls already do the work to make the logical tree operate correctly.  And actually it’s more general than that – Panel, ItemsControl, ContentControl, and Decorator, which are the most common base classes, all do the work to make the logical tree operate already.  For example, if you create a custom Button that subclasses ContentControl, your Content property will pick up your button as the logical parent without you doing anything.

 

Here’s most of the elements in WPF that support the logical tree.  Recall that the logical parent is established by the parent, and should be in response to getting/losing property values.  Consequently this table lists the classes that have special support, and under which property they implement that support.

 

 

Class

Property(ies)

 

System.Windows.Controls

AdornedElementPlaceholder

Child property

ContentControl

Content

Decorator

Child

Grid

Children (inherited from Panel), Columns, Rows

HeaderedContentControl

Content (inherited from ContentControl), Header

HeaderedItemsControl

Items (inherited from ItemsControl), Header

InkCanvas

Children

ItemsControl

Items

Page

Content

Panel

Children

RichTextBox

Document

TextBlock

Text

TextBox

Text

ToolBarTray

ToolBars

ViewBox

Child

 

System.Windows.Controls.Primitives

BulletDecorator

Bullet and Child

DocumentViewerBase

Document

Popup

Child

 

System.Windows.Documents

FixedDocument

Pages

FixedPage

Children

FlowDocument

Blocks

FlowDocumentReader

Document

FlowDocumentScrollViewer

Document

PageContent

Child

Table

RowGroups, Columns

Span

Inlines

 

 

 

Edge cases

 

There’s a few additional special cases for special circumstances.  For example, a ContentControl won’t adopt its Content value as its logical child if the ContentControl is part of a ControlTemplate, and if that new value already has a logical parent.  That’s to avoid the scenario of a template part trying to steal the logical child of the outer (“templated parent”) control.  That’s getting more detailed, and deserves some more words and a picture.  I’ll add that detail soon to complete this story …

 

 

In conclusion …

 

The main take-away from this is that the logical tree supplements the visual tree, to enable things like the Parent property and like property inheritance to behave more intuitively.  It’s interesting to go deep and understand the details and the mechanism, but the bottom line is that you rarely need to understand it; base element classes like ContentControl, ItemsControl, Decorator, and Panel already do the work, so you don’t have to.

 

 

Posted Friday, May 23, 2008 4:46 PM by MikeHillberg | 5 Comments

Filed under:

Model-see, Model-do, and the Poo is Optional

Like a lot of people, I’ve developed software professionally for a lot of different environments:  PC systems and embedded systems; high- and low-level languages; kernel mode, user mode, real mode, and protected mode; system services; domain controllers; bootstrappers; image processors; a debugger; a compiler; a search engine; small systems and really big systems; file systems; COM; real-time and internet time.  I’ve never quite developed professionally for a mainframe, but I’ve worked on clients for mainframe services, and I sat next to a guy that wrote the services (I also sat next to a guy once who would only use a Commodore 64).

 

But I’ve been spending the last bunch of years helping to create WPF and Silverlight.  So in a model/view world, I look at a lot of things from the View point of view.  And from that point of view, you come to realize that all computer applications are really just data visualization applications.  There’s certainly other software – services, device drivers, pi calculators (I’m still waiting for that one to respond) – but end-user applications are all about data viz.

 

Except that’s not quite the right, because you don’t just visualize data; you manipulate it too.  E.g. Netflix.com lets me both see and modify my queue.  And the word “data” isn’t great either, because it makes you think just of the bits stored in a database.  So that got me to all applications being some form of “model visualization and manipulation”.

 

That doesn’t roll of the tongue either though.  So now I just consider all applications to be a case of “model see model do”.  Which makes me a code monkey.

 

 

Models, Views, and Poo

 

As for models and views, in my current View point of view, there’s always a view, and there’s optionally some amount (and maybe a large amount) of model.  The view visualizes the model, and the end-user interacts with that visualization, which propagates back as changes to the model. 

 

A lot of simple apps just need a view – a bunch of UI controls created in Xaml, with event handlers hooked up to some code-behind logic.  But I usually end up splitting up my application logic into some model objects and a view object, with the controls in the view bound to model properties. 

 

For more complex applications, you could have a more complicated view.  But then you can’t change the look of your application easily, and views are more difficult to test.  The solution to that is more sophisticated models.  I like the Model/View/ViewModel approach (MVVM), which is similar to the Model/View/Presenter (MVP) pattern.

 

But I think it’s really useful to be able to pick the right number of layers and sophistication behind my view as is appropriate for the application; it’s all just some form of model in the end.  Dr. WPF and Josh Smith talk about this too.  Dr. WPF put a name on this agnosticism towards the particular model – Model/View/Poo.  I like it.  (There’s a joke in there somewhere about code monkeys and poo.)

 

Another thing I find interesting about the layers of models is that we continue to work to narrow the gap between the model and the view, by making the model more view-friendly:

·         For example, tools generate classes with IEditableObject implementations, which enables view controls to perform cancelable updates to the model objects.

·         For example, Linq makes it easier to convert data sources into objects, or to re-shape existing objects into objects more appropriate for the view.

 

Similarly, we’re making the view more model-friendly:

·         For example, it’s easy to bind to any model object, or to lists of objects, or to find visualizations for model objects, etc.

·         For example, the CollectionView class provides built-in support for the classic problem of synchronizing two parts of the view, such as the master list and the details pane.

 

 

 

Updates to the Model, from the View, in Xaml

 

Along the general lines of the “model do” side of things, it’s common to hook events in the view and write code to update the model, or to define commands in the model that are invoked by e.g. buttons in the view.  As an experiment, I played with one way of somewhat marrying the two approaches – allow the view to phrase a model method in the form of a command.

 

The example is a view that’s bound to a File object; the view shows the file name, but as TextBox so that you can change the name.  When you click the “Rename” button, it fires a command which calls the Rename  method on the File object with the new name from the TextBox. 

 

I.e., here’s the File object, which just has a Name property and a Rename method:

 

public class File : INotifyPropertyChanged

{

    string _name;

    public string Name

    {

        get { return _name; }

        set

        {

            _name = value;

            if (PropertyChanged != null)

                PropertyChanged(this, new PropertyChangedEventArgs("Name"));

        }

    }

 

    public void Rename(string newName)

    {

        // Perform the file rename here

    }

 

    public event PropertyChangedEventHandler PropertyChanged;

}

 

… here’s some sample data getting hooked up as the DataContext:

 

public Window1()

{

    InitializeComponent();

    DataContext = new File() { Name = "TestName" };

}

 

… and here’s the view, which has a TextBox and a Button, a command on the Button that calls the Rename method, and the argument to the Rename coming from the TextBox:

 

<StackPanel>

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

 

    <Button Content="Rename">

        <Button.Command>

 

            <mc:MethodCommand MethodName="Rename">

                <mc:MethodArgument Value="{Binding Text, ElementName=_renameTextBox}" />

            </mc:MethodCommand>

 

        </Button.Command>

    </Button>

</StackPanel>

 

 

The key here is that when the Button is clicked, the MethodCommand is invoked.  That MethodCommand calls the Rename method on the File object, passing in the current value of the TextBox as the newName parameter.  The File object is picked up by the MethodCommand by inheriting the DataContext.

 

The rest of this post is the MethodCommand implementation.  The reason I made the MethodCommand class and the Arguments property of type Freezable was so that the bindings would be fully functional (so that the DataContext would inherit, and so that ElementName bindings could work). 

 

(I haven’t tested this on Commodore 64 yet.)

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows.Input;

using System.Windows;

using System.Reflection;

using System.Collections;

using System.Windows.Data;

using System.ComponentModel;

using System.Windows.Markup;

 

 

namespace MethodCommandNS

{

    [ContentProperty("Arguments")]

    public class MethodCommand

        : Freezable, // Enable ElementName and DataContext bindings

          ICommand,

          INotifyPropertyChanged

    {

 

        // The name of the method to call on Invoke

        public string MethodName { get; set; }

 

        // When this is set, exceptions during invoke are caught, and the exception

        // is set as the Exception property

        [DefaultValue(true)]

        public bool CatchExceptions { get; set; }

 

        // If there is an exception during a command invoke, and CatchExceptions

        // is set, this will have the exception object.

        public Exception Exception

        {

            get { return _exception; }

 

            private set

            {

                _exception = value;

                FirePropertyChanged("Exception");

            }

        }

        Exception _exception;

 

 

        // This holds the arguments to be passed to the method.

        // This is Freezable so that the DataContext and ElementName bindings

        // can work correctly.

        public FreezableCollection<MethodArgument> Arguments

        {

            get { return (FreezableCollection<MethodArgument>)GetValue(ArgumentsProperty); }

        }

        public static readonly DependencyProperty ArgumentsProperty =

            DependencyProperty.Register("Arguments", typeof(FreezableCollection<MethodArgument>), typeof(MethodCommand), null);

 

 

        // This is a private DP that's used to get the inherited DataContext

        // (see it used in the MethodCommand constructor).

        private static readonly DependencyProperty ElementDataContextProperty =

            DependencyProperty.Register("ElementDataContext", typeof(object), typeof(MethodCommand), null);

 

        // The Target property specifies the object on which to invoke the method.

        // If this is null, invoke the method on the DataContext

        public object Target

        {

            get { return (object)GetValue(TargetProperty); }

            set { SetValue(TargetProperty, value); }

        }

        public static readonly DependencyProperty TargetProperty =

            DependencyProperty.Register("Target", typeof(object), typeof(MethodCommand), null);

 

        // Constructor

        public MethodCommand()

        {

            // The Arguments property is read-only and never null

            SetValue(ArgumentsProperty, new FreezableCollection<MethodArgument>());

 

            // Set a default Binding onto the private ElementDataContextProperty.

            // A default binding just binds to the inherited DataContext.  This is how

            // MethodCommand typically gets the object on which to invoke the method.

 

            BindingOperations.SetBinding(

                this,

                ElementDataContextProperty,

                new Binding());

 

            // By default, catch exceptions that are raised by the method.

            CatchExceptions = true;

        }

 

        // Fire the PropertyChanged event when the Exception property changes.

        public event PropertyChangedEventHandler PropertyChanged;

        void FirePropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

 

 

        // We need to implement this method because we're a Freezzable subtype.

        protected override Freezable CreateInstanceCore()

        {

            throw new NotImplementedException();

        }

 

        // Implement the CanExecute members of ICommand

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)

        {

            // We'll always be enabled to be invokes.

            return true;

        }

 

        // ICommand.Execute implementation.  This version calls ExecuteImpl, which does the

        // the actual call.  But this method wraps that call in a try/finally, depending on

        // the value of CatchExceptions.

        public void Execute(object parameter)

        {

            // Clear out any exception of a former invocation.

            Exception = null;

 

            if (CatchExceptions)

            {

                // Invoke the method in a try/finally

                try

                {

                    ExecuteImpl(parameter);

                }

                catch (Exception e)

                {

                    // Any exceptions will likely come back as a

                    // TargetInvocationException, but the original exception

                    // is more interesting.

 

                    if (e is TargetInvocationException)

                    {

                        if (e.InnerException != null)

                            Exception = e.InnerException;

                        else

                            Exception = e;

                    }

                    else

                        Exception = e;

                }

            }

 

            // Otherwise, CatchExceptions isn't set, so just forward the call

            else

            {

                ExecuteImpl(parameter);

            }

        }

 

        // ExecuteImpl is where we actually invoke the method.

        void ExecuteImpl(object parameter)

        {

            // See if the Target property is set

            object target = Target;

            if (target == null)

            {

                // If not, look for an inherited DataContext

                target = GetValue(ElementDataContextProperty);

            }

 

            // We must have a target, either from Target or DataContext

            if (target == null)

            {

                throw new InvalidOperationException("MethodCommand target not found (must set either Target or DataContext)");

            }

 

            // Get the method to be called.  Note that this doesn't support

            // overloaded methods, but it could be updated to do so.

 

            MethodInfo methodInfo = target.GetType().GetMethod(MethodName);

            if (methodInfo == null)

            {

                throw new InvalidOperationException("Method '" + MethodName + "' couldn't be found on type '" + target.GetType().Name + "'");

            }

 

            // Copy the Arguments to an array

 

            object[] arguments = new object[Arguments.Count];

 

            for (int i = 0; i < Arguments.Count; i++)

                arguments[i] = Arguments[i].Value;

 

            // Invoke the method

 

            methodInfo.Invoke(target, arguments);

        }

 

    }

 

 

    // The MethodArgument class plugs into MethodCommand.Arguments

    public class MethodArgument

        : Freezable // Enable ElementName and DataContext bindings

    {

        public MethodArgument() { }

 

        // The value of a method argument

        public object Value

        {

            get { return (object)GetValue(ValueProperty); }

            set { SetValue(ValueProperty, value); }

        }

        public static readonly DependencyProperty ValueProperty =

            DependencyProperty.Register("Value", typeof(object), typeof(MethodArgument), null);

 

        // We need to implement this method since we are a subtype of Freezable.  But since

        // we don't need to support cloning, we won't implement it.

        protected override Freezable CreateInstanceCore()

        {

            throw new NotImplementedException();

        }

    }

}

 

 

 

 

 

Posted Wednesday, May 21, 2008 3:05 PM by MikeHillberg | 4 Comments

Filed under: ,

Binding to anonymous types in an Xbap or Silverlight application

It's easy to use Linq queries to create objects, and to use {Binding}s to bind properties of those objects into your view.  If you're doing this for an application that will run as an Xbap ("WPF Browser Application") or as a Silverlight app, just note that you need to generate nominal types rather than anonymous types.

For example, say you have a bound ListBox like:

<ListBox Name="ListBox1">

    <ListBox.ItemTemplate>

        <DataTemplate>

            <TextBlock Text="{Binding First}" />

        </DataTemplate>

    </ListBox.ItemTemplate>

</ListBox>


... where the ListBox ItemsSource is established with the following code at initialization-time:

public partial class Window1 : Window

{

    private Collection<Person> _collection = new Collection<Person>()

    {

        new Person() { FirstName = "John", LastName = "Doe" },

        new Person() { FirstName = "Jane", LastName = "Doe" },

        new Person() { FirstName = "Fred", LastName = "Flinstone" }

    };

 

    public Window1()

    {

        InitializeComponent();

 

        ListBox1.ItemsSource = from person in _collection

                               where person.LastName == "Doe"

                               select new { First = person.FirstName, Last = person.LastName };

 

    }

 

}


This won’t show anything in the ListBox.  (You might see debug output like “
System.Windows.Data Error: 12 : Cannot get 'First' value (type 'String') from '' (type '<>f__AnonymousType0`2')”).

The issue here is that the select statement in the query (select new { First = person.FirstName, Last = person.LastName }) generates an internal (anonymous) type, and with an Xbap or Silverlight app you can’t bind to internal types.  The solution is to use a nominal type.  E.g., add this class definition:

public class SelectedPerson

{

    public string First { get; set; }

    public string Last { get; set; }

}


... and use it in the select:

ListBox1.ItemsSource = from person in _collection

                       where person.LastName == "Doe"

                       select new SelectedPerson { First = person.FirstName, Last = person.LastName };


... and your list box is populated with “John” and “Jane”.

Posted Monday, May 05, 2008 9:56 AM by MikeHillberg | 4 Comments

Simple way to create a gradient brush in Xaml

 

Rob, Nikhil & I were talking today about the early days of Xaml when you could create linear gradient brushes as an attribute value.  E.g. (borrowing from Rob’s post on this subject) instead of creating a LinearGradientBrush for the fill of a rectangle with this Xaml:

<Rectangle Width="300" Height="200" >

    <Rectangle.Fill>

        <LinearGradientBrush>

            <GradientStop Color="Blue" Offset="0" />

            <GradientStop Color="White" Offset="1" />

        </LinearGradientBrush>

    </Rectangle.Fill>

</Rectangle>


… you could write:

<Rectangle Width="300" Height="200"

           Fill="HorizontalGradient Blue White" >

</Rectangle>


The up side of the latter syntax obviously is that it’s terser.  The drawback is that it’s difficult for a tool to understand it; it has no idea what “HorizontalGradient” means, nor that it has two arguments (nor how those arguments should be interpreted).

For that reason, we created the markup extension primitive for Xaml.  A markup extension is any type that derives from MarkupExtension, and it can be specified in an attribute using “squiggle” syntax.  The most common markup extension you see in Xaml is the binding extension, e.g.:

<TextBlock Text="{Binding FirstName}" />

So we caution against “mini languages” in Xaml, such as the HorizontalGradient example above, because of the lack of toolability and compile-time validation, and we recommend markup extensions instead. 

And so, here’s an example of a definition for such an extension, in this case to create a LinearGradientBrush:

[MarkupExtensionReturnType(typeof(LinearGradientBrush))]

public class LinearGradientBrushExtension : MarkupExtension

{

    public LinearGradientBrushExtension()

    {

        StartColor = EndColor = Colors.White;

    }

 

    public LinearGradientBrushExtension( Color startColor, Color endColor, double angle)

    {

        StartColor = startColor;

        EndColor = endColor;

        Angle = angle;

    }

 

    public Color StartColor { get; set; }

    public Color EndColor { get; set; }

    public double Angle { get; set; }

 

 

    public override object ProvideValue(IServiceProvider serviceProvider)

    {

        return new LinearGradientBrush(StartColor, EndColor, Angle);

    }

 

}

 

… which can be used in Xaml like this:

<Rectangle Width="300" Height="200"

           Fill="{helper:LinearGradientBrush StartColor=red, EndColor=blue, Angle=0}" />


… or, since the LinearGradientBrushExtension has a convenience constructor for setting the properties, the Xaml can simply be:

<Rectangle Width="300" Height="200"

           Fill="{helper:LinearGradientBrush red, blue, 0}" />


Similarly, here’s a markup extension for a RadialGradientBrush:

[MarkupExtensionReturnType(typeof(RadialGradientBrush))]

public class RadialGradientBrushExtension : MarkupExtension

{

    public RadialGradientBrushExtension()

    {

        StartColor = EndColor = Colors.White;

    }

 

    public RadialGradientBrushExtension( Color startColor, Color endColor )

    {

        StartColor = startColor;

        EndColor = endColor;

    }

 

    public Color StartColor { get; set; }

    public Color EndColor { get; set; }

 

    public override object ProvideValue(IServiceProvider serviceProvider)

    {

        return new RadialGradientBrush(StartColor, EndColor);

    }