Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Paged UniformGrid ItemsControl

0.00/5 (No votes)
7 Feb 2017 1  
This tip presents a control that is derived from the ItemsControl that uses a UniformGrid to display.

Introduction

I had a requirement to display data that would be paged, and would handle resizing. It seemed like the UniformGrid was the right choice. I just had to add paging.

Design

To make the control as easy to use as possible, and not require an XAML file, I create a template for the ItemsPanel in code, and set it there. This template just contains a UniformGrid. There are two bindings that I create for the Columns and Rows properties of the UniformGrid that are bound each to a private DependencyProperty in this control.

There is a public DependencyProperty for the minimum column and row size (MinColumnWidth and MinRowHeight), and there is also a MainItemsSource DependencyProperty that is to be used instead of the ItemsSource DependencyProperty. This is because of the paging.

There are also two ICommand properties that are used for Binding buttons to implement the paging.

The UniformGrid is set as the ItemsPanel with the following code:

FrameworkElementFactory factory =
new FrameworkElementFactory(typeof(UniformGrid), "UniformGrid");
Binding bindingColumns = new Binding("UniformGirdColumns")
{
 Mode = BindingMode.OneWay,
 RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, GetType(), 1)
};
Binding bindingRows = new Binding("UniformGirdRows")
{
 Mode = BindingMode.OneWay,
 RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, GetType(), 1)
};
factory.SetBinding(UniformGrid.ColumnsProperty, bindingColumns);
factory.SetBinding(UniformGrid.RowsProperty, bindingRows);

ItemsPanel = new ItemsPanelTemplate(factory);

DependencyProperties

There are three custom DependencyProperty types for this control that are used to control the behavior of the UniformGrid control used as the ItemsPanelTemplate:

MinColumnWidth: This is the minimum column width for the UniformGird ItemsPanelTemplate. If the value is 0 (the default) then there will only be a single column. Otherwise there will be a minimum of 1, or the number of columns equal floor of the width of the ItemsControl divided by the MinColumnWidth. The actual width of the columns will be the width of the ItemsControl divided by this number. The number of columns is maintained in a private DependencyProperty called UniformGirdColumns.

MinRowHeight: This is the minimum row height for the UniformGird ItemsPanelTemplate. If the value is 0 (the default) then there will only be a single row. Otherwise there will be a minimum of 1, or the number of rows equal floor of the height of the ItemsControl divided by the MinRowHeight. The actual height of the rows will be the height of the ItemsControl divided by this number. The number of rows is maintained in a private DependencyProperty called UniformGirdRows.

Orientation: This will specify whether the items will priority horizonally or priority vertically. It is of Type System.Windows.Controls.Orientation. The default is Horizontal. If this is changed to Vertical, there will be empty DataTemplate items in the grid if the ItemTemplate is specified. If the DataTemplate with the correct DataType is specified in the Resources, then the empty DataTemplates will not be displayed, but understand that the DataContext will have to match the Type for the DataTemplate.

INotifyCollectionChanged

There is one problem that requires special consideration, and that if if the collection bound to the MainItemsSource is modified, the control has to know about this change. In my requirement I did not require handling adding or removing from the collection, but there are many situations where there is a need to handle this sitution. An ObservableCollection is usually used to resolve this issue. Therefore I have improved the code to check if the IEnumerable parent class implements INotifyPropertyChanged, and attaches an event handler to IEnumerable that will cause the contents of the ItemsSource property to be recalculator:

  private static void OnMainItemsSourceChanged(DependencyObject d,
      DependencyPropertyChangedEventArgs e)
  {
    var itemsControl = (UniformGirdItemsControl)d;
    if (e.OldValue is INotifyCollectionChanged)
    {
      ((INotifyCollectionChanged)e.OldValue).CollectionChanged -=
         itemsControl.UniformGirdItemsControl_CollectionChanged;
    }
    if (e.NewValue is INotifyCollectionChanged)
    {
      ((INotifyCollectionChanged)e.NewValue).CollectionChanged +=
         itemsControl.UniformGirdItemsControl_CollectionChanged;
    }

 itemsControl.OnDataChanged();
}

private void UniformGirdItemsControl_CollectionChanged(object sender,
    NotifyCollectionChangedEventArgs e)
{
    OnDataChanged();
}

Using the Code

Below is an example of using this Control:

<Grid>
 <Grid.RowDefinitions>
  <RowDefinition Height="*" />
  <RowDefinition Height="Auto" />
 </Grid.RowDefinitions>
 <Border Margin="10"
         Padding="5"
         BorderBrush="Black"
         BorderThickness="1"
         CornerRadius="2">
  <local:UniformGirdItemsControl x:Name="ItemsControl"
                                 MainItemsSource="{Binding ItemsSource}"
                                 MinColumnWidth="100"
                                 MinRowHeight="50" />
 </Border>
 <Button Grid.Row="1"
         Width="80"
         Margin="10"
         HorizontalAlignment="Left"
         Command="{Binding ElementName=ItemsControl, Path=PreviousPageCommand}"
         Content="Previous" />
 <Button Grid.Row="1"
         Width="80"
         Margin="10"
         HorizontalAlignment="Right"
         Command="{Binding ElementName=ItemsControl, Path=NextPageCommand}"
         Content="Next" />
</Grid>

I have included the UniformGridItems Control and two Button controls for navigation to provide a fully functional sample. There is also a TextBox and a Button to add a new Button to the UniformGridItemsControl.

Important Note

This control was designed for a very static environment. It has been updated to handle adding or removing items in the collection. There may be other issues, and would be happy to take into any reports of issues. I would like to thank Alexander Sharykin for his comment.

History

  • 02/07/2016: Initial version
  • 02/08/2016: Update with Important Note section.
  • 03/03/2017: Added the Orientation DependencyProperty

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here