Josh Smith のUsing RoutedCommands と WPF の ViewModel、vbfox のUsing CommandBindings in MVVM、および XAML で CommandBinding を宣言するときに RoutedUICommand Text と KeyGesture を指定するための独自のスピンを組み合わせました。
利点:
- Smith のように ViewModel を明示的に参照する必要はありません。彼はやる:
xmlns:vm="clr-namespace:VMCommanding.ViewModel"
...
<UserControl.CommandBindings>
<jas:CommandSinkBinding
Command="vm:CommunityViewModel.KillAllMembersCommand" />
</UserControl.CommandBindings>
MVVM Light Toolkit のLocator クラスを使用して間接的に ViewModel を取得しているので、これは必要ありません。また、これは内部で通常の CommandBinding を作成しているため、Binding を使用してコマンドを指定することはできません (CommandBinding は DependencyObject から継承されません)。vbfox は、独自の CommandBindings クラスを作成することで、この問題を回避しています。
- vbfox のように個別の ICommand を作成する必要はありません。彼はやる:
<f:Mvvm.CommandBindings>
<f:MvvmCommandBindingCollection>
<f:MvvmCommandBinding
Command="f:MyRoutedCommands.SomeRoutedCommand"
Target="{Binding MyCommandInViewModel}"
CanExecuteChangedSuggestRequery="True" />
</f:MvvmCommandBindingCollection>
</f:Mvvm.CommandBindings>
そのため、RoutedUICommand をどこか (View ディレクトリ内?) で作成し、ViewModel で別の ICommand を作成する必要があります。
代わりに、ViewModel に ToggleReadComand を実装するだけで済みます。
_commandSink.RegisterCommand(_toggleReadCommand,
p => CanToggleRead,
p => ToggleRead());
...
public static readonly RoutedUICommand _toggleReadCommand =
new RoutedUICommand();
public static RoutedUICommand ToggleReadCommand
{
get { return _toggleReadCommand; }
}
private bool CanToggleRead
{
get { return Files.CurrentItem != null; }
}
private void ToggleRead()
{
ToggleFileInfoFields(f => f.Read = !f.Read);
}
そして、私はそれを次のように使用します:
<cmd:Mvvm.CommandBindings>
<cmd:MvvmCommandBindingCollection>
<cmd:MvvmCommandBinding Command="{Binding ToggleReadCommand}"
Gesture="Toggle _Read|Ctrl+R" />
</cmd:MvvmCommandBindingCollection>
</cmd:Mvvm.CommandBindings>
...
<MenuItem Header="_View">
<MenuItem Command="{Binding ToggleReadCommand}" />
</MenuItem>
RoutedUICommands を使用するすべての利点が得られます。コマンド テキストはショートカット キー (サポートの無効化を含む) と共に MenuItem に自動的に表示され、メニュー項目の UI (または ToggleReadCommand にバインドするその他のコントロール) は XAML を介して 1 か所で指定されます。私のビューでは (別の CommandBindings と InputBindings はもうありません)、それでも自分の Command コードをそれが属する ViewModels に置くことができます。
ここに私の質問があります。まず、これを実行できるようにしたいと思います (通常の Window.CommandBindings の宣言方法をエミュレートします):
<cmd:Mvvm.CommandBindings>
<cmd:MvvmCommandBinding Command="{Binding ToggleReadCommand}"
Gesture="Toggle _Read|Ctrl+R" />
</cmd:Mvvm.CommandBindings>
MvvmCommandBindingCollection を明示的に指定する必要がないようにするにはどうすればよいですか?
Gesture="Toggle _Read|Ctrl+R"
ジェスチャ サポートは機能しますが、Visual Studio 2012 XAML エディターの下に「オブジェクト参照がオブジェクトのインスタンスに設定されていません」というエラーが表示されますか? vbfox の MvvmCodeBinding.cs に以下を追加しました。
public static readonly DependencyProperty GestureProperty =
DependencyProperty.Register(
"Gesture",
typeof(String),
typeof(MvvmCommandBinding),
new PropertyMetadata(null, OnGestureChanged));
static void OnGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MvvmCommandBinding) d).OnGestureChanged((String) e.NewValue);
}
private string _gesture;
void OnGestureChanged(String newValue)
{
_gesture = newValue;
var command = _commandSinkBinding.Command as RoutedUICommand;
if (command == null)
return;
CreateGesture(command, newValue);
}
private static void CreateGesture(RoutedUICommand command, string gestureStr)
{
if (string.IsNullOrEmpty(gestureStr) || command == null)
return;
var fields = gestureStr.Split(new char[] {'|'}, 2);
if (fields.Length == 1)
command.Text = gestureStr;
else {
command.Text = fields[0];
var gesture = fields[1];
var kgc = new KeyGestureConverter();
var kg = kgc.ConvertFromString(gesture) as System.Windows.Input.KeyGesture;
command.InputGestures.Add(kg);
}
}
[Bindable(true)]
public String Gesture
{
get { return (String) GetValue(GestureProperty); }
set { SetValue(GestureProperty, value); }
}
(また、vbfoxCommandBinding _commandBinding
を Smith のものに変更CommandSinkBinding _commandSinkBinding
し、コマンドの前にジェスチャーが作成された場合に備えて、OnCommandChanged() から CreateGesture() を呼び出します)。XAML エディターのエラー表示を取り除くにはどうすればよいですか?
最後に、これはややフランケンシュタインの怪物です。あらゆる種類の重複機能があると確信しています。vbfox は、Smith の VMCommanding を使用しないように自分のコードを考え出したと明確に述べています。さらに、MVVM Light Toolkit の使用から始めました。私の現在のものは、私のアプローチが機能することを示すための「概念実証」にすぎません。
すでにどこかに実装されているこれらすべてを行うためのより良い方法があるかどうか誰かが知っていますか? XAML で RoutedUICommand を (Binding 経由で) 1 行で宣言できるようにしたいと考えています。これにより、Text と Gesture も設定されます (別の CommandBinding と InputBinding は必要ありません)。そして、実際の RoutedUICommands を ViewModel に実装したいと考えています。
そうでない場合は、これら 3 つのパッケージをよく調べてリファクタリングを行うことで、多くのクラフトを排除できると思います。