动态对象的 DataGrid 行突出显示
Posted
技术标签:
【中文标题】动态对象的 DataGrid 行突出显示【英文标题】:DataGrid row highlighting for dynamic objects 【发布时间】:2016-05-30 05:43:00 【问题描述】:我正在使用版本 2.4.14475.10340 中的“Xceed Extended WPF Tookit Plus (Complete”) Edition 和生产软件环境中工具包的 DataGrid。
目前我正在努力让突出显示的数据行再次工作。
a.) 网格的数据源是一个标准的 DataTable,所以像下面这样的自定义 DataRow 样式确实对我有用:
<Style TargetType="x:Type xcdg:DataRow">
<Style.Triggers>
<DataTrigger Binding="Binding Path=[IsTrend]" Value="True">
<Setter Property="Background" Value="Gold"/>
</DataTrigger>
</Style.Triggers>
</Style>
由于基础数据的动态特性,我们将源类型更改为“ExpandoObject”,并在其字典形式中使用它来添加/删除数据条目,然后使用字段名称将其映射到数据网格列。更改后上述样式不再起作用,错误见下文:
Cannot get 'Item[]' value (type 'Object') from '' (type 'ExpandoObject'). BindingExpression:Path=[IsTrend]; DataItem='ExpandoObject' (HashCode=34521593); target element is 'DataRow' (Name=''); target property is 'NoTarget' (type 'Object') TargetInvocationException:'System.Reflection.TargetInvocationException
b.) 作为一种解决方法,我们将样式更改为自定义转换器:
<Style TargetType="x:Type xcdg:DataRow">
<Setter Property="Background" Value="Binding RelativeSource=RelativeSource Self, Converter=StaticResource HighlightingConverter"/>
</Style>,
它返回一个 Brush,取决于 DataRow 项 (DataGrid.GetItemFromContainer(row)) 的字段值,因此是 ExpandoObject。
现在数据网格中的行有时会着色,但 a.) 不正确(错误的行)或 b.) 刷新数据后颜色变松。看起来数据行容器已着色,然后重新用于新数据。
我的问题:
-
为什么它适用于 a.) 中的上述样式,但如果我使用像 b.) 中的自定义转换器则不能?使用“[]”语法访问哪个字段或路径?
有没有办法为动态数据源实现行照明?
/编辑: 源集合如下所示。
private ObservableCollection<dynamic> GetDynamicOrders2()
var retVal = new ObservableCollection<dynamic>();
for (int i = 0; i < 50; i++)
dynamic eo = new ExpandoObject();
eo.Name = new CellContent("Order" + i);
eo.IsTrend = new CellContent(i % 2 == 0);
var converted = (IDictionary<string, object>)eo;
converted["Number"] = new CellContent(i % 4);
converted["NumberDouble"] = new CellContent((double)i);
converted["properties_name_first"] = new CellContent("Name " + i);
retVal.Add(eo);
return retVal;
一个单元格对象定义为:
public sealed class CellContent : INotifyPropertyChanged
private object _value;
public object Value
get return _value;
set
if (Equals(value, _value)) return;
_value = value;
OnPropertyChanged();
OnPropertyChanged("DisplayValue");
OnPropertyChanged("BackgroundColor");
OnPropertyChanged("ForegroundColor");
public bool Meta get; set;
public string Format get; set;
public double Threshold get; set;
public string DisplayValue
get
return string.Format(string.Format("0:0", Format), Value);
public SolidColorBrush BackgroundColor
get
var defaultColor = new SolidColorBrush(Colors.Transparent);
var valueType = Value.GetType();
if (valueType != typeof (double)) return defaultColor;
if (double.IsNaN(Threshold)) return defaultColor;
return (double)Value >= Threshold ? new SolidColorBrush(Colors.Red) : defaultColor;
public SolidColorBrush ForegroundColor
get
var defaultColor = new SolidColorBrush(Colors.Black);
var valueType = Value.GetType();
if (valueType != typeof(double)) return defaultColor;
// inactive
if (double.IsNaN(Threshold)) return defaultColor;
return (double)Value >= Threshold ? new SolidColorBrush(Colors.Blue) : defaultColor;
public CellContent(object value, bool meta = false, string format = "", double threshold = double.NaN)
Value = value;
Meta = meta;
Format = format;
Threshold = threshold;
private bool Equals(CellContent other)
return Equals(Value, other.Value);
public override bool Equals(object obj)
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CellContent)obj);
public override int GetHashCode()
return (Value != null ? Value.GetHashCode() : 0);
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
/Edit2: 如果我使用显式路径,绑定错误:
System.Windows.Data Error: 40 : BindingExpression path error: 'IsTrend' property not found on 'object' ''MainWindow' (Name='')'. BindingExpression:Path=IsTrend; DataItem='MainWindow' (Name=''); target element is 'DataRow' (Name=''); target property is 'NoTarget' (type 'Object')
/Edit3:解决方案。 整个问题在于我添加样式的方式。与上面的问题一样,我全局定义了样式,因此它应用于此用户控件/窗口中的每个单个 DataRow 对象。这导致了“MainWindow”的数据上下文,这当然在这里没有更大的用处。 我将样式更改为(已添加键,已更正路径)
<Style TargetType="x:Type xcdg:DataRow" x:Key="RowStyle">
<Style.Triggers>
<DataTrigger Binding="Binding Path=IsTrend.Value" Value="True">
<Setter Property="Background" Value="Gold"/>
</DataTrigger>
</Style.Triggers>
</Style>
并像这样从数据网格中添加了一个引用
<xcdg:DataGridControl x:Name="DataGrid" ItemContainerStyle="StaticResource RowStyle">
完成。它再次起作用了!
非常感谢所有帮助。
提前致谢, 尼科·埃克
【问题讨论】:
您已将背景绑定到 Self 含义 DataRow。由于滚动时DataRow没有刷新,所以刷新(基本上是虚拟化的行容器原因)。如果您能够提供源代码集合以及上述代码,那么提供解决方案将很容易。或者我必须随意制作一个。您的简单解决方案目标应该是绑定源集合类型的属性,这样行颜色就可以完美地工作。 我不确定我是否理解您的评论,但我会将有关源集合的信息添加到原始问题中。 检查答案...please.let me know if you face some questions 我再次更新了这个问题,提供了一个解决方案,该解决方案部分由你的工作组成,谢谢你,以及我自己的见解。 我有点困惑。你还有问题吗?它是什么? 【参考方案1】:让我把它分解成三个问题:
-
使用“[]”语法访问哪个字段或路径?
“[]”语法用于访问对象的Indexer。无论您放在括号内的任何内容都将被视为索引器的参数。
-
为什么它适用于 a.) 中的上述样式,但如果我使用 b.) 中的自定义转换器则不能?
我不确定该问题的第二部分 - 这需要额外调查,但希望没有必要。至于为什么使用DataTable
作为项目源它会起作用- 每行呈现的数据是DataRow
,并且您的触发器绑定到它的Item[String]
索引器。访问该数据的代码隐藏相当于
var isTrend = dataRow["IsTrend"];
-
有没有办法为动态数据源实现行照明?
我假设当您生成数据时,您在 ExpadoObject
上设置了 IsTrend
属性:
dataObject.IsTrend = (...);
如果是这种情况,您可以做的最简单的事情是设置绑定以访问该属性,而不是使用索引器:
<DataTrigger Binding="Binding Path=IsTrend" (...) />
之前的绑定不起作用的原因是ExpandoObject
没有公共的Item[String]
索引器——它确实有一个,但它是一个显式实现的IDictionary<String, Object>.Item[String]
,因此不被视为公共的。可以绑定到 XAML 中显式实现的接口成员,但它不是那么漂亮,而且会特别困难,因为该特定接口是通用接口。
【讨论】:
感谢您的回复,现在我清楚地了解 DataTable 和动态对象案例中发生了什么。不幸的是,您的解决方案也无济于事(我已经尝试过显式路径),因为我收到 BindingExpression 路径错误。我已将数据对象(请注意每个单元格本身就是一个复杂对象)和错误添加到原始问题中。【参考方案2】:我已使用您的 source
并使其与 DataGrid
一起使用。见以下代码:
XAML:
<Window x:Class="TabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControl"
Title="MainWindow" Height="300" Width="300"
xmlns:Interact="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
DataContext="Binding RelativeSource=RelativeSource Mode=Self"
>
<Window.Resources>
<Style TargetType="x:Type DataGridRow" x:Key="myStyle">
<Style.Triggers>
<DataTrigger Binding="Binding IsTrend.Value" Value="True">
<Setter Property="Background" Value="Gold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<ScrollViewer>
<DataGrid ItemsSource="Binding list" x:Name="myGrid" RowStyle="StaticResource myStyle" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="Binding Path=Name.Value,Mode=TwoWay" />
<DataGridTextColumn Header="IsTrend" Binding="Binding Path=IsTrend.Value,Mode=TwoWay" />
<DataGridTextColumn Header="Number" Binding="Binding Path=Number.Value,Mode=TwoWay" />
<DataGridTextColumn Header="properties_name_first" Binding="Binding Path=properties_name_first.Value,Mode=TwoWay" />
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
输出:
建议:
如果您想从每个绑定中删除 .Value
,您可以在 CellContent
类中使用 override
ToString()
方法,如下所示:
public override string ToString()
return Value.ToString();
并且绑定会改变如下:
<DataGridTextColumn Header="Name" Binding="Binding Path=Name,Mode=TwoWay" />
PS:虽然有问题,但是删除.Value
后DataTrigger
还是不行。如果我找到任何解决方案,我正在努力解决,我会在这里更新。
Here is 上述问题的解决方案。
【讨论】:
以上是关于动态对象的 DataGrid 行突出显示的主要内容,如果未能解决你的问题,请参考以下文章
使用 jQuery 突出显示选定的 ASP.NET DataGrid 行