1️⃣

【NXUI】C#でワンライナー・クロスプラットホームデスクトップアプリ【AvaloniaUI】

2024/05/09に公開

NXUI

NXUIというライブラリを使うとワンライナーでデスクトップアプリが作れます。

Run(() => Window().Content(Label().Content("NXUI")),"NXUI", args);

クロスプラットフォーム(Win/Mac/Linux/Web)で動きます。

macOS
見やすく改行を入れるとこんな感じ
Run(
  () => Window().Content(Label().Content("NXUI")), 
  "NXUI", 
  args);

WPFとかの、よくあるC#のデスクトップアプリの記述量を考えると怖いくらい短いです…!

いやいや、どうせカラクリあるんでしょ?

「🤔 C#コード以外に色々ファイルがあるんでしょ?XAMLとか…」と思うかもしれませんが、
アプリ本体の.csファイルと、ビルド設定を記載した.csproj2つ だけです。

「🤔わかった、csprojに色々記載があるんでしょ!」と思うかもしれませんが、csprojはこんな感じです。

csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <Using Include="NXUI.Desktop.NXUI" Static="true" />
    <PackageReference Include="NXUI.Desktop" Version="11.0.0" />
  </ItemGroup>
</Project>

dotnet new consoleしたテンプレートから、

  • OutputTypeWinExe
  • NXUI.Desktop.NXUIをusing include
  • ライブラリのNXUIを参照

しただけです。
※しかもusing includeしているところは,なくてもRun(...)NXUI.Desktop.NXUI.Run(...)になるだけなのであんまり短くなってないという…

いやいや、実用性ないじゃん

ハイ。
さすがにワンライナーで1行で書くとちょっとつらいですね…。
ただ、「1ファイル」に制限を広げれば簡単なデスクトップアプリは十分作れます。

メモアプリをつくってみる

こういうメモ帳的なやつを作ってみます。

UIをつくる

まずは改行OKにして、テキスト入力欄とボタンがあるウィンドウを作ります。

Run(
  () => Window()
    .Width(500).Height(300).Title("NXUI memo app sample")
    .Content(
      StackPanel()
        .Children(
          TextBox(out var tbox)
            .MinHeight(200)
            .AcceptsReturn(true)
            .Watermark("文字入力ボックス"),
          Button()
            .Content("ボタン")
        )
      ),
  "memo app", args);

.Width(500).Height(300).Title("NXUI memo app sample")の行は、ウィンドウの幅と高さ、ウィンドウ名を指定しています。

Contentの中にStackPanelを置いて、そのChildrenTextBoxButtonを配置してます。
NXUIはこういう感じでメソッドチェーンで書いてUIを設定できます。

メモ帳的なやーつにしたいので、TextBoxには改行可能にしたり、高さを指定したり、文字入力されてないときのウォーターマーク表示を指定したりしてます。

TextBox
TextBox(out var tbox)
	.MinHeight(200)
	.AcceptsReturn(true)
	.Watermark("文字入力ボックス"),

TextBox(out var tbox)の引数でout-varで変数tboxを宣言してますが、これは後で使います。

ボタンを押したときの保存処理を作る

次に、ボタンを押した時に保存ダイアログが出てファイルを保存できるようにします。

やり方は色々ありますが、
いちばん簡単なButtonOnClickHandler()をつなげてやる方法はこんな感じです。

Button().OnClickHandler()
Button()
  .Content("ボタン")
  .OnClickHandler( async (b, a) => {
    var top = TopLevel.GetTopLevel(b);
    if (top is null) return;
    var file = await top
      .StorageProvider
      .SaveFilePickerAsync(new() {Title = "テキストの保存"});
    if(file is not null){
      await using var str = await file.OpenWriteAsync();
      using var stw = new StreamWriter(str);
      await stw.WriteAsync(tbox.Text);
    }
  })

TextBox(out var tbox)で宣言したtboxTextプロパティから文字列を取得して保存してます。
保存ダイアログいらないなら、もっと短くもできますね…!

完成

とりあえずこれで完成です…!

