Changing WPF Grid Structure Dynamically

October 19, 2012

tags: , , ,
no comments

In an application I was working on recently, I needed to implement a split screen, like those on the closed-circuit cameras monitors. Usually, a simple Grid or a UniformGrid would do the trick, but in this case, the requirement was to allow the user to dynamically change the grid’s structure. The following screenshots shows the grid’s structure when the user chooses to display two, three, or four screens, respectively:

TwoScreens

ThreeScreens

fourScreens

As you can see, different user selections requires entirely different grid structure, with different number of rows and columns and even different spans for some of the grid’s children. After giving the problem some thought, I came up with a solution that enabled me to do just that, and which can be easily extended in the future to include additional structures.

Introducing DynamicGridFormationBehavior

DynamicGridFormationBehavior is a behavior that can be applied to a grid to allow changing its structure to one of several predefined formations, based on the desired number of visible children. The behavior contains the following dependency properties:

   1: public static readonly DependencyProperty FormationsProperty =

   2:         DependencyProperty.Register("Formations",

   3:                                     typeof(DynamicGridFormationCollection),

   4:                                     typeof(DynamicGridFormationBehavior),

   5:                                     new PropertyMetadata(new DynamicGridFormationCollection(), OnFormationsChanged));

   6:                                     

   7: public static readonly DependencyProperty NumberOfVisibleChildrenProperty =

   8:         DependencyProperty.Register("NumberOfVisibleChildren",

   9:                                     typeof (int),

  10:                                     typeof (DynamicGridFormationBehavior),

  11:                                     new PropertyMetadata(1, OnVisibleChildrenCountChanged));

The NumberOfVisibleChildren property contains the desired number of the grid’s children to display. Based on this value, a predefined formation will be chosen.

The Formations property contains the predefined formations, as defined in the xaml file. Each formation is of type DynamicGridFormation.

   1: public class DynamicGridFormation : Freezable

   2: {

   3:     public int NumberOfChildren

   4:     {

   5:         get { ... }

   6:         set { ... }

   7:     }

   8:  

   9:     public int NumberOfRows

  10:     {

  11:         get { ... }

  12:         set { ... }

  13:     }

  14:  

  15:     public int NumberOfColumns

  16:     {

  17:         get { ... }

  18:         set { ... }

  19:     }

  20:  

  21:     public DynamicGridChildPropertiesCollection ChildProperties { get; set; }

  22:     

  23:     ...

  24: }

Each formation contains the following properties:

  • NumberOfChildren – The number of visible children, for which the formation should be used.
  • NumberOfRows – The number of rows that the grid will contain when this formation is used.
  • NumberOfColumns – The number of columns that the grid will contain when this formation is used.
  • ChildProperties – A collection of DynamicGridChildProperties elements.
   1: public class DynamicGridChildProperties : Freezable

   2: {

   3:     public int Index

   4:     {

   5:         get { ... }

   6:         set { ... }

   7:     }

   8:  

   9:     public int Row

  10:     {

  11:         get { ... }

  12:         set { ... }

  13:     }

  14:  

  15:     public int Column

  16:     {

  17:         get { ... }

  18:         set { ... }

  19:     }

  20:  

  21:     public int RowSpan

  22:     {

  23:         get { ... }

  24:         set { ... }

  25:     }

  26:  

  27:     public int ColumnSpan

  28:     {

  29:         get { ... }

  30:         set { ... }

  31:     }

  32:  

  33:     public Visibility Visibility { get; set; }

  34: }

The DynamicGridChildProperties class contains grid-related properties for each child that will be shown in the formation. When the formation is applied, each visible child will be placed within the grid based on the following properties:

  • Index – The index in the grid’s Children collection, which contains the child onto which these properties should be applied.
  • Row – The grid’s row in which the child should be placed.
  • Column – The grid’s column in which the child should be placed.
  • RowSpan – The number of rows that the child will span within the grid.
  • ColumnSpan – The number of columns that the child will span within the grid.
  • Visibility – The visibility of the child.

Notice that both the DynamicGridFormation class and the DynamicGridChildProperties class inherits from the Freezable class. It is needed to support DataContext inheritance.

