Avalonia UI - メッセージボックス

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動

概要

Avalonia UIでは、現在、ネイティブのメッセージボックスコンポーネントを提供していない。
この機能は、ツールキットの機能の拡張と強化を継続的に追求しているため、将来の開発に向けて検討中である。

メッセージボックスコンポーネントを必要とする開発者は、サードパーティ製ライブラリを使用する必要がある。


また、Avalonia.Dialogsクラス、あるいは、DialogHost.Avaloniaクラスを使用してダイアログベースのメッセージボックスウインドウを定義することもできる。


MsBox.Avaloniaライブラリ

MsBox.Avaloniaライブラリとは

MsBox.Avaloniaは、Avalonia UIフレームワーク用のメッセージボックスライブラリである。
このライブラリを使用することにより、開発者はAvalonia UIアプリケーションに洗練されたカスタマイズ可能なメッセージボックスを簡単に実装できる。

MsBox.Avaloniaの主な特徴として、高度なカスタマイズ性が挙げられる。
開発者はメッセージボックスのタイトル、本文、ボタン、アイコンなどを自由に設定することができる。
また、表示するボタンの種類や配置も柔軟に変更可能であり、アプリケーションの要件に合わせて調整できる。

シンプルなAPIを提供しており、数行のコードでメッセージボックスを表示することができる。
非同期操作にも対応しているため、モダンなC#プログラミングの流れに沿った実装が可能である。

デザイン面では、Avalonia UIの美しさを損なうことなく、アプリケーションの全体的な外観と調和するメッセージボックスを定義できる。
さらに、ダークモード、ライトモード等の異なるテーマにも対応している。

MsBox.Avaloniaは、単純な情報表示からユーザの確認や選択を求める複雑な対話まで、様々な用途に適している。
例えば、操作の確認、エラー通知、警告メッセージの表示等に使用できる。

MsBox.Avaloniaは、NuGetパッケージマネージャーを通じて簡単にプロジェクトに追加できるため、必要な名前空間をインポートするだけですぐに使用を開始することができる。

MsBox.Avaloniaライブラリは、MITライセンスに準拠している。


MsBox.Avaloniaライブラリのインストール

RiderまたはVisual StudioからNuGetを使用して、MsBox.Avaloniaライブラリをインストールする。

  • Riderの場合
    1. プロジェクトを開く。
    2. [ツール]メインメニュー - [Nuget] - [ソリューション の Nuget パッケージを管理] (または、[<プロジェクト名> の Nuget パッケージを管理])を選択する。
    3. メイン画面下部にある[パッケージ]タブから MsBox.Avalonia と入力して検索する。
    4. メイン画面下部の右にある[+]ボタンを押下して、MsBox.Avaloniaライブラリをインストールする。

  • Visual Studioの場合
    1. プロジェクトを開く。
    2. NuGetパッケージマネージャーを開く。
      • [ツール]メインメニュー - [NuGetパッケージマネージャー]を選択して、[ソリューションのNuGetパッケージの管理]を選択する。
      • または、ソリューションエクスプローラーでプロジェクトを右クリックして、コンテキストメニューから[NuGetパッケージの管理]を選択する。
    3. MsBox.Avaloniaライブラリを検索する。
      NuGetパッケージマネージャーの検索ボックスに MsBox.Avalonia と入力して検索する。
    4. MsBox.Avaloniaライブラリのインストール
      検索結果からMsBox.Avaloniaライブラリを選択して、[インストール]ボタンを押下する。
    5. インストールの確認ダイアログが表示されるので、[OK]ボタンを押下してインストールを完了する。
    6. 参照の確認
      インストールが完了した後、プロジェクトの参照にMsBox.Avaloniaライブラリが追加されていることを確認する。

  • パッケージマネージャーコンソールからインストールする場合
    1. プロジェクトを開く。
    2. [表示]メインメニュー - [その他のウィンドウ] - [パッケージマネージャーコンソール]を選択して、パッケージマネージャーコンソールを開く。
    3. パッケージマネージャーコンソールから、MsBox.Avaloniaライブラリをダウンロードしてインストールする。
      Install-Package MsBox.Avalonia
    4. ソリューションエクスプローラーのプロジェクトの参照において、MsBox.Avaloniaライブラリが追加されていることを確認する。

  • dotnetコマンドを使用する場合
    1. ターミナルを開く。
    2. プロジェクトのルートディレクトリに移動する。
    3. MsBox.Avaloniaライブラリをインストールする。
      最新の安定版をインストールする場合
      dotnet add package MsBox.Avalonia

      バージョンを指定してインストールする場合
      dotnet add package MsBox.Avalonia --version <バージョン>

    ※注意
    プロジェクトがGit等のバージョン管理システムを使用している場合、これらの変更がトラッキングされることを確認すること。
    プロジェクトを再ビルドして、新しく追加されたパッケージが正しく統合されていることを確認することを推奨する。


