0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

初めての WinUI 3 (3) カスタムコントロール

Last updated at Posted at 2025-06-06

前回

基礎練として、簡単なViewとViewModelの実装をしました。
最初に作った物をコピペしたので、XAMLが同じような内容(Grid周り)です。
記事を見直して、自分のことながら残念な気持ちに。

今回

そうならないために、カスタムコントロール/ユーザーコントロールを作って共通化するのが定石です。

カスタムコントロール/ユーザーコントロールの違い

私「違いは何ですか?要約して教えてください。」

AI「カスタムコントロール は 汎用性が高く、再利用しやすい 独立した UI 部品。テンプレートを自由にカスタマイズでき、複数のプロジェクトで利用可能。
ユーザーコントロール は 特定用途向け に既存のコントロールを組み合わせて作る UI 部品。再利用性は限定的で、そのプロジェクト内で使うことが多い。
違いは「汎用性」と「設計の自由度」! どちらを使うかは開発目的次第です。😃」

私「今回は、カスタムコントロールをやってみよう。」

先人の知恵に学ぶ

なんなら作りたくないし、あるもの利用するだけにしたいです。そんなもんです。
というわけで、こちら。

Windows Community Toolkit の SettingsCard

SettingsCard は、エクスペリエンスで設定を表示するために使用できるコントロールです。 これは、Windows 11 の既定のスタイルを使用し、使いやすく、すべてのアクセシビリティ標準を満たしており、設定ページの見栄えを良くします。

利用します。

Nugetします

XAMLだけ書き換えます

なんということでしょう!
あれだけ冗長だったXAMLがすっきり!!
Viva!!!

xaml MainWindow.xaml
<Window
    ~略~
  
    xmlns:ui="using:CommunityToolkit.WinUI"
    xmlns:controls="using:CommunityToolkit.WinUI.Controls">

    ~略~
  
        <controls:SettingsCard 
            HeaderIcon="{ui:FontIcon Glyph='&#xF19E;'}"
            Header="ToggleSwitch &amp; Change Theme"
            Description="トグルスイッチでアプリケーションのテーマを編集します。">

            <!-- 本体 -->
            <ToggleSwitch x:Name="toggleSwitch" IsOn="{x:Bind ViewModel.ToggleSwitchValue, Mode=TwoWay}"  Toggled="toggleSwitch_Toggled" OffContent="ライトモード" OnContent="ダークモード" />

        </controls:SettingsCard>

実行

image.png

まさにWin11の設定画面のそれですね。
ちなみに、クリックしたら下部領域が開く奴は SettingExpander です。

SettingsCard のソースを調査

SettingsCardは、どうやって実現されているのか?ソースコードはこちら

よし、見た。

簡易版を実装してみる

SettingsCard は、機能が充実していて、ソースコードももりもりです。
ここは、シンプルにすることで理解が進むはず。
やりたいことは、
・ヘッダーアイコンを指定できる
・ヘッダーを指定できる
・説明文を指定できる
・コンテンツは、自由にカスタマイズ可能とする
です。

実装 (C#)

・アイコン・ヘッダー、説明文、コンテンツのDependencyPropertyを宣言する。
・それぞれのget;set;を実装する。
・カスタムコントロールの子要素として指定するのは"Content"であることを宣言する。(ContentProperty)

Csharp SettingsCard.cs
    [ContentProperty(Name = "Content")]
    public partial class SettingsCard : Control
    {
        public static DependencyProperty BuildDp<T, O>(string nameOf, T defaultValue, PropertyChangedCallback callback)
        {
            return DependencyProperty.Register(nameOf, typeof(T), typeof(O), new PropertyMetadata(defaultValue, callback));
        }

        public static DependencyProperty FontIconProperty =
            BuildDp<object, SettingsCard>(nameof(HeaderIcon), new FontIcon(), static (d, e) => { });

        public static DependencyProperty HeaderProperty =
            BuildDp<string, SettingsCard>(nameof(Header), string.Empty, static (d, e) => { });

        public static DependencyProperty DescriptionProperty =
            BuildDp<string, SettingsCard>(nameof(Description), string.Empty, static (d, e) => { });

        public static readonly DependencyProperty ChildrenProperty =
            BuildDp<object, SettingsCard>(nameof(Content), new(), static (d, e) => { });


        public SettingsCard() : base()
        {
            this.DefaultStyleKey = typeof(SettingsCard);
        }

        public object HeaderIcon
        {
            get => GetValue(FontIconProperty);
            set => SetValue(FontIconProperty, value);
        }

        public string Header
        {
            get => (string)GetValue(HeaderProperty);
            set => SetValue(HeaderProperty, value);
        }

        public string Description
        {
            get => (string)GetValue(DescriptionProperty);
            set => SetValue(DescriptionProperty, value);
        }

        public object Content
        {
            get => GetValue(ChildrenProperty);
            set => SetValue(ChildrenProperty, value);
        }
    }

テンプレート(ResourceDictionary)

xaml SettingsCard.xaml
  <Style TargetType="local:SettingsCard">
    <Setter  Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:SettingsCard">
          <!-- テンプレート全体 -->
          <Grid x:Name="PART_Panel" 
                Padding="16" CornerRadius="4"
                Background="{ThemeResource ControlFillColorDefaultBrush}"
                HorizontalAlignment="Stretch">

            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="1*" />
              <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <!-- ヘッダーアイコン -->
            <ContentPresenter 
              Grid.Column="0" 
              x:Name="PART_HeaderIcon" Content="{TemplateBinding HeaderIcon}" 
              Margin="6 6 12 6" />

            <!-- ヘッダー・説明文 -->
            <StackPanel 
              Grid.Column="1" 
              x:Name="PART_Header" 
              Orientation="Vertical" Spacing="4" 
              VerticalAlignment="Center" HorizontalAlignment="Stretch">
              <TextBlock Text="{TemplateBinding Header}" 
                FontSize="14" TextWrapping="WrapWholeWords" />
              <TextBlock Text="{TemplateBinding Description}" 
                FontSize="12" TextWrapping="WrapWholeWords" />
            </StackPanel>

            <!-- コンテンツ -->
            <ContentPresenter Grid.Column="2" 
                      x:Name="PART_Content" Content="{TemplateBinding Content}" 
                      VerticalContentAlignment="Center" 
                      HorizontalContentAlignment="Right" />

          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

利用する(XAML)

xaml MainWindow.xaml
        <local:SettingsCard 
            HeaderIcon="{ui:FontIcon Glyph='&#xE8B0;'}"
            Header="local:SettingsCard" 
            Description="SettingsCard風カスタムコントロール">
            <!-- コンテンツ -->
            <Grid>
                <Button Content="Button" />
            </Grid>
        </local:SettingsCard>

実行結果

image.png

簡易版のSettingsCardが完成して、基本は理解できたと思います。
本来は、状態によって部品の表示/非表示、デザイン変更とかが必要なはず。
DependencyProperty定義するの楽にならないかな。

また、次回があればどうぞ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?