繁体   English   中英

如何使用MVVM在功能区中强制调用CanExecute

[英]How to force to call CanExecute in Ribbon using MVVM

如何使用MVVM在功能区中强制调用CanExecute我有一个简单的应用程序,带有选项卡控件和每个选项卡项目上的编辑控件。 它链接到ViewModel,在ViewModel上还定义了命令(而不是路由命令)。 它们在菜单中使用。 到目前为止,一切工作正常。

现在,我想使用功能区,其中包含根据某些条件启用的按钮(例如,只有在任何选项卡项处于活动状态时,才启用“保存”或“ WordWrap”)。 不幸的是,应用程序加载时,所有命令的CanExecute方法仅被调用一次。 如何更改ViewModel中的某些内容时进行检查?

更多详细信息(WPF 4.6.1):

RelayCommand:

public class RelayCommand : ICommand
{
    private Action _execute;
    private Func<bool> _canExecute;

    public RelayCommand(Action execute, Func<bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute != null)
            return _canExecute();
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _execute();
    }
}

WordWrap命令:

private RelayCommand _ChangeWordWrapCommand;

public RelayCommand ChangeWordWrapCommand
{
    get
    {
        if (_ChangeWordWrapCommand == null)
            _ChangeWordWrapCommand = new RelayCommand(
                () => { ((TextDocument)ActiveDocument).WordWrap = !((TextDocument)ActiveDocument).WordWrap; },
                () => { return (ActiveDocument != null); }
            );

        return _ChangeWordWrapCommand;
    }
}

ActiveDocument和打开的文档属性:

public ObservableCollection<Document> OpenedDocuments { get; private set; }

private Document _ActiveDocument; 
public Document ActiveDocument
{
    get { return _ActiveDocument; }
    set
    {
        _ActiveDocument = value;
        OnPropertyChanged(nameof (ActiveDocument));
    }
}

NewFile命令执行:

private Document NewFile()
{
    TextDocument result = new TextDocument(new Model.TextDocument());
    result.FileName = $"Untitled {UntitledFileNumber++}";
    OpenedDocuments.Add(result);
    ActiveDocument = result;
    return result;
}

功能区定义(仅一部分):

<Ribbon x:Name="Ribbon" SelectedIndex="0" UseLayoutRounding="False">
    <RibbonTab Header="Home" KeyTip="H" >
        <RibbonGroup x:Name="Files" Header="Files" >
            <RibbonButton  Label="New" KeyTip="N" Command="{Binding Path=NewFileCommand, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
            <RibbonButton  Label="WordWrap" KeyTip="W" Command="{Binding Path=ChangeWordWrapCommand, Mode=OneWay, UpdateSourceTrigger=Default}" />
        </RibbonGroup>
    </RibbonTab>
</Ribbon>

在开始时,没有打开文件,因此禁用了WordWrap。 功能区上的按钮也是如此。 当执行NewFileCommand时,将使用控件创建新选项卡,但WordWrap命令将保持禁用状态。

我发现本文的WPF MVVM命令可以执行启用/禁用按钮 ,建议在ActiveDocument设置器中调用RaiseCanExecuteChanged:

public Document ActiveDocument
{
    get { return _ActiveDocument; }
    set
    {
        _ActiveDocument = value;
        OnPropertyChanged(nameof(ActiveDocument));
        ChangeWordWrapCommand.RaiseCanExecuteChanged();///really
    }
}

尽管这有效,但对我来说听起来很奇怪。 为什么该属性应该知道使用该属性的所有命令并应加以照顾? 有没有更清洁的解决方案来解决这个问题?

为什么该属性应该知道使用该属性的所有命令并应加以照顾?

因为这是视图模型类实现的应用程序逻辑的一部分。 除了这个类,没有人可以知道何时调用命令的CanExecute方法。

因此,您实际上需要引发CanExecuteChanged事件,以告诉WPF随时刷新命令/按钮的状态,而执行此RaiseCanExecuteChanged方法是调用DelegateCommandRaiseCanExecuteChanged方法。 无论何时何地,您都希望刷新命令。

有没有更清洁的解决方案来解决这个问题?

您可以看看ReactiveUI 这是一个MVVM库,具有反应性属性和反应性命令的概念,每当属性更改时,刷新该命令将变得更加容易:

ChangeWordWrapCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.ActiveDocument));

但是,如果您不使用这种反应式框架, CanExecuteChanged每当您要刷新命令状态时,都应调用RaiseCanExecuteChanged引发CanExecuteChanged事件。 没有“更清洁”的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM