AvaloniaUIでマルチプラットフォームなGUI (入門)

この記事は[SFC-RG Advent Calendar 2019](https://qiita.com/advent-calendar/2019/sfc-rg)の10日目です。

#概要
AvaloniaUIで簡単になんか作る。

#動機
マルチプラットフォームなGUIを作りたい。
子プロセスで別のプログラム動かしたい。
HTMLとCSSが苦手。
C#が好き。

#注意
筆者もそこまでAvaloniaUIとか詳しくない。
MVVMを全く理解していない。
XAMLの書き方も我流。

間違ってたら優しく教えてくれると嬉しいです。

#環境構築
1. DotnetCoreSDKを入れる。（本記事では3.0.100）
https://dotnet.microsoft.com/download

2. AvaloniaUIのテンプレートを入れる。

```
cd ~
mkdir workspace
cd workspace
git clone https://github.com/AvaloniaUI/avalonia-dotnet-templates
dotnet new --install ~/workspace/avalonia-dotnet-templates
```

#プロジェクトの作成
MyAppという名前でプロジェクトを作成

```
cd ~/workspace
dotnet new avalonia.mvvm -o MyApp
cd MyApp
```

初期状態はこんな感じ

```
> ls
App.xaml	Assets		MyApp.csproj	ViewLocator.cs	Views
App.xaml.cs	Models		Program.cs	ViewModels	nuget.config
```

動かしてみる

```
dotnet run
```

<img width="102" alt="スクリーンショット 2019-12-11 22.23.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185727/9df0012b-d6ad-c2b1-94f2-9725226c7d71.png">

windowが出ました。

#UIを作っていく
Views/MainWindow.xamlを弄っていく。

WidthとHeightを追加してwindowのサイズを決める。(ここでは800 * 800)

```
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:MyApp.ViewModels;assembly=MyApp"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        Width="800" Height="800"
        x:Class="MyApp.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="MyApp">
```

ボタンを足してやる。あとTextBlockは邪魔なので消す。
Window.Stylesでは各パーツのデザインをクラス毎にまとめて設定できる。
(ここではButtonパーツのtestというクラス)


```
    <Design.DataContext>
        <vm:MainWindowViewModel/>
    </Design.DataContext>
    <Window.Styles>
        <Style Selector="Button.test">
            <Setter Property="Width" Value="100" />
            <Setter Property="Height" Value="25" />
        </Style>
    </Window.Styles>

    <Button Classes="test"> Click Me! </Button>

</Window>
```
実行してみる
<img width="802" alt="スクリーンショット 2019-12-11 22.35.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185727/9bd5bb85-af85-a55b-d085-619779544915.png">

ど真ん中にボタンが登場。

#バインドしてみる
ボタンに名前をつける。

```
<Button Classes="test" Name ="testButton"> Click Me! </Button>
```

MainzWindow.xaml.csを弄る。

```
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Interactivity;

namespace MyApp.Views
{
    public class MainWindow : Window
    {
        private Button _testButton;

        public MainWindow()
        {
            InitializeComponent();
            // xaml内から該当のボタンを探す
            _testButton = this.FindControl<Button>("testButton");
            // バインドする
            _testButton.Click += TestButtonClicked;
        }

        private void InitializeComponent()
        {
            AvaloniaXamlLoader.Load(this);
        }

        private void TestButtonClicked(object sender, RoutedEventArgs e)
        {
            // クリックされたらボタンのサイズを変える
            _testButton.Height = 200;
        }
    }
}
```

実行

```
dotnet run
```

結果
![n4uge-a771e.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185727/9a6a46f5-1f2a-27bb-d7db-3072ada6e3f8.gif)


#まとめ
とりあえず簡単にバインドするところまで。

デフォルトでファイル選択ダイアログとかも使えるし、グラフとかもhttps://github.com/AvaloniaUI/oxyplot-avalonia あたり使えば書けるので簡単なGUIはこれで足りそう。

ただ、バージョンがが1.0未満でドキュメントがほとんどないのでgithubで他の人のコードを漁ることになる。

xamlの書き方がUWPと微妙に違うので結構困る。

困ったら、公式のAPI等をみることになる。 
https://avaloniaui.net/api/

あと公式からチュートリアルも出てるのでそちらも是非に。
https://avaloniaui.net/docs/tutorial/

気が向いたら続き書きます。

#追記
@lensouko 様よりコメントを頂いていた（１年以上経って気付いた...）ので貼り付けておきます。
通常、
```_testButton.Click += TestButtonClicked;```
はバインドとは言いません。
イベントハンドラを登録するとかそういう感じの表現にします。
WPFやAvaloniaではCommandをバインドします。
やり方はこちらを参照
大雑把に説明すると、Nameは不要になります。
Window.csに処理を書く必要がなくなります。
やりたいことややった結果などと画面の紐づけはViewModelで完結します。（コードは多少増えるけど、あっちゃこっちゃに分散しない）
WPFだとコマンドクラスを自作しなきゃいけなかったのでPrismなどのフレームワークやライブラリを使うのが常套手段でしたが、Avaloniaだとテンプレでプロジェクトを作れば用意してくれているようです。
記事の書き方はFormアプリケーションしか知らない人が手を出しやすくするための互換的なものであってWPFやAvaloniaの本来の形ではないということになります。

```
public ReactiveCommand<Unit, Unit> やりたい処理コマンド { get; }

public コンストラクタ()
{
    やりたい処理コマンド = ReactiveCommand.Create(やりたい処理);
}

void やりたい処理()
{
}
```
```
<Button Command="{Binding やりたい処理コマンド}">Do the thing!</Button>
```