Grid rows
scroll

Grid with dynamic number of rows and columns, part 1

 

The post showcases a WPF datagrid with dynamic columns where the number of rows and columns is defined but all cells have the same size.

Source code

Download application - 43.4 KB
Download source - 26.5 KB
GitHub

Introduction

The post is devoted to the WPF gridview with a dynamically-defined number of rows and columns but all cells have the same width and height. For example, such a grid could be used in chess or checkers games for 8x8 field.

Application has the following features:

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

You can look at the WPF grid example in the image below, which shows a 10x5 grid table. The values for the number of rows and columns may change dynamically.

Grid main window

Let’s take a look at how WPF creates a grid programmatically. 

Background

Solution uses C#6, .Net 4.6.1, Wpf with MVVM pattern.

Solution

Wpf application

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.

10 columns 10 rows

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:

Dynamic Grid properties

The Datagrid column definition in WPF and its corresponding row definition are represented by two parameters - GridWidth and GridHeight.

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>

Here, WPF grid binding data is provided by “MultiBinding Converter” and “Binding RelativeSource.”

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 datacontext="{Binding}" isenabled="True" istabstop="False" itemssource="{Binding Path=Cells}" x:name="DynamicGrid">
  <datagrid.columns>
    <datagridtemplatecolumn width="*">
      <datagridtemplatecolumn.celltemplate>
        <datatemplate>
          <itemscontrol itemssource="{Binding}" itemtemplate="{DynamicResource CellTemplate}">
            <itemscontrol.itemspanel>
              <itemspaneltemplate>
                <stackpanel orientation="Horizontal">
              </stackpanel></itemspaneltemplate>
            </itemscontrol.itemspanel>
          </itemscontrol>
        </datatemplate>
      </datagridtemplatecolumn.celltemplate>
    </datagridtemplatecolumn>
  </datagrid.columns>
</datagrid>

If you need WPF to populate datagrid, please refer to the following discussion on Microsoft’s Q&A blog. 

Conclusions

In the following article, the following features will be implemented:

  1. asynchronous method of adding/deleting cells;
  2. resize timer that prevents too frequent cell updating;
  3. preserve active cells;
  4. fixed size of cells;
  5. dependency container;
  6. logging.
by Illya Reznykov, Europe Lead 0f Cloud Practice
March 15, 2017

Related articles

Rows
Grid with dynamic number of rows and columns, part 2
by Illya Reznykov, Europe Lead 0f Cloud Practice
March 30, 2017
article
bootstrap extend grid system
Bootstrap 3.3 Extend Grid System (LESS)
by Svitla Team
October 17, 2016
article

Let's meet Svitla

We look forward to sharing our expertise, consulting you about your product idea, or helping you find the right solution for an existing project.

Thank you! We will contact very shortly.

Your message is received. Svitla's sales manager of your region will contact you to discuss how we could be helpful.