响应式列表问题

3
我们对ReactiveUI还比较陌生,这可能是我们在使一个视图模型时遇到问题的原因。
在我们的视图模型中,我们有一份ReactiveList列表,其中包含一个类,该类中有一个“selected”的属性。
在视图模型中,我们想要一个“AnySelected”属性,如果列表中至少有1个项目被标记为选定,则AnySelected为true。
我们在努力解决此问题。
作为一个小型的测试应用程序,我们尝试了一些字符串,在变化发生时的信息没有出现。
public class TestRx : ReactiveObject
{
    private ReactiveList<string> mySelectedItems;

    public ReactiveList<string> MySelectedItems
    {
        get { return mySelectedItems; }
        set { this.RaiseAndSetIfChanged(ref mySelectedItems, value); }
    }

    public TestRx()
    {
        var firstList = new ReactiveList<string>();

        var t = this.WhenAnyValue(x => x.MySelectedItems);
        var t1 = t.Select(x => x ?? new ReactiveList<string>());

        var changed = t1.Select(x => x.Changed.Select(_ => Unit.Default));
        var itemChanged = t1.Select(x => x.ItemChanged.Select(_ => Unit.Default));
        var countChanged = t1.Select(x => x.CountChanged.Select(_ => Unit.Default));

        t.Subscribe(x => Debug.WriteLine("T HAS CHANGED {0}", x == firstList));
        t1.Subscribe(z => Debug.WriteLine("T1 Changed {0}", z == firstList));

        changed.Subscribe(x => Debug.WriteLine("Changed :"));
        itemChanged.Subscribe(x => Debug.WriteLine("Item Changed :"));

        var replacementList = new ReactiveList<SelItem>(new[] {
                new SelItem() { Selected = false } 
        });

        Debug.WriteLine("***********************Assign 1st list");
        MySelectedItems = firstList;
        Thread.Sleep(100);

        Debug.WriteLine("***********************Adding item 2 list");
        MySelectedItems.Add("a new string");
        // we don't get any debug messages  as a result of the above
        Thread.Sleep(100);

        Debug.WriteLine("***********************Assign null");
        MySelectedItems = null;
        Thread.Sleep(100);
    }
}

我们做错了什么?

我使用这个模式来处理 ReactiveList<>:public ReactiveList<string> MySelectedItems { get; private set; }。虽然没有时间查看所有细节,但这个模式非常实用。 - kenny
1个回答

3
这是一种常见的模式,但它有点棘手,因为您必须处理以下所有情况:
  1. 列表已设置
  2. 列表项更改
  3. 任何项目上的“Selected”属性更改。请记住,由于#1或#2,您想要观看的项目会发生更改。

我该怎么做?

这里有一种方法可以实现。它很复杂,并且暗示了未来版本的RxUI可以改善事情,但现在您可以这样做。
IObservable<bool> WhenAnyAreTrue(IEnumerable<ViewModel> currentElements)
{
    // NB: 'Unit' here means, we don't care about the actual value, just
    // that something changed
    var notifyWhenAnySelectedItemChanges = currentElements
        .Select(x => x.WhenAny(y => y.Selected, _ => Unit.Default).Skip(1))
        .Merge();

    return notifyWhenAnySelectedItemChanges
        .StartWith(Unit.Default)
        .Select(_ => currentElements.Any(x => x.Selected));
}

// Any time MySelectedItems change or when the items in it change,
// create a new WhenAnyAreTrue and switch to it
this.WhenAnyObservable(x => x.MySelectedItems.ItemsChanged)
    .Select(_ => WhenAnyAreTrue(MySelectedItems))
    .Switch()
    .ToProperty(this, x => x.AnySelected, out anySelected);

1
ReactiveList没有一个ChangeTrackingEnabled属性来帮助处理这种情况吗? - Cameron MacFarland
谢谢回复,我正在逐渐理解这个问题。不过这里有一些“边缘”情况:我看不到 ItemsChanged,但是有 ItemsAdded 和 ItemsRemoved,但是似乎没有 ItemsReplaced(例如,在 ReactiveList 中替换指定索引位置的项)。因此,我可以看到插入和删除的更改,但是无法看到类似 list[2] = new ... 的替换。我是否遗漏了什么(ItemsChanging 用于同一实例属性更改但不用于列表替换)? - jamie
根据 RxUI 的版本,这可能被简单地称为“Changed”。 - Ana Betts
我已经更新到最新的ReactiveUI预发布版本,并修改了您上面所做的内容,以尝试适应MySelectedItems为空的情况,并将修订后的代码放在这里的Gist中https://gist.github.com/jcmm33/2a297814a531d11cf89d。有没有什么方法可以用“更短的方式”表示这个?提前感谢任何指针。 - jamie
RxUI的未来版本可能会使事情变得更好。这已经发生了吗? - Taylor Buchanan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接