🙆

WPF-UIでダイアログを使用する方法(ContentDialogService)

2025/01/30に公開

WPF-UIの使用例などがあまりないため、以下では、Wpf.Ui ライブラリが提供する ContentDialogService を利用してダイアログを表示するサンプルアプリケーションの実装例をもとに、手順やポイントを解説します。今回のサンプルでは DialogService クラスを作成し、ViewModel からダイアログを簡単に呼び出せる仕組みを構築しています。


1. 全体の流れ

  1. XAML上にダイアログの表示先ContentPresenter)を置く
    ContentPresenter は、ダイアログを表示するための領域となります。
  2. DialogServiceクラス で、ContentDialogService を用いてダイアログを表示するメソッドを実装
    • IDialogService インターフェイスを用いて抽象化
    • 実際にダイアログを表示するのは ContentDialogService クラス
  3. ViewModel からダイアログを呼び出すために、DialogService のインスタンスを設定
  4. ボタン押下等のイベントDialogService のメソッドを呼び出し、ダイアログを表示

2. サンプルコードの解説

2.1 DialogService (IDialogServiceの実装)

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Wpf.Ui.Controls;

namespace UiDesktopApp1.Services
{
    public class DialogService : IDialogService
    {
        private ContentPresenter _dialogHost;
        private ContentDialogService _contentDialogService;

        public DialogService()
        {
        }

        public DialogService(ContentPresenter dialogHost)
        {
            _dialogHost = dialogHost;
            _contentDialogService = new ContentDialogService();
            _contentDialogService.SetDialogHost(dialogHost);
        }

        public void SetContentPresenter(ContentPresenter dialogHost)
        {
            _dialogHost = dialogHost;
        }

        public async Task ShowDialogAsync(string title, string content)
        {
            await Application.Current.Dispatcher.InvokeAsync(async () =>
            {
                if (_contentDialogService == null)
                {
                    throw new InvalidOperationException("DialogHost was not set properly.");
                }

                var dialog = new ContentDialog
                {
                    Title = title,
                    Content = content,
                    CloseButtonText = "Close",
                };

                await _contentDialogService.ShowAsync(dialog, CancellationToken.None);
            });
        }
    }
}

ポイント

  • DialogService は、ダイアログ表示のための処理をカプセル化 するクラスです。
  • コンストラクタで受け取った ContentPresenter (ダイアログの表示先) を ContentDialogService にセットしています。
  • ShowDialogAsync メソッドで ContentDialog を生成し、ShowAsync で表示しています。
  • Application.Current.Dispatcher.InvokeAsync(...) を利用することで、UI スレッド上でダイアログを表示させています。

2.2 IDialogService (抽象化インターフェース)

using System.Threading.Tasks;
using System.Windows.Controls;

namespace UiDesktopApp1.Services
{
    public interface IDialogService
    {
        Task ShowDialogAsync(string title, string content);
        void SetContentPresenter(ContentPresenter dialogHost);
    }
}

ポイント

  • IDialogService は、ダイアログ表示を行うサービスのインターフェース です。
  • これを用意することで、ViewModel などからは ShowDialogAsync というメソッド呼び出しのみでダイアログが表示できるようになり、実装を意識しなくてもよくなります。

2.3 XAML (DashboardPage) での ContentPresenter

<Page
    x:Class="UiDesktopApp1.Views.Pages.DashboardPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:UiDesktopApp1.Views.Pages"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
    mc:Ignorable="d"
    Title="DashboardPage"
    d:DesignHeight="450"
    d:DesignWidth="800">

    <Grid VerticalAlignment="Top">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <!-- ダイアログを呼び出すためのボタンとカウント表示 -->
        <ui:Button
            Grid.Column="0"
            Command="{Binding ViewModel.CounterIncrementCommand, Mode=OneWay}"
            Content="Click me!"
            Icon="Fluent24" />
        <TextBlock
            Grid.Column="1"
            Margin="12,0,0,0"
            VerticalAlignment="Center"
            Text="{Binding ViewModel.Counter, Mode=OneWay}" />

        <!-- ダイアログの表示先 -->
        <ContentPresenter x:Name="ContentPresenterForDialogs" Panel.ZIndex="1" />
    </Grid>
