ContentをもつことができるUserContorlをつくる

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

<Button>のように、

<Button>
    <Button.Content>
        <TextBlock Text="aaa"/>
    </Button.Content>
</Button>

と書かなくても、

<Button>
    <TextBlock Text="aaa"/>
</Button>

と、直接?値を入れられるようなプロパティを作りたい。

それで、自前のコントロールを作りたい。

やったこと

そういうUserControlを作ろうと思ったが、UserControlは、もともと「Content」のプロパティを持っていて、もともとそういうことができてしまう。

ほなそれを使えばよい、のだが、そういう仕組みがどうなっているかを勉強したかったので、今回UserContolではなくCustomControlでそれをやってみようと思う。

やったこと

[ContentProperty(Name = "プロパティ名")] を付けると、今回やりたいことができるようになる。

下記の実験コードでやってみた。

実験コード

CustomControl部分

MySignal.cs
※以前CustomControlを勉強したときの流用コードなので、名前は気にしない

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;

namespace CustomControlJikken2
{
    [ContentProperty(Name = "MyContent")] // ★★←これが肝!!!
    public sealed class MySignal : Control
    {
        private Grid _mainGrid;

        public MySignal()
        {
            this.DefaultStyleKey = typeof(MySignal);
        }

        public object MyContent
        {
            get => (object)GetValue(MyContentProperty);
            set { SetValue(MyContentProperty, value); }
        }
        public static readonly DependencyProperty MyContentProperty = DependencyProperty.Register(nameof(MySignal), typeof(object), typeof(MySignal), new PropertyMetadata(null));

        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _mainGrid = this.GetTemplateChild("MainGrid") as Grid;
            _mainGrid.Children.Add((UIElement)MyContent);
        }
    }
}

generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CustomControlJikken2">

    <Style TargetType="local:MySignal" >
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MySignal">
                    <Grid x:Name="MainGrid" BorderBrush="Red" BorderThickness="3">
                        <ContentPresenter />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

CustomControlを使うMainWindow部分

上で作ったCustomControlを使う部分

<Window
    x:Class="CustomControlJikken2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CustomControlJikken2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <local:MySignal>
        <TextBlock Text="AAA" Foreground="White"/>
    </local:MySignal>
</Window>

ContentProperty(Name="プロパティ名") を書いていないと

ContentProperty(Name="プロパティ名") を書いていないと、Mssing Content Property Definition云々のエラーで怒られる。

※このエラーで知ったが、今回やったようなことを「ダイレクトコンテンツ」というらしい。

ContentProperty(Name="プロパティ名")が無い場合は、Contentを書く場合でも下記のように書く必要がある。

<local:MySignal>
    <local:MySignal.MyContent>
        <TextBlock Text="AAA" Foreground="White"/>
    </local:MySignal.MyContent>
</local:MySignal>

参考

[ContentProperty("InnerContent")] で、自前コントロールにContentを持たすことができるようになるとかが書かれてる

https://stackoverflow.com/questions/36446440/how-to-use-a-contentpresenter-inside-a-usercontrol

同じ内容のかずきさんVer ListBoxみたいに、直接なかにいれたいものを並べて書けるようにするやり方が書かれてる!

https://blog.okazuki.jp/entry/20130103/1357202019

めちゃわかりやすい↓

https://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control