この記事はSFC-RG Advent Calendar 2019の10日目です。
#概要
AvaloniaUIで簡単になんか作る。
#動機
マルチプラットフォームなGUIを作りたい。
子プロセスで別のプログラム動かしたい。
HTMLとCSSが苦手。
C#が好き。
#注意
筆者もそこまでAvaloniaUIとか詳しくない。
MVVMを全く理解していない。
XAMLの書き方も我流。
間違ってたら優しく教えてくれると嬉しいです。
#環境構築
-
DotnetCoreSDKを入れる。(本記事では3.0.100)
https://dotnet.microsoft.com/download -
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
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>
ど真ん中にボタンが登場。
#バインドしてみる
ボタンに名前をつける。
<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
#まとめ
とりあえず簡単にバインドするところまで。
デフォルトでファイル選択ダイアログとかも使えるし、グラフとかもhttps://github.com/AvaloniaUI/oxyplot-avalonia あたり使えば書けるので簡単なGUIはこれで足りそう。
ただ、バージョンがが1.0未満でドキュメントがほとんどないのでgithubで他の人のコードを漁ることになる。
xamlの書き方がUWPと微妙に違うので結構困る。
困ったら、公式のAPI等をみることになる。
https://avaloniaui.net/api/
あと公式からチュートリアルも出てるのでそちらも是非に。
https://avaloniaui.net/docs/tutorial/
気が向いたら続き書きます。
#追記
@lensouko 様よりコメントを頂いていた(1年以上経って気付いた...)ので貼り付けておきます。
通常、
_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>