プロジェクトにおいて、MsBox.Avaloniaライブラリを使用する場合は、ソースコードファイルの先頭にusingステートメントを追加する。

 using MsBox.Avalonia;
 using MsBox.Avalonia.Enums;


ボタンの種類

  • [OK]ボタンのみ
    ButtonEnum.Ok
  • [はい] / [いいえ]ボタン
    ButtonEnum.YesNo
  • [OK] / [キャンセル]ボタン
    ButtonEnum.OkCancel
  • [OK] / [中止]ボタン
    ButtonEnum.OkAbort
  • [はい] / [いいえ] / [キャンセル]ボタン
    ButtonEnum.YesNoCancel


アイコンの種類

  • 警告
    Icon.Warning
  • エラー
    Icon.Error
  • 情報
    Icon.Info
  • 質問
    Icon.Question
  • 成功
    Icon.Success


メッセージボックスの表示

MessageBoxManager.GetMessageBoxStandardメソッドを実行して、メッセージボックスを定義する。
タイトル、メッセージ、ボタンの種類、アイコンを指定することができる。

ShowAsyncメソッドを実行して、メッセージボックスを非同期で表示する。

 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
 using MsBox.Avalonia;
 using MsBox.Avalonia.Enums;
 
 public partial class MainWindow : Window
 {
    public MainWindow()
    {
       InitializeComponent();
    }
 
    private void InitializeComponent()
    {
       AvaloniaXamlLoader.Load(this);
    }
 
    public async void ShowMessageBox(object sender, RoutedEventArgs e)
    {
       var messageBoxStandardWindow = MessageBoxManager.GetMessageBoxStandard("<タイトル>",
                                                                              "<メッセージボックスの本文>",
                                                                              ButtonEnum.OkCancel,
                                                                              Icon.Info);
       // メッセージボックスを表示
       var result = await messageBoxStandardWindow.ShowAsync();
 
       if (result == ButtonResult.Ok)
       {  // OKボタンが押下された場合
          await MessageBoxManager.GetMessageBoxStandard("結果", "OKボタンが押下された").ShowAsync();
       }
       else
       {
          // Cancelボタンが押下された場合
          await MessageBoxManager.GetMessageBoxStandard("結果", "キャンセルボタンが押下された").ShowAsync();
       }
    }
 }


MsBox.Avaloniaライブラリのカスタムメッセージボックスを使用する場合は、MessageBoxManagerクラスのGetMessageBoxCustomメソッドを使用する。

 var msgbox = MessageBoxManager.GetMessageBoxCustom("<タイトル>",
                                                    "<メッセージ本文>",
                                                    "はい",        // ボタン1
                                                    "いいえ",      // ボタン2
                                                    "キャンセル",,  // ボタン3
                                                    Icon.Question);



カスタムメッセージボックス : Windowクラスの使用

プロパティ

カスタムメッセージボックスでは、多くのプロパティや設定を追加して、より柔軟で機能的なメッセージボックスを定義することができる。

ウインドウのプロパティ
  • Width / Height
    ウインドウの固定サイズを設定
  • MinWidth / MinHeight
    最小サイズを設定
  • MaxWidth / MaxHeight
    最大サイズを設定
  • WindowStartupLocation
    ウインドウの初期位置を指定
    例: 中央、オーナーの中央
  • Topmost
    常に最前面に表示するかどうかを指定する。
  • ShowInTaskbar
    タスクバーに表示するかどうかを指定する。
  • CanResize
    ユーザによるリサイズを許可するかどうかを指定する。
  • SystemDecorations
    ウインドウの装飾 (最小化、最大化、閉じるボタン) の表示設定


コンテンツのレイアウト
  • Padding
    ウイィンドウ内のコンテンツの余白
  • Margin
    各コントロールの外側の余白
  • HorizontalAlignment / VerticalAlignment
    コンテンツの水平・垂直方向の配置
  • Spacing
    StackPanel内の要素間の間隔


テキストブロックのプロパティ
  • FontSize
    フォントサイズ
  • FontWeight
    フォントの太さ
  • FontFamily
    フォントファミリー
  • Foreground
    テキストの色
  • TextWrapping
    テキストの折り返し設定
  • TextAlignment
    テキストの配置


