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