WhenAnyValue不是由RaisePropertyChanged触发的

时间:2017-11-08 15:32:38

标签: c# reactiveui

我在我正在处理的应用程序中使用ReactiveUI 7.4。其中一个视图是主/详细视图。选择新的主记录时,我想从数据库加载详细信息项。这可以使用WhenAnyValue observable按预期工作,在下面的示例代码中设置ObservableAsPropertyHelper的值。

现在,我有ReactiveCommand用于保存数据。此命令调用存储过程,该存储过程对与当前所选主项目相关的子项目进行一些处理和更新。因为相关项可能会在数据库端发生变化,所以我想使用我已在WhenAnyValue可观察文件中设置的逻辑来触发相关项目重新加载。

下面是一个非常简单的例子,它重现了我尝试做的事情。 我尝试在保存结束时调用RaisePropertyChanged,但它不会触发WhenAnyValue可观察对象。我可以将所选项目设置为null,然后将其重置为原始值,但这里感觉不对。

有没有更好的方法来处理这种情况,或者是另一种触发WhenAnyValue / ObservableAsPropertyHelper的方式?

public class SampleViewModel : ReactiveObject
{
    public SampleViewModel()
    {
        // log any changes (we can see the RaisePropertyChanged triggers this observable, but not the one above).
        this.Changed.Skip(1).Subscribe(x => Console.WriteLine($"\tChanged = {x.PropertyName}"));

        // when the selected item changes, we want to load related items from the database
        _relatedItems = this.WhenAnyValue(x => x.SelectedItem)
            .Where(row => row != null)
            .Select(row =>
            {
                // get related data from the cache or database...
                Console.WriteLine($"\tGetting Related Items for {row["Value"]}");
                return new DataTable();
            })
            .ToProperty(this, x => x.RelatedItems);

        Save = ReactiveCommand.Create(() =>
        {
            // Save the selected item. Stored procedure does some extra processing of related items

            // Now, we want to trigger a reload of the related items.
            this.RaisePropertyChanged(nameof(SelectedItem));

            /* NOTE: The following works, but feels wrong.
            var oldSelection = SelectedItem;
            SelectedItem = null;
            SelectedItem = oldSelection;
            */
        });
    }

    public ReactiveCommand Save { get; }

    private readonly ObservableAsPropertyHelper<DataTable> _relatedItems;
    public DataTable RelatedItems => _relatedItems.Value;

    private DataRow _selectedItem;
    public DataRow SelectedItem
    {
        get { return _selectedItem; }
        set { this.RaiseAndSetIfChanged(ref _selectedItem, value); }
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        using (var table = new DataTable())
        {
            table.Columns.Add(new DataColumn("Value", typeof(string)));
            table.Rows.Add(new[] { "First Row" });
            table.Rows.Add(new[] { "Second Row" });

            var instance = new SampleViewModel();
            Console.WriteLine("Selecting First Row");
            instance.SelectedItem = table.Rows[0];

            Console.WriteLine();
            Console.WriteLine("Saving Data");
            Observable.Start(() => { }).InvokeCommand(instance.Save);

            Console.WriteLine();
            Console.WriteLine("Selecting Second Row");
            instance.SelectedItem = table.Rows[1];

            Console.WriteLine();
        }
    }
}

输出

Selecting First Row
    Changed = SelectedItem
    Getting Related Items for First Row
    Changed = RelatedItems

Saving Data
    Changed = SelectedItem
    // should be getting related items here...

Selecting Second Row
    Changed = SelectedItem
    Getting Related Items for Second Row
    Changed = RelatedItems

修改(工作代码)

Lee McPherson's Answer指出了我正确的方向。构造函数已更新为以下内容:

public SampleViewModel()
{
    // log any changes
    this.Changed.Skip(1).Subscribe(x => Console.WriteLine($"\tChanged = {x.PropertyName}"));

    Save = ReactiveCommand.Create(() =>
    {
        // Save the selected item. Stored procedure does some extra processing of related items
    });

    // when the save command finishes execution, get the currently selected "master" record
    var saveCommandDone = Save.IsExecuting
        .Where(executing => !executing).Skip(1)
        .Do(_ => Console.WriteLine("\tCommand finished execution"))
        .Select(_ => SelectedItem);

    // when the master record changes, or data is saved, reload the related items
    _relatedItems = Observable.Merge(saveCommandDone, this.WhenAnyValue(x => x.SelectedItem))
        .Where(row => row != null)
        .Select(row =>
        {
            // get related data from the cache or database...
            Console.WriteLine($"\tGetting Related Items for {row["Value"]}");
            return new DataTable();
        })
        .ToProperty(this, x => x.RelatedItems);
}
Selecting First Row
    Changed = SelectedItem
    Getting Related Items for First Row
    Changed = RelatedItems

Saving Data
    Command finished execution
    Getting Related Items for First Row
    Changed = RelatedItems

Selecting Second Row
    Changed = SelectedItem
    Getting Related Items for Second Row
    Changed = RelatedItems

另一个可能的答案: 我还发现我可以转换&#34;加载相关的项目&#34;将逻辑转换为ReactiveCommand并使用InvokeCommand来完成同样的事情。

var loadRelatedData = ReactiveCommand.Create<DataRow, DataTable>(row => new DataTable());
_relatedItems = loadRelatedData.ToProperty(this, x => x.RelatedItems);

// when the selected item changes, trigger the command used to load related data
this.WhenAnyValue(x => x.SelectedItem)
    .Where(row => row != null)
    .InvokeCommand(loadRelatedData);

Save = ReactiveCommand.Create(() =>
{
    // Save the data and trigger a reload of the related data.
    Observable.Return(SelectedItem).InvokeCommand(loadRelatedData);
});

1 个答案:

答案 0 :(得分:0)

您的保存ReactiveCommand有一个IsExecuting IObservable。您可以尝试Observable.Merge将您创建的可观察对象与WhenAnyValue与调用命令相结合。

Observable.Merge(this.Save.IsExecuting, _relatedItems).Subscribe(x=>{
    //do stuff
});

这是一个相关的答案: How do I merge several observables using WhenAny(...) in ReactiveUI?

现在我想起来了,我不知道ObservableAsPropertyHelper是否会起作用。但是如果它没有,你可以在用ToProperty(...)转换之前将Observable创建为一个单独的项目。