动态对象的 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&lt;String, Object&gt;.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:虽然有问题,但是删除.ValueDataTrigger还是不行。如果我找到任何解决方案,我正在努力解决,我会在这里更新。

Here is 上述问题的解决方案。

【讨论】:

以上是关于动态对象的 DataGrid 行突出显示的主要内容,如果未能解决你的问题,请参考以下文章

使用 jQuery 突出显示选定的 ASP.NET DataGrid 行

突出显示 WPF DataGrid 的已编辑单元格

当 SelectionMode 设置为 Cell 时,如何突出显示 WPF DataGrid 中的一行

WPF Datagrid:加载时,选择当前项目(突出显示)

如何使选定的行在未聚焦的数据网格中突出显示?

WPF DataGrid 动态样式