使用Reactiveui订购可观察的集合

时间:2013-01-28 06:22:12

标签: wpf mvvm prism reactiveui

我在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)有更好的方法吗?

编辑:澄清

我现在采取的方法是:

  • {x 1}}集合每隔x秒重置一次。
  • 一旦调用Tickets函数,我将按以下方式对此集合进行排序和重置:

    sortTickets

  • 再次加载Tickets = Tickets.OrderBy(c => c.Name).ToList(); //for example集合时,我会检查是否应对其进行排序并在设置Tickets属性之前对其进行排序。

这感觉有点hacky因为我基本上设置了两个点 - 在加载和排序时。此外,加载后我使用ReactiveUI帮助程序 - &gt; ToProperty:

Tickets

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

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

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

提前致谢!

3 个答案:

答案 0 :(得分:2)

刚看了Paul的博客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;

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

相反,您可以尝试使用普通的observable,您可以使用OnNext来推送新值

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

或使用ObservableForProperty&lt;&gt;并且通常只使用该属性

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了解CombineLatest

new ObservableForProperty<..>(x=>x.SortModel)
    .CombineLatest(_ticketsObservable) 
    .Subscribe( (x,y)=>
    {
         //Refactor to SortMethod
         _tickets = y.OrderBy(x.Selector);
    });

答案 1 :(得分: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
    });

答案 2 :(得分:0)

  1. 我很确定OrderBy(..)的结果是IEnumerable而不是ObservableCollection。幸运的是,它有一个可以进行转换的构造函数,即
  2. Tickets = new ObservableCollection&lt; ...&gt;(Tickets.OrderBy(...));

    1. “更好”可以是非常主观的衡量标准。首先,您没有解释Tickets集合的完整生命周期及其包含的票证,因此我们无法真正说明。 您可以考虑使用SortedTickets字段/属性而不是覆盖您的票证属性,您可能会发现它不需要是一个可观察的集合,因为您知道它何时需要来自Tickets observable的NotfyProperryChanged。这一切都取决于您的门票可能改变的频率。
    2. P.S。另外,请确保在添加其他故障单时测试了排序列表中发生的情况,但不能只是