65.9K
CodeProject 正在变化。 阅读更多。
Home

具有动态行数和列数的 Grid - 第 1 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2017年1月3日

CPOL

2分钟阅读

viewsIcon

34655

downloadIcon

3020

具有相同大小的、动态定义的行数和列数的 WPF 数据网格。

引言

本文档介绍具有动态定义行数和列数的 WPF 数据网格,但所有单元格具有相同的宽度和高度。例如,这种网格可以用于国际象棋或跳棋游戏中的 8x8 棋盘。

该应用程序具有以下功能:

  1. 行数和列数可以在运行时更改
  2. 所有单元格具有相同的宽度和高度
  3. 网格占据尽可能多的空间
  4. 单击输入会切换单元格的状态

背景

该解决方案使用 C#6、.NET 4.6.1、WPF 和 MVVM 模式。

解决方案

WPF 应用程序

WPF 应用程序采用 MVVM 模式,包含一个主窗口。动态网格实现为用户控件,其中包含绑定到单元格视图模型集合的集合的 DataGrid 控件。在这种实现中,如果网格宽度或网格高度发生更改,则每次都会重新创建单元格集合,这会导致应用程序出现一些暂停。在后续文章中,通过异步方法更新单元格数组来解决此问题。此外,还可以使用其他实现来处理单元格;例如,单元格 ICellViewModels[][] 的二维数组效果很好。

代码详解

动态网格视图模型实现了 IDynamicGridViewModel 接口,该接口具有用于网格宽度和高度的两个大小属性(行数和列数)、单元格视图模型集合的集合以及几个颜色属性

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; }
}

颜色属性的值分配给 CellView 控件的相应属性。每个单元格的视图模型实现了 ICellViewModel 接口,该接口定义了实现 ICell 接口的数据模型属性以及用于更改单元格状态的命令。

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

最后,ICell 接口包含一个布尔属性 State

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

XAML

单元格的相同高度由 DataGrid 样式中定义的 RowHeight 属性控制

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

单元格高度等于数据网格的实际高度减去 2 除以行数。单元格的相同宽度由单元格数据模板的 Width 属性控制

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

类似地,单元格宽度等于数据网格的实际宽度减去 2 除以列数。并且有 DataGrid 控件的定义

<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. 异步添加/删除单元格的方法
  2. 防止过于频繁的单元格更新的重置计时器
  3. 保留活动单元格
  4. 固定单元格大小
  5. 依赖注入容器
  6. 日志记录

历史

  • 2017 年 1 月 3 日:初始文章
© . All rights reserved.