wpf 如何使一个ObservableCollection中的更改触发另一个ObservableCollection中的通知?

am46iovg  于 2023-11-21  发布在  其他
关注(0)|答案(2)|浏览(166)

在我的C#应用程序中,我有两个ObservableCollection属性:

public ObservableCollection<Material> Materials { get; init; } = new();

public ObservableCollection<string> MaterialNames
{
    get
    {
        ObservableCollection<string> names = new();

        foreach (Material material in Materials)
        {
            names.Add(material.Name);
        }
        return names;
    }
}

字符串
第二个属性隐藏了第一个属性,从第一个列表中的对象返回Name。WPF ListBox绑定到第二个属性。我需要的是第二个列表在第一个列表中添加或删除成员时触发更新,以便ListBox更新。现在,这不会发生。实现这一点的最简单的解决方案是什么?“连接”两个ObservableCollections的通知?
免责声明:我知道绑定到第一个属性并将DisplayMemberPath设置为Name更容易,但由于应用程序中的某些约束,我需要绑定到一个单独的属性。

dgenwo3n

dgenwo3n1#

最简单的方法是使用一个不可变集合作为源,并在需要时替换整个集合。所有更改都必须通过替换不可变集合来完成。

private ImmutableArray<Materials> MyMaterials{
   get => myMaterials;
   set{
       myMaterials = value;
       Materials = new ObservableCollection<Material>(value);
       MaterialNames = new ObservableCollection<string>(value.Select(m => m.Name));
       OnPropertyChanged();
   }
}
public ObservableCollection<Material> Materials {
   get => ...
   set => ...
}
public ObservableCollection<string> MaterialNames  {
   get => ...
   set => ...
}

字符串
你可以做一些事情,比如将一个列表的变化投射到另一个列表,只需要监听CollectionChanged并将这些变化复制到你的另一个集合。但是我发现很难准确地弄清楚发生了什么变化。所以我个人倾向于使用一个自定义列表,其中包含更多的信息事件,我可以用来将变化复制到一个ObservableCollection。
如果你需要序列化这些数据,我建议你创建单独的“数据传输对象”(Data Transfer Objects,DTO),其中只包含序列化库可以处理的普通列表/数组。然后你可以从中创建你的UI模型。这样你就不会把UI问题和序列化问题混在一起。
但更好的解决方案几乎肯定是解决阻止您使用DisplayMemberPath的任何问题

brgchamk

brgchamk2#

总的来说,我认为你的实现是错误的。ListBox应该绑定到Materials集合,但只显示Name Material属性。因此,MaterialNames集合是多余的。
但我认为实现你需要的绑定会很有趣。下面是我的实现。看看吧。

using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace Core2023.SO.Justinas_Rubinovas
{
    public class BindingCollections
    {
        public ObservableCollection<Material> Materials { get; } = new();

        public ReadOnlyObservableCollection<string> MaterialNames { get; }
        private readonly ObservableCollection<string> _materialNames = new();

        public BindingCollections()
        {
            MaterialNames = new(_materialNames);
            Materials.CollectionChanged += OnMaterialsChanged;
        }

        private void OnMaterialsChanged(object? sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    _ = e.NewItems ?? throw new NullReferenceException();
                    {
                        for (int i = e.NewItems.Count - 1; i >= 0; i--)
                        {
                            _materialNames.Insert(e.NewStartingIndex, ((Material?)e.NewItems[i])?.Name ?? string.Empty);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    _ = e.NewItems ?? throw new NullReferenceException();
                    {
                        if (e.NewStartingIndex < e.OldStartingIndex)
                            for (int i = 0; i < e.NewItems.Count; i++)
                            {
                                _materialNames.Move(e.OldStartingIndex + i, e.NewStartingIndex + i);
                            }
                        else if (e.NewStartingIndex > e.OldStartingIndex)
                            for (int i = e.NewItems.Count - 1; i >= 0; i--)
                            {
                                _materialNames.Move(e.OldStartingIndex + i, e.NewStartingIndex + i);
                            }
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    _ = e.OldItems ?? throw new NullReferenceException();
                    {
                        for (int i = e.OldItems.Count - 1; i >= 0; i--)
                        {
                            _materialNames.RemoveAt(e.OldStartingIndex);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    _ = e.NewItems ?? throw new NullReferenceException();
                    {
                        for (int i = 0; i < e.NewItems.Count; i++)
                        {
                            _materialNames[e.NewStartingIndex + i] = ((Material?)e.NewItems[i])?.Name ?? string.Empty;
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    {
                        int i = 0;
                        for (; i < Materials.Count && i < _materialNames.Count; i++)
                        {
                            _materialNames[i] = Materials[i].Name;
                        }
                        for (; i < Materials.Count; i++)
                        {
                            _materialNames.Add(Materials[i].Name);
                        }
                        for (; i < _materialNames.Count;)
                        {
                            _materialNames.RemoveAt(_materialNames.Count - 1);
                        }
                    }
                    break;
                default:
                    throw new NotImplementedException();

            }
        }
    }

    public class Material
    {
        public string Name { get; set; } = string.Empty;
    }
}

字符串

相关问题