如何解决使用 INotifyPropertyChanged 而不是 ObservableCollection
我有一个 ListView
绑定到一个集合,我希望 ListView
在一个项目添加到集合中时自动更新。我设法使用 ObservableCollection
让它工作,但我更愿意使用 INotifyPropertyChanged
。也许你可以给我一个提示我做错了什么?
首先,这是 XAML 的(相关部分):
<StackPanel DataContext="{Binding Family}"> <!-- DataContext is of type Family -->
<TextBlock Text="{Binding LastName}"/>
<ListView ItemsSource="{Binding Members}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FirstName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
以下是相关类:
public class Family : INotifyPropertyChanged
{
public string LastName { get; private set; }
private readonly IList<Member> _members;
public IEnumerable<Member> Members { get => _members; }
public Family(string lastName,IEnumerable<Member> members)
{
LastName = lastName;
_members = members.ToList();
}
public void AddMember(string name)
{
var member = new Member { FirstName = name };
_members.Add(member);
OnPropertyChanged(nameof(Members));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(name));
}
}
public class Member
{
public string FirstName { get; set; }
}
如果我使用此代码并在某处调用 AddMember
,它不会更新 ListView GUI。我不明白为什么不,因为 AddMember
调用 OnPropertyChanged(nameof(Members))
,而 Members
是 ListView
所绑定的。因此,它应该收到有关更改的通知。
那我做错了什么?
如果我将 IList<Member> _members
相应地更改为 ObservableCollection<Member> _members
并将 _members = members.ToList()
相应地更改为 _members = new ObservableCollection<Member>(members)
,它会按预期工作。
解决方法
将项目添加到 _members
集合后,Members
返回的引用仍然相同。集合的 Equals
方法通常会比较引用,而不是项目。因此,绑定不会检测到更改并且不会重新评估属性。
如果您想让它工作,您可以执行以下操作之一:
-
暂时分配
null
,提高属性改变,重新分配集合并提高属性再次改变,因此绑定检测到更改的引用(感谢@Ash)。public void AddMember(string name) { var member = new Member { FirstName = name }; _members.Add(member); var members = _members; _members = null; OnPropertyChanged(nameof(Members)); _members = members; OnPropertyChanged(nameof(Members)); }
-
幼稚的方法,在添加成员时重新创建集合,例如:
public void AddMember(string name) { var member = new Member { FirstName = name }; _members = _members.ToList(); _members.Add(member); OnPropertyChanged(nameof(Members)); }
由于大量不必要的分配,这样做代价高昂,请不要这样做。
如您所见,这两种方法都有其缺点,要么触发额外的属性更改通知,要么进行不必要的分配,这会额外导致 ListView
每次删除并重新创建其所有项目 .这就是为什么有一个实现 INotifyCollectionChanged
的 ObservableCollection<T>
类型,它允许专门通知添加和删除的项目,以及其他操作。
首先,您认为 Family 不是 ObservableCollection 是件好事。毕竟,您可以使用 ObservableCollections 做很多在 Families 中无法完成的事情。例如:在家庭环境中 Replace(Member) 是什么意思?
问题是,您忘记实现 INotifyCollectionChanged。 通过此界面,您可以通知其他人您添加了元素(以及移动、删除等)
public class Family : INotifyPropertyChanged,INotifyCollectionChanged
{
...
因为您还必须通知元素是否被移动/删除等。如果您的家人可以做的不仅仅是添加,这将花费一些开发工作。
因此,改变您的
private readonly IList<Member> _members;
转换为 ObservableCollection<Member>
,并通过此 ObservableCollection 实现接口。
class Family : INotifyPropertyChanged,INotifyCollectionChanged
{
public string LastName { get; private set; }
private readonly ObservableCollection<Member> members;
public IEnumerable<Member> Members => this.members;
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add => this.members.NotifyCollectionChangedEventHandler.CollectinChanged += value;
remove => this.members.NotifyCollectionChangedEventHandler.CollectinChanged -= value;
}
现在您的添加/删除/替换/移动/等方法将是单行的;将引发适当的事件。
public void Add(Member member)
{
this.members.Add(member);
}
public void Remove(Member member)
{
this.members.Remove(member);
}
不确定是否需要移动和替换家庭成员的方法,但即使需要,它们也会对相应的 ObservableCollection 方法进行单行调用
您完全隐藏了您正在使用 ObservableCollection<Member>
,因此如果将来您需要完全摆脱 ObservableCollection,您的用户将不必更改,只要您承诺实现接口.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。