備忘録的なSomething

素敵なAnything

【C#,WPF】プログラム作成のルーティン(MVVM)

WPFはやっぱりMVVMでつくりたいですが、最初のプロジェクトをつくるところがちょっと面倒です。
本当はプロジェクトテンプレート的なものにすればいいと思うんですが、とりあえずいつもの手順を書き出してみます。

プロジェクトをつくる

  • Visual Studio 2019 を起動し、新しいアプリケーションの作成→WPFアプリケーション(C#) を選択します。
  • 適当に情報を入力してプロジェクトをつくります。今回は WPFTestApp という名前にしています。
  • プロジェクトができたら、MVVMフレームワークとして、NuGet で ReactiveProperty をインストールします。
    (ReactivePropertyについては こちら

メイン画面をつくる

  • Windows という名前のフォルダをつくります。ここにView, ViewModelを入れることにします。
  • 自動でできた MainWindow.xaml は削除して、Windows フォルダの中に新しくウィンドウ(WPF) MainWindow.xaml を追加します。※もちろん別の名前でもよいです
  • MainWindow.xaml で、Heidht, Width の指定をSizeToContent="WidthAndHeight"に書き換えます。(とりあえず)
  • ViewModelとしてクラス MainWindowViewModel.cs も追加し、アクセス修飾子をpublicにします。また、使わないけど INotifyPropertyChanged を実装します。(メモリリーク対策)
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }
  • MainWindow.xaml.cs は、コンストラクタで MainWindowViewModel を受け取って DataContext にセットするようにします。
        public MainWindow(MainWindowViewModel viewModel)
        {
            InitializeComponent();
            DataContext = viewModel;
        }

メイン画面を開く

デフォルトではエントリポイントに相当する App.xaml から MainWindow.xaml を開く指定になっていますが、C#のコードのほうがいろいろ制御しやすいので、そのように変えます。

  • App.xaml で、StartupUri="MainWindow.xaml"Startup="Application_Startup"に書き換えます。
  • App.xaml.cs で、Application_Startupのイベントハンドラに、MainWindowを開くコードを書きます。
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            var mainWindowVM = new MainWindowViewModel();
            new MainWindow(mainWindowVM).ShowDialog();
        }

まとめ

ここまで作業を行うと、以下のような状態になります。
f:id:kmua:20211113170841p:plain
MainWindow.xaml

<Window x:Class="WpfTestApp.Windows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        SizeToContent="WidthAndHeight">
    <Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace WpfTestApp.Windows
{
    public partial class MainWindow : Window
    {
        public MainWindow(MainWindowViewModel viewModel)
        {
            InitializeComponent();
            DataContext = viewModel;
        }
    }
}

MainWindowViewModel.cs

namespace WpfTestApp.Windows
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

App.xaml

<Application x:Class="WpfTestApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="Application_Startup">
</Application>

App.xaml.cs

using System.Windows;
using WpfTestApp.Windows;

namespace WpfTestApp
{
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            var mainWindowVM = new MainWindowViewModel();
            new MainWindow(mainWindowVM).ShowDialog();
        }
    }
}

バインディングを確認する

MainWindowViewModel から MainWindow にバインディングができることを確認します。

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public ReactivePropertySlim<string> Greeting { get; } = new ReactivePropertySlim<string>("Hello, world!");
    }
<Window x:Class="WpfTestApp.Windows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        SizeToContent="WidthAndHeight">
    <Grid>
        <TextBlock Text="{Binding Greeting.Value}" FontSize="100" />
    </Grid>
</Window>

f:id:kmua:20211113174133p:plain
世界に挨拶できました。

修正履歴:

  • 2021/11/13 ViewModel の INotifyPropertyChanged 実装を追加(メモリリーク対策)