c# - 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')
解决方案
您正在尝试对收入属性求和。
您可以通过使用您获得的数据行视图中的列而不是使用绑定来做到这一点。
foreach (DataRowView rowView in view)
{
var val = rowView["Income"];
if (val != null)
{
sumVal += (Decimal)val;
}
}
不过,这不是使用 WPF 的好方法,您应该研究 MVVM,定义一个实现 inotifypropertychanged 的类作为行视图模型。将 itemssource 绑定到 this 的 observablecollection。然后使用绑定集合和命名属性。
推荐阅读
- python - 循环列以验证条件并在需要时使用 pandas 更改单元格值
- html - 为什么 Bootstrap Collapse 不会为每次迭代显示不同的项目?
- sql - 带条件的循环标识符
- laravel - Laravel 个人资料照片仅有时显示
- css - 带有 css `hyphens` 的自动换行无法换行子字符串
- xamarin - Visual Studio 2019 Xamarin Dotfuscator CE 配置文件创建问题
- r - R - 删除所有不符合特定条件的行
- javascript - 如何在单击带有 Node.js 和 HTML 的按钮时发送带有特定 URL 的 POST 请求?
- azure - Azure 管道在使用模板时展开每个参数
- unit-testing - 创建 JWT 以测试受 Auth0 保护的 API