【问题标题】:How to bind ObservableCollection<T> of mutable model to ReadOnlyObservableCollection<T> of viewmodel using ReactiveUI and DynamicData如何使用 ReactiveUI 和 DynamicData 将可变模型的 ObservableCollection<T> 绑定到 ViewModel 的 ReadOnlyObservableCollection<T>
【发布时间】:2020-02-05 19:26:37
【问题描述】:

我在我的 C# 项目中使用 ReactiveUI 和 DynamicData。但是,域模型类仍然依赖于 C# 事件、INotifyPropertyChanged 和 INotifyCollectionChanged 接口。

有 Model 和 ViewModel 类:

public class Model
{
    public ObservableCollection<int> Collection { get; } = new ObservableCollection<int>();
}

public class ViewModel : ReactiveObject, IDisposable
{
    private readonly CompositeDisposable _cleanUp;
    private readonly SourceList<int> _collectionForCurrentBModel = new SourceList<int>();
    private Model _model = new Model();
    private IDisposable _tempCleanUp = Disposable.Empty;

    public ViewModel()
    {
        _cleanUp = new CompositeDisposable();
        _collectionForCurrentBModel.Connect()
            .Bind(out var aModelsForCurrentBModel)
            .Subscribe(Console.WriteLine)
            .DisposeWith(_cleanUp);
        CollectionForCurrentBModel = aModelsForCurrentBModel;

        this.WhenAnyValue(x => x.Model.Collection) // Every time Model in ViewModel changes:
            .Subscribe(collection =>
            {
                // we dispose previous subscription:
                _tempCleanUp.Dispose();
                // then we manually reset SourceList<int> to match new collection:
                _collectionForCurrentBModel.Edit(x =>
                {
                    x.Clear();
                    x.AddRange(collection);
                });
                // finally, we manually subscribe to ObservableCollection<int>'s events to synchronize SourceList<int>.
                _tempCleanUp = collection.ObserveCollectionChanges().Subscribe(pattern =>
                {
                    switch (pattern.EventArgs.Action)
                    {
                        case NotifyCollectionChangedAction.Add:
                            _collectionForCurrentBModel.AddRange(pattern.EventArgs.NewItems.Cast<int>());
                            break;
                        case NotifyCollectionChangedAction.Remove:
                            _collectionForCurrentBModel.RemoveRange(pattern.EventArgs.OldStartingIndex,
                            pattern.EventArgs.OldItems.Count);
                            break;
                        case NotifyCollectionChangedAction.Replace:
                            for (var i = 0; i < pattern.EventArgs.NewItems.Count; i++)
                                _collectionForCurrentBModel.Replace((int) pattern.EventArgs.OldItems[i],
                                (int) pattern.EventArgs.NewItems[i]);
                            break;
                        case NotifyCollectionChangedAction.Move:
                            break;
                        case NotifyCollectionChangedAction.Reset:
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                });
            });
    }

    public ReadOnlyObservableCollection<int> CollectionForCurrentBModel { get; }

    public Model Model
    {
        get => _model;
        set => this.RaiseAndSetIfChanged(ref _model, value);
    }

    public void Dispose()
    {
        _cleanUp.Dispose();
    }
}

因此,ViewModel 具有 Model 属性。当前模型可以更改为另一个模型。 ViewModel 也有 CollectionForCurrentModel 属性,在这个例子中它基本上等于它的源(Model.Collection)(但是,应该有一些排序、过滤等)。 CollectionForCurrentModel 属性应该是只读的。 下面的代码按预期工作:

private static void Main(string[] args)
{
    using var viewModel = new ViewModel();
    // viewModel.Collection: {}
    viewModel.Model.Collection.Add(0);
    // viewModel.Collection: {0}
    viewModel.Model.Collection.Add(1);
    // viewModel.Collection: {0, 1}
    var oldModel = viewModel.Model;
    viewModel.Model = new Model();
    // viewModel.Collection: {}
    viewModel.Model.Collection.Add(2);
    // viewModel.Collection: {2}
    oldModel.Collection.Add(3);
    // viewModel.Collection: {2}
}

但是,向 ViewModel 添加新字段以存储最新订阅、手动取消订阅以及手动同步集合似乎很丑陋。有没有其他订阅方式:

IObservable<IObservable<IChangeSet<T>>>
\\ is result of
this.WhenAnyValue(x => x.ObservableCollection, selector: collection => collection.ToObservableChangeSet();

? DynamicData 能否自动管理内部订阅,将可变属性中的 observable 集合绑定到其他集合?

【问题讨论】:

    标签: system.reactive inotifypropertychanged reactiveui inotifycollectionchanged


    【解决方案1】:

    这应该很容易。你可以这样做:

    this.WhenAnyValue(x => x.Model.Collection)
        .Select(collection => collection.ToObservableChangeSet())
        .Switch() //this is the dynamic data overload of rx.Switch() 
        .Bind(out var aModelsForCurrentBModel)
        .Subscribe();
    

    select 语句返回一个可观察的可观察更改集,它本身并不是很有用。这就是为什么需要 switch 语句的原因。当集合被设置时,它会从缓存中清除现有项目,并从新的 observable 集合中加载项目。之后,您可以简单地绑定到目标 observable 集合。

    使用这种技术意味着无需手动维护源列表。

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      • 2011-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多