WPF|初心者ガイド機能を素早く追加(MVVM対応)

WPF|初心者ガイド機能を素早く追加(MVVM対応)

このWPFライブラリを使用して、アプリケーションに初心者ガイド機能を素早く追加します

最終更新 2022/05/23 23:37
沙漠尽头的狼
読了目安 7 分
カテゴリ
WPF
テーマ
WPF MVVMフレームワーク Prismシリーズ
タグ
.NET WPF MVVM 初心者ガイド

目次

  1. はじめに
  • 事例1
  • 事例2
  • 事例3(本記事で紹介する方法)
  1. 使い方
  2. コントロールの開発方法
  3. まとめ

1. はじめに

事例1

サイト管理者が共有した眾尋氏の記事「WPF 簡易初心者ガイド」では、下図のような秀逸な初心者ガイド効果が紹介されています。

この記事のコードはMVVM方式を採用しておらず、ヒントボックスはユーザーコントロール、マスクウィンドウのスタイルとコードビハインドが分離されていません。しかし、初心者ガイド機能を開発するための良い参考例となっています。

事例2

オープンソースプロジェクトAIStudio.Wpf.Controlsの初心者ガイド効果は以下の通りです。

このプロジェクトも前述の「WPF 簡易初心者ガイド」を参考にしており、MVVM方式でリファクタリングされているため、バインディングが容易です。

さらに、ヒントボックスの表示位置はターゲットコントロールの親ウィンドウ内での位置に応じて柔軟に変化し、マスクウィンドウの外に表示されることはありません。以下の図をご参照ください。

ターゲットコントロールの右側にヒントを表示するのに十分なスペースがある場合は、ヒントボックスが右側に表示されます。右側のスペースが不足している場合は、ターゲットコントロールの左側に表示されます。

事例3(本記事で紹介する方法)

サイト管理者は上記のオープンソースプロジェクトAIStudio.Wpf.Controlsを基に、独自のバージョンDotnet9WPFControlsを作成しました。「前へ」ボタンを削除し、タイトルバインディング、次のボタンのコンテンツバインディング、ヒントボックスのスタイル変更などを追加したものです。効果は以下の通りです。

以降のセクションでは、Dotnet9WPFControlsを使用して初心者ガイド機能を追加する方法を紹介し、このカスタムコントロールの開発の詳細について簡単に触れます。主要な原理については、前述の「WPF 簡易初心者ガイド」を参照してください。

プロジェクトに初心者ガイド機能を追加したい方の参考になれば幸いです。本記事を通じて、ご自身のニーズに合った効果に修正することも可能です。

2. 使い方

2.1 WPFプロジェクトの作成

.NET 6|7を使用して、ソリューション名を"NewbieGuideDemo"とするWPFプロジェクトを作成します。

2.2 NuGetパッケージの導入

  • NuGetパッケージ1: Dotnet9WPFControlsを追加

このパッケージはガイドコントロールとそのスタイルを提供します。「プレリリースを含める」にチェックを入れ、インストールをクリックします。

  • NuGetパッケージ2: Prism.DryIoc を追加

このパッケージは、Prismが提供するMVVMIOC機能を利用するためのものです。

上記2つのNuGetパッケージ追加後のプロジェクトファイルは以下の通りです。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Dotnet9WPFControls" Version="0.1.0-preview.2" />
    <PackageReference Include="Prism.DryIoc" Version="8.1.97" />
  </ItemGroup>

</Project>

2.3 スタイルファイルの追加

App.xamlファイルを開き、Dotnet9WPFControlsのデフォルトテーマファイルを追加します。

<prism:PrismApplication
    x:Class="NewbieGuideDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/">
    <prism:PrismApplication.Resources>

        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/Dotnet9WPFControls;component/Themes/Dotnet9WPFControls.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </prism:PrismApplication.Resources>
</prism:PrismApplication>

ルート要素が <prism:PrismApplication /> になっていることに注意してください。同時に App.xaml.cs ファイルも変更します。詳細はPrismを参照してください。

using Prism.DryIoc;
using Prism.Ioc;
using System.Windows;

namespace NewbieGuideDemo
{
    public partial class App : PrismApplication
    {
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }

        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }
    }
}

2.4 ガイド情報の定義

メインウィンドウ MainWindow にViewModelクラス MainWindowViewModel.cs を追加します。

using Dotnet9WPFControls.Controls;
using Prism.Mvvm;
using System.Collections.Generic;

namespace NewbieGuideDemo
{
    public class MainWindowViewModel : BindableBase
    {
        private GuideInfo? _guide;

        public GuideInfo Guide =>
            _guide ??= new GuideInfo("快速添加新手引导", "这样添加新手引导,或许比较优雅");

        public List<GuideInfo> Guides => new() {Guide};
    }
}

