結果
テストアプリのコード
2024WPF/20241207_ItemsControlCanvasPanelThumb at master · gogowaten/2024WPF
github.com
作成と動作環境
- Windows 10 Home バージョン 22H2
- Visual Studio Community 2022 Version 17.12.3
- WPF
- C#
- .NET 8.0
CustomControl1.cs
using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Markup; namespace _20241207_ItemsControlCanvasPanelThumb { [ContentProperty(nameof(MyItems))] public class ItemsControlThumb : Thumb { public ObservableCollection<FrameworkElement> MyItems { get { return (ObservableCollection<FrameworkElement>)GetValue(MyItemsProperty); } set { SetValue(MyItemsProperty, value); } } public static readonly DependencyProperty MyItemsProperty = DependencyProperty.Register(nameof(MyItems), typeof(ObservableCollection<FrameworkElement>), typeof(ItemsControlThumb), new PropertyMetadata(null)); static ItemsControlThumb() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsControlThumb), new FrameworkPropertyMetadata(typeof(ItemsControlThumb))); } public ItemsControlThumb() { DataContext = this; MyItems = []; } } }
Thumbクラスと継承したItemControlThumbクラス
FrameworkElement型のコレクションの依存関係プロパティのMyItemsを持たせた
コンストラクタでMyItemsを初期化しているけど、もしこれを依存関係プロパティの登録のPropertyMetadataで初期化すると起動時に無限ループになる(なった)なので、登録時はnullを既定値に指定している
BindingはDataContextに自身を指定
[ContentProperty(nameof(MyItems))]
クラスの頭でこれを書いておくと、XAMLで直接子要素をいれることができるようになる
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:_20241207_ItemsControlCanvasPanelThumb"> <Style TargetType="{x:Type local:ItemsControlThumb}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ItemsControlThumb}"> <ItemsControl ItemsSource="{Binding MyItems}" Background="{TemplateBinding Background}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
ItemsControlThumbの要素の構成
TemplateにItemsControlを指定、さらにItemPaneltemplaにCanvasを指定。これで追加された要素はCanvasに配置されるので、Canvas.leftとかを使って表示する位置を指定できる
Bindingは
ItemsControlのItemsSourceにMyItemsを指定
BackgroundはTemplateBindingでItemsControlThumbのBackgroundを引き継ぐ
MainWindow.xaml
<Window x:Class="_20241207_ItemsControlCanvasPanelThumb.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:_20241207_ItemsControlCanvasPanelThumb" mc:Ignorable="d" Title="MainWindow" Height="350" Width="400"> <Grid> <Canvas> <Canvas.Resources> <Style TargetType="local:ItemsControlThumb"> <EventSetter Event="DragDelta" Handler="Thumb_DragDelta"/> </Style> </Canvas.Resources> <local:ItemsControlThumb Canvas.Left="0" Canvas.Top="0" Width="300" Height="200" Background="MistyRose"> <TextBlock Canvas.Left="10" Canvas.Top="10" Text="Group 1"/> <TextBlock Canvas.Left="10" Canvas.Top="30" Text="ItemsControlCanvasPanelThumb 1"/> <Button Canvas.Left="10" Canvas.Top="50" Content="Button"/> <local:ItemsControlThumb Canvas.Left="20" Canvas.Top="100" Width="100" Height="100" Background="LightSalmon"> <TextBlock Canvas.Left="10" Canvas.Top="10" Text="Group 1-1"/> <TextBlock Canvas.Left="10" Canvas.Top="30" Text="ItemsControlCanvasPanelThumb 2"/> <CheckBox Canvas.Left="10" Canvas.Top="50" Content="CheckBox"/> </local:ItemsControlThumb> <local:ItemsControlThumb Canvas.Left="150" Canvas.Top="60" Width="120" Height="100" Background="LightSalmon"> <TextBlock Canvas.Left="10" Canvas.Top="10" Text="Group 1-2"/> <TextBlock Canvas.Left="10" Canvas.Top="30" Text="ItemsControlCanvasPanelThumb 3"/> <local:ItemsControlThumb Canvas.Left="10" Canvas.Top="50" Width="100" Height="50" Background="Tomato"> <TextBlock Canvas.Left="10" Canvas.Top="10" Text="Group 1-2-1"/> <TextBlock Canvas.Left="10" Canvas.Top="30" Text="ItemsControlCanvasPanelThumb 4"/> </local:ItemsControlThumb> </local:ItemsControlThumb> </local:ItemsControlThumb> </Canvas> </Grid> </Window>
MyItemsはContentPropertyに指定してあるので、直接子要素を入れることができている
マウスドラッグ移動のイベントDragDeltaをResourcesのStyleで指定
<Canvas.Resources> <Style TargetType="local:ItemsControlThumb"> <EventSetter Event="DragDelta" Handler="Thumb_DragDelta"/> </Style> </Canvas.Resources>
Styleってプロパティを指定するものだと思っていたけど、イベントを指定するEventSetterってのがあったので使ってみた、へー便利
MainWindow.xaml.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; namespace _20241207_ItemsControlCanvasPanelThumb { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Thumb_DragDelta(object sender, DragDeltaEventArgs e) { if (sender is Thumb t) { Canvas.SetLeft(t, Canvas.GetLeft(t) + e.HorizontalChange); Canvas.SetTop(t, Canvas.GetTop(t) + e.VerticalChange); e.Handled = true; //↑ trueにしないと、重なっているThumb全てにイベントが伝播して動いてしまう } } } }
マウスドラッグ移動の処理だけが書いてある
前回の方向キーでの移動でも使ったe.Handled = true;
これを書いておかないと
Thumbが重なったところでドラッグ移動すると動きがおかしくなる、2つ重なったくらいならガクガクするくらいだけど、3つ重なっていたら少しマウスを動かしただけで画面外にすっ飛んでいく
参照したところ
ドラッグ移動可能なオブジェクトをたくさん並べる - CoMoの日記
como-2.hatenadiary.org
マツオソフトウェアブログ: Canvasにリストの中身をBindingする方法
my-clip-devdiary.blogspot.com
F#でWPF --- 可変個のコントロール - 何でもプログラミング any-programming.hatenablog.com
感想
エクセルの図形のグループ化みたいなのを目指しているけど、先は長い
要素の動的追加と削除、グループ化と解除、それに伴う処理が
要素同士の重なりの順番の調整
Groupのサイズの変更
サイズ変更は親要素にも知らせる必要がある?
キーボードフォーカスの範囲の設定と変更
2年前に挑戦したときはサイズ変更関係で詰まった
関連記事
次回は2日後
WPF、自動サイズCanvasをGroupThumbに使ってみた - 午後わてんのブログ
gogowaten.hatenablog.com
1ヶ月後
WPF、この一ヶ月でのカスタムコントロールThumbのマウスドラッグ移動のまとめ - 午後わてんのブログ
gogowaten.hatenablog.com
一昨日
WPF、カスタムコントロール使ってみた、マウスドラッグ移動できるTextBlockを作成 - 午後わてんのブログ
gogowaten.hatenablog.com