フルソース
フルソース
Run(
  () => Window()
    .Width(500).Height(300).Title("NXUI memo app sample")
    .Content(
      StackPanel()
        .Children(
          TextBox(out var tbox)
            .MinHeight(200)
            .AcceptsReturn(true)
            .Watermark("文字入力ボックス"),
          Button()
            .Content("ボタン")
            .OnClickHandler( async (b, a) => {
              var top = TopLevel.GetTopLevel(b);
              if (top is null) return;
              var file = await top
                .StorageProvider
                .SaveFilePickerAsync(new() {Title = "テキストの保存"});
              if(file is not null){
                await using var str = await file.OpenWriteAsync();
                using var stw = new StreamWriter(str);
                await stw.WriteAsync(tbox.Text);
              }
            })
        )
      ),
  "memo app", args);

https://github.com/InuInu2022/NXUISamples

これくらいなら1ファイルで完成します。
ちょっとしたボタンが数個、みたいなUIがあるだけのツールにめっちゃいいのでは!?

NXUIのカラクリ

ここからは、NXUIのカラクリを説明します!

https://github.com/wieslawsoltes/NXUI

NXUIはAvalonia UIのライブラリ

NXUIは実はAvalonia UIというUIフレームワークのライブラリです。

NXUI (nex-ui), next-gen UI - Create minimal Avalonia applications using C# 10 and .NET 6, 7 & 8

https://avaloniaui.net/

Avalonia UIは、WPFやXamarinやらMAUIみたいに普通はUIの記述にXMLの一種のXAMLを使うんですが、C#だけでも書くことができます。ただ、そのままだと冗長だったりファイルが多くなったりします。

NXUIはC#のコードだけでAvalonia UIを書きやすくしたライブラリになります。
似たようなライブラリには「Avalonia.Markup.Declarative」とかF#むけの「Avalonia.FuncUI」とかもありますが、
それと比べても記述量が少ないです。

開発者の方はAvalonia UI本体の開発者(Wiesław Šoltésさん)でもあるので"ほぼ公式"と言ってもいいと思います。

https://twitter.com/wieslawsoltes/status/1787152839862616404

機能的にはAvalonia UIのものが使えます。

ドキュメントはほとんどない!

Avalonia UIも日本語ドキュメントはほとんどないですが、NXUI自体のドキュメントは公式のReadmeとサンプルだけでほぼありません…。

とはいえ、Avalonia UIをXAMLを使わないでC#やF#だけで書いているようなものなので、Avalonia UIのAPIリファレンスが使えます。

https://reference.avaloniaui.net/api/

普通はXAMLでやるところを全部C#やF#でやってるので、Avalonia UIの公式のサンプルコードだけだとツライですね。
あんまり凝ったことには向いてないかな?と思います。

いぬいぬは個人的にはAvalonia UIのライブラリ「FluentAvalonia」のサンプルアプリ、「FAControlGallery」の機能でAPIを調べることが多いです。

入門記事としては、英語ですけどこういう記事があります。

https://khalidabuhakmeh.com/writing-a-cross-platform-clock-app-with-avalonia-ui-and-nxui

Avalonia UIに関しては、Scrapにちょっとまとめてます。

https://zenn.dev/inuinu/scraps/7687183f4a4b02

NXUIできること・できないこと

基本的にはAvalonia UIでできることはできますが、一部できないのがあります!
XAML関係は当たり前ですけど難しいですね。
凝ったことも難しいと思いますので、やっぱりUIのパーツが数個、みたいなミニツールとかに使うのがいいかなと思います。

できる

  • クロスプラットフォーム(除くモバイル)
  • シングルファイルアプリ
    • .NETの機能

できない

  • プレビュー
    • XAMLがないので、Avalonia UI向けのプレビュー拡張やツールは使えないみたいです
  • モバイル対応
    • Avalonia UIとは違ってモバイルむけには対応してなさそうです
      • NXUIが省略してかけるようにしているところを全部自分で書けばできるかもしれません…
  • HotReload
    • 公式にはサポートしてないみたいです
    • Avalonia UIにはLive.AvaloniaやHotAvaloniaというライブラリがありますが…
      • HotAvalonia:XAML前提なのでむりそう
      • Live.Avalonia:FuncUI向けのものを対応すればできる可能性ある?

不明

  • ネイティブバイナリ書き出し
    • できそうな気もするけど…
  • ヘッドレステスト
    • Run()関数じゃなくてAppBuilderUseHeadlessすればできそう?

Discussion