0

使用 ReactiveUI/DynamicData,我在SelectedItem更新ItemSource. 我使用 aSourceList<T>来保存可变集合以及 Connect/Bind/Subscribe 来更新ReadOnlyObservableCollection绑定到ComboBox.ItemSource.

ComboBox视图中是这样绑定的:

            this.OneWayBind(ViewModel, vm => vm.EventYearsList, v => v.EventYearsComboBox.ItemsSource).DisposeWith(d);
            this.Bind(ViewModel, vm => vm.SelectedEventYear, v => v.EventYearsComboBox.SelectedItem).DisposeWith(d);

在 ViewModel 中,我定义了 aReactiveCommand来更新SourceList, aSourceList保存从服务层返回的数据, aReadOnlyObservableCollection绑定到ComboBox.ItemSource,以及一个属性来保存SelectedItem.

    public ReactiveCommand<Unit, Unit> GetEventYearsListCommand { get; }

    private readonly SourceList<short> _eventYearsSourceList = new SourceList<short>();

    private readonly ReadOnlyObservableCollection<short> _eventYearsList;
    public ReadOnlyObservableCollection<short> EventYearsList => _eventYearsList;

    private short? _selectedEventYear;
    public short? SelectedEventYear
    {
        get => _selectedEventYear;
        set => this.RaiseAndSetIfChanged(ref _selectedEventYear, value);
    }

在 ViewModel 的构造函数中,我设置了订阅:

        GetEventYearsListCommand = ReactiveCommand.CreateFromTask(ExecuteGetEventYearsListCommand);
        GetEventYearsListCommand.ThrownExceptions.Subscribe(ex => Trace.WriteLine(ex.ToString()));

        _eventYearsSourceList
            .Connect()
            .ObserveOn(RxApp.MainThreadScheduler)
            .Bind(out _eventYearsList)
            .Subscribe();

        // when a series is selected, this fires off the command to update the event years
        this.WhenAny(x => x.SelectedSeries, _ => Unit.Default)
            .ObserveOn(RxApp.MainThreadScheduler)
            .InvokeCommand(GetEventYearsListCommand);

请注意,当用户在另一个ListBox...

最后,更新命令SourceList如下所示:

   private async Task ExecuteGetEventYearsListCommand()
    {
        var eventYearsList = new List<short>();
        if (SelectedSeries != null)
        {
            eventYearsList = await seriesApi.QueryEventYears(SelectedSeries.OrgSeriesPath);
        }

        _eventYearsSourceList.Edit(list =>
        {
            list.Clear();
            if (eventYearsList != null) list.AddRange(eventYearsList);
        });

        // This trace shows the correct number of items in the list
        Trace.WriteLine($"_eventYearsSourceList count = {_eventYearsSourceList.Count}");

        // But here, I don't get the right count, EventYearsList has not been updated yet!!!
        Trace.WriteLine($"EventYearsList count = {EventYearsList.Count}");

        // and this check isn't even valid, because EventYearsList hasn't been updated...
        if (EventYearsList?.Count > 0)
        {
            SelectedEventYear = EventYearsList.First();
        }
    }

所以直接看上面的命令...我已经更新了SourceList,但ReadOnlyObservableCollection还没有更新...它仍然显示SourceList我更新之前的内容。

第一个问题是,我什至没有从新更新的列表中获得第一项,因为它还没有更新。

第二个问题是,即使我现在设置所选项目,当 EventYearsList 最终更新时,该选择也会丢失。由于所选项目的双向绑定(我假设),SelectedEventYear在 EventYearsList 最终更新后设置为 null 。因此,即使我现在可以设置所选项目,它也会被覆盖。(我已通过在 SelectedEventYear 属性设置器上设置断点来确认此行为)

要么我做错了什么......要么我需要找到一种方法来(单独)连接到一些东西,告诉我什么时候底层的来源发生了ReadOnlyObservableCollection变化。

所以回到最初的问题......我如何SelectedItem将更新后设置为ItemSource

4

2 回答 2

1

通常我会做这样的事情,因为它似乎大多数时候都能解决问题。

    _eventYearsSourceList
        .Connect()
        .ObserveOn(RxApp.MainThreadScheduler)
        .Bind(out _eventYearsList)
        .Do(_=> \\some logic to set the selected item) 
        .Subscribe();

使用该Do语句并不理想,因为您需要从中选择一个项目_eventYearsList。通常在反应式编程中,不建议在函数之外触摸一些对象状态,因为这样做会引入并发和状态问题。但是,在这种情况下,您可能需要第一个日期,或者您可能需要迭代可观察集合,但安全措施是绑定发生在主线程上,Do 语句中的逻辑也是如此,因此您将没有并发问题。

当然,另一种选择是您可以在Subscribe.

于 2020-03-11T18:57:26.277 回答
0

好吧,这么多头发拉扯和咬牙切齿,我想出了一些有效的方法。不知道这是否是一种“好”的方法,或者是否有一种“更好”的方法(如果存在的话,我当然想听听更好的方法)。

关键是我订阅了SourceList两次的更改。

曾经像我最初对视图/XAML 可以绑定到BindSourceListto a所做的那样:ReadOnlyObservableCollection

        _eventYearsSourceList
            .Connect()
            .ObserveOn(RxApp.MainThreadScheduler)
            .Bind(out _eventYearsList)
            .Subscribe();

然后再做一个简单地寻找对 , 的任何更改SourceList,然后去做一些事情:

        _eventYearsSourceList
            .Connect()
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(async _ => await SelectFirstEventYear());

这将导致我的SelectFirstEventYear()方法被触发,我可以在其中将SelectedEventYear属性设置为第一项。

边注:

出于某种原因,在此方法中设置SelectedEventYear属性(利用RaiseAndSetIfChanged)并没有触发它WhenAny

        this.WhenAny(x => x.SelectedEventYear, _ => Unit.Default)
            .ObserveOn(RxApp.MainThreadScheduler)
            .InvokeCommand(GetEventsListCommand);

我不确定为什么这是...任何时间更改的时间都会更改,我还有另一个需要更新的列表框,这就是这样GetEventsListCommand做的。我最终在我的SelectFirstEventYear()方法中添加了对该命令的调用,一切似乎都很好。

于 2020-03-11T18:47:59.060 回答