WPF DataGrid - 对单个单元格进行条件格式设置的触发器

WPF DataGrid - Triggers for Conditional Formatting on Individual Cells

我需要让我的 DataGrid 中的单元格反映条件规则...到目前为止我已经花了太多时间在这上面我希望有人能帮助我吗?所以,这就是我的网格当前在 运行:

时的样子

在这里你可以看到(忽略 Outcome & Criterion 列)这里有三组两列需要关注.我目前正在将条件规则应用于 XPercVerified 列,但最终我希望 EF[X] 列显示基于值的背景变化在相应的 PercVerified 列中。另一点是,可以有任意数量的这些双列组合...

然而,我目前遇到的问题是,我似乎只能应用规则来有条件地格式化整个行。在上面的屏幕截图中,您可以看到 1PercVerified 列的第一行有一个 1。这使整行都变成绿色。代码如下:

Private Sub dgUnitMatrix_AutoGeneratingColumn(sender As Object, e As DataGridAutoGeneratingColumnEventArgs) Handles dgUnitMatrix.AutoGeneratingColumn

    If (e.Column.Header.ToString().Contains("PercVerified")) Then

        e.Column.CellStyle = TryCast(Application.Current.FindResource("PercVerified"), Style)

    End If

End Sub

这里我们调用 AutoGeneratingColumn 事件,当我们有一个列是 like PercVerified,调用应用资源样式:

<Application x:Class="Application"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml"
ShutdownMode="OnMainWindowClose">

    <Application.Resources>

        <Style x:Key="PercVerified" TargetType="{x:Type DataGridCell}" >
            <Style.Triggers>
                <DataTrigger Binding="{Binding 1PercVerified}" Value="0">
                    <Setter Property="Background" Value="DarkRed"></Setter>
                    <Setter Property="Foreground" Value="White"></Setter>
                    <Setter Property="Margin" Value="-2.0"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding 1PercVerified}" Value="1">
                    <Setter Property="Background" Value="DarkGreen"></Setter>
                    <Setter Property="Foreground" Value="White"></Setter>
                    <Setter Property="Margin" Value="-2.0"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </Application.Resources>
</Application>

存在于Application.XAML文件中。 我似乎遇到的另一个问题是,即使我能让上面的内容只在单元格(而不是行)上工作——我似乎只能在我的条件下确定(即绑定值必须等于——我不能在整数大于或小于的地方做?)...这是真的吗?

我开始把目光投向更远的地方,因为我觉得这种样式无法满足我的需求……因此,IValueConverters 出现了几次,但我似乎无法做到让这个工作。值得我花时间研究这些只是为了发现这是另一个死胡同吗??

我可以提供任何人可能需要的任何额外信息...我非常想解决这个问题,因为我已经在 WPF 中的某些东西上浪费了太多时间,而在 Winforms 中却非常简单!

编辑

遵循 J.H 的出色回答。我已将其调整为与作为对象值传递的 DataRowView 一起工作(而不是示例 Class 对象)。

在转换器中,我们正在创建变量 dc。整个代码块已更改为:

    ' Get the cells DataContext as our data class '
    Dim dc As DataRowView
    dc = TryCast(cell.DataContext, DataRowView)
    If IsNothing(dc) Then Exit Function

    ' Get the column number of the columnName that matches the Path 
    Dim ColNo As Integer
    Dim idx As Integer = 0
    For Each column As DataColumn In dc.DataView.Table.Columns
        If column.ColumnName = path Then
            ColNo = idx
        End If
        idx = idx + 1
    Next

然后在对条件格式进行比较时,而不是:

Dim pv = dc.GetType().GetProperty(path).GetValue(dc)

我们使用

Dim pv As String = dc.Row.Item(ColNo).ToString()

除了因差异而导致的一些其他变化外,这是一种享受!

您可以使用 IValueConverter 来完成此操作。您需要将 DataGridCell 传递给转换器。从单元格中,您可以获得单元格的数据上下文和绑定。一旦你有了这些,一些反思可以让你获得 PercVerifiedX 字段的值。您可以检测 EFX 字段,然后获取相应的 PercVerifiedX 字段。

这是一些代码,请注意我的 属性 名称与您的不太一样(不能以数字开头 属性)因此您可能需要对代码进行一些调整。此外,我本可以使用数字,但为 DataTrigger.Value 选择了字符串,只是为了表明它不是 PercVerified 值,而是转换器的 return 值。 而且,我把它混合了一点——红色代表 0 percs,绿色代表 1-3 percs,紫色代表 4+ percs。请参阅 xaml 和转换器的代码。