ボタンのプロパティ
  • Width / Height
    ボタンのサイズ
  • Background
    ボタンの背景色
  • Foreground
    ボタンのテキスト色
  • BorderBrush / BorderThickness
    ボタンの枠線の色と太さ
  • CornerRadius
    ボタンの角の丸み


アイコンとイメージ

メッセージボックスにアイコンやイメージを追加する。
例: 警告、エラー、情報等

複数のボタン
  • [OK]、[キャンセル]、[Yes]、[NO]等の複数のボタンを追加する。
  • 各ボタンに対応するDialogResultを設定する。


カスタムスタイル
  • Styleプロパティを使用して、ウインドウや各コントロールにカスタムスタイルを適用する。


アニメーション
  • 表示時や閉じる時のアニメーション効果を追加する。


キーボードショートカット

[Esc]キーでウインドウを閉じる等、キーボードショートカットを設定する。

アクセシビリティ

AutomationProperties.NameやAutomationProperties.HelpTextを使用して、スクリーンリーダーのサポートを改善する。

使用例 1

以下の例では、Avalonia UIを使用してカスタムメッセージボックスを定義している。

"OK"という名前のボタンを検索しているため、ボタンにNameプロパティに"OK"を設定する、あるいは、別の方法でボタンを参照する必要があることに注意する。

 // メッセージボックスを表示するヘルパーメソッド
 // タイトルとメッセージを受け取り、カスタムメッセージボックスを非同期に表示する
 private async Task ShowMessageAsync(string title, string message)
 {
    await new MessageBoxWindow(title, message).ShowDialog(this);
 }
 
 // シンプルなメッセージボックスウィンドウのクラス
 public class MessageBoxWindow : Window
 {
    public MessageBoxWindow(string title, string message)
    {
       Title = title;
 
       // ウインドウのサイズは内容に合わせて自動調整される
       SizeToContent = SizeToContent.WidthAndHeight;
 
       // StackPanelを使用して、本文と[OK]ボタンを縦に配置
       Content = new StackPanel
       {
          Children =
          {
             new TextBlock { Text = message, Margin = new Thickness(20) },
             new Button { Content = "OK", HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Margin = new Thickness(0, 0, 0, 20) }
          }
       };
 
       // [OK]ボタンがクリックされた時にウインドウを閉じるイベントハンドラを設定
       this.FindControl<Button>("OK").Click += (sender, args) => Close();
    }
 }


使用例 2

