WPF DataGrid中加载行事件时交替行颜色行颜色加倍

WPF DataGrid中加载行事件时交替行颜色行颜色加倍,wpf,.net-core,datagrid,Wpf,.net Core,Datagrid,我通过LoadingRow事件以编程方式交替行颜色。 这是因为我需要在某些行上指定特定的颜色,即标记为删除的行和具有修改数据的行 这很好,直到我在DataGrid中向上滚动,我得到了一个非常奇怪的交互,它将行颜色增加了两倍或三倍 向下滚动时显示正确 我尝试过使用AlternationIndex,将AlternationCount设置为2,并使用bool在两者之间切换,这两种方法都会导致完全相同的问题 如果我没有在LoadingRow事件中设置此选项,并使用DataGrid Alternatin

我通过LoadingRow事件以编程方式交替行颜色。 这是因为我需要在某些行上指定特定的颜色,即标记为删除的行和具有修改数据的行

这很好,直到我在DataGrid中向上滚动,我得到了一个非常奇怪的交互,它将行颜色增加了两倍或三倍

向下滚动时显示正确

我尝试过使用AlternationIndex,将AlternationCount设置为2,并使用bool在两者之间切换,这两种方法都会导致完全相同的问题

如果我没有在LoadingRow事件中设置此选项,并使用DataGrid AlternatingRowBackground,则当我在表格中滚动时,行颜色会渗入其他行

private void dataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
        {
            // Get the DataRow corresponding to the DataGridRow that is loading.
            var item = e.Row.Item as ExpandoObject;
            if (loadedTable.ToDelete.Contains(item))
            {
                e.Row.Background = new SolidColorBrush(Colors.OrangeRed);
                return;
            }
            else if (loadedTable.Modified.Contains(loadedTable.Rows.IndexOf(item)))
            {
                e.Row.Background = new SolidColorBrush(Colors.LightYellow);
                return;
            }
            else if (e.Row.AlternationIndex == 0)
            {
                e.Row.Background = new SolidColorBrush(Colors.WhiteSmoke);
            }
            else if (e.Row.AlternationIndex == 1)
            {
                e.Row.Background = new SolidColorBrush(Colors.LightGray);
            }
        }

您遇到的问题是因为DataGrid重用DataGridRow对象(可以尝试启用RowVirtualization=“False”)

您要做的是使用样式根据DataGridRow的数据/项设置其背景

下面是一个测试应用程序,它可以完成您想要做的事情

XAML


代码

使用系统;
使用System.Collections.Generic;
利用制度全球化;
使用System.Windows;
使用System.Windows.Data;
命名空间WpfApp9
{
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
}
公共类虚拟机
{
公共列表项{get;set;}
公共列表ToDelete{get;set;}
公共虚拟机()
{
项目=新列表();
ToDelete=新列表();
对于(int i=0;i<1000;i++)
{
var eo=新系统.Dynamic.ExpandoObject();
var d=作为索引的eo;
d[“Col1”]=$“字符串{i}”;
d[“Col2”]=i;
项目。添加(eo);
//将一些项目添加到ToDelete列表
如果(i%10==0)
{
添加(eo);
}
}
}
}
公共类IsDeletedConverter:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
如果(values.Length!=2)
抛出新ArgumentException($“IsDeletedConverter需要2个值,但得到了{values.Length}个值!”,nameof(values));
如果(值[0]为System.Dynamic.ExpandooObject eo&&values[1]为VM)
{
if(vm.ToDelete.Contains(eo))
返回true;
}
返回false;
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
}