XAML

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication16"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:PercVerifiedConverter x:Key="PercVerifiedConverter" />
        <Style x:Key="PercVerified" TargetType="{x:Type DataGridCell}" >
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource PercVerifiedConverter}}" Value="Red">
                    <Setter Property="Background" Value="DarkRed"></Setter>
                    <Setter Property="Foreground" Value="White"></Setter>
                    <Setter Property="Margin" Value="-2.0"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource PercVerifiedConverter}}" Value="Green">
                    <Setter Property="Background" Value="DarkGreen"></Setter>
                    <Setter Property="Foreground" Value="White"></Setter>
                    <Setter Property="Margin" Value="-2.0"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource PercVerifiedConverter}}" Value="Purple">
                    <Setter Property="Background" Value="Purple"></Setter>
                    <Setter Property="Foreground" Value="Yellow"></Setter>
                    <Setter Property="Margin" Value="-2.0"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <DataGrid ItemsSource="{Binding Data}" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" />
</Window>

.VB(带有评论的 PITA 使颜色编码正确)

Imports System.Globalization

Class MainWindow
    Public Sub New()
        ' This call is required by the designer. '
        InitializeComponent()

        Dim vm As New VM
        vm.Data = New List(Of MyData) From {
                New MyData() With {.Outcome = "Outcome 1", .Criterion = "1.1", .PercVerified1 = 1, .EF1 = "EP, ECH", .PercVerified3 = 0, .EF3 = "", .PercVerified4 = 0, .EF4 = "EWT"},
                New MyData() With {.Outcome = "", .Criterion = "1.2", .PercVerified1 = 0, .EF1 = "", .PercVerified3 = 1, .EF3 = "O, EP", .PercVerified4 = 0, .EF4 = ""},
                New MyData() With {.Outcome = "", .Criterion = "1.3", .PercVerified1 = 0, .EF1 = "", .PercVerified3 = 0, .EF3 = "O, EP", .PercVerified4 = 4, .EF4 = ""}
            }
        Me.DataContext = vm
    End Sub

    Private Sub DataGrid_AutoGeneratingColumn(sender As Object, e As DataGridAutoGeneratingColumnEventArgs)
        If (e.Column.Header.ToString().Contains("PercVerified") Or e.Column.Header.ToString().Contains("EF")) Then
            e.Column.CellStyle = TryCast(Me.FindResource("PercVerified"), Style)
        End If
    End Sub
End Class

Public Class MyData
    Public Property Outcome As String
    Public Property Criterion As String
    Public Property PercVerified1 As String
    Public Property EF1 As String
    Public Property PercVerified3 As String
    Public Property EF3 As String
    Public Property PercVerified4 As String
    Public Property EF4 As String
End Class

Public Class VM
    Public Property Data As List(Of MyData)
End Class

Public Class PercVerifiedConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
        Convert = Nothing
        Dim cell As DataGridCell
        Dim dc As MyData

        ' Get the DataGridCell passed in '
        cell = TryCast(value, DataGridCell)
        If IsNothing(cell) Then Exit Function

        ' Get the cells DataContext as our data class '
        dc = TryCast(cell.DataContext, MyData)
        If IsNothing(dc) Then Exit Function

        ' Get the cells column - need it for the binding '
        Dim tc As DataGridTextColumn ' Assuming your cells are DataGridTextColumns '
        tc = TryCast(cell.Column, DataGridTextColumn)
        If IsNothing(tc) Then Exit Function

        ' Get the columns binding '
        Dim b As Binding
        b = TryCast(tc.Binding, System.Windows.Data.Binding)
        If IsNothing(b) Then Exit Function

        ' Get the path off the binding '
        Dim path As String
        path = b.Path.Path ' Name of the property this column is bound to - PercVerified1, EF1, etc... '

        ' If one of the "EF" properties, convert path to the appropriate "PercVerified" path '
        If path.Contains("EF") Then
            Dim pvNum = path.Replace("EF", String.Empty) ' EF1 becomes 1 '
            path = "PercVerified" + pvNum ' path is now PercVerified1 '
        End If
        If path.Contains("PercVerified") Then
            Dim pv = dc.GetType().GetProperty(path).GetValue(dc)
            If pv = 0 Then
                Convert = "Red"
            ElseIf pv >= 1 And pv <= 3 Then
                Convert = "Green"
            ElseIf pv >= 4 Then
                Convert = "Purple"
            End If
        End If
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class

还有截图: