2017-02-23 19 views
0

MVVMを使用してリボンでCanExecuteを呼び出す方法 各タブ項目でタブコントロールと編集コントロールを持つシンプルなアプリがあります。これはViewModelにリンクされており、コマンド(ルーティングされていないもの)も定義されています。メニューで使用されます。これまでにすべてがうまくいきました。MVVMを使用してリボンでCanExecuteを強制呼び出しする方法

ここでは、いくつかの条件(たとえば、[保存]または[ワードラップ]は、タブ項目がアクティブになっている場合のみ有効になります)で有効になっているボタン付きのリボンを使用します。残念ながら、すべてのコマンドの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(); 
    } 
} 

コマンド:

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)); 
    } 
} 

N ewFileコマンド実行:

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> 

は、何のファイルが、そこ開かれません。リボンのボタンも同様です。 NewFileCommandを実行すると、コントロールで新しいタブが作成されますが、WordWrapコマンドは無効のままです。

私は、勧告はのActiveDocumentセッターでRaiseCanExecuteChangedを呼び出すことで、この記事を見つけたWPF MVVM command canexecute enable/disable button

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

これは動作しますが、それは私には非常に奇妙に聞こえます。なぜプロパティは、それを使用するすべてのコマンドを知っている必要があり、それらを世話する必要がありますか?この問題の解決策はありますか?

+0

CommandManager.RequerySuggested? – Aybe

答えて

0

なぜプロパティは、それを使用するすべてのコマンドを知っている必要があり、それらを処理する必要がありますか?

これは、ビューモデルクラスが実装するアプリケーションロジックの一部です。このクラスは、コマンドのCanExecuteメソッドを呼び出すタイミングを知ることはできません。

だから、実際にあなたがしたい時はいつでも、コマンド/ボタンの状態をリフレッシュするためにWPFを伝えるためにCanExecuteChangedイベントを発生させる必要があり、これを行う方法はDelegateCommandRaiseCanExecuteChangedメソッドを呼び出すことです。コマンドを更新するたびに、いつでも更新することができます。

この問題の解決策はありますか?

ReactiveUIをご覧ください。これは、簡単にコマンドをリフレッシュすることができ、反応性と反応性のコマンドの概念を持っているMVVMライブラリである時はいつでもプロパティの変更:

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

しかし、あなたは、このような反応性フレームワークを使用していない場合は、呼び出す必要がありますRaiseCanExecuteChangedを使用して、コマンドのステータスを更新するたびにCanExecuteChangedイベントを発生させます。これを行うための「クリーン」な方法はありません。

関連する問題