- 結果
- Visual Studio起動からのCustomControl作成手順
- マウスドラッグ移動できるTextBlock
- 移動できて文字表示できるEllipse●
- 一つのファイルに複数のカスタムコントロール
- テストアプリのコード
- 参照したところ
- 感想
- 関連記事
結果
Visual Studio起動からのCustomControl作成手順
今回は20241205_Customにした
今なら.NET8.0で問題ないはず
いつものデザイン画面、ここまではいつもと同じ
メニューにある表示からでも表示できるし、ctrl+alt+Lでも表示できる
プロジェクト名のところで右クリック
メニューから追加→新しい項目をクリック
一覧からWPFのカスタムコントロールを選択、名前をつけて追加する
今回の名前はCustomControl1にした
ソリューションエクスプローラーで確認すると、CustomControl1.csとThemesフォルダと、その中にGeneric.xamlが追加されている
どうやらこの2つが連携していて、カスタムコントロールになるみたい
わかっている人向けの使い方が書いてある
こっちはエラーを示す波線が表示されている
何のエラーなのか見てみると
CustomControl1が見つからないって言ってる、すぐ隣りにあるのに?
これはビルドすると認識してくれる
マウスドラッグ移動できるTextBlock
その前に
2つのファイルの初期状態を見てみる
一応これでCustomControl1っていうカスタムコントロールはできているってことかな?
目立つ機能はないし、Generic.xamlを見ても、中身はBorderだけ
でも、Templateを指定しているので、これを真似して書いていけばできそう(できた)
Thumbコントロールはドラッグ移動系のイベントが用意されているので、これを継承して、見た目をTextBlockに変更する
見た目の変更はTemplateプロパティにTextBlockを指定する
CustomControl1.csにTextThumbクラスを追加したらビルド
TextThumbはThumbを継承
MyTextはDependencyProperty(依存関係プロパティ)
これは、TextBlockにあるような文字列を表示するTextプロパティが、Thumbにはないので追加した
これをTextBlockのTextプロパティとBindingする
staticとpublicなコンストラクタ?
staticの方は初期状態にあったのを真似て書いただけで、最初はここにBinding用のDataContext = this;を書いたらエラーになったので、publicなのを用意して書いた
言われてみればなんとなくわかるけど、まだ直感的には理解できない
これでCustomControl1.csのほうは完成、次はGeneric.xaml
TextThumbのほうをビルドしてあれば、入力候補に出てくる
こっちも初期状態で書いてあったのを見ながら書いた
ControlTemplateにTextBlockを指定して、TextプロパティをMyTextプロパティとBindingしただけ
これでCustomControlのTextThumb完成!
使ってみる
その前に確認
入力候補に出てくる
これも入力候補に出てくる
Thumbコントロールを継承しているので、DragDeltaイベントも候補に出てくる
F12キーで定義に移動して
マウスドラッグ移動の処理を追加、11行目と、27から30行目
これでマウスドラッグ移動できるはず
背景色とかも指定したい
画像ではBackground(背景色)の指定が反映されているけど、一手間掛ける必要があった
ThumbにもBackgroundっていういかにも背景色を指定できそうなプロパティはあるんだけど、指定しても無視されるので、Templateで使っているTextBlockのBackgroundとBindingすることにした
Background以外のForegroundやFontSizeは何もしなくても反映された、プロパティによって違うんだねえ
Generic.xamlに戻ってBackgroundのBinding
25行目がそれ
TextBlockのBackgroundプロパティと
ThumbのBackgroundプロパティを
TemplateBindingってのを使ってBinding
これでBackgroundも反映される
同じようなプロパティが双方にある場合のBindingはTemplateBindingってのを使えば、わざわざ自前でDependencyPropertyを作らなくてもできるみたい
移動できて文字表示できるEllipse●
さっきのTextThumbを継承して、MaruThumbクラスを作成
78行目から88行目がそれ
文字列を受け取るTextプロパティは、TextThumbから継承しているMyTextプロパティがあるので、新たに書く必要がない、楽ちん
Generic.xamlにMaruThumb用のStyleを追加
31行目から44行目がそれ
Gridの中でEllipseにTextBlockを重ねている
36行目
EllipseのFillプロパティとTextThumbのBackgroundプロパティをTemplateBindingでBindingしている、プロパティの名前は違うけど型はどちらも同じBrush型なのでこれで問題ないみたい
37行目
同じようにTemplateBindingを使ってTextBlockのTextプロパティとTextThumbのMyTextプロパティをBinding
その下2行はTextBlockの中央寄せ表示
一つのファイルに複数のカスタムコントロール
プロジェクトにカスタムコントロールを新規作成で追加されたCustomControl1.csには、複数のカスタムコントロールを書いてもいいみたいで、期待通りに動いている
テストアプリのコード
2024WPF/20241205_Custom at master · gogowaten/2024WPF
CustomControl1.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; namespace _20241205_Custom { public class TextThumb : Thumb { public string MyText { get { return (string)GetValue(MyTextProperty); } set { SetValue(MyTextProperty, value); } } public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register(nameof(MyText), typeof(string), typeof(TextThumb), new PropertyMetadata(string.Empty)); static TextThumb() { DefaultStyleKeyProperty.OverrideMetadata(typeof(TextThumb), new FrameworkPropertyMetadata(typeof(TextThumb))); } public TextThumb() { DataContext = this; } } public class MaruThumb : TextThumb { static MaruThumb() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MaruThumb), new FrameworkPropertyMetadata(typeof(MaruThumb))); } public MaruThumb() { DataContext = this; } } }
Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:_20241205_Custom"> <Style TargetType="{x:Type local:TextThumb}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:TextThumb}"> <TextBlock Text="{Binding MyText}" Background="{TemplateBinding Background}"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type local:MaruThumb}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MaruThumb}"> <Grid> <Ellipse Fill="{TemplateBinding Background}"/> <TextBlock Text="{TemplateBinding MyText}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
MainWindow.xaml.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; namespace _20241205_Custom { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void TextThumb_DragDelta(object sender, DragDeltaEventArgs e) { if (sender is TextThumb tt) { Canvas.SetLeft(tt, Canvas.GetLeft(tt) + e.HorizontalChange); Canvas.SetTop(tt, Canvas.GetTop(tt) + e.VerticalChange); } } } }
MainWindow.xaml
<Window x:Class="_20241205_Custom.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:_20241205_Custom" mc:Ignorable="d" Title="MainWindow" Height="350" Width="400"> <Grid> <Canvas> <local:TextThumb Canvas.Left="10" Canvas.Top="10" MyText="TextBlockThumb" Background="Tomato" Foreground="LemonChiffon" FontSize="30" DragDelta="TextThumb_DragDelta"/> <local:MaruThumb Canvas.Left="100" Canvas.Top="100" Width="100" Height="100" Background="MediumAquamarine" Foreground="Azure" MyText="bbb" FontSize="30" DragDelta="TextThumb_DragDelta"/> </Canvas> </Grid> </Window>
作成と動作環境
- Windows 10 Home バージョン 22H2
- Visual Studio Community 2022 Version 17.12.3
- WPF
- C#
- .NET 8.0
参照したところ
カスタムコントロール(CustomControl)を作る - tera1707’s blog
感想
今までは移動できるTextBlockを作成するときには、ThumbのTemplateを改変するところをC#でのコード(コードビハインド)で書いていたけど、今回はTemplate改変をXAMLで書くことができるCustomControlで作成してみた
なんかねえ、Templateをコードビハインドで書くのは良くて、理由は、書いた通りにならないことがあるからってことらしい
でも今回ので解決できた
WPFのコントロール作成にはユーザーコントロールとカスタムコントロールがある
ユーザーコントロールでは、以前にNumericUpDownもどきを作ったことがあったので、これでできないか試したんだけど、Thumbを継承しての作成はできなかった
ユーザーコントロールっていうコントロールの中に別のコントロールを組み込んでいく感じで、どこまで行ってもユーザーコントロールが主体、基軸
一方のカスタムコントロールは難しそうな雰囲気があるので敬遠していたけど、もうこれしかないと思って試してみたらできたってわけ
カスタムコントロールは継承元を選べるので自由度が高い
関連記事
次、2日後
WPF、ItemsControlを使って要素を入れ子にできるカスタムコントロールThumb、グループ化みたいなもの - 午後わてんのブログ
gogowaten.hatenablog.com
19日後、同じようなものをDataTemplateSelectorで表現できた
DataTemplateSelector使ってみた、実際便利 - 午後わてんのブログ
gogowaten.hatenablog.com
1ヶ月後
WPF、この一ヶ月でのカスタムコントロールThumbのマウスドラッグ移動のまとめ - 午後わてんのブログ
gogowaten.hatenablog.com
1ヶ月後
WPF、サイズ可変+マウスドラッグ移動可能なCanvasを「できるだけ簡易」にカスタムコントロールで作ってみた - 午後わてんのブログ
gogowaten.hatenablog.com
2年前
WPF、マウスドラッグ移動できるTextBox、Templateを改変したThumbで作成 - 午後わてんのブログ
gogowaten.hatenablog.com
3年前
WPF、Image(画像)をマウスドラッグ移動、ThumbのTemplateを変更して作成 - 午後わてんのブログ