🌊

【C#】WPFのMVVMでEventTriggerを使用して各種イベントをバインドする

2022/09/07に公開約6,000字

はじめに

前回の記事で WPF で MVVM をフレームワーク無しで構築してみました。
※こちらの記事はその続きになりますので良ければ参照ください。

https://zenn.dev/takuty/articles/b12f4011871058

構築したアプリケーションではボタンをクリックするとTextBoxの文字列が結合されるという処理でしたが、今のままではクリック時の処理を処理しか実装することができません。

そこで Microsoft が出しているBehaviorsというライブラリでEventTriggerを使用して
クリック以外の各種イベントに対して処理を実装しViewModelへバインドするようアプリケーションを構築していこうと思います。

環境

  • Windows10
  • .NET6.0
  • Visual Studio Community 2022
  • WPF

Behaviors インストール

Behaviors を Nuget からインストールしていきます。
Nuget パッケージマネージャーで「Behaviors」と検索すると一番上に出てくるのでこちらをインストールします。
behaviorsインストール

インストール完了後、BihaviorsViewで使用していきたいので参照を追加します。ここでは MainWindow.xaml 上部にxmlns:i="http://schemas.microsoft.com/xaml/behaviors"を追加しました。

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:local="clr-namespace:WpfApp1"
        xmlns:vm="clr-namespace:WpfApp1.ViewModel"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="490">

EventTrigger 実装

EventTrigger を使用する準備ができたので、各種イベントを実装していくのですが
今回は、

  • Window ロード後に別 Window を開く
  • ComboBox の選択を変更すると選択した値が TextBlock に表示される

の 2 点を実装したいと思います。

Window ロード後に別 Window を開く

ロード後に Command を実行するには以下のように記述します。

MainWindow.xaml
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

これでMainWindowLoadedイベントに対してLoadedCommandを実施するようになります。

LoadedCommand の実装内容は以下のような感じです。

using System;
using System.Windows.Input;
using WpfApp1.ViewModel;

namespace WpfApp1
{
    public class LoadedCommand : ICommand
    {
        MainWindowViewModel m_vm;
        public LoadedCommand(MainWindowViewModel vm)
        {
            m_vm = vm;
        }

        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        /// <summary>
        /// コマンドが利用可能かどうか
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public bool CanExecute(object? parameter)
        {
            return true;
        }


        /// <summary>
        /// 実行時の処理
        /// </summary>
        /// <param name="parameter"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void Execute(object? parameter)
        {
            var dlg = new LoadedWindow();
            dlg.ShowDialog();
        }
    }
}

これをViewModelでバインドすることでMainWindowがロード後にExcuteが走り、別 Window が表示されるようになります。

ComboBox の選択を変更すると選択した値が表示される

こちらではまずMainWindowViewComboBoxを以下のように作成します。

MainWindow.xaml
<ComboBox
    HorizontalAlignment="Left"
    VerticalAlignment="Top"
    VerticalContentAlignment="Center"
    Height="25"
    Width="170"
    Margin="150,400,0,0"
    SelectedItem="{Binding SelectedItem}">
    <ComboBoxItem Content="ぶどう" />
    <ComboBoxItem Content="りんご" />
    <ComboBoxItem Content="みかん" />
    <ComboBoxItem Content="もも" />
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>

ComboBoxの選択変更後に処理を走らせたいのでSelectionChangedに対してEventTriggerを設定します。
また、選択した値も取得する必要があるのでSelectedItemViewModeへバインドしています。

SelectionChangedCommand の実装内容は以下のような感じです。

using System;
using System.Windows.Input;
using WpfApp1.ViewModel;

namespace WpfApp1
{
    public class SelectionChangedCommand : ICommand
    {
        MainWindowViewModel m_vm;
        public SelectionChangedCommand(MainWindowViewModel vm)
        {
            m_vm = vm;
        }

        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        /// <summary>
        /// コマンドが利用可能かどうか
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public bool CanExecute(object? parameter)
        {
            return m_vm.SelectedItem != null;
        }

        /// <summary>
        /// 実行時の処理
        /// </summary>
        /// <param name="parameter"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void Execute(object? parameter)
        {
            object content = m_vm.SelectedItem.Content;
            if (content != null)
            {
                m_vm.ResultValue = content.ToString();
            }
        }
    }
}

これでSelectedItemの値をTextBlockへ表示することができます。

実装後動作

画面ロード完了後

MainWindowが起動すると自動的に別 Window が表示されました。
EventTriggerLoadedイベントが走っていることが確認できます。
画面ロード完了後

ComboBox 選択変更

はじめはComboBoxは何も選択されておらずTextBlockも空白です。
ComboBox変更前

ComboBoxで「ぶどう」を選択してみます。
ComboBoxぶどう変更後

TextBlockに「ぶどう」が表示されたので次に「みかん」を選択してみます。
ComboBoxみかん変更後

TextBlockの値が「みかん」に変更されました。これでEventTriggerLoadedイベントが走っていることが確認できました。

まとめ

今回は WPF の MVVM で各種イベントをEventTriggerで処理する方法をお伝えしました。
ここまでできれば簡単なアプリケーションであれば実装できますが、高度なアプリケーションを MVVM でやろうとするとかなり難しいので、その時がフレームワークの使い時かもしれません。
次回以降、MVVM フレームワークも機会があれば紹介していきたいと思います。

参考資料

https://qiita.com/tera1707/items/7ecde6e97a19437cbf72
GitHubで編集を提案

Discussion

ログインするとコメントできます