[C#/WPF]ビューモデルからビューのメソッドを呼ぶ代わりに、EventTriggerでMouseDown等のイベントを拾ってビューのメソッドを呼ぶ


やりたいこと

以前の記事で、View(コードビハインド)でしかできないことがあるので、コードビハインドに書いたメソッドを、ViewModelから呼ぶ、ということを実験した。

ViewModelのタイミングで(例えば、ボタンを押したときにプロパティの値をif分で判定して、条件を満たすときだけ)Viewのメソッドを呼ぶ、とかだと以前の記事のやり方をしないといけなさそうだが、だた単純に「ボタンを押されたときにViewのメソッドを呼びたい」という場合なら、もっと簡易的に簡単にできる方法を思いついたので備忘メモする。

やり方

下記を使う。

  • Interaction.Triggers
  • EventTrigger
  • CallMethodAction

やることとしては、

  • Interaction.TriggersとEventTriggerで何をトリガーにするか決めて
  • CallMethodActionでコードビハインドに書いたメソッドを呼ぶ

だけ。

サンプルコード

MainWindow.xaml
<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:i="http://schemas.microsoft.com/expression/2010/interactivity" 
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        Name="Root">
    <Grid>
        <TextBlock Margin="40" Background="Yellow">この黄色いTextBlockを押すと、トリガーが発火します
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseDown">
                    <!-- ViewModelのメソッドの呼び方1 -->
                    <i:InvokeCommandAction Command="{Binding func}"/>
                    <!-- ViewModelのメソッドの呼び方2(ViewModelの中のメソッドを呼ぶ) -->
                    <ei:CallMethodAction TargetObject="{Binding}" MethodName="EventFunc"/>
                    <!-- ViewModelのメソッドの呼び方3(コードビハインドの中のメソッドを呼ぶ) -->
                    <ei:CallMethodAction TargetObject="{Binding ElementName=Root}" MethodName="CodeBehindFunc"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBlock>
    </Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new ViewModel();
        }

        public void CodeBehindFunc()
        {
            MessageBox.Show("CallMethodAction でトリガー発火(コードビハインドのメソッド)");
        }
    }
}
ViewModel.cs
using System.Windows;
using System.Windows.Input;

namespace WpfApp1
{
    class ViewModel
    {
        public ICommand func { get; set; }

        public ViewModel()
        {
            func = new DelegateCommand(
                () =>
                {
                    MessageBox.Show("トリガー発火");
                    return;
                });
        }

        public void EventFunc()
        {
            MessageBox.Show("CallMethodAction でトリガー発火(ViewModelのメソッド)");
        }
    }
}

説明

今回やりたかったのが、MainWindow.xamlの中の「ViewModelのメソッドの呼び方3(コードビハインドの中のメソッドを呼ぶ)」のところ。

<i:EventTrigger EventName="MouseDown">で、TextBlockでマウスが押されたときに、コードビハインドに書かれたメソッド「CodeBehindFunc」を呼んでいる。

この書き方であれば、「EventTrigger」で表現できるイベントであれば、「ViewModelのICommand」「ViewModelクラスのメソッド」「コードビハインドのメソッド」のどれでも呼べる。

EventTriggerのEventNameを変えてやれば、クリックしたときだけでなく、表示時に一回なにかする、とかもできる。

また、サンプルのように、<i:EventTrigger EventName="MouseDown">の中に書いたTriggerActionを継承したクラスのオブジェクト(CallMethodActionやInvokeCommandActionなど)をセットしてやれば、上から順番に実行してくれるので、ViewModelのICommandとコードビハインドのメソッドの両方を実行するとかもできそう。(それが良いかどうかは別問題)

備考

書き終わってから、「コントロールのイベントハンドラを追加して、そこに処理を書けばこんなことしなくて済むのでは?」と思ってしまった。

もしかすると、i:EventTriggerよりも、PropertyChangedEventTriggerを使って、ViewModelのプロパティが変わった時に、コードビハインドのメソッドを呼べる、とかの方が、真価なのかもしれない。

コード

関連記事

[C#/WPF]ビューモデルからビューのメソッドを呼ぶ
https://qiita.com/tera1707/items/158db72db21b17a1d9c5

【C#/WPF】EventTriggerを使って、Buttonでなくてもクリック時のCommandをかけるようにする
https://qiita.com/tera1707/items/7ecde6e97a19437cbf72

参考

WPF4.5入門 その58「Behavior」
https://blog.okazuki.jp/entry/2014/12/21/205558