DataGrid行突出显示动态对象

时间:2016-05-30 05:43:00

标签: c# wpf data-binding

我正在使用版本2.4.14475.10340中的“Xceed Extended WPF Tookit Plus(Complete)”版本以及生产软件环境中工具包的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”,并在其字典表单中使用它来添加/删除数据条目,然后使用字段名称将其映射到datagrid列。在更改之后,上述样式不再起作用,错误见下文:

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。)颜色松散。它看起来像是一个数据行集容器,然后重新用于新数据。

我的问题:

  1. 为什么它在。)中使用上述样式,但如果我使用b中的自定义转换器则不行。)?使用“[]”sytax?
  2. 访问哪个字段或路径
  3. 有没有办法为动态数据源实现行照明?
  4. /编辑: 源集合看起来像这样。

    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”的datacontext,这在这里当然没什么用处。 我改变了样式(添加了键,修正了路径)

    <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}">
    

    完成。它又有效了!

    非常感谢所有帮助。

    提前感谢, 尼科·艾克尔

2 个答案:

答案 0 :(得分:0)

让我把它分解为三个问题:

  1. 使用“[]”语法访问哪个字段或路径?
  2. “[]”语法用于访问对象的Indexer。无论你在括号之间放置什么,都将被视为索引器的参数。

    1. 为什么它适用于。)中的上述样式,但如果我使用b中的自定义转换器则不行。)
    2. 我不确定该问题的第二部分 - 这需要进一步调查,希望没有必要。至于为什么如果你使用DataTable作为项目来源它会起作用 - 每行显示的数据是DataRow,你的触发器会绑定它的Item[String]索引器。访问该数据的代码隐藏等同于

      var isTrend = dataRow["IsTrend"];
      
      1. 有没有办法为动态数据源实现行照明?
      2. 我假设您在生成数据时在IsTrend上设置ExpadoObject属性:

        dataObject.IsTrend = (...);
        

        如果是这种情况,您可以做的最简单的事情是设置绑定以访问该属性而不是使用索引器:

        <DataTrigger Binding="{Binding Path=IsTrend}" (...) />
        

        之前的绑定不起作用的原因是因为ExpandoObject没有公开Item[String]索引器 - 它确实有一个,但它是一个明确实现的IDictionary<String, Object>.Item[String] ,因此不被视为公开。它可以绑定到XAML中明确实现的接口成员,但它并不那么漂亮,因为特定接口是通用接口,所以特别困难。

答案 1 :(得分:0)

我已使用您的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>

<强> 输出:

Gold

建议:

如果您要从每个绑定中删除.Value,您可以overrideToString()方法中CellContent方法,如下所示:

 public override string ToString()
    {
        return Value.ToString();
    }

和Binding会改变如下:

<DataGridTextColumn Header="Name" Binding="{Binding Path=Name,Mode=TwoWay}" />

PS: 虽然它有问题,但在删除.Value DataTrigger后无效。我正在努力,如果我找到任何解决方案,我会在这里更新。

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