以下の例では、Avalonia UIを使用して、カスタムメッセージボックスにアイコン、イメージ、複数のボタンを追加・定義している。

  • アイコンおよびイメージの追加
    MessageBoxIcon enumを使用して、情報、警告、エラー、質問のアイコンを選択することができる。
    アイコンはGrid内の左側に配置される。
    アイコン画像は、"avares://"URIを使用して参照される。
    実際の使用時には、プロジェクト内の正しいパスに画像ファイルを配置する必要がある。

  • 複数のボタンの追加
    MessageBoxButton enumを使用して、OK、OKCancel、YesNo、YesNoCancelのボタン構成を選択することができる。
    ボタンはStackPanel内に動的に追加されて、右下に配置される。
    各ボタンにはクリックイベントハンドラが設定され、適切な結果 (true、false、null) を返す。


 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Layout;
 using Avalonia.Media.Imaging;
 using System;
 using System.Threading.Tasks;
 
 public class CustomMessageBox : Window
 {
    public enum MessageBoxButton
    {
       OK,
       OKCancel,
       YesNo,
       YesNoCancel
    }
 
    public enum MessageBoxIcon
    {
       None,
       Information,
       Warning,
       Error,
       Question
    }
 
    private TaskCompletionSource<bool?> _resultTcs;
 
    public CustomMessageBox()
    {
       this.InitializeComponent();
    }
 
    private void InitializeComponent()
    {
       Title = "Message";
       SizeToContent = SizeToContent.WidthAndHeight;
       MinWidth  = 300;
       MinHeight = 100;
    }
 
    public static Task<bool?> Show(Window owner, string message, string title = "Message", 
                                   MessageBoxButton buttons = MessageBoxButton.OK, MessageBoxIcon icon = MessageBoxIcon.None)
    {
       var messageBox = new CustomMessageBox
       {
          Title = title
       };
 
       var grid = new Grid
       {
          ColumnDefinitions = new ColumnDefinitions("Auto,*"),
          RowDefinitions = new RowDefinitions("*,Auto")
       };
 
       // アイコンの指定
       if (icon != MessageBoxIcon.None)
       {
          var iconImage = new Image
          {
             Width  = 32,
             Height = 32,
             Margin = new Thickness(10),
             VerticalAlignment = VerticalAlignment.Top
          };
 
          string iconPath = icon switch
          {
             MessageBoxIcon.Information => "avares://YourAssemblyName/Assets/information.png",
             MessageBoxIcon.Warning => "avares://YourAssemblyName/Assets/warning.png",
             MessageBoxIcon.Error => "avares://YourAssemblyName/Assets/error.png",
             MessageBoxIcon.Question => "avares://YourAssemblyName/Assets/question.png",
                _ => throw new ArgumentOutOfRangeException(nameof(icon))
          };
 
          iconImage.Source = new Bitmap(iconPath);
          Grid.SetColumn(iconImage, 0);
          Grid.SetRow(iconImage, 0);
          grid.Children.Add(iconImage);
       }
 
       // メッセージを指定
       var messageTextBlock = new TextBlock
       {
          Text         = message,
          TextWrapping = Avalonia.Media.TextWrapping.Wrap,
          Margin = new Thickness(10)
       };
       Grid.SetColumn(messageTextBlock, 1);
       Grid.SetRow(messageTextBlock, 0);
       grid.Children.Add(messageTextBlock);
 
       // ボタンの指定
       var buttonPanel = new StackPanel
       {
          Orientation         = Orientation.Horizontal,
          HorizontalAlignment = HorizontalAlignment.Right,
          Margin = new Thickness(0, 10, 10, 10)
       };
 
       void AddButton(string content, bool? dialogResult)
       {
          var button = new Button { Content = content, MinWidth = 60, Margin = new Thickness(5, 0, 0, 0) };
          button.Click += (_, __) => 
          {
             messageBox._resultTcs.SetResult(dialogResult);
             messageBox.Close();
          };
          buttonPanel.Children.Add(button);
       }
 
       switch (buttons)
       {
          case MessageBoxButton.OK:
               AddButton("OK", true);
               break;
          case MessageBoxButton.OKCancel:
               AddButton("OK", true);
               AddButton("Cancel", false);
               break;
          case MessageBoxButton.YesNo:
               AddButton("Yes", true);
               AddButton("No", false);
               break;
          case MessageBoxButton.YesNoCancel:
               AddButton("Yes", true);
               AddButton("No", false);
               AddButton("Cancel", null);
               break;
       }
 
       Grid.SetColumn(buttonPanel, 1);
       Grid.SetRow(buttonPanel, 1);
       grid.Children.Add(buttonPanel);
 
       messageBox.Content = grid;
       messageBox._resultTcs = new TaskCompletionSource<bool?>();
 
       if (owner != null)
       {
          messageBox.ShowDialog(owner);
       }
       else
       {
          messageBox.Show();
       }
 
       return messageBox._resultTcs.Task;
    }
 }



カスタムメッセージボックス : Avalonia.Dialogsクラスの使用

Avalonia.Dialogsクラスを使用してカスタムメッセージボックスを定義することができる。