上記のViewModelでは、ガイドプロパティ Guide を定義しています。このプロパティはヒントボックスにバインドされて表示されます。

  • 最初の引数:ガイドヒントボックスのタイトル「快速添加新手引导
  • 2番目の引数:ガイドヒントボックスの内容「这样添加新手引导,或许比较优雅

2つ目のプロパティ Guides はガイド情報のリストで、複数のガイドをバインドできます。ボタンをクリックすると次のガイドに切り替わります。本サンプルでは説明のために1つのガイドのみ記述しています。

2.5 画面へのガイド情報のバインド

まずMainWindow.xamlの全コードを示します。

<Window
    x:Class="NewbieGuideDemo.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:prism="http://prismlibrary.com/"
    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    xmlns:dotnet9="https://dotnet9.com"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="Dotnet9 WPF新手引导功能" Width="800" Height="450"
    prism:ViewModelLocator.AutoWireViewModel="True"
    AllowsTransparency="True" Background="Transparent" WindowStyle="None"
    WindowStartupLocation="CenterScreen"
    mc:Ignorable="d">
    <Window.Resources>
        <dotnet9:BindControlToGuideConverter x:Key="BindControlToGuideConverter" />
    </Window.Resources>
    <Border
        Background="White" BorderBrush="#ccc" BorderThickness="1" MouseLeftButtonDown="Border_MouseDown">
        <Grid>
            <Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="点击测试新手引导">
                <dotnet9:GuideHelper.GuideInfo>
                    <MultiBinding Converter="{StaticResource BindControlToGuideConverter}">
                        <Binding RelativeSource="{RelativeSource Self}" />
                        <Binding Path="Guide" />
                    </MultiBinding>
                </dotnet9:GuideHelper.GuideInfo>
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <i:ChangePropertyAction PropertyName="Display" TargetName="GuideControl" Value="True" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>

            <dotnet9:GuideControl x:Name="GuideControl" Guides="{Binding Guides}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Loaded">
                        <i:ChangePropertyAction PropertyName="Display" Value="True" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </dotnet9:GuideControl>
        </Grid>
    </Border>
</Window>

以下、簡単に説明します。

2.5.1 名前空間の説明

上記のコードでは dotnet9prismi の3つの名前空間が導入されています。

  • dotnet9 名前空間

ガイドコントロール GuideControl とコンバーター BindControlToGuideConverter を導入します。

  • prism 名前空間

主な用途は、prism:ViewModelLocator.AutoWireViewModel="True" のコードで、ビュー MainWindow.xamlMainWindowViewModel.cs をバインドすることです。Prism のソースコードを参照すると、ビューがViewModelをどのように発見するかについてのルールを理解できます。

  • i 名前空間

この名前空間のトリガーを使用して、イベントでプロパティを変更します。

2.5.2 主要コードの簡単な説明

上記のコードは、ガイドコントロール(カスタムコントロール)の使用方法を示しています。(管理者注Dotnet9WPFControls にはガイドウィンドウの方法もありますが、紙面の都合上ここでは説明しません。コントロールのデモ GuideWindowView を参照してください。)

a: ガイドコントロールをコンテナの最前面に配置

後ろの数行に注目してください。

<Grid>
    <!--ここに業務コントロールのレイアウトを記述-->
    <dotnet9:GuideControl x:Name="GuideControl" Guides="{Binding Guides}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <i:ChangePropertyAction PropertyName="Display" Value="True" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </dotnet9:GuideControl>
</Grid>
  • ガイドコントロールを Grid の最後に配置することで、全てのコントロールの最前面に表示されるようにしています(同じ階層に複数のコントロールを追加した場合、後に追加したコントロールは先に追加したコントロールの上に表示され、オーバーラップ効果が生じます)。
  • 前述の MainWindowViewModel で定義したガイド情報リスト Guides にバインドしています。次のボタン(本記事では「我知道了」と表示)をクリックすると、リストの順にガイド情報が切り替わります。
  • i:Interaction.Triggers を使用して、コントロールの読み込み完了時に自動的にガイドヒントを表示します(上記の事例3の効果を参照)。

b: ターゲットコントロールとガイドプロパティのバインド

ターゲットコントロールのガイドプロパティとターゲットコントロールの参照をバインドします。ガイド画面表示時にターゲットコントロールの位置とサイズを計算し、正確にターゲットコントロールを識別し、ガイドヒントボックスの位置を適切に設定できるようにします。

<dotnet9:BindControlToGuideConverter x:Key="BindControlToGuideConverter" />
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="点击测试新手引导">
    <dotnet9:GuideHelper.GuideInfo>
        <MultiBinding Converter="{StaticResource BindControlToGuideConverter}">
            <Binding RelativeSource="{RelativeSource Self}" />
            <Binding Path="Guide" />
        </MultiBinding>
    </dotnet9:GuideHelper.GuideInfo>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <i:ChangePropertyAction PropertyName="Display" TargetName="GuideControl" Value="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

