2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WindowsフォームのForm_Load()メソッドをWPFのMVVMで置き換える

Posted at

はじめに

WindowsフォームアプリからWPFアプリに移行すると
MVVMの呪縛にかかり、コードビハインドに何も書けなくなる (※ 個人差があります)

たとえば、フォーム(WPFではウィンドウ)を読み込んだ直後に何かの初期化処理をしたい場合など・・

  • フォームアプリでは、フォームをダブルクリックすると自動的にForm_Load()メソッドが作られるので、そこに処理を書けばよい
  • 一方、WPFアプリでは・・・Windowをいくら高速クリックしてもメソッドは生成されない😣

これは困ったことなので、どうしたらいいかを書く

目次のようなもの

<< ゴール >>
WPFアプリにおいて、ウィンドウが読み込まれた直後になんらかの初期化メソッドを呼ぶ
(ただし、コードビハインドには何も追記しない)

流れとしては、まずフォームアプリで所望の動作をさせてみる。
そのあと、WPFアプリで同じことをする

フォームアプリ

おなじみのWindowsフォームアプリ

プロジェクトを立ち上げ直後は、まっさらなフォームが表示される

1.png

フォームの上でマウスを左ダブルクリックすると、メソッドが生成される

// フォームをダブルクリックすると、このメソッドが生成される
private void Form1_Load(object sender, EventArgs e)
{
    // フォームをロードし終えたときの動作
}

あとはこの関数の中に、初期化などの処理を書けばいい

フォームアプリについては以上

WPFアプリ

つづいてWPFアプリの2パターン
コードビハインドを使う場合と、使わない場合

Case1: MVVMの呪縛に囚われていない場合

WindowタグにLoaded="Window_Loaded"を追加する

XAMLファイル
 <Window x:Class="MyWpfApp.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:MyWpfApp"
         mc:Ignorable="d"
         Title="MainWindow" Height="450" Width="800"
+        Loaded="Window_Loaded">
     <Grid>
 
     </Grid>
 </Window>

コードビハインドに同名のメソッドを追加し、中身を書けば終わり

コードビハインド
 public partial class MainWindow : Window
 {
     public MainWindow()
     {
         InitializeComponent();
     }
 
+    private void Window_Loaded(object sender, RoutedEventArgs e)
+    {
+        // ここに処理を書く
+    }
 }

Tips) IDEの補助機能を有効につかう

ViewでLoaded="まで入力すると、「新しいイベントハンドラー」ポップアップが表示される
このポップアップをクリックすると、コードビハインドにメソッドが自動で追加される

image.png

おまけ)ほかのイベントについて

ウィンドウのプロパティを開いて右上の⚡を押せば、使えるであろうたくさんのイベントが表示される

3.png

Case2: MVVMの呪縛でコードビハインドに何も書けない場合(本題)

MVVMに従う場合、何はともあれViewModelのファイルを作成する
今回はMainWindowViewModel.csをプロジェクトに追加するものとする

NuGetで必要なライブラリをインストールする

Xaml.Behaviors.Wpfをインストールする
これはViewのイベントとViewModelに定義したコマンドを紐づけるためにつかう
詳しくは・・・System.Windows.Interactivity.dll から Xaml.Behaviors.Wpf へ

4.png

ReactivePropertyをインストールする
これはコマンドを簡単に書くためのライブラリである

5.png

View (XAML) を編集する

さきほどのLoaded=...イベントは削除する。
代わりにiに関わるものを追加する。

新たに追加した行を見ると

  • イベントトリガーという名前からして、なにかのイベントに連動して処理が行われそうだとわかる
  • Loadedと書かれており、きっとウィンドウがロードされたイベントを指定していそうだとわかる
  • Command="{Binding ...}"でViewModelのコマンドに連動しそうだとわかる
MainWindow.xaml

 <Window x:Class="MyWpfApp.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:MyWpfApp"
+        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
         mc:Ignorable="d"
         Title="MainWindow" Height="450" Width="800"
-        Loaded="Window_Loaded">
+     <i:Interaction.Triggers>
+         <i:EventTrigger EventName="Loaded">
+             <i:InvokeCommandAction Command="{Binding Loaded_Command}"/>
+         </i:EventTrigger>
+     </i:Interaction.Triggers>
     <Grid>
 
     </Grid>
 </Window>

ViewModelを編集する

MainWindowViewModel.cs
using Reactive.Bindings;
using System.ComponentModel;

namespace MyWpfApp
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        // Viewにバインドするコマンド
        public ReactiveCommand Loaded_Command { get; } = new();

        public MainWindowViewModel() // コンストラクター内で
        {
            // ボタンが押された時の動作を定義する
            Loaded_Command.Subscribe(()=> System.Diagnostics.Debug.WriteLine("Loaded !!"));
        }

        // これはひとまず気にしないでいい
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

このまま実行しても"Loaded !!"とは表示されない

最後にもう1度、View (XAML) を編集する

ViewとViewModelをつなぐために、Viewを編集する
DataContextMainWindowViewModelを指定する

MainWindow.xaml

 <Window x:Class="MyWpfApp.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:MyWpfApp"
         xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
         mc:Ignorable="d"
         Title="MainWindow" Height="450" Width="800">
+    <Window.DataContext>
+        <local:MainWindowViewModel/>
+    </Window.DataContext>
     <i:Interaction.Triggers>
         <i:EventTrigger EventName="Loaded">
             <i:InvokeCommandAction Command="{Binding Loaded_Command}"/>
         </i:EventTrigger>
     </i:Interaction.Triggers>
     <Grid>
 
     </Grid>
 </Window>

なお、MVVMの呪縛に囚われていない場合は
コードビハインドでthis.DataContext = new MainViewModel();としてもよい。

まとめ

ことLoadedイベントだけに関してみれば、WinFormのほうが何も考えずにできてよいかもしれない

しかし、ひるがえってみれば、LoadedのためだけにNuGetを通してわざわざ2つのライブラリをインストールするWPFに対しては、なにかこの先大きなことをしてくれるのではないかと期待してしまう

というまとめ。

2
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?