以下の例では、カスタムメッセージボックスのサイズにおいて、動的サイズとして自動調整を行っている。

  • SizeToContentプロパティをWidthAndHeightによりコンテンツに合わせて自動調整する。
  • 最小サイズ、最大サイズの範囲に制限する。
  • メッセージ本文が長い場合は、ScrollViewerで対応する。(必要な場合のみ表示)


 <!-- CustomMessageBox.axamlファイル -->
 
 <Window xmlns="https://github.com/avaloniaui"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         x:Class="YourNamespace.CustomMessageBox"
         Title="メッセージ"
         SizeToContent="WidthAndHeight"
         WindowStartupLocation="CenterOwner"
         MinWidth="200"
         MaxWidth="600">
 
    <Window.Styles>
       <Style Selector="Button">
          <Setter Property="Padding" Value="12,6"/>
          <Setter Property="MinWidth" Value="80"/>
          <Setter Property="HorizontalContentAlignment" Value="Center"/>
       </Style>
    </Window.Styles>
 
    <Grid Margin="20" RowDefinitions="Auto,*,Auto">
       <!-- アイコンとタイトル部分 -->
       <TextBlock Grid.Row="0" 
                  Text="{Binding Title}"
                  FontWeight="Bold"
                  Margin="0,0,0,10"
                  IsVisible="{Binding !!Title}"/>
 
       <!-- メッセージ部分 -->
       <ScrollViewer Grid.Row="1" 
                     MaxHeight="400"
                     HorizontalScrollBarVisibility="Disabled"
                     VerticalScrollBarVisibility="Auto">
          <TextBlock Text="{Binding Message}" 
                     TextWrapping="Wrap"
                     Margin="0,10"/>
       </ScrollViewer>
 
       <!-- ボタン部分 -->
       <ItemsControl Grid.Row="2" 
                     ItemsSource="{Binding Buttons}"
                     Margin="0,20,0,0">
          <ItemsControl.ItemsPanel>
             <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" 
                            HorizontalAlignment="Right" 
                            Spacing="10"/>
             </ItemsPanelTemplate>
          </ItemsControl.ItemsPanel>
          <ItemsControl.ItemTemplate>
             <DataTemplate>
                <Button Content="{Binding Text}"
                        Command="{Binding Command}"/>
             </DataTemplate>
          </ItemsControl.ItemTemplate>
       </ItemsControl>
    </Grid>
 </Window>


 // CustomMessageBox.axaml.csファイル
 
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using System.Collections.ObjectModel;
 using System.Windows.Input;
 using System.Linq;
 
 namespace YourNamespace
 {
    public partial class CustomMessageBox : Window
    {
       public string Title { get; set; }
       public string Message { get; set; }
       public ObservableCollection<MessageBoxButton> Buttons { get; }
       public MessageBoxResult Result { get; private set; }
 
       public CustomMessageBox()
       {
          InitializeComponent();
          Buttons = new ObservableCollection<MessageBoxButton>();
          DataContext = this;
       }
 
       private void InitializeComponent()
       {
          AvaloniaXamlLoader.Load(this);
       }
 
       public class MessageBoxButton
       {
          public string Text { get; set; }
          public ICommand Command { get; set; }
          public MessageBoxResult Result { get; set; }
       }
 
       public enum MessageBoxResult
       {
          None,
          OK,
          Cancel,
          Yes,
          No
       }
 
       private void AddButton(string text, MessageBoxResult result)
       {
          Buttons.Add(new MessageBoxButton
          {
             Text = text,
             Result = result,
             Command = new RelayCommand(() =>
             {
                Result = result;
                Close();
             })
          });
       }
 
       public static async Task<MessageBoxResult> ShowDialog(Window parent, string message, string title = null,
                                                             MessageBoxButtons buttons = MessageBoxButtons.OK)
       {
          var dialog = new CustomMessageBox
          {
             Message = message,
             Title = title
          };
 
          // ボタンの設定
          switch (buttons)
          {
             case MessageBoxButtons.OK:
                  dialog.AddButton("OK", MessageBoxResult.OK);
                  break;
             case MessageBoxButtons.OKCancel:
                  dialog.AddButton("OK", MessageBoxResult.OK);
                  dialog.AddButton("キャンセル", MessageBoxResult.Cancel);
                  break;
             case MessageBoxButtons.YesNo:
                  dialog.AddButton("はい", MessageBoxResult.Yes);
                  dialog.AddButton("いいえ", MessageBoxResult.No);
                  break;
             case MessageBoxButtons.YesNoCancel:
                  dialog.AddButton("はい", MessageBoxResult.Yes);
                  dialog.AddButton("いいえ", MessageBoxResult.No);
                  dialog.AddButton("キャンセル", MessageBoxResult.Cancel);
                  break;
          }
 
          await dialog.ShowDialog(parent);
          return dialog.Result;
       }
    }
 
    public enum MessageBoxButtons
    {
       OK,
       OKCancel,
       YesNo,
       YesNoCancel
    }
 
    // 簡易的なRelayCommand実装
    public class RelayCommand : ICommand
    {
       private readonly Action _execute;
       public RelayCommand(Action execute)
       {
          _execute = execute;
       }
 
       public event EventHandler CanExecuteChanged;
       public bool CanExecute(object parameter) => true;

       public void Execute(object parameter)
       {
          _execute();
       }
    }
 }


 // 使用例
 
 // 基本的な例
 var result1 = await CustomMessageBox.ShowDialog(this,
                                                 "処理を実行しますか?",
                                                 "確認",
                                                 MessageBoxButtons.YesNo);
 
 // 長いメッセージの例
 var result2 = await CustomMessageBox.ShowDialog(this,
                                                 "これは長いメッセージの例である\n\n" +
                                                 "メッセージの長さに応じてウインドウサイズが自動的に調整される。" +
                                                 "とても長い文章の場合は、スクロールバーが表示される。" +
                                                 "ただし、横幅は最大600pxに制限されており、それ以上は折り返して表示される。",
                                                 "情報",
                                                 MessageBoxButtons.OK);
 
 // 結果の処理
 switch (result1)
 {
    case CustomMessageBox.MessageBoxResult.Yes:
       // [はい]ボタンが押下された場合
       break;
    case CustomMessageBox.MessageBoxResult.No:
       // [いいえ]ボタンが押下択された場合
       break;
 }