gpt4 book ai didi

wpf - 连接 CollectionChanged 和 PropertyChanged (或 : Why do some WPF Bindings not refresh? )

转载 作者:行者123 更新时间:2023-12-02 08:56:34 25 4
gpt4 key购买 nike

WPF DataBindings 曾经让我很开心。我刚才偶然发现的一件事是,在某些时候它们只是没有按预期刷新。请看一下以下(相当简单)代码:

<Window x:Class="CVFix.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0"
ItemsSource="{Binding Path=Persons}"
SelectedItem="{Binding Path=SelectedPerson}"
x:Name="lbPersons"></ListBox>
<TextBox Grid.Row="1" Grid.Column="0" Text="{Binding Path=SelectedPerson.Name, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>

XAML 背后的代码:

using System.Windows;
namespace CVFix
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ViewModel Model { get; set; }

public MainWindow()
{
InitializeComponent();
this.Model = new ViewModel();
this.DataContext = this.Model;
}
}
}

最后,这是 ViewModel 类:

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace CVFix
{
public class ViewModel : INotifyPropertyChanged
{
private PersonViewModel selectedPerson;

public PersonViewModel SelectedPerson
{
get { return this.selectedPerson; }
set
{
this.selectedPerson = value;

if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("SelectedPerson"));
}
}

public ObservableCollection<PersonViewModel> Persons { get; set; }

public ViewModel()
{
this.Persons = new ObservableCollection<PersonViewModel>();
this.Persons.Add(new PersonViewModel() { Name = "Adam" });
this.Persons.Add(new PersonViewModel() { Name = "Bobby" });
this.Persons.Add(new PersonViewModel() { Name = "Charles" });
}

public event PropertyChangedEventHandler PropertyChanged;
}
}

public class PersonViewModel : INotifyPropertyChanged
{
private string name;

public string Name
{
get { return this.name; }
set
{
this.name = value;
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}

public override string ToString()
{
return this.Name;
}

public event PropertyChangedEventHandler PropertyChanged;
}

我想要发生的事情:当我从列表框中选择一个条目并在文本框中修改其名称时,列表会更新以显示新值。

发生了什么:什么都没有。如果我有任何判断的话,这就是正确的行为。我确保 SelectedItem 的 PropertyChanged 被触发,但这(当然)不会导致 CollectionChanged 被触发。

为了解决这个问题,我创建了一个 ObservableCollection 派生类,它具有公共(public) OnCollectionChanged 方法,请参见此处:

public class PersonList : ObservableCollection<PersonViewModel>
{
public void OnCollectionChanged()
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ));
}
}

我从 ViewModel 的构造函数访问它,如下所述:

    public ViewModel()
{
PersonViewModel vm1 = new PersonViewModel()
{
Name = "Adam"
};
PersonViewModel vm2 = new PersonViewModel()
{
Name = "Bobby"
};
PersonViewModel vm3 = new PersonViewModel()
{
Name = "Charles"
};
vm1.PropertyChanged += this.PersonChanged;

this.Persons = new PersonList();


this.Persons.Add(vm1);
this.Persons.Add(vm2);
this.Persons.Add(vm3);
}

void PersonChanged(object sender, PropertyChangedEventArgs e)
{
this.Persons.OnCollectionChanged();
}

它有效,但不是一个干净的解决方案。我的下一个想法是创建 ObservableCollection 的派生类,它在 CollectionChanged 处理程序中自动进行连接。

public class SynchronizedObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
foreach (INotifyPropertyChanged item in e.NewItems)
{
item.PropertyChanged += this.ItemChanged;
}
break;
}

case NotifyCollectionChangedAction.Remove:
{
foreach (INotifyPropertyChanged item in e.OldItems)
{
item.PropertyChanged -= this.ItemChanged;
}
break;
}
}
base.OnCollectionChanged(e);
}

void ItemChanged(object sender, PropertyChangedEventArgs e)
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}

问题是:有更好的方法吗?这真的有必要吗?

提前非常感谢您的任何意见!

最佳答案

不,根本没有必要。您的示例失败的原因很微妙,但非常简单。

如果您没有为 WPF 提供数据项模板(例如列表中的 Person 对象),它将默认使用 ToString() 方法来显示。这是一个成员,而不是一个属性,因此当值更改时您不会收到任何事件通知。

如果您将 DisplayMemberPath="Name" 添加到列表框,它将生成一个正确绑定(bind)到您的Name 的模板 - 然后该模板将自动更新正如您所期望的。

关于wpf - 连接 CollectionChanged 和 PropertyChanged (或 : Why do some WPF Bindings not refresh? ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4385485/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com