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> |
|
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> |
|
… 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> |
|
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> |
|
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> |
|
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> |
|
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>
|
|
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}" /> |
|
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> |
|
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> |
|
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> |
|
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>
|
|
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.
Attachment(s): TreeViewStepByStep.zip
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;
}
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:
… and here I am opening the ComboBox, and changing the customer to Betty (her CustomerID is 2):
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. 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):
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>
Attachment(s): SelectedValue.zip
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();
}
}
(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.
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):
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>
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();
}
}
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:
… 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:
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>
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.
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.
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();
}
}
}
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”.
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);
}
}
… which enables this Xaml:
<Rectangle Width="300" Height="200"
Fill="{local:RadialGradientBrush red, blue}" />
Here's an example of a way to add context-sensitive help to your application.
The main idea is to simply use the built-in ApplicationCommands.Help command. This command is already tied to the F1 key, and so executes when you hit F1, and tells your command handler what element the user was on when it was hit.
First, start with this little application:
<Window x:Class="ContextSensitiveHelp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width='auto'/>
<ColumnDefinition Width='*'/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height='auto'/>