Click here to Skip to main content
15,867,765 members
Articles / Desktop Programming / WPF
Tip/Trick

WPF Paged UniformGrid ItemsControl

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
7 Feb 2017CPOL3 min read 21.7K   774   5   4
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:

C#
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:

C#
  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:

XML
<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>

Image 1

Image 2

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
Questionhello, good job. but If u have VB ?? thanks Pin
fadyrashwan18-May-19 3:26
fadyrashwan18-May-19 3:26 
hello, good job. but If u have VB ?? thanks
Bugdoesn't display changes in MainItemsSource Pin
Alexander Sharykin7-Feb-17 22:06
Alexander Sharykin7-Feb-17 22:06 
GeneralRe: doesn't display changes in MainItemsSource Pin
Clifford Nelson8-Feb-17 8:06
Clifford Nelson8-Feb-17 8:06 
AnswerRe: doesn't display changes in MainItemsSource Pin
Clifford Nelson8-Feb-17 19:17
Clifford Nelson8-Feb-17 19:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.