| Evgeny's profileEvgeny's spaceBlogGuestbookNetwork | Help |
|
|
July 26 Expression Blend 3 – secrets of working with data 2In my previous blog I talked about data drag-and-drop secrets. I hope end users found the blog useful. Today my audience will be control vendors and developers who want to improve their control behavior in Blend, make it more design time friendly. Part 2: secrets of making controls design time friendlyCertain parts of Blend data binding functionality are driven by metadata, attributes applied to classes and properties. There are two ways to apply your attributes, a classic way (which I use in this blog for simplicity) and advanced with use of design assembly (more details at http://silverlight.net/blogs/justinangel/archive/2008/11/17/silverlight-design-time-extensibility.aspx). Blend internally uses advanced technique to provide additional metadata for many platform types such as ItemsControl, Selector, TreeView, etc. DefaultBindingProperty attribute. Blend uses this attribute during drag-and-drop operations. I mentioned it it my previous blog. Here is an example:
Bindable attribute. There are two reasons to use this attribute. First, you may want to hide certain properties from the list of properties Blend displays to a user in Create Data Binding dialogs.
If you mark a property of your Silverlight control as TwoWay bindable like this
then Blend will set mode to TwoWay by default. BTW Blend uses this attribute for Silverlight TextBox.Text, ToggleButton.IsChecked and Slider.Value properties. It worth mentioning that Bindable attribute has no effect on read-only properties, i.e. properties which do not have accessible setter, e.g. string MyProperty { get; privaye set; }. Blend will always emit OneWay mode for both Silverlight and WPF bindings.
DataContextValueSource attribute. You are familiar with DataContext. It provides data source for source-less bindings (well, not exactly, but we’ll keep it simple). Look at this XAML: <UserControl.Resources><x:Company x:Key="MyCompany"> </UserControl.Resources> <Grid x:Name="LayoutRoot" DataContext="{StaticResource MyCompany}"> <ListBox x:Name="master" ItemsSource="{Binding Employees}"> You can tell that ListBox.ItemsSource is Employees collection, which is a property of Company object. ItemsSource binding does not specify source but we figured it out because we know that we need to look at DataContext property. ListBox does not have its own DataContext, but it inherits it from Grid. Now let’s look at another example: <UserControl.Resources><x:Company x:Key="MyCompany"> </UserControl.Resources> <Grid x:Name="LayoutRoot" DataContext="{StaticResource MyCompany}"> <ListBox x:Name="master" ItemsSource="{Binding Employees}"> <Grid x:Name="details" DataContext="{Binding SelectedItem, ElementName=master}"> <TextBlock Text="{Binding FirstName}"/> <CheckBox IsChecked="{Binding IsMarried}"/> </Grid> What is DataContext of details Grid? Well, we can figure it out too because we know that SelectedItem of master ListBox is an item of Employees collection, i.e. it is Employee. While Blend understands DataContext property and how other properties rely on it Blend does not know anything about a relationship between ItemsSource and SelectedItem properties. One may wonder why Blend even needs to know about such a relationship. Why not to look at instance of details Grid and check its DataContext property. The thing is Blend 3 relies on XAML only. There are quite a few scenarios where Blend does not have any instances. For example did you know that when you rename a property in sample data Blend will find all references (bindings) to the property in all the documents (even unopened ones) and update them. To understand such relationship Blend uses DataContextValueSource attribute. Let’s analyze this code:
Here CurrentItem property is marked with DataContextValueSource("ItemsSource", true). This means that CurrentItem gets its data context not from DataContext property, but rather from ItemsSource (which in turn gets data context the normal way, i.e. from DataContext property). Second parameter – isCollectionItem – is TRUE meaning that ItemsSource is expected to be a collection and thus we need to take a collection item. Given this info Blend can better understand our master-details XAML example, i.e. it can figure out that details Grid.DataContext is master ListBox.SelectedItem, which in turn is a collection item of ListBox.ItemsSource (Employees collection), which in turn gets its data source from layout root Grid.DataContext. You can imagine what chains Blend needs to unwind to figure out data contexts in some cases. CellTemplate Property is also marked with DataContextValueSource, exactly the same way as CurrentItem property. This tells Blend data context of cell DataTemplate is also a collection item of ItemsSource. When you start editing CellTemplate (via Edit Additional Template –> … context menu) Blend will figure out data context of DataTemplate and thus create correct bindings during drag-and-drop operations. For example try this:
The reason this works is because of DataContextValueSource attribute, which Blend uses to mark ItemsControl.CurrentItem property, as well as a few others. Now onto a more complex example. Look at how we applied DataContextValueSource attribute to MyColumn.Binding property. What is that "Columns\" ancestor path parameter? So far we’ve been looking at sibling properties, i.e. both ItemsSource and CurrentItem are (sibling) properties of MyDataGrid. MyBinding property requires a bit more work. First we need to navigate MyColumn ancestors. To better understand what is going on we’ll use this XAML: <MyGrid ItemsSource="{Binding Employees}"><MyGrid.Columns> <MyColumn MyBinding="{Binding FirstName}"/> </MyGrid.Columns> Here MyColumn.ByBinding gets its data context from its grandparent, MyGrid.ItemsSource property. Thus there are two steps in “Columns\” ancestor path, "Columns" and “\". This means that we need to look for data context in a grandparent element (second level ancestor) and we need to make sure that we can navigate from the grandparent to MyColumn following Columns –> collection item path. And to make it clear "\" is a special character meaning collection item. If ancestor path is a set of properties then they should be separated with ".", e.g. GridViewColumn.Celltemplate is marked with DataContextValueSource("ItemsSource", @"View.Columns\", true). You may have noticed that DataContextValueSource attribute describes data relationship between properties not 100% correct. For example ListBox can use Items property (which is mutually exclusive with ItemsSource property) to load items. In such a case ListBox.CurrentItem is driven by Items property. However DataContextValueSource was designed to be a practical compromise between ease of use and correct description of relationship between properties from data stand point of view. Comments (10)TrackbacksThe trackback URL for this entry is: http://etvorun.spaces.live.com/blog/cns!6054141F335D00D3!154.trak Weblogs that reference this entry
|
|
|