可以为数据网格添加xaml吗?你在做虚拟化吗?除了执行代码隐藏之外,您还考虑过使用datagrid行样式吗?我通常使用它来改变背景颜色,然后在其中使用datatriggers来改变背景颜色。它将尊重您的原始交替或默认背景色,并且仅在触发数据触发器时更改。在datatrigger的绑定中,您可以使用转换器来执行复杂的逻辑。@JMIII现在添加了XML。我想你和J.H.建议的是走的路线,谢谢。哈哈,我想发布一个带有datatriggers的样式示例,但你抢先了我一步+1到目前为止最好的方法。@jmii-LOL。我在提交之前就看到了你的评论。还有,为了纪念原来的bg/alt bg——我试过了,但没有成功。。。我确信这是因为我没有在样式中设置它们,而是在DataGrid上设置它们(这样,您就不能在样式中重写)。@JH当您必须在相同的样式中进行设置时,您是正确的,该样式在触发器本身之后保存数据触发器,并且只有一个标准setter属性作为背景。我不知道这是为什么,但它是有效的。我在我的应用程序中使用了自定义nlog目标,以便在需要调试时写入wpf窗口,并根据日志记录级别突出显示不同的行。谢谢,看起来这是一种方法。虽然这解决了这里的主要问题,它仍然存在一个问题,即在代码中设置背景的行在滚动RowVirtualization表时继续保持该颜色,而不会更改回其交替颜色。在进行更改时,是否有方法基于这些触发器刷新行样式?
<DataGrid CanUserAddRows="False" GridLinesVisibility="All" VerticalGridLinesBrush="Gray" HorizontalGridLinesBrush="Gray"
                                 FontSize="15" FrozenColumnCount ="1" x:Name="xmlData" EnableRowVirtualization="True" AlternationCount="1"
                                 AlternatingRowBackground="LightGray" Background="WhiteSmoke"
                                 Grid.Column="1" Margin="0,-31,5,10" AutoGenerateColumns="False" Grid.Row="2" SelectionUnit="Cell" 
                                 PreviewKeyDown="DataGridKeyDown_Event" IsReadOnly="True" CanUserDeleteRows="True"
                                 LoadingRow="dataGrid_LoadingRow"/>
<Window x:Class="WpfApp9.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:WpfApp9"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
    <local:VM />
</Window.DataContext>
<Grid>
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}" AlternationCount="2">
        <DataGrid.Resources>
            <!-- Converter used to convert the DataRow's Item and the VM.ToDelete list to bool (true = it is deleted) -->
            <local:IsDeletedConverter x:Key="IsDeletedConverter" />
        </DataGrid.Resources>
        <DataGrid.RowStyle>
            <Style TargetType="DataGridRow">
                <Style.Triggers>
                    <!-- Setup the background color for normal rows using AlternationIndex -->
                    <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                        <Setter Property="Background" Value="WhiteSmoke" />
                    </Trigger>
                    <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                        <Setter Property="Background" Value="LightGray" />
                    </Trigger>
                    <!-- Override the above background colors if it is in the deleted list - NOTE: these styles are processed in order, make sure this is after the above triggers -->
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <MultiBinding Converter="{StaticResource IsDeletedConverter}">
                                <!-- This is the DataContext of the DataGridRow - the item (ExpandoObject) we will check for in the deleted list -->
                                <Binding />
                                <!-- Need the deleted list, which is in VM -->
                                <Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="DataContext" />
                            </MultiBinding>
                        </DataTrigger.Binding>
                        <DataTrigger.Setters>
                            <Setter Property="Background" Value="OrangeRed" />
                        </DataTrigger.Setters>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.RowStyle>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Col1" Binding="{Binding Col1}" />
            <DataGridTextColumn Header="Col2" Binding="{Binding Col2}" />
        </DataGrid.Columns>
    </DataGrid>
</Grid>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace WpfApp9
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class VM
    {
        public List<System.Dynamic.ExpandoObject> Items { get; set; }
        public List<System.Dynamic.ExpandoObject> ToDelete { get; set; }

        public VM()
        {
            Items = new List<System.Dynamic.ExpandoObject>();
            ToDelete = new List<System.Dynamic.ExpandoObject>();

            for (int i = 0; i < 1000; i++)
            {
                var eo = new System.Dynamic.ExpandoObject();
                var d = eo as IDictionary<string, object>;
                d["Col1"] = $"String {i}";
                d["Col2"] = i;
                Items.Add(eo);

                // Add some items to ToDelete list
                if (i % 10 == 0)
                {
                    ToDelete.Add(eo);
                }
            }
        }
    }

    public class IsDeletedConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length != 2)
                throw new ArgumentException($"IsDeletedConverter is expecting 2 values but got {values.Length} values!", nameof(values));

            if (values[0] is System.Dynamic.ExpandoObject eo && values[1] is VM vm)
            {
                if (vm.ToDelete.Contains(eo))
                    return true;
            }
            return false;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}