個人的にWPFの学習の最終段階はカスタムコントロールだと思っていましたが、汎用的なGUIコントロールを使う場面は、個人開発では意外と少なく、それに労力を払うより、ユーザーコントロールでソースコードを分割するぐらいがちょうど良い感じがします。
カスタムコントロールの場合、使い方さえ知っていれば、使うことが出来る汎用コントロールの私家版みたいな物で、汎用性を考えてコードを書く必要があります。ユーザーコントロールは、カスタムコントロールと似ている部分はありますが、振る舞いなどのコードをアプリケーションに合わせてカスタマイズすることが前提の使い方に成ります。
使う側としてユーザーコントロール方がハードルが高いことに成りますが、作る側としてカスタムコントロールは超高難易度に成ります。ユーザーコントロールが対象となるアプリケーションの事だけ考えてコードを書けば良いですが、カスタムコントロールは多くのアプリケーションを想定したコードを求められます。
サンプルコード
ファイル名:MiniLabel.cs
using System.Windows;
using System.Windows.Controls;
namespace UserControlSample;
public class MiniLabel : UserControl
{
// MiniLabelにはテキストブロックを配置
private readonly TextBlock _textBlock;
public MiniLabel()
{
// テキストブロックを生成
_textBlock = new TextBlock();
// MiniLabelのContentにセット
Content = _textBlock;
}
// TextをDPとしてXAMLでバインド可能にしている。
// TextPropertyの変更イベントで新しい値を_textBlock.Textに代入している。
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text),
typeof(string),
typeof(MiniLabel),
new FrameworkPropertyMetadata(string.Empty, (d, e) =>
((MiniLabel)d)._textBlock.Text = (string)e.NewValue));
// Textプロパティ
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
}
UserControl
を継承したMiniLabel
があります。
MiniLabel
はフィールドとしてTextBlock
を保持していて、コンストラクタでTextBlock
のオブジェクトを生成し、MiniLabel
のコンテンツにしているので、レイアウト的にもMiniLabel
上にTextBlock
が配置されることに成ります。ただ、MiniLabel
の親クラスのUserControl
はコンテナで、見えるのはTextBlock
のみとなります。
MiniLabel
で文字を表示するためにText
プロパティを定義します。これは普通のクラスのプロパティ(CLR)です。
そしてTextProperty
という名前のプロパティがあり、こちらは依存関係プロパティ(DP)で、TextプロパティをXAML側からバインド可能にしています。
Text
プロパティ(CLR)ではTextProperty
に値をセットしたり、取得していることが確認できます。
TextProperty
(DP)では、変更イベントで、値をTextBlock.Text
に代入しています。
大まかなデータの流れは
Text
(CLR) ⇒ TextProperty
(DP) ⇒ _textBlock.Text
とつながっており、DPを経由することでXAMLでTextプロパティがバインド可能にしている点を除けば、普通のC#のコードに成ります。
以上がUserControlの基本的なコントロールの配置のサンプルに成ります。
ただ、このままでは、機能を限定したTextBlockとなってしまいます。コントロールで発生するイベントを処理するコードビハインドを記述することで、固有の振る舞いをするユーザーコントロールとして 機能し始めます。
複数のコントロールを配置することも出来ます。コントロール以外のフィールドで情報を持たせることも可能です。
UserControlを継承し、プロパティをバインド可能にする処理以外は、普通のC#のクラスと同じと考えて差支えないと思います。
・ユーザーコントロールMiniLabelを使う側のXAMLは以下の様になります。
ファイル名:MainWindow.xaml
<Window x:Class="UserControlSample.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:local="clr-namespace:UserControlSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Margin="12">
<local:MiniLabel Text="こんにちは、MiniLabel!"/>
</StackPanel>
</Window>
MiniLabelというコントロールを配置し、Textプロパティにテキストを直接セットしています。
TextプロパティはDPとして定義しているのでバインディングの書式も受け入れ可能です。
実行イメージ
感想
ユーザーコントロールとして全く意味のないサンプルコードですが、外側に公開するプロパティ(DP)を決めて上げれば後は、コードビハインドですから、WinFormsみたいにイベントドリブンのコードを書くことに成ります。また、ICommandをバインドすることも出来ます。

ただ、こちらはユーザーコントロールのXAMLが定義してあり、レイアウトは直観的でよいのですが、バインドの関係がややこしくなるので、今回のサンプルコードはC#オンリーで作成してみました。
コメント