sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

WPF UI(UI ライブラリ)の使い方(3)バージョン3対応

WPF UI のメジャーバージョンが「3」になりました。アップデートに伴って、旧バージョンになった「2」は NuGet でも非推奨になってしまいました。

SnackBar の使い方を書いた記事をあげていたので、変更点をメモ。

なお、公式リソースが足りていないので以下の内容は正しい使い方なのかどうか確認することができません。こうすると動かすことができます、という一例の内容でご了承ください。

現在のリソース

基本的な使い方を理解する上では、以下が基本になります。

公式のドキュメントもあるのですが、更新が追いついていないのでわからない。どうしようもなくなったら wpfui のGitHub のコードを確認する。このときは「Gallery」はサンプルになっているからおススメ。

ちなみに、公開されているリソースを調べても SnackBar のようによくわからなかったりして、結局 GitHub のコードを見てみると使い方が違うことがある。

ちなみに「2」から「3」への変更は破壊的な変更点が多くて「2」を利用している状態から「3」にアップデートすると、エラーが大量に出ると思います。安易にアップデートすると後悔するかもしれないです。

SnackBar XAML 変更点

XAML のコードの変更点は Snackbar 自体をタグで追加するのではなくて SnackbarPresenter を乗せるようになりました。

<Window x:Class="Sample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        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"
        xmlns:local="clr-namespace:Sample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
    </Window.Resources>
    <Grid Background="#FBFBFD">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!-- version 3 -->
        <ui:SnackbarPresenter Grid.Row="1" x:Name="SnackbarPresenter" />
        <!-- version 2
        <ui:Snackbar Grid.Row="1" 
                     x:Name="RootSnackbar"
                     MaxWidth="600"
                     CloseButtonEnabled="False"
                     Timeout="5000"
                     Appearance="Secondary" />
        -->
    </Grid>
</Window>

(最適ではないけど)動作する最小サンプル

ポイントは SnackbarService のインスタンスを作成してから Presenter を設定します。以前は SnackBar 自体を xaml に置いて、SnackBar を SetSnackbarControl メソッドで設定していました。コード上のやっていることは変わらないんだけど、それが Presenter になった。

public partial class MainWindow : Window
{
    private readonly ISnackbarService _snackbarService;

    public MainWindow()
    {
        _snackbarService = new SnackbarService();

        InitializeComponent();
    }

    private void CardAction_Click(object sender, RoutedEventArgs e)
    {
        // 1回設定すればいい(けど、コンストラクタだとインスタンスがまだ null かも)
        _snackbarService.SetSnackbarPresenter(SnackbarPresenter);
        _snackbarService.Show(
            "サンプルタイトル", 
            "デイリークエストを達成しました。", 
            new SymbolIcon(SymbolRegular.Alert32), 
            ControlAppearance.Primary, 
            TimeSpan.FromSeconds(5));
    }
}

また Show メソッドの引数が変更されました。最後に TimeSpan で表示する秒数を設定します。加えて SymbolRegular を引数にしていた部分が SymbolIcon になったのでラッパーしてやる必要があります。これは xaml 上でも同様で後述するように、「2」からアップデートすると変更箇所が大量に出てきます。

Prism DI を使った例

App で SnackbarService のインスタンスを用意しておけば、サービスをプログラム全体で共有できます。

public partial class App
{
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        // SnackBar サービスを DI に用意する
        containerRegistry.RegisterInstance<SnackbarService, "name">();
    }
}

ちなみにこの記述は公式のサンプルでも「こんな感じ」で DI を使ってる。SnackBar のままではなくて、Interface を渡すような設計になっています。

private static readonly IHost _host = Host.CreateDefaultBuilder()
        .ConfigureAppConfiguration(c =>
        {
            _ = c.SetBasePath(AppContext.BaseDirectory);
        })
        .ConfigureServices(
            (_1, services) =>
            {
                // ... 略
                _ = services.AddSingleton<ISnackbarService, SnackbarService>();
                // ... 略
                // All other pages and view models
                _ = services.AddTransientFromNamespace("Wpf.Ui.Gallery.Views", GalleryAssembly.Asssembly);
                _ = services.AddTransientFromNamespace(
                    "Wpf.Ui.Gallery.ViewModels",
                    GalleryAssembly.Asssembly
                );
            }
        )
        .Build();

こうしておくと SnackbarService をどこからでも呼び出せるので Singleton みたいなものですからね。

ちなみにバージョン「2」から「3」になったときに名前空間が結構変更になっていることに気づきました。ここでもアップデートをするとエラーが発生しまくる。

そのほかのアップデートの注意点

WPFUI なんだから Button コントロールくらい使っていたんじゃないかと思います。例えば、バージョン2

<ui:Button Icon="Copy24" 
           ToolTip=""
           Command="{Binding CopyCommand}" />

これも SymbolIcon を挟むようになったので下のようになります。

<ui:Button Icon="{ui:SymbolIcon Copy24}" 
           ToolTip=""
           Command="{Binding CopyCommand}" />

参考