UniformGrid

The UniformGrid panel is a lightweight panel that provides a simple way to create a grid of items, where each item is of the same size. We can set its Row and Column properties to specify how many rows and columns we want our grid to have. If we do not set one or both of these properties, the panel will implicitly set them for us, depending upon the available space it has and the size of its children.

It also provides us with a FirstColumn property that will affect the column that the first child item will be rendered in. For example, if we set this property to 2 then the first child will be rendered in the third column. This is perfect for a calendar control, so let's take a look at how we might create the following output using the UniformGrid:

As you can see, a calendar control often needs to have blank spaces in the first few columns and so the FirstColumn property achieves this requirement simply. Let's see the XAML that defines this calendar example:

<StackPanel TextElement.FontSize="14" Background="White"> 
  <UniformGrid Columns="7" Rows="1"> 
    <UniformGrid.Resources> 
      <Style TargetType="{x:Type TextBlock}"> 
        <Setter Property="Height" Value="35" /> 
        <Setter Property="HorizontalAlignment" Value="Center" /> 
        <Setter Property="Padding" Value="0,5,0,0" /> 
      </Style> 
    </UniformGrid.Resources> 
    <TextBlock Text="Mon" /> 
    <TextBlock Text="Tue" /> 
    <TextBlock Text="Wed" /> 
    <TextBlock Text="Thu" /> 
    <TextBlock Text="Fri" /> 
    <TextBlock Text="Sat" /> 
    <TextBlock Text="Sun" /> 
  </UniformGrid> 
  <ItemsControl ItemsSource="{Binding Days}" Background="Black"
Padding="0,0,1,1"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Columns="7" FirstColumn="2" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Border BorderBrush="Black" BorderThickness="1,1,0,0" Background="White"> <TextBlock Text="{Binding}" Height="35" HorizontalAlignment="Center" Padding="0,7.5,0,0" /> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel>

We start with a StackPanel that is used to stack one UniformGrid panel directly above an ItemsControl that uses another one as its ItemsPanel and specifies a font size to use within the control. The top UniformGrid panel declares a single row of seven columns and some basic TextBlock styles. It has seven child TextBlock items that output the names of the days in a week.

The ItemsControl element has its Background property set to Black to black out days not in the current month, and its Padding set to make the background appear like a border to the right and bottom of the calendar. The top and left borders come from the individual cells in the UniformGrid panel. The ItemsControl.ItemsSource property is data bound to a Days property in our View Model, so let's take a look at that now:

private List<int> days = Enumerable.Range(1, 31).ToList(); 
 
... 
 
public List<int> Days 
{ 
  get { return days; } 
  set { days = value; NotifyPropertyChanged(); } 
} 

Note the use of the Enumerable.Range method to populate the collection. It provides a simple way to generate a contiguous sequence of integers from the supplied start and length input parameters. As a LINQ method, it is implemented using deferred execution and the actual values are not generated until actually accessed.

The second UniformGrid panel, which is set as the ItemsControl.ItemsPanel, only specifies that it should have seven columns, but leaves the number of rows to be calculated from the number of data bound items. Note also that we have hard coded a value of 2 to the FirstColumn property, although in a proper control, we would typically data bind the value for the relevant month to it instead.

Finally, we use a DataTemplate to define what each day on the calendar should look like. Note that we do not need to specify a value for its DataType property in this example, because we are data binding to the whole data source object, which in this case is just an integer. Let's now move on to investigate the WrapPanel panel.