【问题标题】:using INotifyPropertyChanged instead of ObservableCollection使用 INotifyPropertyChanged 而不是 ObservableCollection
【发布时间】:2021-05-20 03:59:56
【问题描述】:

我有一个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)),而MembersListView 所绑定的。所以它应该会收到有关更改的通知。

那我做错了什么?

如果我相应地将IList&lt;Member&gt; _members 更改为ObservableCollection&lt;Member&gt; _members 并将_members = members.ToList() 更改为_members = new ObservableCollection&lt;Member&gt;(members),它会按预期工作。

【问题讨论】:

  • “它应该收到有关更改的通知” - 它收到通知,然后比较当前值和以前的值,发现它们相等并且什么都不做。你可以在你的类中实现 INotifyCollectionChanged。仅添加一项后重建所有项目可能会很昂贵
  • @ASh 你能指出一些链接来解释你的暗示(或解释你自己)吗?关于该比较:比较什么以及何时比较?为什么先前值和当前值相等? (当然它们在引用方面是相同的,但内容发生了变化。)重构所有项目是什么意思?
  • 在通知Members 属性后,比较ItemsSource 的当前值和Members 的新值。它们是对 _members 列表的相同引用。收藏内容无所谓
  • 分配新的Itemssource时,ListView会删除所有项目,然后为新集合的元素创建新项目(重构)
  • 当您对 ObservableCollection 说“它按预期工作”时,为什么不坚持使用这种标准方法呢?这是您通常会做的事情。其他的一切看起来都很尴尬。

标签: c# wpf observablecollection inotifypropertychanged


【解决方案1】:

_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 每次删除并重新创建所有项目。这就是为什么有一个实现INotifyCollectionChangedObservableCollection&lt;T&gt; 类型,它允许特别通知添加和删除的项目,以及其他操作。

【讨论】:

  • “如果你想让它工作,你每次都需要重新创建集合”——根本不需要。只需将 property 重置为 null,发出通知,将相同的集合重新分配给属性,再次发出通知。无需分配内存
  • @ASh 非常感谢,我没想到。有错别字,链接是对的,但是名字不对,当然是_INotifyCollectionChanged_。
【解决方案2】:

首先,您决定 Family 不是 ObservableCollection 是件好事。毕竟,你可以用 ObservableCollections 做很多在 Families 中做不到的事情。例如:Replace(Member) 在家庭环境中意味着什么?

问题是,您忘记实现INotifyCollectionChanged。 使用此界面,您可以通知其他人您添加了一个元素(以及移动、删除等)

public class Family : INotifyPropertyChanged, INotifyCollectionChanged
{
    ...

因为如果元素被移动/删除/等,您还必须通知。如果您的家人可以做的不仅仅是添加,这将花费一些开发工作。

因此,更改您的 private readonly IList&lt;Member&gt; _members; 进入ObservableCollection&lt;Member&gt;,并通过这个 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&lt;Member&gt;,所以如果将来你需要完全摆脱 ObservableCollection,你的用户不必改变,只要你承诺实现接口。

【讨论】:

    猜你喜欢
    • 2012-04-05
    • 2014-02-01
    • 1970-01-01
    • 2011-12-17
    • 2013-06-03
    • 1970-01-01
    • 2021-05-15
    • 1970-01-01
    • 2011-06-03
    相关资源
    最近更新 更多