Thursday, May 15, 2008

wpf: key concepts

databinding and itemtemplate

databinding is key to writing less code for populating fields. you can databind to anything that implements INotifyPropertyChanged (for objects) or INotifyCollectionChanged (such as ObservableCollection, for collections). the syntax uses “{}” brackets and the keyword “Binding” and looks like this:

<TextBox Text={Binding Path=Name} />

elements in your xaml inherit the DataContext (the object or collection that you’re binding to) from their parent object(s). you can set the DataContext in code or via markup with the Source attribute of a Binding markup that generally points to a static resource:

<TextBox Text={Binding Path=Name, Source={StaticResource MyCollection}} />

the syntax definitely feels weird at first, but it gets easier as you start using it, and it lets you have nice MVC-style UI/business logic separation.

ItemTemplate works like a repeater or grid template in asp.net. you define exactly how you want the items in your ItemsContainer (e.g. a listbox) to be displayed:

<ListBox ItemsSource="{Binding Conferences, Source={StaticResource ConferenceManagerDS}}" SelectionChanged="ConferencesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Id}"></TextBlock>
<ListBox ItemsSource="{Binding Members}"></ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

see here for a good overview: http://www.vsj.co.uk/dotnet/display.asp?id=500

dependency properties

the idea here is that the property owner doesn’t necessarily know the value of his own property, since it “depends” on external factors, like whether or not an animation is occurring. so instead of creating a normal {get; set;} property, you “register” your property with the global, static “PropertyManager”, and then you access the value of your property via the instance method DependencyObject.GetValue(). so if you have a Button, you access it’s FontStyle property like this:

b.GetValue(Button.FontStyleProperty)

almost always the property author will implement a normal C#-style property accessor as well, which just calls the GetValue() and SetValue() dependency methods for you, so you can still say b.FontStyle = new Font() or in this example canvas.InkThickness = 4:

public int InkThickness
{
get { return (int)GetValue(InkThicknessProperty); }
set { SetValue(InkThicknessProperty, value); }
}

use the snippet "propdp" and most of the magic will be taken care of for you.

attached properties

attached properties are a special type of dependency property that allow a child element to set a property value that its parent reads.

as an example of why this is useful, think of how in WinForms every UI component derives from “Control”, and “Control” has properties like “Left” and “Top”. when a container goes to layout its children, it uses Control.Left. this means that the container has to have hard-coded logic that only something that derives from “Control” can be one of its children. this works out ok for core properties like Left and Top, but if you want to add another property that some UI components need, you either need to derive from Control and then have your custom controls derive from that derivation, or you need to edit the source for Control (if you have it) and add the property there. the problem is that the new property may or may not be relevant to all derived classes, and it may even interfere with a property that the derived class has added to itself.

attached properties let the container specify “i have these properties that are impt to me but may or may not be of interest to you”, and then the child can optionally “attach” a value to them. for example, to specify that a textbox in a canvas should have a left of 40, you write:

<TextBox Canvas.Left=”40” />

and to specify the row an element should be in inside a grid, you write:

<TextBox Grid.Row=”1” />

this allows a container to contain any type of child, not just those that derive from an expected type.

use the snippet “propa” and most of the magic will be taken care of for you.

routed events

this is just an elegant way of having Preview and non-Preview events. Preview events go top-down (they “tunnel”), and allow a parent to handle the event before it gets to a child. non-Preview events (they “bubble”) go from the child up. you register your events with the EventManager in a similar way that you do with dependency properties and the PropertyManager.

commands

these are WPF-only (i.e. they don’t exist in Silverlight 2 Beta 1). commands enable a MVC-type of design pattern, where the view (xaml) just asks the Controller (who implements ICommand) to perform an action or if an action is performable. josh smith has a good article on using commands here: http://www.codeproject.com/KB/WPF/MVCtoUnitTestinWPF.aspx.

triggers

another WPF-only feature, triggers do what you’d imagine: they let you specify a condition that causes an action. these are used extensively in styles when you want to have the style change on some event, for example:

<Style x:Key="MyContainer" TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>

No comments: