0

我在我的 ViewModel 中订购一个可观察的集合时遇到了一些困难。这是我的情况:

在我的视图模型中,我有以下列表:

public List<TicketModel> Tickets
        {
            get { return _Tickets.Value; }
            set
            {
                {
                    this.RaiseAndSetIfChanged(c => c.Tickets, value);
                }
            }
        }
        private ObservableAsPropertyHelper<List<TicketModel>> _Tickets;

此列表使用以下内容填充ReactiveAsyncCommand

LoadTickets.RegisterAsyncFunction(x => loadTickets())
                .ToProperty(this, x => x.Tickets);

到目前为止所有工作。

我有另一个命令,SortByCommand每当用户想要对集合进行排序时都会调用它。该命令如下所示:

SortByCommand = new ReactiveCommand(this.WhenAny(c => c.Tickets, ((tickets) => tickets.Value != null && tickets.Value.Count > 0)));
SortByCommand.Subscribe(c => sortTickets((SortByModel)c));

该命令还调用一个函数,该函数使用 order by 子句对集合进行排序:

private void sortTickets(SortByModel model)
{
    Tickets = Tickets.OrderBy(model.Selector).ToList();
}

每当sortTickets调用该函数时,都会抛出一个异常,它说:

Unable to cast object of type 'ReactiveUI.ObservableAsPropertyHelper`1[System.Collections.Generic.List`1[Bugmine.Modules.MyPage.Models.TicketModel]]' to type 'System.Collections.Generic.List`1[Bugmine.Modules.MyPage.Models.TicketModel]'.

我有几个问题:

1)为什么我不能直接设置Tickets模型?我是否需要先将结果转换为OrderBy某种可观察的集合?

2)有没有更好的方法来做到这一点?

编辑:澄清

我现在采取的方法是:

  • Tickets集合每 x 秒重置一次。
  • 调用该sortTickets函数后,我将按以下方式排序和重置此集合:

    Tickets = Tickets.OrderBy(c => c.Name).ToList(); //for example

  • 再次Tickets加载集合时,我会检查它是否应该排序,并在设置Tickets属性之前对其进行排序。

这感觉有点 hacky,因为我基本上将集合设置在两个点 - 在加载和排序时。此外,在加载时,我正在使用 ReactiveUI 助手 -> ToProperty:

LoadTickets.RegisterAsyncFunction(x => loadTickets())
    .ToProperty(this, x => x.Tickets);

然而,在排序时,我自己正在这样做:

Tickets = Tickets.OrderBy(model.Selector).ToList();

我想知道是否有更好的方法来使用我在加载时已经使用的 ReactiveUI 方法进行排序。

提前致谢!

4

3 回答 3

2

解决此问题的另一种方法是通过CreateDerivedCollection

SortedTickets = Tickets.CreateDerivedCollection(
    x => new TicketViewModel(x),
    orderer: (l,r) => SortModel.Selector(l, r),  // Returns CompareTo() result
    signalReset: this.WhenAny(x => x.SortModel, x => x.Value));  // Reorder on SortModel change

请注意,如果重复设置Tickets(在这种情况下是这样),这会发生故障 - 您可以将模型更改为在 ctor 中进行初始化,然后清除并添加所有项目,即Tickets

LoadTickets.RegisterAsyncFunction(x => loadTickets())
    .Subscribe(x => {
        // TODO: Make sure Tickets is a ReactiveCollection
        Tickets.Clear();
        Tickets.AddRange(x);   // Will trigger resorting of SortedTickets
    });
于 2013-01-28T18:58:49.933 回答
2

刚刚查看了 Ana 的博客http://blog.paulbetts.org/index.php/2010/07/05/reactivexaml-series-implementing-search-with-observableaspropertyhelper/

//
// This is the canonical way to make a read-only property whose value
// is backed by an IObservable
//

ObservableAsPropertyHelper<List<FlickrPhoto>> _Photos;

我认为只读是那里的重点。

相反,您可以尝试使用普通的可观察对象,您可以在其中使用 OnNext 推送新值

private Observable<List<TicketModel>> _Tickets = new Observable<Lis<TicketModel>>();
_Tickets.OnNext(newValue);

或使用 ObservableForProperty<> 并正常使用该属性

public List<TicketModel> _Tickets { get;set;}
private Observable<List<TicketModel>> _ticketsObservable= ObservableForProperty<..>(x=>x.Tickets);

这两种方法都暴露了一个 Observable,我们可以在稍后的排序中使用它。

为什么不尝试以相同的方式将这两个输入处理到您的排序中,那么它就不会感觉如此 hacky。这样你也会有一个

public SortModel SortModel {get;set;}

您的排序命令实现变为

SortByCommand.Subscribe(c => _Sort = c));

但是随后您同时订阅了票证更改和排序标准更改,请参阅http://rxwiki.wikidot.com/101samples#toc44 for CombineLatest

new ObservableForProperty<..>(x=>x.SortModel)
    .CombineLatest(_ticketsObservable) 
    .Subscribe( (x,y)=>
    {
         //Refactor to SortMethod
         _tickets = y.OrderBy(x.Selector);
    });
于 2013-01-28T14:05:34.100 回答
0
  1. 我很确定 OrderBy(..) 的结果是 IEnumerable 而不是 ObservableCollection。不过幸运的是,它有一个可以进行转换的构造函数,即

门票 = new ObservableCollection<...>(Tickets.OrderBy(...));

  1. “更好”可能是非常主观的衡量标准。首先,您还没有解释门票集合的完整生命周期及其包含的门票,所以我们无法确定。您可能会考虑使用 SortedTickets 字段/属性而不是覆盖您的门票属性,您可能会发现您不需要成为可观察的集合,因为您会知道何时需要从门票可观察的 NotfyProperryChanged。这完全取决于您的门票可能多久更换一次。

PS 还要确保你已经测试了当你添加另一张票时你的排序列表会发生什么,因为你不能只是

于 2013-01-28T07:44:50.970 回答