</Page>

ポイント

  • <ContentPresenter x:Name="ContentPresenterForDialogs" /> で、ダイアログを表示させる領域を指定しています。
  • Panel.ZIndex="1" を設定することで、他のコントロールより前面にダイアログが表示されるように指定しています。

2.4 DashboardPage のコードビハインド

using UiDesktopApp1.Services;
using UiDesktopApp1.ViewModels.Pages;
using Wpf.Ui.Controls;

namespace UiDesktopApp1.Views.Pages
{
    public partial class DashboardPage : INavigableView<DashboardViewModel>
    {
        public DashboardViewModel ViewModel { get; set; }

        public DashboardPage(DashboardViewModel viewModel)
        {
            ViewModel = viewModel;
            DataContext = this;

            InitializeComponent();
            Loaded += DealsManagerPage_Loaded;
        }

        private void DealsManagerPage_Loaded(object sender, RoutedEventArgs e)
        {
            // ページがロードされた時点でDialogServiceにContentPresenterを設定
            ViewModel.Initialize(new DialogService(ContentPresenterForDialogs));
        }
    }
}

ポイント

  • ページのロード後 (Loaded イベント) に、DialogServiceContentPresenterForDialogs を渡して初期化しています。
  • これにより ViewModelIDialogService のメソッドを使った際に正しくダイアログが表示されます。

2.5 DashboardViewModel (ダイアログ呼び出しの実装)

using UiDesktopApp1.Services;

namespace UiDesktopApp1.ViewModels.Pages
{
    public partial class DashboardViewModel : ObservableObject
    {
        private IDialogService _dialogService;

        [ObservableProperty]
        private int _counter = 0;

        [RelayCommand]
        private void OnCounterIncrement()
        {
            ShowConfirmationDialog();
            Counter++;
        }

        public void Initialize(IDialogService dialogService)
        {
            _dialogService = dialogService;
        }

        public async Task ShowConfirmationDialog()
        {
            await _dialogService.ShowDialogAsync("Dialog Test", "Have you checked?");
        }
    }
}

ポイント

  • Initialize メソッドで DialogService のインスタンスを受け取り、ローカル変数に保持しています。
  • OnCounterIncrement コマンドハンドラが呼ばれると、ShowConfirmationDialog() を通じて DialogServiceShowDialogAsync が呼ばれます。
  • ダイアログ表示後にカウントを加算しているため、ダイアログを閉じてからカウンターがインクリメントされるようになっています。

3. 実行イメージ

  1. アプリを起動し、DashboardPage を表示する。
  2. 「Click me!」ボタンを押すと、
    • OnCounterIncrement()ShowConfirmationDialog()ShowDialogAsync("Dialog Test", "Have you checked?")
      が呼ばれ、画面上に「Dialog Test」というタイトル、本文「Have you checked?」、Closeボタンのついたダイアログが表示されます。
  3. ダイアログを閉じると、カウンターの値がインクリメントされ、TextBlock に反映されます。

4. まとめ

  • ContentDialogService は Wpf.Ui が提供するダイアログ制御用クラスで、ContentPresenter をダイアログの表示先として指定します。
  • ContentDialog にタイトルやコンテンツ、ボタンを指定でき、ShowAsync メソッドでダイアログ表示が可能です。
  • DialogServiceIDialogService を用意し、ViewModel 側から簡潔な呼び出しができるようにする構成が便利です。
  • ContentPresenterForDialogs がダイアログの表示先となるため、ページ (またはウィンドウ) 上の XAML に忘れずに配置し、DialogService にセットする必要があります。

Discussion