首页 > 解决方案 > WPF。为 DataContext 属性赋值不会改变绑定属性的值

问题描述

为 DataContext 属性分配值不会更改绑定属性的值。

我需要为表中的每条记录多次获取绑定属性的值,以计算 DataGrid 中列的总和值。

为此,我使用了一个特殊的虚拟 FrameworkElement 对象。

目标代码如下所示:

  public class BindValueReader : FrameworkElement
  {
    public static readonly DependencyProperty ValueProperty =
      DependencyProperty.Register("Value",
                                  typeof(object),
                                  typeof(BindValueReader),
                                  new PropertyMetadata(null));

    public object Value
    {
      get { return GetValue(ValueProperty); }
      set { SetValue(ValueProperty, value); }
    }
  }

它仅用于从绑定中获取值。

我使用以下代码来获取每条记录的绑定属性的值。

  Decimal sumVal = 0;
  foreach (DataRowView rowView in view)
  {
    BindValueReader valueReader = new BindValueReader();
    valueReader.DataContext = rowView;
    valueReader.SetBinding(BindValueReader.ValueProperty, bdn);
    if (valueReader.Value != null)
      sumVal += (Decimal)valueReader.Value;
  }

它工作正常,但速度相当慢。

当我尝试像这样优化代码时

  Binding bdn = new Binding("Income");

  BindValueReader valueReader = new BindValueReader();
  valueReader.SetBinding(BindValueReader.ValueProperty, bdn);

  DataView view = dataTable.DefaultView;
  Decimal sumVal = 0;

  foreach (DataRowView rowView in view)
  {
    valueReader.DataContext = rowView;
    if (valueReader.Value != null)
      sumVal += (Decimal)valueReader.Value;
  }

它停止工作。

我不知道为什么。

这是完整的程序代码:

XAML:

<Window x:Class="EhLibTestApp.TestWindow1"
        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:EhLibTestApp"
        mc:Ignorable="d"
        Title="TestWindow1" Height="450" Width="800">
    <Grid>
    <StackPanel HorizontalAlignment="Stretch" Height="63" VerticalAlignment="Top" Orientation="Horizontal" >
      <Button Content="Button" Width="87" Margin="10" Click="Button_Click"/>
    </StackPanel>

  </Grid>
</Window>

CS:

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Data;
using System.Diagnostics;

namespace EhLibTestApp
{
  /// <summary>
  /// Interaction logic for TestWindow1.xaml
  /// </summary>
  public partial class TestWindow1 : Window
  {
    public TestWindow1()
    {
      InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      DataTable dataTable = new DataTable();
      dataTable.Columns.Add("EmployeeID", typeof(Int32));
      dataTable.Columns.Add("LastName", typeof(String));
      dataTable.Columns.Add("Income", typeof(System.Decimal));

      dataTable.Rows.Add(1, "Davolio", 100);
      dataTable.Rows.Add(2, "Fuller", 100);
      dataTable.Rows.Add(3, "Leverling", 100);
      dataTable.Rows.Add(4, "Peacock", DBNull.Value);

      Debug.WriteLine("");
      Debug.WriteLine("Test1");
      Test1(dataTable);

      Debug.WriteLine("");
      Debug.WriteLine("Test2");
      Test2(dataTable);
    }

    private void Test1(DataTable dataTable)
    {
      Binding bdn = new Binding("Income");
      DataView view = dataTable.DefaultView;

      int ticks = Environment.TickCount;
      Decimal sumVal = 0;
      foreach (DataRowView rowView in view)
      {
        BindValueReader valueReader = new BindValueReader();
        valueReader.DataContext = rowView;
        valueReader.SetBinding(BindValueReader.ValueProperty, bdn);
        if (valueReader.Value != null)
          sumVal += (Decimal)valueReader.Value;
      }

      Debug.WriteLine("Sum of Income = " + sumVal.ToString());
      Debug.WriteLine("Ticks = " + (Environment.TickCount - ticks).ToString());
    }

    private void Test2(DataTable dataTable)
    {
      Binding bdn = new Binding("Income");

      BindValueReader valueReader = new BindValueReader();
      valueReader.SetBinding(BindValueReader.ValueProperty, bdn);

      DataView view = dataTable.DefaultView;
      Decimal sumVal = 0;
      int ticks = Environment.TickCount;

      foreach (DataRowView rowView in view)
      {
        valueReader.DataContext = rowView;
        if (valueReader.Value != null)
          sumVal += (Decimal)valueReader.Value;
      }

      Debug.WriteLine("Sum of Income = " + sumVal.ToString());
      Debug.WriteLine("Ticks = " + (Environment.TickCount - ticks).ToString());
    }
  }

  public class BindValueReader : FrameworkElement
  {
    public static readonly DependencyProperty ValueProperty =
      DependencyProperty.Register("Value",
                                  typeof(object),
                                  typeof(BindValueReader),
                                  new PropertyMetadata(null));

    public object Value
    {
      get { return GetValue(ValueProperty); }
      set { SetValue(ValueProperty, value); }
    }
  }

}

调试输出:

Test1
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Income; DataItem='DataRowView' (HashCode=22689808); target element is 'BindValueReader' (Name=''); target property is 'Value' (type 'Object')
Sum of Income = 300
Ticks = 16

Test2
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Income; DataItem=null; target element is 'BindValueReader' (Name=''); target property is 'Value' (type 'Object')
Sum of Income = 0
Ticks = 0
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Income; DataItem='DataRowView' (HashCode=22689808); target element is 'BindValueReader' (Name=''); target property is 'Value' (type 'Object')

标签: c#wpfbindingdatacontext

解决方案


您正在尝试对收入属性求和。

您可以通过使用您获得的数据行视图中的列而不是使用绑定来做到这一点。

foreach (DataRowView rowView in view)
{
   var val = rowView["Income"];
   if (val != null)
   {
      sumVal += (Decimal)val;
   }
}

不过,这不是使用 WPF 的好方法,您应该研究 MVVM,定义一个实现 inotifypropertychanged 的​​类作为行视图模型。将 itemssource 绑定到 this 的 observablecollection。然后使用绑定集合和命名属性。


推荐阅读