The last days I have been playing with WPF trying to get an understanding of how it works ”under the hood”. So I have written a bunch of tiny applications where I prototype a function. When building these applications I don’t care much about its user interface, but I still like to have my controls lined up neatly. To do this I usually use a grid control with some rows and columns. This usually works well, but the grids syntax gets tiresome to write and rewrite all the time.
Yesterday this annoyed me so much that I decided to fix it! The reason was not only the syntax, but also that I needed a grid that let me set the number of rows and columns dynamically using data binding. The result is the DynamicGrid control. It allows you to replace this code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
With this code:
<grid:DynamicGrid NumColumns="8" NumRows="8">
</grid:DynamicGrid>
Nice! And it also allows you to use data binding to setup the number of rows or columns. Like this:
<grid:DynamicGrid NumColumns="{Binding ColumnCount}" NumRows="{Binding RowCount}">
</grid:DynamicGrid>
So your grid can adapt to your data source. On top of all that, it even plays nice with the old grid-markup. So if you need a header row with a fixed size then you can insert it using a RowDefinition and let the DynamicGrid add the rest of the rows and columns for you automatically.
How does it work?
It turned out that implementing the grid was really easy. All I needed to do was to subclass the Grid class, add two new dependency properties and add the code that added the columns and rows automatically.
I created two new dependency properties named NumColumns and NumRows. They are both identical except for the naming:
public static readonly DependencyProperty NumColumnsProperty =
DependencyProperty.Register("NumColumns", typeof(Int32), typeof(DynamicGrid));
public Int32 NumColumns
{
get { return (Int32)GetValue(NumColumnsProperty); }
set { SetValue(NumColumnsProperty, value); }
}
There is not much to note about them – we default to one column and one row.
I then wrote an override for the OnInitialized method:
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
RecreateGridCells();
}
This will call the RecreateGridCells method that does the actual setup of the dynamic grid. Since it is called after the base grid is initialized we support its functionality.
The RecreateGridCells method does the custom setup:
private void RecreateGridCells()
{
int numRows = NumRows;
int currentNumRows = RowDefinitions.Count;
while (numRows > currentNumRows)
{
RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
currentNumRows++;
}
while (numRows < currentNumRows)
{
currentNumRows--;
RowDefinitions.RemoveAt(currentNumRows);
}
int numCols = NumColumns;
int currentNumCols = ColumnDefinitions.Count;
while (numCols > currentNumCols)
{
ColumnDefinitions.Add(new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star) });
currentNumCols++;
}
while (numCols < currentNumCols)
{
currentNumCols--;
ColumnDefinitions.RemoveAt(currentNumCols);
}
}
Here we read out the number of rows we want and the number of rows we have. If we need more rows we add them to the RowDefinitions collection and set their heights to *. The * means that they should scale automatically. If we have too many rows we remove them starting with the last one.
After setting up the rows we setup the columns the same way.
That is all there is to it!
So this code:
<Window x:Class="DynamicGridDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:grid="clr-namespace:DynamicGridDemo"
Title="MainWindow" Height="350" Width="525">
<grid:DynamicGrid NumColumns="2" NumRows="2">
<Button Grid.Column="0" Grid.Row="0" Content="At 0,0" />
<Button Grid.Column="0" Grid.Row="1" Content="At 0,1" />
<Button Grid.Column="1" Grid.Row="0" Content="At 1,0" />
<Button Grid.Column="1" Grid.Row="1" Content="At 1,1" />
</grid:DynamicGrid>
</Window>
Gives you this result:

Let’s add a header row:
<Window x:Class="DynamicGridDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:grid="clr-namespace:DynamicGridDemo"
Title="MainWindow" Height="350" Width="525">
<grid:DynamicGrid NumColumns="2" NumRows="3">
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="Header label here at 0,0 and 1,0" FontSize="20" Background="Yellow" />
<Button Grid.Column="0" Grid.Row="1" Content="At 0,1" />
<Button Grid.Column="0" Grid.Row="2" Content="At 0,2" />
<Button Grid.Column="1" Grid.Row="1" Content="At 1,1" />
<Button Grid.Column="1" Grid.Row="2" Content="At 1,2" />
</grid:DynamicGrid>
</Window>
Gives this result:

And it even plays nice together with the old syntax. This is useful if you need a header row with a fixed size for example:
<Window x:Class="DynamicGridDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:grid="clr-namespace:DynamicGridDemo"
Title="MainWindow" Height="350" Width="525">
<grid:DynamicGrid NumColumns="2" NumRows="3">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Content="Header label here at 0,0 and 1,0" FontSize="20" Background="Yellow" />
<Button Grid.Column="0" Grid.Row="1" Content="At 0,1" />
<Button Grid.Column="0" Grid.Row="2" Content="At 0,2" />
<Button Grid.Column="1" Grid.Row="1" Content="At 1,1" />
<Button Grid.Column="1" Grid.Row="2" Content="At 1,2" />
</grid:DynamicGrid>
</Window>
This gives the following output (note the size of the first row):

I haven't done much testing of the code, so it may include some nasty bugs, but I think it may be useful. Either if you are prototyping an application or if you need a grid that sizes to your content.
Download the source code here: DynamicGridDemo.zip (77.44 kb) (Visual Studio 2010 b2, WPF4 required)
-Grid-control-for-WPF.aspx)
1553fd61-2b90-4dcb-a6f9-f36b238867e2|1|3.0
.NET, WPF
.net, wpf