【发布时间】:2018-07-05 16:10:02
【问题描述】:
这里有几个“WPF 数据绑定未更新”的问题,我已经通读了所有这些问题,包括排名第一的问题: WPF DataBinding not updating?
但是,在我的情况下,我正在交换整个对象,更糟糕的是,当我尝试创建我正在做的事情的可重现版本时,它在那个版本中工作:
<Window x:Class="WpfApp2.MainWindow"
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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Label Content="Contact Address" />
<TextBox x:Name="txtContactAddress" Text="{Binding Path=Contact.Address, Mode=OneWay}" />
<Label Content="Organization Address" />
<TextBox x:Name="txtOrgAddress" Text="{Binding Path=Organization.Address, Mode=OneWay}" />
<Button Content="Next Contact" Click="Button_Click" />
</StackPanel>
</Window>
代码隐藏:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public RecordWrapper CurrentRecord;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
CurrentRecord = new RecordWrapper(new ContactData());
this.DataContext = CurrentRecord;
}
}
public class RecordWrapper : INotifyPropertyChanged
{
private ContactData _Contact;
public ContactData Contact
{
get { return _Contact; }
set
{
_Contact = value;
OnPropertyChanged("Contact");
Organization = _Contact.Organization;
}
}
private OrganizationData _Organization;
public OrganizationData Organization
{
get { return _Organization; }
set
{
_Organization = value;
OnPropertyChanged("Organization");
}
}
public RecordWrapper(ContactData contactData)
{
Contact = contactData;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
}
public class ContactData
{
public string Address { get; set; }
public OrganizationData Organization { get; set; }
public ContactData()
{
Address = new Random().Next(1000, 9999) + " Contact Street";
Organization = new OrganizationData();
}
}
public class OrganizationData
{
public string Address { get; set; }
public OrganizationData()
{
Address = new Random().Next(1000, 9999) + " Org Street";
}
}
}
所以这个特定示例按预期工作 - 当我单击“下一个联系人”按钮时,它会生成一个包含新 ContactData 和新 OrganizationData 对象的新 RecordWrapper 对象,然后将新对象分配给 DataContext 然后我看到文本框正确更新。
由于我要替换 DataContext 中的整个对象,我什至可以放弃属性通知并将 RecordWrapper 减少到:
public class RecordWrapper
{
public ContactData Contact { get; set; }
public OrganizationData Organization { get; set; }
public RecordWrapper(ContactData contactData)
{
Contact = contactData;
Organization = Contact.Organization;
}
}
...它仍然很好用。
然而,我的真实代码反映了第一个示例。
在实际代码中,似乎用新对象更新 this.DataContext 是行不通的。什么都没有更新,但也没有错误。在 XAML 中设置路径也不起作用。事实上,我可以让它工作的唯一方法是使用代码隐藏将绑定直接设置到数据对象:
BindingOperations.SetBinding(txtContactAddress, TextBox.TextProperty, new Binding() { Source = this.RecordWrapper, Path = new PropertyPath("Contact.Address"), Mode = BindingMode.OneWay });
当我这样做时,我可以看到它正在工作,但如果我每次都必须在每个字段上运行代码以更新绑定,那显然错过了绑定点。
我很困惑,因为除了具有更多字段、不同字段名称以及与数据或绑定无关的差异之外,实际代码与示例几乎相同。当我点击一个按钮时,这是确切的代码:
this.Data = new DataBindings(CurrentContact.FetchFake());
this.DataContext = this.Data;
FetchFake() 方法生成一个带有一些假数据的新 CurrentContact 对象,并且我已经验证,在第一行运行后, this.Data.CurrentContact.Login 被填充并且是 CurrentContact 对象的只读属性(不是字段)。
...其中一个 XAML 文本框如下所示:
<TextBox x:Name="txtAccountNumber" Grid.Column="1" Grid.Row="1" Width="Auto" Text="{Binding Path=CurrentContact.Login, Mode=OneWay}"/>
结果为空白。我也尝试过改变顺序(例如,首先将 DataContext 设置为 this.Data,然后设置 this.Data.CurrentContact 属性并让属性更改通知启动,但仍然没有运气。但如果我运行:
BindingOperations.SetBinding(tabAccount.txtAccountNumber, TextBox.TextProperty, new Binding() { Source = this.Data, Path = new PropertyPath("CurrentContact.Login"), Mode = BindingMode.OneWay });
...然后我会看到值显示出来。
基于这些症状,以及我的第一个示例有效但实际代码无效的事实,任何人都可以想到我应该在哪里寻找一些罪魁祸首吗?
【问题讨论】:
-
执行真实代码时,调试输出是否有绑定错误?
-
不,没有。
-
也许您的 Contact Data 和 OrganizationData 类也应该实现 INotifyPropertyChanged 接口?
-
@Jackdaw - 他们没有在初始示例中实现该接口。
-
这是糟糕的(或者不是非常 MVVM)的设计。您应该有一个视图模型,该视图模型在 xaml 或窗口构造函数(甚至 DI 它)中设置为上下文,然后让该视图模型更新一些 observablecollection 属性..比如说使用按钮上的绑定命令而不是按钮单击名为 CurrentContact事件。一旦窗口加载后强制一个新的数据上下文引用是非常低效的,因为所有的绑定都需要在后台重新创建,而不是仅仅通过正常的绑定操作来更新。这就是为什么它在没有适当绑定通知的情况下“工作”的原因
标签: c# wpf data-binding