上記のコードで BindControlToGuideConverter コンバーター を導入しています。このコンバーターは接着クラスであり、ターゲットコントロールの参照をガイドオブジェクトに追加します。コンバーターの具体的な定義は以下の通りです。

public class BindControlToGuideConverter : IMultiValueConverter
{
    public object? Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length < 2)
        {
            return null;
        }

        var element = values[0] as FrameworkElement;
        var guide = values[1] as GuideInfo;
        if (guide != null)
        {
            guide.TargetControl = element;
        }

        return guide;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

ターゲットコントロールの参照がガイドオブジェクトの TargetControl プロパティに代入されます。

以上でデモコードは完了です。プロジェクトを実行すると以下の効果が得られます。ソースコードは NewbieGuideDemo にあります。

3. コントロールの開発方法

原理については、WPF 簡易初心者ガイド の記事が詳しく解説しているので、まずそちらをご覧ください。

本サンプルの実装方法については、ここでは詳しく説明しません。詳細はソースコード Dotnet9WPFControls を直接ご確認ください。この記事の後半で簡単に触れます。

コードの構成は以下の通りです。

  • GuideInfo:ガイド情報クラス(タイトル、内容、次のボタンの表示内容など)を定義。
  • GuideHintControl:ガイドヒントボックスコントロール。ガイドのタイトル、内容、次のボタンを表示します。つまり GuideInfo にバインドされるコントロールです。
  • GuideControl:ガイドコントロール。ターゲットコントロールが自身のウィンドウを取得できない場合(例:開発したプログラムがサードパーティのプラグインである場合)に使用します。上記のコードはこのガイドコントロールを使用して実現されています。
  • GuideWindow:ガイドウィンドウ。GuideControl ガイドコントロールの補完的存在。
  • GuideControlBase:ガイドコントロールのヘルパークラス。
  • BindControlToGuideConverter:ガイド情報とガイドのターゲットコントロールをバインドするコンバーター。
  • GuideHelper:ガイドヘルパークラス。ターゲットコントロールのガイド情報のバインドに使用し、さらに ガイドウィンドウ を表示する静的コマンドも提供します。
  • Guide.xaml:ガイドマスクレイヤー(GuideControlGuideWindow)、ガイドヒントボックス(GuideHintControl)のスタイルを定義するリソースファイル。外観を変更する場合はこのファイルを編集します。

重点:

a) GuideControlBase

GuideControlBaseGuideControlGuideWindow のヘルパークラスです。これらの2つのクラスは類似した機能を実現するため、大部分の機能を GuideControlBase にカプセル化しています。例えば、マスクレイヤーからターゲットコントロール領域を Clip で切り抜き、GuideHintControl ヒントボックスコントロールをマスクレイヤーの上に追加して、初心者ガイド効果を表示します。

b) GuideControl と GuideWindow

GuideControl は、ターゲットコントロールを含むコンテナ内で使用するためのものです。GuideControl を配置するコンテナはターゲットコントロールの直接のコンテナである必要はなく、入れ子が可能です。例えば、ターゲットコントロールが ListBox の子項目 ListBoxItem 内にある場合、ガイドコントロール GuideControlListBox の外側のコンテナの上に配置できます。

GuideWindow は、ターゲットコントロールが存在するウィンドウに重ねて表示するためのものです。 GuideWindow はターゲットコントロールのウィンドウの子ウィンドウとして、Show() メソッドで表示します(ShowDialog() は使用できません。なぜなら、ShowDialog() によりガイドウィンドウ以外のウィンドウが無効状態(disable)になるためです)。

これらの2つの方法(GuideControl と GuideWindow)の全体的な表示効果は同じです。ターゲットコントロールが存在するウィンドウがカスタムウィンドウの場合、デモは以下の効果を正常に表示できます。通常のウィンドウの場合は、ターゲットコントロールの Clip 位置とヒントボックスの位置をオフセット処理する必要があります。位置の修正は GuideControl および GuideWindow のメソッド ShowGuide(FrameworkElement? targetControl, GuideInfo guide) で行います。

コントロールに含まれる2つの初心者ガイドデモは以下の通りです。

初心者ガイド デモ1

GuideControl 方式(管理者推奨)。ガイドをコントロールとして表示します。コードはこちら

初心者ガイド デモ2

GuideWindow 方式。ガイドを子ウィンドウとして表示します。コードはこちら

開発の詳細についてはここでは触れません。すべてコードに記述されています。

4. まとめ

以上、長くなりましたが、オープンソースの力に感謝します。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2024/01/25

C# WPFにおけるFluentValidationの応用

この記事では、C# WPFプロジェクトでFluentValidationを使用してプロパティ検証を行う方法と、MVVMパターンを通じてこの機能を実装する方法について詳しく説明します。

続きを読む