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





5.00/5 (6投票s)
具有相同大小的、动态定义的行数和列数的 WPF 数据网格。
引言
本文档介绍具有动态定义行数和列数的 WPF 数据网格,但所有单元格具有相同的宽度和高度。例如,这种网格可以用于国际象棋或跳棋游戏中的 8x8 棋盘。
该应用程序具有以下功能:
- 行数和列数可以在运行时更改
- 所有单元格具有相同的宽度和高度
- 网格占据尽可能多的空间
- 单击输入会切换单元格的状态
背景
该解决方案使用 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>
结论
在下一篇文章中,将实现以下功能:
- 异步添加/删除单元格的方法
- 防止过于频繁的单元格更新的重置计时器
- 保留活动单元格
- 固定单元格大小
- 依赖注入容器
- 日志记录
历史
- 2017 年 1 月 3 日:初始文章