The following markup shows how to use the behavior:

   1: <ItemsControl Grid.Column="0" Grid.Row="1" ItemsSource="{Binding Items}">

   2:  

   3:     <ItemsControl.ItemTemplate>

   4:         ...

   5:     </ItemsControl.ItemTemplate>

   6:  

   7:     <ItemsControl.ItemsPanel>

   8:         <ItemsPanelTemplate>

   9:             <Grid>

  10:                 <i:Interaction.Behaviors>

  11:                     <behaviors:DynamicGridFormationBehavior 

  12:                         NumberOfVisibleChildren="{Binding NumberOfVisibleItems}">

  13:                         

  14:                         <behaviors:DynamicGridFormationBehavior.Formations>

  15:                             <behaviors:DynamicGridFormationCollection>

  16:                             

  17:                                 <!-- Formation for one visible child -->

  18:                                 <behaviors:DynamicGridFormation 

  19:                                     NumberOfChildren="1" 

  20:                                     NumberOfRows="1" 

  21:                                     NumberOfColumns="1">

  22:                                     <behaviors:DynamicGridFormation.ChildProperties>

  23:                                         <behaviors:DynamicGridChildPropertiesCollection>

  24:                                             <behaviors:DynamicGridChildProperties 

  25:                                                 Index="0" 

  26:                                                 Row="0" 

  27:                                                 Column="0" />

  28:                                         </behaviors:DynamicGridChildPropertiesCollection>

  29:                                     </behaviors:DynamicGridFormation.ChildProperties>

  30:                                 </behaviors:DynamicGridFormation>

  31:                                 

  32:                                 <!-- Formation for two visible children -->

  33:                                 <behaviors:DynamicGridFormation 

  34:                                     NumberOfChildren="2" 

  35:                                     NumberOfRows="1" 

  36:                                     NumberOfColumns="2">

  37:                                     <behaviors:DynamicGridFormation.ChildProperties>

  38:                                         <behaviors:DynamicGridChildPropertiesCollection>

  39:                                             <behaviors:DynamicGridChildProperties 

  40:                                                 Index="0" 

  41:                                                 Row="0" 

  42:                                                 Column="0" />

  43:                                             <behaviors:DynamicGridChildProperties 

  44:                                                 Index="1" 

  45:                                                 Row="0" 

  46:                                                 Column="1" />

  47:                                         </behaviors:DynamicGridChildPropertiesCollection>

  48:                                     </behaviors:DynamicGridFormation.ChildProperties>

  49:                                 </behaviors:DynamicGridFormation>

  50:                                 

  51:                                 <!-- Formation for three visible children -->

  52:                                 <behaviors:DynamicGridFormation 

  53:                                     NumberOfChildren="3" 

  54:                                     NumberOfRows="2" 

  55:                                     NumberOfColumns="2">

  56:                                     <behaviors:DynamicGridFormation.ChildProperties>

  57:                                         <behaviors:DynamicGridChildPropertiesCollection>

  58:                                             <behaviors:DynamicGridChildProperties 

  59:                                                 Index="0" 

  60:                                                 Row="0" 

  61:                                                 Column="0" />

  62:                                             <behaviors:DynamicGridChildProperties 

  63:                                                 Index="1" 

  64:                                                 Row="1" 

  65:                                                 Column="0" />

  66:                                             <behaviors:DynamicGridChildProperties 

  67:                                                 Index="2" 

  68:                                                 Row="0" 

  69:                                                 Column="1" 

  70:                                                 RowSpan="2" />

  71:                                         </behaviors:DynamicGridChildPropertiesCollection>

  72:                                     </behaviors:DynamicGridFormation.ChildProperties>

  73:                                 </behaviors:DynamicGridFormation>

  74:                                 

  75:                                 <!-- Formation for four visible children -->

  76:                                 <behaviors:DynamicGridFormation 

  77:                                     NumberOfChildren="4" 

  78:                                     NumberOfRows="2" 

  79:                                     NumberOfColumns="2">

  80:                                     <behaviors:DynamicGridFormation.ChildProperties>

  81:                                         <behaviors:DynamicGridChildPropertiesCollection>

  82:                                             <behaviors:DynamicGridChildProperties 

  83:                                                 Index="0" 

  84:                                                 Row="0" 

  85:                                                 Column="0" />

  86:                                             <behaviors:DynamicGridChildProperties 

  87:                                                 Index="1" 

  88:                                                 Row="0" 

  89:                                                 Column="1" />

  90:                                             <behaviors:DynamicGridChildProperties 

  91:                                                 Index="2" 

  92:                                                 Row="1" 

  93:                                                 Column="0" />

  94:                                             <behaviors:DynamicGridChildProperties 

  95:                                                 Index="3" 

  96:                                                 Row="1" 

  97:                                                 Column="1" />

  98:                                         </behaviors:DynamicGridChildPropertiesCollection>

  99:                                     </behaviors:DynamicGridFormation.ChildProperties>

 100:                                 </behaviors:DynamicGridFormation>

 101:                                 

 102:                             </behaviors:DynamicGridFormationCollection>

 103:                         </behaviors:DynamicGridFormationBehavior.Formations>

 104:                         

 105:                     </behaviors:DynamicGridFormationBehavior>

 106:                 </i:Interaction.Behaviors>

 107:             </Grid>

 108:         </ItemsPanelTemplate>

 109:     </ItemsControl.ItemsPanel>

 110:  

 111: </ItemsControl>

Four formations are defined to represent the states shown in the screenshots above. Also, notice how the NumberOfVisibleChildren property is bound to the value of the view model’s NumberOfVisibleItems property, allowing a clear separation between the logic that determines how many children to show, and the way in which they are shown.

The code is available on GitHub, as part of my WPFUtils project.

Cross-posted from http://www.programmingtidbits.com/post/2012/10/18/Changing-WPF-Grid-Structure-Dynamically.aspx

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*