【WPF】【MVVM】GUIのマウス/キー操作処理をコードビハインドから駆逐する


**駆逐する**は大分盛りました。
BindingやCommandという仕組みがあるのはわかっているが、
マウス操作とかキー操作に対する処理をViewModelで行う方法がわからず、コードビハインドでViewクラスにイベント定義している様なコードを無くそうという程度の内容です。

多くの実装方法で実現できるので、代表的なものや自身がよく使用しているものを記載しています。
誤りや、その他の手法があればコメント頂けると幸いです。


#**準備**
各イベントで呼び出す処理をCommandで定義します。

1. [ツール] → [NuGetパッケージマネージャー] → [ソリューションのNuGetパッケージの管理]
2. Prism.Coreを選択してインストール
3. ViewModelを用意して、ViewのDataContextに設定

**MainWindowViewModel.cs**

```csharp
using Prism.Commands;
using Prism.Mvvm;
using System.Windows.Input;

namespace WpfApp1 {
    public class MainWindowViewModel : BindableBase {

        private ICommand sampleCommand;

        public ICommand SampleCommand {
            get { return this.sampleCommand ?? (this.sampleCommand = new DelegateCommand<object>(Execute, CanExecute)); }
        }

        private bool CanExecute(object param) {
            return true;
        }

        private void Execute(object param) {

        }
    }
}
```

**MainWindow.xaml**

```Xml
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Button x:Name="button1" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"/>
        <TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="10,40,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
    </Grid>
</Window>
```
ViewにはButtonとTextBlockを配置しました。
このSampleCommandを各操作から呼び出してみます。

#**マウスイベント**
####**MouseClick**

#### 1.Commandを使う
Commandにクリックのアクションが割り当たっているようなコントロールはCommandを使うのが良いと思います。
ボタンの場合は普通にCommandが使用できます、CommandParameterで引数も渡せます。

```xml
<Button x:Name="button1" Command="{Binding SampleCommand}" CommandParameter="{Binding ElementName=button1}" 
Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"/>
```  
　
#### 2.ControlTemplateでボタンの外観を好きなコントロールに変更する
TextBlockのようにComamndが使えない場合、ButtonをTextBlockのような外観にしてCommandを使う方法です。
ControlTemplateで柔軟に見た目がカスタマイズできるので、コントロールは見た目ではなく、振る舞いや動作によって決定した方が良いと思います。

```xml
<Button x:Name="button2" Content="TextBlock" Command="{Binding SampleCommand}" CommandParameter="{Binding ElementName=button2}">
    <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
            <TextBlock Text="{TemplateBinding Content}" />
        </ControlTemplate>
    </Button.Template>
</Button>
```
　
#### 3.InputBindings + MouseBindingsを使用する
MouseAction列挙体に定義されている動作なら、この方法で下記のような実装ができます。
個人的には後述するKeyBindingも使用できるので使いやすいです。

```xml
<TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="10,40,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top">
    <TextBlock.InputBindings>
        <MouseBinding Command="{Binding SampleCommand}" CommandParameter="{Binding ElementName=textBlock1}" MouseAction="LeftClick"/>
    </TextBlock.InputBindings>
</TextBlock>
```

***MouseAction列挙体***

|メンバー名|説明|
|:--|:--|
|LeftClick|マウスの左ボタンをクリックします。|
|LeftDoubleClick|マウスの左ボタンをダブルクリックします。|
|MiddleClick|マウスの中央ボタンをクリックします。|
|MiddleDoubleClick|マウスの中央ボタンをダブルクリックします。|
|None|アクションなし。|
|RightClick|マウスの右ボタンをクリックします。|
|RightDoubleClick|マウスの右ボタンをダブルクリックします。|
|WheelClick|マウス ホイールを回転します。|
　
####**MouseDoubleClick**
#### 1.MouseClickの「3.InputBindings + MouseBindingsを使用する」と同様の方法でMouseActionにLeftDoubleClickかRightDoubleClickを定義するとViewModelで補足ができます。

　
####**MouseDown, MouseUp, MouseEnter, MouseLeave, MouseMove**
#### 1.BlendSDKのSystem.Windows.InteractivityのInvokeCommandActionを使用する
プロジェクトにSystem.Windows.Interactivity.dllを参照追加して、Windowタグに「xmlns:i～」の行を追加します。
下記のコード例ではMouseMoveを指定していますが、MouseEnterなどを指定することで対応するイベントがトリガーになります。

```xml
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

・・・中略・・・

<TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="10,40,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseMove">
            <i:InvokeCommandAction Command="{Binding SampleCommand}" CommandParameter="{Binding ElementName=textBlock1}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBlock>
```

MouseMoveではマウス座標が知りたい事があると思うので、
Commandが呼び出すメソッドにマウスのコントロールに対する座標取得例を記載します。

MainWindowViewModel.cs

```csharp
private void Execute(object param) {
    // マウス座標取得
    var element = (System.Windows.IInputElement)param;
    var position = Mouse.GetPosition(element);
}
```


#**キーイベント**

####**KeyDown**
#### 1.InputBindings + KeyBindingsを使用する
KeyBindingにKey+Modifiers、またはGestureを指定すると特定のキー操作からCommandが呼び出せます。
コードではWindowに定義しました。

*Key + Modifier*

```xml
<Window.InputBindings>
    <KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding SampleCommand}" CommandParameter="{Binding ElementName=textBlock1}"/>
</Window.InputBindings>
```

*Gesture*

```xml
<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+C" Command="{Binding SampleCommand}" CommandParameter="{Binding ElementName=textBlock1}"/>
</Window.InputBindings>
```
　
####**KeyUp**
#### 1.MouseDown, MouseUp, MouseEnter, MouseLeave, MouseMoveと同様の方法で、EventNameをKeyUpにすることでトリガーがKeyUpになります。


#**ウィンドウイベント**
####**Loaded、Closed**
#### 1.MouseDown, MouseUp, MouseEnter, MouseLeave, MouseMoveと同様の方法で、EventNameをLoadedやClosedにすることで対応する動作をトリガーにCommandが呼び出されます。

　　
#まとめ
操作別で纏めたかったので手段が重複しています。　　　

本記事内のシンプルな操作程度なら、簡単にViewから追い出すことができますが、
コードビハインドで書かないと難しい場合や、開発効率や可読性からコードビハインドで書いた方がいいんじゃないかなと思う場面はあります。
特別なことがない限り、MVVMで開発する事や、コードビハインドを無くす事が真の目的ではないはずなので、状況に合わせて選択してください！