2

WPF を学ぶにあたって、私はたくさんの本や Web サイトを読んできました。私を回避し続けているように見えることの 1 つは、RoutedCommands を適切に接続する方法です。ある記事で、作成者は、XAML ファイルの分離コードには InitializeComponent の呼び出しだけを含める必要があると指摘しました。私はそれを後回しにすることができます。これにより、XAML ファイルは単なるプレゼンテーション ドキュメントに過ぎず、懸念事項の分離に対する私の不浄な渇望が満たされます。

一方、ダブルクリック イベントに対処するサンプルはどれも、コードを書く必要があるようです。コードビハインド ファイルに反復的なコードを含めることから逃れたいというのが私の理解でした (繰り返しますが、私はそれで大賛成です)。メニュー コマンド、ツールバー ボタンのクリックなどについても同様です。

たとえば、ドキュメントを開くコマンドがあるとします。そのコマンドは、[開く] ダイアログを表示してから、ドキュメントを開いてアプリケーション状態にキャッシュする必要があります。(このアプリケーションでは、一度に 1 つのドキュメントでしか作業できません。) ユーザーは、次のいずれかの方法でこのコマンドを呼び出すことができます。

  • メニューから [ファイル] → [開く] を選択します。
  • Ctrl+O を入力します。
  • ツールバーの [開く] ボタンをクリックします。

Web 上のほとんどのソースを信頼する場合、少なくとも 2 つの Click イベント ハンドラーを記述してコマンドを呼び出し、コード ビハインド ファイルを汚染する必要があります。私には、コマンドを持つ目的を無効にするように思えます。XAMLでコマンドをこれらのものに宣言的にバインドする方法があり、実行できない場合はコマンドを無効にすることさえできるとどこかで読んだと思いました。しかし、今はそれを見つけることができないようであり、それを行う方法の適切な例でもありません。

誰かが私にこれを説明してもらえますか? この時点で、すべてがブードゥー教と榴散弾のように見え始めています。

4

2 に答える 2

4

コマンドで分離コードを回避する通常の方法は、RoutedCommands を回避することです。MVVM (Model-View-ViewModel) テーマのさまざまなバリエーションでは、ICommand のカスタム実装を使用する傾向があります。UI の DataContext に配置される ViewModel クラスを記述します。この ViewModel は ICommand 型のプロパティを公開し、それらのコマンド プロパティはデータ バインディングを通じてメニュー項目、ボタンなどに接続されます。(そして、これは通常、何度も何度も使用される ICommand の 1 つの実装です。RelayCommand、DelegateCommand、または DelegatingCommand のいずれかを Web で検索すると、パターンが表示されます。これは基本的に、デリゲートのラッパーとしての ICommand であり、オプションで有効な/無効。)

このイディオムでは、ApplicationCommands.Open などの組み込みコマンドを使用することはほとんどありません。これらの唯一の実際の用途は、フォーカスに依存するコマンドをコントロールによって本質的に処理する場合です。たとえば、TextBox には、編集、コピー、貼り付けなどのコマンド処理が組み込まれています。これは完全なカスタム コントロールであり、カスタム コントロールには実際にはコード ビハインドがなく、すべてコードであるため、コード ビハインドの問題が回避されます。(Xaml は実際には完全に別のオブジェクトである Template にあり、実際にはコントロールの一部ではありません。) いずれにせよ、それはコードではありません。コマンドをサポートする方法を既に知っているコントロールがあるので、ここで Xaml 内に完全に保持できます。

コマンド ルーティングは、さまざまな編集コントロールに関連付けられた 1 セットのメニュー項目を配置できるため、この特定のシナリオでは興味深いものです。ルーティング システムは、フォーカスがどこにあるかに基づいて、どのテキスト ボックス (またはその他のもの) がコマンドを処理するかを判断します。それが望ましくない場合、コマンド ルーティングはおそらくあまり役​​に立たないでしょう。

ただし、分離コードに実際にコードを配置する必要がある場合にどうするかという、より大きな問題があります。カスタムの ICommand 実装を使用する場合、通常、コマンドはそのシナリオの例ではありませんが (奇妙な例外があります)、もう少し興味深いユーザー入力イベントがあります。あなたはダブルクリックについて言及していますが、また、何らかの珍しい対話を行っている場合は、マウスの上下などの操作が必要になる傾向があります。

この場合、通常のアプローチは、コードを分離コードに入れることですが、イベント ハンドラーごとに 1 行に抑えるようにします。基本的に、分離コードはビューモデルの関連するメソッドを呼び出すだけで、それが実際にイベントを処理します。

これのすばらしいところは、自動化されたテストを非常に簡単に作成できることです。UI の特定の部分に入るマウスをシミュレートしたいですか? 単体テストの UI 自動化フレームワークをいじる必要はありません。関連するメソッドを直接呼び出すだけです。

于 2010-11-05T00:45:51.897 に答える
2

WPF でコマンドを実行するのは非常に面倒ですが、IsEnabled の更新の問題は解決されます。これが正規の例です。定型文の量を減らすための組み込みの一般的なコマンドがたくさんあるため、ステップ 1 はオプションです。

ステップ 1. (オプション) 静的クラスでコマンドを作成する

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;

namespace WpfApplication1
{
   public static class Commands
   {
      public static RoutedCommand WibbleCommand = new RoutedUICommand
      (
         "Wibble",
         "Wibble",
         typeof(Commands),
         new InputGestureCollection()
            {
               new KeyGesture(Key.O, ModifierKeys.Control)
            }
      );
   }
}

ステップ 2: xaml でコマンド バインディングを宣言する

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
   <Window.CommandBindings>
      <CommandBinding
         Command="{x:Static local:Commands.WibbleCommand}"
         Executed="WibbleCommandExecuted"
         CanExecute="WibbleCommandCanExecute"
      />
   </Window.CommandBindings>

ステップ 3: コントロール (メニュー項目、ボタンなど) を接続する

ここでの長いバインディングは、デフォルトでは Button がコマンド テキストを使用しないという事実を修正するためのものです。

  <Button Command="{x:Static local:Commands.WibbleCommand}" Width="200" Height="80">
     <TextBlock Text="{Binding Path=Command.Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}">
     </TextBlock>
  </Button>

ステップ 4: コードビハインドで Execute および CanExecute のハンドラーを実装する

CanExecute には注意してください。これはかなり頻繁に呼び出されるため、ここで高価なことをしないようにしてください。

  private void WibbleCommandExecuted(object sender, ExecutedRoutedEventArgs e)
  {
     MessageBox.Show("Wibbled!");
  }

  private void WibbleCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
     e.CanExecute = DateTime.Now.Minute % 2 == 0;
  }
于 2010-11-04T22:26:38.133 に答える