WPF MVVMサンプル(足し算)
1)概要
WPFでMVVMを使った簡単なサンプルを作ります。
数字を2つ入力してボタンを押すと合計が表示されます。
MVVMライブラリにはWCT MVVM Toolkit(Microsoft.Toolkit.Mvvm)を使用します。
2)環境
- Windows 10 Version 21H1
- .NET Framework 4.7.2 / .NET 5.0
- Visual Studio 2019 Version 16.10.2
- WPF
- Microsoft.Toolkit.Mvvm Version 7.0.2
- ModernWpfUI Version 0.9.4
3)プロジェクトの作成
MVVM Toolkitを使ったプロジェクトはWindows Template Studioでも作れますが、
Template Studioは使わずに一から作っていきます。
.NET Framework 4.7.2の場合
新規プロジェクトでWPFアプリ(.NET Framework)
を選択します。
検索ボックスにwpf
と入力すると探しやすいです。
プロジェクト名にMVVMSample001
と入力し、フレームワークに.NET Framework 4.7.2
を選択します。
.NET 5.0の場合
新規プロジェクトでWPFアプリケーション
を選択します。
プロジェクト名にMVVMSample001
と入力します。
ターゲットフレームワークに.NET 5.0
を選択します。
4)プロジェクトの設定
.NET Framework 4.7.2の場合
[ツール]-[オプション]-[NuGetパッケージマネージャー]-[既定のパッケージ管理方式]でPackageReference
を指定します。
.NET 5.0の場合
プロジェクトをダブルクリックしてcsprojファイルを開きます。
TargetFramework
の値をnet5.0-windows10.0.18362.0
に編集します。
5)NuGetパッケージ追加
以下のパッケージをNuGetで追加します。
Microsoft.Toolkit.Mvvm
ModernWpfUI
6)ファイル準備
MainWindow.xaml
を削除します。
プロジェクト直下にViewModels
フォルダとViews
フォルダを作成します。
ViewModels
フォルダにクラスMainWindowViewModel.cs
を追加します。
Views
フォルダにウィンドウ(WPF)MainWindow.xaml
を追加します。
7)画面デザイン変更
App.xaml
に
xmlns:ui="http://schemas.modernwpf.com/2019"
を追加し、
StartupUri
をViews/MainWindow.xaml
に修正し、
Application.Resources
にResourceDictionary
を追加します。
<Application
x:Class="MVVMSample001.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMSample001"
xmlns:ui="http://schemas.modernwpf.com/2019"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources />
<ui:XamlControlsResources />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml
に
xmlns:ui="http://schemas.modernwpf.com/2019"
と
ui:WindowHelper.UseModernWindowStyle="True"
を追加します。
<Window
x:Class="MVVMSample001.Views.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:local="clr-namespace:MVVMSample001.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
Title="MainWindow"
Width="800"
Height="450"
ui:WindowHelper.UseModernWindowStyle="True"
mc:Ignorable="d">
<Grid>
</Grid>
</Window>
8)ViewModelの作成
MainWindowViewModel.cs
を以下のように編集します。
-
ObservableObject
を継承するようにします。 - 計算に使う2つの入力エリア、
Value1
,Value2
プロパティを定義します。 - 計算結果に使う
Result
プロパティを定義します。 - 計算実行用のコマンド
CalculateCommand
を定義します。 - コンストラクタで計算コマンドを初期化しています。入力エリア2つとも値が入っていないと実行できないようにしています。
-
Value1
,Value2
が更新されるたびにコマンドのNotifyCanExecuteChanged
を呼び出し、コマンドの実行可否を更新するようにしています。
-
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
namespace MVVMSample001.ViewModels
{
class MainWindowViewModel : ObservableObject
{
private string _value1;
/// <summary>
/// 入力値1
/// </summary>
public string Value1
{
get => _value1;
set
{
SetProperty(ref _value1, value);
// 実行可否を更新
CalculateCommand?.NotifyCanExecuteChanged();
}
}
private string _value2;
/// <summary>
/// 入力値2
/// </summary>
public string Value2
{
get => _value2;
set
{
SetProperty(ref _value2, value);
// 実行可否を更新
CalculateCommand?.NotifyCanExecuteChanged();
}
}
private string _result;
/// <summary>
/// 計算結果
/// </summary>
public string Result
{
get => _result;
set => SetProperty(ref _result, value);
}
/// <summary>
/// 計算コマンド
/// </summary>
public IRelayCommand CalculateCommand { get; }
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindowViewModel()
{
CalculateCommand = new RelayCommand(
execute: () =>
{
try
{
Result = $"{Value1} + {Value2} = {int.Parse(Value1) + int.Parse(Value2)}";
}
catch
{
Result = "Error!";
}
},
canExecute: () =>
{
return !string.IsNullOrEmpty(Value1) && !string.IsNullOrEmpty(Value2);
});
}
}
}
9)Viewの作成
ViewModelの設定
MainWindow.Xaml
を開き、
Window
に
xmlns:vm="clr-namespace:MVVMSample001.ViewModels"
と
d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel, IsDesignTimeCreatable=True}"
を追加します。
これでXAML編集時にViewModelのプロパティが参照できるようになります。
<Window
x:Class="MVVMSample001.Views.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:local="clr-namespace:MVVMSample001.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:vm="clr-namespace:MVVMSample001.ViewModels"
Title="MainWindow"
Width="800"
Height="450"
d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel,
IsDesignTimeCreatable=True}"
ui:WindowHelper.UseModernWindowStyle="True"
mc:Ignorable="d">
<Grid>
</Grid>
</Window>
MainWindow.xaml.cs
を開き、
InitializeComponent
の下に
DataContext = new MainWindowViewModel();
を追加します。
using MVVMSample001.ViewModels;
using System.Windows;
namespace MVVMSample001.Views
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
}
画面作成
Window
にResizeMode="CanResizeWithGrip"
を追加し、
Width
を350
, Height
を300
に編集します。
Grid
をStackPanel
に変更し、
TextBox
2つ、Button
、TextBlock
を追加します。
それぞれViewModelで定義したプロパティとバインディングします。
<Window
x:Class="MVVMSample001.Views.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:local="clr-namespace:MVVMSample001.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:vm="clr-namespace:MVVMSample001.ViewModels"
Title="MainWindow"
Width="350"
Height="300"
d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel,
IsDesignTimeCreatable=True}"
ui:WindowHelper.UseModernWindowStyle="True"
ResizeMode="CanResizeWithGrip"
mc:Ignorable="d">
<StackPanel>
<!-- 入力数値 -->
<TextBox Margin="10" Text="{Binding Value1, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Margin="10" Text="{Binding Value2, UpdateSourceTrigger=PropertyChanged}" />
<!-- 計算ボタン -->
<Button
Margin="10"
Command="{Binding CalculateCommand}"
Content="Calculate" />
<!-- 計算結果 -->
<TextBlock Margin="10" Text="{Binding Result}" />
</StackPanel>
</Window>
完成です。
10)実行
実行してみます。
初期状態はCalculateボタンは無効化しています。
2つのテキストボックスに数値を入力するとボタンが有効化します。
ボタンを押すと計算結果が下に表示されます。
11)参考
12).NET 6.0の場合
12-1)概要
Visual Studio 2022
と.NET 6
で作ってみます。
ViewとViewModelはプロジェクトを分けます。
12-2)環境
- Windows 11 Version 21H2
- .NET 6
- Visual Studio 2022 Version 17.0.4
- WPF
- Microsoft.Toolkit.Mvvm Version 7.1.2
- ModernWpfUI Version 0.9.4
12-3)プロジェクトの作成
新規プロジェクトでWPF アプリケーション
を選択します。
プロジェクト名にMVVMSample001v2
と入力し、フレームワークに.NET 6.0
を選択します。
12-4-a)プロジェクトの設定
プロジェクトをダブルクリックしてcsprojファイルを開きます。
TargetFramework
の値をnet6.0-windows10.0.18362.0
に編集します。
12-4-b)プロジェクトの追加
ソリューションに新しいプロジェクトクラスライブラリ
を追加します。
名前はMVVMSample001v2.VM
、フレームワークは.NET 6.0
にします。
12-5)NuGetパッケージ追加
以下のパッケージをNuGetで追加します。
-
MVVMSample001v2
プロジェクトModernWpfUI
-
MVVMSample001v2.VM
プロジェクトMicrosoft.Toolkit.Mvvm
12-6-a)ファイル準備
MVVMSample001v2
プロジェクト
MainWindow.xaml
を削除します。
プロジェクト直下にViews
フォルダを作成します。
Views
フォルダにウィンドウ(WPF)MainWindow.xaml
を追加します。
MVVMSample001v2.VM
プロジェクト
Class1.cs
を削除します。
プロジェクト直下にクラスMainWindowViewModel.cs
を追加します。
12-6-b)プロジェクト参照設定
MVVMSample001v2
プロジェクトの依存関係
を右クリックし、プロジェクト参照の追加
を選択します。
MVVMSample001v2.VM
にチェックを入れOK
します。
これで View → ViewModel の参照関係になります。
12-7)画面デザイン変更
App.xaml
に
xmlns:ui="http://schemas.modernwpf.com/2019"
を追加し、
StartupUri
をViews/MainWindow.xaml
に修正し、
Application.Resources
にResourceDictionary
を追加します。
<Application x:Class="MVVMSample001v2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMSample001v2"
xmlns:ui="http://schemas.modernwpf.com/2019"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources />
<ui:XamlControlsResources />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml
に
xmlns:ui="http://schemas.modernwpf.com/2019"
と
ui:WindowHelper.UseModernWindowStyle="True"
を追加します。
<Window x:Class="MVVMSample001v2.Views.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:MVVMSample001v2.Views"
xmlns:ui="http://schemas.modernwpf.com/2019"
ui:WindowHelper.UseModernWindowStyle="True"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
12-8)ViewModelの作成
MainWindowViewModel.cs
を以下のように編集します。
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
namespace MVVMSample001v2.VM
{
public class MainWindowViewModel : ObservableObject
{
private string? _value1;
/// <summary>
/// 入力値1
/// </summary>
public string? Value1
{
get => _value1;
set
{
SetProperty(ref _value1, value);
// 実行可否を更新
CalculateCommand?.NotifyCanExecuteChanged();
}
}
private string? _value2;
/// <summary>
/// 入力値2
/// </summary>
public string? Value2
{
get => _value2;
set
{
SetProperty(ref _value2, value);
// 実行可否を更新
CalculateCommand?.NotifyCanExecuteChanged();
}
}
private string? _result;
/// <summary>
/// 計算結果
/// </summary>
public string? Result
{
get => _result;
set => SetProperty(ref _result, value);
}
/// <summary>
/// 計算コマンド
/// </summary>
public IRelayCommand CalculateCommand { get; }
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindowViewModel()
{
CalculateCommand = new RelayCommand(
execute: () =>
{
try
{
Result = $"{Value1} + {Value2} = {int.Parse(Value1 ?? "") + int.Parse(Value2 ?? "")}";
}
catch
{
Result = "Error!";
}
},
canExecute: () =>
{
return !string.IsNullOrEmpty(Value1) && !string.IsNullOrEmpty(Value2);
});
}
}
}
12-9)Viewの作成
ViewModelの設定
MainWindow.Xaml
を開き、
Window
に
xmlns:vm="clr-namespace:MVVMSample001v2.VM;assembly=MVVMSample001v2.VM"
と
d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel, IsDesignTimeCreatable=True}"
を追加します。
MainWindow.xaml.cs
を開き、
InitializeComponent
の下に
DataContext = new VM.MainWindowViewModel();
を追加します。
画面作成
Window
にResizeMode="CanResizeWithGrip"
を追加し、
Width
を350
, Height
を300
に編集します。
Grid
の部分を以下のように書きかえます。
<StackPanel>
<!-- 入力数値 -->
<TextBox Margin="10"
Text="{Binding Value1,Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Margin="10"
Text="{Binding Value2,Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
<!-- 計算ボタン -->
<Button Margin="10"
Command="{Binding CalculateCommand}"
Content="Calculate" />
<!-- 計算結果 -->
<TextBlock Margin="10"
Text="{Binding Result,Mode=OneWay}" />
</StackPanel>
Discussion