【问题标题】:How to use RX to control command's availability in complex scenarios?复杂场景下如何使用RX控制命令的可用性?
【发布时间】:2022-01-02 02:10:37
【问题描述】:

设置

让我们假设以下内容。我们有一个用于 WPF 应用程序的以下 理论 视图模型类:

public MyViewModel
{

    public MyViewModel()
    {
        // Condition under which this command may be executed is:
        // this.ActiveDocument.Highlighting.Type == Highlighting.Xml && 
        //    !this.ActiveDocument.IsReadOnly && 
        //    (this.License.Kind == LicenseKind.Full || this.License.TrialDay < 30)
        MyCommand = new Command(obj => DoSomething());
    }

    public ICommand MyCommand { get; } 
    // (all other required properties)
}

另外:

  • 当前类正确实现INotifyPropertyChanged
  • 成员访问链中的所有类都正确实现INotifyPropertyChanged(例如,可从ActiveDocument 属性访问的文档视图模型)
  • ActiveDocument 可能是 nullActiveDocument.Highlighting 也可以为空。

问题

我希望仅在满足评论中的条件时启用该命令。

不带 RX 的选项

我写了my own library 来处理这种情况。解决方案是:

public MyViewModel
{
    private readonly Condition commandAvailableCondition;

    public MyViewModel()
    {
        commandAvailableCondition = new LambdaCondition(this, 
            vm => m.ActiveDocument.Highlighting.Type == Highlighting.Xml && 
                !vm.ActiveDocument.IsReadOnly && 
                (vm.License.Kind == LicenseKind.Full || vm.License.TrialDay < 30),
            false);

        MyCommand = new AppCommand(obj => DoSomething(), commandAvailableCondition);
    }

    public ICommand MyCommand { get; } 
    // (all other required properties)
}

或者 - 如果您希望代码更具可读性,以便可以重用部分条件 - 就像这样:

public MyViewModel
{
    private readonly Condition commandAvailableCondition;

    public MyViewModel()
    {
        var highlightingIsXml = new LambdaCondition(this, 
            vm => vm.ActiveDocument.Highlighting.Type == Highlighting.Xml, 
            false);
        var documentIsReadonly = new LambdaCondition(this,
            vm => vm.ActiveDocument.IsReadOnly, 
            false);
        var appIsLicensed = new LambdaCondition(this,
            vm => vm.License.Kind == LicenseKind.Full || this.License.TrialDay < 30,
            false);

        commandAvailableCondition = highlightingIsXml & !documentIsReadonly & appIsLicensed;

        MyCommand = new AppCommand(obj => DoSomething(), commandAvailableCondition);
    }

    public ICommand MyCommand { get; } 
    // (all other required properties)
}

我的库(或者更准确地说,LambdaCondition 类)所做的是:

  • 它跟踪所有实现INotifyPropertyChanged 的实例并处理更改(例如,当ActiveDocument 更改或ActiveDocument.Highlighting 更改或ActiveDocument.Highlighting.Type 更改等时)
  • 它会跟踪可能的nulls,在这种情况下它将返回默认值(在这种情况下为false
  • 它会自动报告命令可用性的更改(但仅更改),以便在需要时刷新 UI。

问题

如何在 C# 中使用 System.Reactive 实现上述场景?是否可以轻松地做到这一点,同时保持关于INotifyPropertyChanged 的所有要求、途中的空值和默认值?您可以在需要时做出任何合理的假设。

【问题讨论】:

    标签: c# wpf mvvm system.reactive


    【解决方案1】:

    ReactiveUI 框架有一个 ReactiveCommand 类,它使用 IObservable&lt;T&gt; 刷新命令的状态(即引发 ICommandCanExecuteChanged 事件)。

    请参考docs,了解如何使用它来控制可执行性:

    var canExecute = this.WhenAnyValue(
        x => x.UserName, x => x.Password,
        (userName, password) => 
            !string.IsNullOrEmpty(userName) && 
            !string.IsNullOrEmpty(password));
    
    var command = ReactiveCommand.CreateFromTask(LogOnAsync, canExecute);
    

    【讨论】:

    • 它是否遵循属性访问链中的 INotifyPropertyChange 实现者?如果任何属性为空,它是否也能正常工作?您能否展示一下我的具体示例在 RX 中实现时的外观?
    • UsernamePassword 属性中的任何一个引发PropertyChanged 事件时,WhenAnyValue 确实会发出一个新值。你的具体例子是什么?你尝试了什么?
    • 您阅读了我的整个问题吗?我已经提出了应该被观看的确切情况:vm =&gt; m.ActiveDocument.Highlighting.Type == Highlighting.Xml &amp;&amp; !vm.ActiveDocument.IsReadOnly &amp;&amp; (vm.License.Kind == LicenseKind.Full || vm.License.TrialDay &lt; 30), 如果例如。 ActiveDocument 为空? RX会妥善处理吗?在这种情况下,我希望条件具有其默认值(或默认情况下至少为 false)
    • 如果ActiveDocument,你会得到一个NullReferenceException。这与 RX 无关。
    • 在我的图书馆里我不会。这就是为什么我对 RX 是否非常适合这项任务感兴趣的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-04
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 1970-01-01
    相关资源
    最近更新 更多