時々、複雑な方法を何度も使用しましたが、タスクを実行するための最も簡単な方法を忘れていました。
コマンドバインディングの方法は知っていますが、常に同じ方法を使用します。
ICommandインターフェイスを実装するクラスを作成し、ビューモデルからそのクラスの新しいインスタンスを作成します。バインディングはチャームのように機能します。
これは、コマンドバインディングに使用したコードです
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
testCommand = new MeCommand(processor);
}
ICommand testCommand;
public ICommand test
{
get { return testCommand; }
}
public void processor()
{
MessageBox.Show("hello world");
}
}
public class MeCommand : ICommand
{
public delegate void ExecuteMethod();
private ExecuteMethod meth;
public MeCommand(ExecuteMethod exec)
{
meth = exec;
}
public bool CanExecute(object parameter)
{
return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
meth();
}
}
しかし、私はこれを行う基本的な方法を知りたい、サードパーティのdll、新しいクラスの作成はありません。単一のクラスを使用して、この単純なコマンドバインディングを実行します。実際のクラスはICommandインターフェイスから実装され、作業を行います。
プリズム すでに提供 Microsoft.Practices.Prism.Commands.DelegateCommand
サードパーティと見なされているかどうかはわかりません。少なくともそれは公式であり、MSDNに文書化されています。
コピー、貼り付けなどの一部のネイティブビルトインコマンドは、ICommandインターフェイスを実装します。私見それは、オープン(拡張用)/クローズ(変更用)の原則に従っています。独自のコマンドを実装できるようにします。
WPF Commandingが here を文書化したように、抜粋...
WPFには一連の定義済みコマンドが用意されています。 Cut、BrowseBack、BrowseForward、Play、Stop、Pauseなど。
コマンドライブラリクラスのコマンドがニーズを満たさない場合は、独自のコマンドを作成できます。カスタムコマンドを作成するには2つの方法があります。 1つ目は、最初から始めて、ICommandインターフェイスを実装することです。他の方法、およびより一般的なアプローチは、 RoutedCommand または RoutedUICommand を作成することです。
私は最初にRoutedCommandモデルを試しましたが、ICommandの実装に終わりました。
XAMLバインディングのサンプル
<CommandBinding Command="{x:Static custom:Window1.CustomRoutedCommand}"
Executed="ExecutedCustomCommand"
CanExecute="CanExecuteCustomCommand" />
RoutedCommandはRoutedEventと同じです。これは、ボタンの「Clicked」イベントハンドラーのようです。それは目的を果たします:アプリのロジックをビューから分離しますが、いくつかのDependencyPropertyまたは分離コードをアタッチする必要があります。
個人的には、ICommandを実装するだけでより快適に感じる。
私は、RelayCommandが組み込まれているMVVM lightフレームワークを使用する傾向があります。
ICommandプロパティをビューモデルに追加してから、それにリレーコマンドを割り当てます。
ICommand ClickMeCommand {get;set;}
private void InitCommands()
{
ClickMeCommand = new RelayCommand(()=>HasBeenClicked=true);
//or
ClickMeCommand = new RelayCommand(ClickMeEvent);
}
public void ClickMeEvent()
{
HasBeenClicked=true;
}
Xamlでは通常のバインディングを使用します:-
<Button Content='Push me' Command='{Binding ClickMeCommand}' />
新しいクラスを作成したくない場合は、routedコマンドを使用してください。ここに小さなスニペットがあります。ルーティングされたコマンドをSaveとして作成し、それをwindowのコマンドバインディングにバインドし、button.and voila!.....からコマンドを発生させます。
public partial class Window1 : Window
{
public static readonly RoutedCommand Foo = new RoutedCommand();
public Window1()
{
InitializeComponent();
}
void Foo_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// The Window gets to determine if the Foo
// command can execute at this time.
e.CanExecute = true;
}
void Foo_Executed(object sender, ExecutedRoutedEventArgs e)
{
// The Window executes the command logic when the user wants to Foo.
MessageBox.Show("The Window is Fooing...");
}
}
<Window.CommandBindings>
<CommandBinding
Command="{x:Static local:Window1.Foo}"
CanExecute="Foo_CanExecute"
Executed="Foo_Executed"
/>
私はあなたの問題をよく理解したと思います。
PS:コマンドデザインパターンの必要性は、実行のロジックとコマンドの呼び出し元を分離することです。そのため、コマンドクラスはロジックをカプセル化する方法です。
VSでコードを実行せずに行うことから判断すると、問題はInitializeComponent
(レンダリングXAML)を呼び出してから、DataContext
に値を設定せずにtest
を設定することです。プロパティと最後にプライベートメンバーを設定します。 UIは、コマンドがnullではなくなったことにどのように気づくはずですか(プロパティで最後に見つかった値でした)。
なぜそのように怠惰にコマンドをインスタンス化しないのですか?
_public ICommand test
{
get
{
if(testCommand== null)
{
testCommand= new MeCommand(processor);
}
return testCommand;
}
}
_
これにより、必要に応じてすぐに表示され、変更通知は必要ありません(実行時に後でそのコマンドを変更しない限り)。
ちなみに、コマンドバインディングシナリオでコードが実行されていないように感じる箇所がいくつかあります。
1)CanExecute()はfalseを返します。trueを返すか、ビジネスケースに従って評価します。
2)CanExecuteの条件が変化するため、trueが返されますが、呼び出されません。CommandManagerクラスを使用してコマンドをフックできます。 ここ を参照してください。これを試す前に、1)が解決されていることを確認してください。これにより、UIがCanExecute
をかなり頻繁に再クエリし、それでも不十分な場合は、メソッドCommandManager.InvalidateRequerySuggested()
を明示的に呼び出します。
3)あなたの拘束力は間違っています。バインディングコードを次のように変更します。
_Command="{Binding Path=test, PresentationTraceSources.TraceLevel=High}"
_
これにより、バインディングの値がプルまたはプッシュされるたびに出力ウィンドウがスパムされます。 「最終値を使用する」という用語に注意してください。 nullの場合、コマンドは(まだ)想定されている場所にありません。
まあ、あなたはほとんどそれを持っています。 CanExecute()がtrueを返すことを確認してください。それ以外の場合、コードは実行されません。 (私は実際にはそうなると思いますが、それでも)。また、「NotifyPropertyChanged( 'yourCommand')」を必ず追加してください
public ICommand test
{
get { return testCommand; }
set { testCOmmand = value; NotifyPropertyChanged("test"); }
}
ここへ。
または、test = new MeCOmmand();を実行することもできます。 DataContext = this;
WPFウィンドウコンストラクターでキーボードショートカットをリンクするには、デリゲートを使用してバインディングを追加し、キージェスチャーに関連付けます。
public YourWindow() //your constructor
{
...
//bind keyboard command shortcuts
InputBindings.Add(new KeyBinding( //add a new key-binding, bind it to your command object which takes a delegate
new WindowCommand(this)
{
ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
}, new KeyGesture(Key.P, ModifierKeys.Control)));
...
}
実行デリゲートを受け取り、それに設定されたメソッドを起動する単純なWindowCommandクラスを作成します。
public class WindowCommand : ICommand
{
private MainWindow _window;
public Action ExecuteDelegate { get; set; }
public WindowCommand(MainWindow window)
{
_window = window;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (ExecuteDelegate != null)
{
ExecuteDelegate();
}
else
{
throw new InvalidOperationException();
}
}
}