この記事は N/S高等学校 Advent Calendar 2022 の2日目の記事になります。
概要
タイトル通りにはなりますが、MacでVisual Studioを触ったことがないので.NET MAUIがリリースされた&Macでも利用可能になったということで触れてみました。ついでに最近のMVVMパターンの書き方も進化しているようなのでこちらも少し触れてみました。
.NET MAUIとは
C#とXAMLを使ってWindows、Mac、Android、iOSのアプリケーションを作成するためのクロスプラットフォームフレームワークです。
今まではXamarinでしたがこれがMAUIに置き換わったのですね。
環境
執筆時時点でMacOS上でMAUIアプリの作成には以下の環境が必要でした。
- Visual Studio 2022 for Mac 17.4
- macOS Monterey 12.5
- Xcode 14.1
触るモノ
プロジェクトを作成すると出来上がる、ボタンをクリックした数をカウントするアプリです。
プロジェクト作成時のコード
コードビハインドで記述されています。
OnCounterClicked()
でボタンのテキストを直接変更しているだけですね。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DefaultApp.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
~~~~~~中略~~~~~~
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
SemanticScreenReader.Announce(CounterBtn.Text);
}
}
MVVMを自力で書く場合
せっかくなのでMVVMにしてみます。
INotifyPropertyChanged
を実装したクラスを用意し、それを継承したMainPageViewModel
(ViewModelクラス)を作成します。
ViewModelにはバインディングするためのプロパティとコマンドを書いていきます。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:OldBinding.ViewModel"
x:Class="OldBinding.MainPage">
<ContentPage.BindingContext>
<local:MainPageViewModel />
</ContentPage.BindingContext>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
~~~~~~中略~~~~~~
<Button
x:Name="CounterBtn"
Text="{Binding Text}"
SemanticProperties.Hint="Counts the number of times you click"
Command="{Binding Command}"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
public abstract class ViewModelBase : INotifyPropertyChanged
{
private string NameProperty;
public string Name
{
get
{
return NameProperty;
}
set
{
if (NameProperty != value)
{
NameProperty = value;
NotifyPropertyChange("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChange(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class MainPageViewModel : ViewModelBase
{
private string text = "click me";
private int count = 0;
public MainPageViewModel()
{
Command = new Command(Count);
}
public string Text
{
get
{
return text;
}
set
{
if (text != value)
{
text = value;
NotifyPropertyChange("Text");
}
}
}
public ICommand Command
{
get;
protected set;
}
private void Count()
{
count++;
if (count == 1)
Text = $"Clicked {count} time";
else
Text = $"Clicked {count} times";
}
}
うーん、とても面倒くさいですね。。
CommunityToolkit.Mvvmを使ってみる
Microsoft謹製のMVVM開発をサポートしてくれる便利なライブラリーがあります。
Nugetパッケージの管理からライブラリーをインストールします。
それではこのライブラリーを使ってコードを書いてみます。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NewBinding.ViewModel"
x:Class="NewBinding.MainPage">
<ContentPage.BindingContext>
<local:MainPageViewModel />
</ContentPage.BindingContext>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
~~~~~~中略~~~~~~
<Button
x:Name="CounterBtn"
Text="{Binding Text}"
SemanticProperties.Hint="Counts the number of times you click"
Command="{Binding CommandCommand}"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
[INotifyPropertyChanged]
public partial class MainPageViewModel
{
private int count = 0;
public MainPageViewModel()
{
}
[ObservableProperty]
private string text = "click me";
[RelayCommand]
private void Command()
{
count++;
if (count == 1)
Text = $"Clicked {count} time";
else
Text = $"Clicked {count} times";
}
}
なんということでしょう。コード量がこれだけになってしまいました。
INotifyPropertyChanged
はクラスに属性をつけるだけで実装は不要となりました。
プロパティも変数にObservableProperty
属性をつけると自動的に生成してくれるようです。
この場合text
からText
プロパティを生成してくれます。
コマンドに関してもメソッドにRelayCommand
属性をつけるとメソッド名+Command
というコマンドが自動生成されるようです。
サンプルのメソッド名がCommand
なのでCommandCommand
というおかしな名前になってしまいましたが、、
あと、クラスをpartialにしないとエラーになってしまうことが注意点でした。(なぜわざわざpartialにしないとダメなのでしょう。)
最後に
さて、今回初めてMac上でMAUIアプリを作成してみました。
前提条件としてXCodeのインストールが必要なのが手間ですがそこは仕方ありませんね。
Xamlには慣れる必要がありますが、C#でクロスプラットフォームアプリが作成できるのは魅力的ですね。
CommunityToolkit.Mvvmは裏でコードが自動生成されるので初見ではなんで動いてるんだ?と思わせかねないコードになってしまう感じがしますがルールさえ押さえておけばスッキリと書けるので良いのではないでしょうか。