Grid with dynamic number of rows and columns, part 1

Grid 10x10
Grid 10×10

The post is devoted to the Wpf datagrid with dynamically defined number of rows and columns but all cells has the same size. For example, such grid could be used in chess or checkers game for 8×8 field. I used code from some sources, for example How to populate a WPF grid based on a 2-dimensional array and Populating a DataGrid with Dynamic Columns in a Silverlight Application using MVVM. Full code is accessible on GitHub Blog repository.
Grid 20x7
Grid 20×7

Features
Application has the following features:

  1. number of rows and columns can be changed at run-time;
  2. all cells has the same width and the same height;
  3. grid occupies as much space as possible;
  4. input click switches the state of the cell.

Application

Main window
Main window

Wpf application is done in MVVM pattern with one main window. Dynamic grid is implemented as user control that contains DataGrid control bound to observable collection of collections of cell view models. In this implementation collection of cells is recreated each time if grid width or grid height is changed, and it leads to some application pauses. In the following post this issue is solved with asynchronous method that updates cell array. Also, other implementation for cells could be used, for example, 2-dimensional array of cells ICellViewModels[][] works well.

Behind code
Dynamic grid view model implements IDynamicGridViewModel interface that has two size’s properties for grid width and height that are number of rows and columns, observable collection of collections of cell view models, and several color properties:

public interface IDynamicGridViewModel
{
  /// <summary>
  /// 2-dimensional collections for CellViewModels.
  /// </summary>
  ObservableCollection<ObservableCollection<ICellViewModel>>
    Cells { get; }

  /// <summary>
  /// Number of grid columns.
  /// </summary>
  int GridWidth { get; }

  /// <summary>
  /// Number of grid rows.
  /// </summary>
  int GridHeight { get; }

  /// <summary>
  /// Start, the lightest, color of cells.
  /// </summary>s
  Color StartColor { get; set; }

  /// <summary>
  /// Finish, the darkest, color of cells.
  /// </summary>
  Color FinishColor { get; set; }

  /// <summary>
  /// Color of borders around cells.
  /// </summary>
  Color BorderColor { get; set; }
}

Values of color properties are assigned to corresponding properties of CellView control. View model for each cell implements ICellViewModel interface that defines property for data model that implements ICell interface and command for changing state for the cell.

public interface ICellViewModel
{
  ICell Cell { get; set; }
  ICommand ChangeCellStateCommand { get; }
}

And, finally, ICell interface contains one Boolean property State:

public interface ICell
{
  /// <summary>
  /// State of the cell.
  /// </summary>
  bool State { get; set; }
}

XAML
The same height of cells is controlled by RowHeight property defined in style of DataGrid:

<Style TargetType="{x:Type DataGrid}">
  <Setter Property="RowHeight">
    <Setter.Value>
      <MultiBinding Converter="{StaticResource DivideDoubleConverter}"
                    ConverterParameter="2">
        <Binding RelativeSource="{RelativeSource Self}"
                 Path="ActualHeight" Mode="OneWay"
                 Converter="{StaticResource SubstractConverter}"
                 ConverterParameter="2"/>
        <Binding Path="DataContext.GridHeight"
                 RelativeSource="{RelativeSource Self}"
                 Mode="OneWay"/>
      </MultiBinding>
    </Setter.Value>
  </Setter>
</Style>

Cell height equals to actual height of data grid minus 2 divided by number of rows. The same width of cells is controlled by Width property of cell data template:

<DataTemplate x:Key="CellTemplate">
  <Border BorderBrush="Transparent"
      BorderThickness="1 0 1 0"
      DataContext="{Binding}">
    <Border.Width>
      <MultiBinding Converter="{StaticResource DivideDoubleConverter}" ConverterParameter="2">
        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}"
              Path="ActualWidth" Mode="OneWay"
              Converter="{StaticResource SubstractConverter}" ConverterParameter="2"/>
        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}"
              Path="DataContext.GridWidth" Mode="OneWay"/>
      </MultiBinding>
    </Border.Width>

    <views:CellView
      DataContext="{Binding}"
      BorderColor="{Binding DataContext.BorderColor,
              RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}},
              Mode=OneWay, FallbackValue=#FF000000}"
      StartColor="{Binding DataContext.StartColor,
              RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}},
              Mode=OneWay, FallbackValue=#FFF0F0F0}"
      FinishColor="{Binding DataContext.FinishColor,
              RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}},
              Mode=OneWay, FallbackValue=#FF0F0F0F}"/>
  </Border>
</DataTemplate>

Similarly, cell width equals to actual width of data grid minus 2 divided by number of columns. And there is a definition of DataGrid control:

<DataGrid
  x:Name="DynamicGrid"
  DataContext="{Binding}"
  ItemsSource="{Binding Path=Cells}"
  IsEnabled="True"
  IsTabStop="False">

  <DataGrid.Columns>
    <DataGridTemplateColumn Width="*">
      <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
          <ItemsControl ItemsSource="{Binding}"
                ItemTemplate="{DynamicResource CellTemplate}">
            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
          </ItemsControl>
        </DataTemplate>
      </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
  </DataGrid.Columns>
</DataGrid>

1. All used IP-addresses, names of servers, workstations, domains, are fictional and are used exclusively as a demonstration only.
2. Information is provided «AS IS».

One thought on “Grid with dynamic number of rows and columns, part 1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.