MVVM项目中的动态DataGrid单元样式

目录

介绍

先决条件

使用代码

转换器

数据网格绑定

按钮绑定

结论


介绍

这个小应用程序演示了一种基于DataGrid单元格内容动态修改单元格样式的方法。动态样式的一个示例:如果单元格中的值变为负数,则可能需要将单元格的背景色更改为红色。尤其强调了Model-View-ViewModelMVVM)模式来演示此样式。我提供完整的Visual Studio源代码和其他项目文件。可以从本文头部下载该项目的工作示例。在应用程序中,每当用户单击[CHANGE VALUES] DataGrid中的单元格就会填充19的新随机整数。单元格的背景颜色会根据单元格的新内容而变化。

先决条件

该解决方案是使用Visual Studio 2019社区版16.3.9版和.NET 4.7.2构建的。它还需要Expression.Blend.Sdk版本1.0.2,但是此SDK与项目文件打包在一起。

还假定读者对CWPF项目和MVVM模式有基本的了解。

使用代码

当您生成并运行代码时,将出现以下窗口:

注意:单元格中的整数是由随机生成器生成的,每次运行代码时以及每次单击[CHANGE VALUES]按钮时都会不同。我意识到颜色有点扎眼,但目的是清楚地显示当DataGrid单元格内容更改时如何更改单元格的背景色。它不旨在符合Microsoft关于用户界面样式的平淡准则。

动态单元格样式由MultiValueConverter来完成。Converter是项目View中名为CellColorConverter.cs的文件的一部分。这些转换器要求将对象数组传递给它们,它们将基于该对象数组进行处理。在我们的例子中,此对象数组将由两个对象组成:DataGrid中的DataGridCellDataRow,用于容纳必须为其设置属性的单元格。

每个单元格包含一个介于19之间的随机整数。每次单击[CHANGE VALUES]时,这些数字都会随机变化。单元格的背景颜色也会发生变化,如下所述。

在上面的示例中,单元格的背景色设置为First列中所有单元格的系统颜色Colors.LightGoldenrodYellow,而不考虑单元格的内容。对于Second列至Eighth列中的所有单元格,背景颜色设置如下:

如果单元格包含一个数123,背景颜色设置为系统颜色Colors.LightGreen

如果单元格包含一个数456,背景颜色设置为系统颜色Colors.LightSteelBlue

如果单元格包含一个数789,背景颜色设置为系统颜色Colors.LightSalmon

对于First” 列中的单元格,字体大小设置为20,字体样式设置为斜体。当然,这可以通过更简单的方法来完成,但是这里的目的是显示Converter可以如何影响其他属性(例如字体设置)的更改。

首先,我们需要知道Converter是如何与项目联系在一起的:

看一下标签<Window.Resources>下的XAML主文件:MainWindow.xaml。在这里,转换器被列为带有Key的资源:"ColorConverter"

<local:CellColorConverter x:Key="ColorConverter" />

现在在DataGrid定义样式的位置向下看一点:

<Style x:Key="BlueDataGridStyle" TargetType="{x:Type DataGrid}">
            <Setter Property="CellStyle" Value="{DynamicResource BlueDataGridCellStyle}" />

.................................

BlueDataGridCellStyle中的单元格背景色定义如下:

<Setter Property="Background">
    <Setter.Value>
        <MultiBinding Converter="{StaticResource ColorConverter}">
            <MultiBinding.Bindings>
                <Binding RelativeSource="{RelativeSource Self}" />
                <Binding Path="Row" />
            </MultiBinding.Bindings>
        </MultiBinding>
    </Setter.Value>
</Setter>

在这里,您可以看到将两个对象传递给Converter,首先是单元格本身,然后是包含该单元格的行:

<Binding RelativeSource="{RelativeSource Self}" />
<Binding Path="Row" />

关于单元格的注意事项:单元格将传递至Converter,而转换器没有其内容。在Converter中,您可能会尝试按如下方式访问单元格的内容Cell.Content这在语法上是正确的,但是它将始终返回null。稍后再详细介绍。

因此,当系统必须渲染单元格的背景时,它将看到需要将单元格及其行传递到Converter,并且Converter会返回用于背景的颜色。

转换器

现在该看看最重要的Converter了。任何MultiValueConverter都必须遵守IMultiValueConverter接口中指定的契约。该接口指示Converter代码应具有两种方法:

object Convert(object[] values, Type targetType, 
               object parameter, System.Globalization.CultureInfo culture)

和:

object[] ConvertBack(object value, Type[] targetType, 
                     object parameter, System.Globalization.CultureInfo culture)

通常, ConvertBac方法不执行任何操作,但必须提供该方法才能满足该接口。Convert(...)方法的第一个参数object[]将包括所呈现的单元格和包含该单元的DataGrid行组成。在此不使用此方法的其他参数。

这是Converter的整个Convert(...)方法:

public object Convert(object[] values, Type targetType,
                      object parameter, System.Globalization.CultureInfo culture)
{
 if (values[0] is DataGridCell cell && values[1] is DataRow row)
 {
  try
  {
   string columnName = (string)cell.Column.Header;
   int content = row.Field<int>(columnName); // Header must be same as column name
   int columnIndex = cell.Column.DisplayIndex;

   if (columnIndex == 0)
   {
    cell.FontStyle = FontStyles.Italic;
    cell.FontWeight = FontWeights.Bold;
    cell.FontSize = 20;
    return new SolidColorBrush(Colors.LightGoldenrodYellow);
   }

   if (content < 4)
   {
    return new SolidColorBrush(Colors.LightGreen);
   }

   if (content > 6)
   {
    return new SolidColorBrush(Colors.LightSalmon);
   }

   return new SolidColorBrush(Colors.LightSteelBlue);
  }
  catch (Exception)
  {
   return new SolidColorBrush(Colors.Black); // Error! An Exception was thrown
  }
 }
 return new SolidColorBrush(Colors.DarkRed); // Error! object[] is invalid.
}

首先,我们需要验证传递给 Converterobject[]是否由DataGridCellDataRow组成,同时为这两个对象指定名称:

if (values[0] is DataGridCell cell && values[1] is DataRow row)

由于我们无法直接访问单元格的content,因此我们从对应于该单元格的行Field中获取它:

string columnName = (string)cell.Column.Header;
int content = row.Field<int>(columnName); // Header must be same as column name

content现在将DataGrid单元格中的整数传递给Converter

注意:为了使这种方法起作用,DataGrid列标题必须与列名称相同。这通常不是问题。

接着, Converter得到ColumnDisplayIndex,如果索引是零(最左边的列)时,Converter做将对单元格字体进行一些更改,并返回单元格背景的系统颜色Colors.LightGoldenrodYellow。这是将整个列设置为具有相同背景色的方式:

int columnIndex = cell.Column.DisplayIndex;

if (columnIndex == 0)
{
 cell.FontStyle = FontStyles.Italic;
 cell.FontWeight = FontWeights.Bold;
 cell.FontSize = 20;
 return new SolidColorBrush(Colors.LightGoldenrodYellow);
}

接着,在列索引大于零的情况下,Converter返回Colors.LightGreencontent小于4Colors.LightSalmoncontent大于6Colors.LightSteelBlue456

if (content < 4)
{
 return new SolidColorBrush(Colors.LightGreen);
}

if (content > 6)
{
 return new SolidColorBrush(Colors.LightSalmon);
}

return new SolidColorBrush(Colors.LightSteelBlue);

数据网格绑定

我们如何将整数放入DataGrid的单元格中?如果您查看ViewModelMainViewModel.cs),将会看到一个名为 ValuesArrayDataTable,其中的列名与DataGrid的列名相同ViewMainWindow.xaml)中,您将看到该窗口的DataContext设置为MainViewModel

xmlns:viewmodel="clr-namespace:DataGridProject.ViewModel"

和:

<Window.DataContext>
    <viewmodel:MainViewModel />
</Window.DataContext>

另外,在MainWindow.xaml中的DataGrid定义中:

ItemsSource="{Binding Path=ValuesArray}"

这会将DataGrid单元格中的值绑定到的相应DataTable ValuesArray单元格的值。换言之,ViewViewModel之间的耦合非常松散。正如MVVM模式的原则要求的那样,ViewModelView中的任何控件都没有直接的知识。

按钮绑定

同样,这两个按钮通过绑定松散地连接到ViewModel中的方法。单击[CHANGE VALUES]ViewModel中的以下方法通过绑定执行:

private void ChangeValues()
{
 DataRow tableRow;
 int row;
 Random rnd = new Random();

 ValuesArray.Rows.Clear();

 for (row = 0; row < 16; row++)
 {
  tableRow = ValuesArray.NewRow();

  tableRow.SetField<int>("First", rnd.Next(1, 10));
  tableRow.SetField<int>("Second", rnd.Next(1, 10));
  tableRow.SetField<int>("Third 3", rnd.Next(1, 10));
  tableRow.SetField<int>("Fourth", rnd.Next(1, 10));
  tableRow.SetField<int>("Fifth", rnd.Next(1, 10));
  tableRow.SetField<int>("Sixth", rnd.Next(1, 10));
  tableRow.SetField<int>("Seventh", rnd.Next(1, 10));
  tableRow.SetField<int>("Eighth", rnd.Next(1, 10));

  ValuesArray.Rows.Add(tableRow);
 }
}

ViewModel将整数保存在DataTable ValuesArray。它不知道 这些值是通过绑定传递到View的。

结论

我花了一段时间才弄清楚如何使用MultiValueConverter。我的目标是执行与ViewDataGrid单元的样式相关的所有操作,并使ViewModel不参与这些操作。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值