簡體   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