【问题标题】:Ordering an observable collection with Reactiveui使用 Reactiveui 订购 observable 集合
【发布时间】:2013-01-11 11:09:59
【问题描述】:

在我的 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 =&gt; 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 方法进行排序。

提前致谢!

【问题讨论】:

    标签: wpf mvvm prism reactiveui


    【解决方案1】:

    解决此问题的另一种方法是通过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】:

      刚刚看了安娜的博客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 并正常使用该属性

      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);
          });
      

      【讨论】:

      • 感谢您的详尽回答!我会试一试并更新结果。我现在拥有的一个“hacky”修复程序是触发我的 LoadTickets 命令,该命令将在 ticktes 加载后和通过管道传输到 ObservableAsPropertyHelper 之前检查当前的 SortModel 和 order。但是,我热衷于理解“ReactiveUI 方式”的实现。
      • 对不起,它很复杂,我宁愿它只是简单的。 :-)
      【解决方案3】:
      1. 我很确定 OrderBy(..) 的结果是 IEnumerable 而不是 ObservableCollection。不过幸运的是,它有一个可以进行转换的构造函数,即

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

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

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

      【讨论】:

      • 感谢您的回答。但是,Ticket 的类型是 List,它使用 ObservableAsPropertyHelper> 作为其后备存储。因此,由于这些属性的类型,设置 Tickets = new ObservableCollection.... 不起作用(或者我做错了什么)。此外,我将编辑问题以澄清我所说的“更好”的意思。关于门票的生命周期 - 该列表每 x 秒加载一次,并且“门票”属性会使用新列表重置。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多