Archives for: March 2007
31/03/07
WPF: IntSequence helper class updated, including dependency properties
This struck me right after my recent post about binding to arbitrary sequences: my helper class was implemented with traditional .NET properties, which isn't optimal for use with WPF. One thing specifically isn't good for my purpose, which is the fact that a "normal" property can't be the target of WPF data binding. Like in this case:
<Window.Resources>
<engine:Grid x:Key="gameGrid" />
<helpers:IntSequence x:Key="rowDummyList" EndVal="{Binding Source={StaticResource gameGrid}, Path=RowCount}" />
</Window.Resources>
In this case I'm trying to bind the EndVal of the sequence to a value obtained from a different object, and that didn't work using the standard properties. So I have created an updated version of the IntSequence class, which uses dependency properties to make this kind of binding possible. Here it is:
public class IntSequence : DependencyObject, IEnumerable<int> {
public static readonly DependencyProperty StartValProperty;
public static readonly DependencyProperty EndValProperty;
static IntSequence( ) {
StartValProperty = DependencyProperty.Register("StartVal",
typeof(int), typeof(IntSequence), new PropertyMetadata(1));
EndValProperty = DependencyProperty.Register("EndVal",
typeof(int), typeof(IntSequence), new PropertyMetadata(10));
}
public int StartVal {
get { return (int) GetValue(StartValProperty); }
set { SetValue(StartValProperty, value); }
}
public int EndVal {
get { return (int) GetValue(EndValProperty); }
set { SetValue(EndValProperty, value); }
}
IEnumerator<int> IEnumerable<int>.GetEnumerator( ) {
for (int val = StartVal; val <= EndVal; val++)
yield return val;
}
IEnumerator IEnumerable.GetEnumerator( ) {
foreach (int item in this)
yield return item;
}
}
WPF: Binding to a sequence
In certain contexts I have always found it useful to bind to a sequence of numbers, or sometimes even just a dummy collection with a certain number of elements. It's like a for-loop, just for data binding. I've found three good ways of doing that in XAML, as the following two source code snippets show:
<Window x:Class="WPFSequenceBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WPFSequenceBinding"
Title="WPFSequenceBinding" Height="300" Width="300">
<Window.Resources>
<x:Array x:Key="list1" Type="{x:Type sys:Int32}">
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
<sys:Int32>4</sys:Int32>
<sys:Int32>5</sys:Int32>
</x:Array>
<ObjectDataProvider x:Key="list2" ObjectType="{x:Type sys:Array}" MethodName="CreateInstance">
<ObjectDataProvider.MethodParameters>
<sys:Type>sys:Int32</sys:Type>
<sys:Int32>5</sys:Int32>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<local:IntSequence x:Key="list3" StartVal="1" EndVal="5" />
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding Source={StaticResource list1}}" />
<ListBox ItemsSource="{Binding Source={StaticResource list2}}" />
<ListBox ItemsSource="{Binding Source={StaticResource list3}}" />
</StackPanel>
</Window>
public class IntSequence : IEnumerable<int> {
private int startVal = 1;
public int StartVal {
get { return startVal; }
set { startVal = value; }
}
private int endVal = 10;
public int EndVal {
get { return endVal; }
set { endVal = vlue; }
}
IEnumerator<int> IEnumerable<int>.GetEnumerator( ) {
for (int val = startVal; val <= endVal; val++)
yield return val;
}
IEnumerator IEnumerable.GetEnumerator( ) {
foreach (int item in this)
yield return item;
}
}
All these approaches come with their own advantages and disadvantages. The x:Array block can be defined completely in XAML and is obviously not restricted to sequential values. Then again, for a simple sequence, there's a lot of typing and/or copying/pasting to be done and the result is extremely verbose. The ObjectDataProvider with the dynamically created array is an interesting approach, but the array is not actually initialized with values (so it contains a bunch of zeros) and the XAML code is also quite verbose. Finally the IntSequence looks great in XAML, but it requires a helper class, however simple. Well, there's something for everyone here...
WPF: Bottom dwelling with an ItemsControl
Here's something I just stumbled upon. Not quite intuitive, so I thought I'd write it down. Consider this piece of XAML (you can paste it into XamlPad to try it out):
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <DockPanel LastChildFill="False"> <Button DockPanel.Dock="Bottom" Background="Yellow" Content="X" /> <Button DockPanel.Dock="Bottom" Background="Red" Content="X" /> <Button DockPanel.Dock="Bottom" Background="Blue" Content="X" /> <Button DockPanel.Dock="Bottom" Background="Green" Content="X" /> <Button DockPanel.Dock="Bottom" Background="Magenta" Content="X" /> <Button DockPanel.Dock="Bottom" Background="Black" Content="X" /> <Button DockPanel.Dock="Bottom" Background="Orange" Content="X" /> </DockPanel> </Page>
What it does is pretty obvious: it creates a bunch of buttons and docks them all to the bottom of the panel.
Now look at the following XAML. It uses an ItemsControl to create a dynamic structure based on data binding, that also contains a number of buttons. Similar to before, the buttons are children of a DockPanel and they have the DockPanel.Dock attribute attached and set to the value Bottom.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<x:Array x:Key="sequence" Type="sys:Int32">
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
<sys:Int32>4</sys:Int32>
<sys:Int32>5</sys:Int32>
<sys:Int32>6</sys:Int32>
<sys:Int32>7</sys:Int32>
<sys:Int32>8</sys:Int32>
</x:Array>
</Page.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource sequence}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel LastChildFill="False" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" DockPanel.Dock="Bottom" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Page>
If you paste this code into XamlPad, you might be as surprised as I was initially to find that this is the output:
So why is that? The answer can also be found in XamlPad, comparing an important part of the visual trees for both examples:
As you can see, the visual tree for the databound case is quite a bit more complex, and most importantly, the Button instances are wrapped in ContentPresenter instances before being included in the DockPanel. So the reason why the docking doesn't work is because the DockPanel.Dock property is not attached to the actual children of the DockPanel!
Here's the solution I found for this problem. It is possible to configure the style for the ContentPresenter that is wrapped around the items, and to attach the property to it instead of the Button itself. The ItemsControl has a property called ItemContainerStyle, and it can be used like this:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<x:Array x:Key="sequence" Type="sys:Int32">
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
<sys:Int32>4</sys:Int32>
<sys:Int32>5</sys:Int32>
<sys:Int32>6</sys:Int32>
<sys:Int32>7</sys:Int32>
<sys:Int32>8</sys:Int32>
</x:Array>
</Page.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource sequence}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel LastChildFill="False" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="DockPanel.Dock" Value="Bottom" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Page>
With this change, the result is finally what I want - all the buttons are docked to the bottom correctly.
There may be a second possible solution, by making the ItemControl somehow skip creating that additional wrapper altogether, but I haven't tried doing that.
16/03/07
Cross tables in Windows Forms - data binding magic
I have this sample from a recent talk at Basta! conference in Germany, which shows (among other things) how to bind a cross table to a DataGridView (the standard .NET 2 data grid). A cross table is basically the result of transposing some data and using one of the fields for a second dimension.
The sample I have works on two tables of data. It doesn't actually use a database and the reference that points from the list of votes to that of features is implemented as an object reference, but the relationship is like this:
Now, let's assume there's this data in the two tables/lists:
|
| |||||||||||||||||||||||||||||||||||||||||
In a cross table the same data could look like this:
| Feature/Year | 2004 | 2005 | 2006 |
| Synthesize out-of-the-box supply-chains | 30 | 40 | 70 |
| Syndicate vertical mindshare | 70 | 60 | 30 |
This is exactly the kind of transformation demonstrated in my sample. Now, there are a number of other things in that program, so don't be confused. I suggest you focus on the workings of the VoteValuePropertyDescriptor class.
Here's the download: FeatureVoting.zip

13/03/07
MSDN Roadshow and food, Zi Makki style
March 21st is the date of the MSDN Roadshow in London. Should be interesting to hear some news about LINQ, the Entity Framework, AJAX and XAML… I’ll be there, and hoping to meet a few of you! Registration is apparently still open, so follow this link if you haven’t signed up yet.
After the event, there’s another fabulous geek dinner organized by the every more experienced geek-dinner-organizer Zi Makki :-) If you’re interested in meeting some of the other guys you read about in the blog-o-sphere (or maybe you just think you’re going to be hungry <g>
, be there! Sign-up is here.


