WPF、Image(画像)をマウスドラッグ移動、ThumbのTemplateを変更して作成
結果
コード
MainWindow.xaml
<Window x:Class="_20210226_FlatThumb移動画像.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:_20210226_FlatThumb移動画像" mc:Ignorable="d" Title="MainWindow" Height="350" Width="400"> <Grid> <Canvas x:Name="MyCanvas" UseLayoutRounding="True"/> </Grid> </Window>
MainWindow.xaml.cs
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using System.Windows.Controls.Primitives; namespace _20210226_FlatThumb移動画像 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); MyInitialize(); } private void MyInitialize() { //画像1枚だけのThumb ImageThumb t1 = new(); t1.DragDelta += Thumb_DragDelta; Canvas.SetLeft(t1, 0); Canvas.SetTop(t1, 0); //画像ファイルからBitmapImage作成してThumbにセット BitmapImage bmp1 = new(new Uri(@"D:\ブログ用\テスト用画像\collection_1.png")); bmp1.Freeze(); t1.SetImage(new Image() { Source = bmp1 }); //MainWindowに表示 MyCanvas.Children.Add(t1); //画像2枚表示のThumb ImageThumb t2 = new(); t2.DragDelta += Thumb_DragDelta; Canvas.SetLeft(t2, 200); Canvas.SetTop(t2, 0); //1枚めセット t2.SetImage(new Image() { Source = bmp1 }); //2枚めセット BitmapImage bmp2 = new(new Uri(@"D:\ブログ用\テスト用画像\collection_2.png")); bmp2.Freeze(); //少し位置をずらしてセットしてみた t2.SetImage(new Image() { Source = bmp2 }, 40, 40); MyCanvas.Children.Add(t2); } //マウスドラッグで移動 private void Thumb_DragDelta(object sender, DragDeltaEventArgs e) { var t = sender as ImageThumb; //移動先の座標は、今の座標 + マウスの移動量 Canvas.SetLeft(t, Canvas.GetLeft(t) + e.HorizontalChange); Canvas.SetTop(t, Canvas.GetTop(t) + e.VerticalChange); } } /// <summary> /// Image表示専用Thumb /// ControlTemplateを使ってCanvasだけで構成されたThumb /// </summary> public class ImageThumb : Thumb { private const string BASE_PANEL_NAME = "canvas"; private Canvas BasePanel;//ThumbのベースパネルにするCanvas public ImageThumb() { ControlTemplate template = new(typeof(Thumb)); template.VisualTree = new FrameworkElementFactory(typeof(Canvas), BASE_PANEL_NAME); this.Template = template; //テンプレート再構築、これで中の要素を名前で検索取得できるようになる this.ApplyTemplate(); BasePanel = (Canvas)this.Template.FindName(BASE_PANEL_NAME, this); } //画像をCanvasにセット public void SetImage(Image img, double left = 0, double top = 0) { BasePanel.Children.Add(img); Canvas.SetLeft(img, left); Canvas.SetTop(img, top); } } }
今回の方法
Thumbにはマウスドラッグ移動用のイベントDragDeltaがあるので、Thumbに画像(Imageコントロール)が表示できればいい
ThumbのTemplateを変更
イメージ的には
- Thumb.Template
- ┗ Canvas
- ┗ Image
- ┗ Canvas
こんな感じかな
今回はTemplateにCanvasを使ったけど、要はImageを追加できればいいので、GridとかStackPanelとかのパネル系コントロールならできると思う、Gridはできた
Thumbを継承させたImageThumbクラス作成
74~77行目
ControlTemplateを使ってTemplate変更する
Canvasだけで構成されたTemplateを作成して、これをThumbのTemplateに指定する
80行目
Templateの中のCanvasを取得しておく
84~89行目
CanvasにImageを追加すれば画像が表示されたThumbになる
ImageThumbを使う
追加先はCanvas限定
ImageThumbの座標指定はCanvas.SetLeftとCanvas.Topで行ったので、追加(表示)するパネルはCanvas限定になる、他のパネル系(Gridとか)だと動かない
作成してCanvasに追加
23、24行目
表示位置の指定。指定しなくても(0,0)に表示されるけど、移動できなくなるので必要
DragDeltaイベントでドラッグ移動
このイベントがImageコントロールにも、あればいいんだけどねえ
感想
5年前から同じことしてる
前よりは簡潔にかけたような気がするけどどうかなあ
あと今回はImageだけに絞ったけど、CanvasがTemplateなので基本的には他のコントロールも追加できて、TextBlockやBorderは普通に追加できて移動もできた。ButtonやTextBoxは追加表示はできるけど移動ができなかったけど、IsEnableをfalseにしたら動いたので、IsEnableがtrueだとクリックとかキー入力が優先されてDragDeltaイベントがスルーされるみたい
エクセルの図形のグループ化を真似したいってのがあって、今回のだとImageThumbの中にImageThumbを入れる形にして、それぞれを移動できるようになればできそうなんだけど、そうしてみたらDragDeltaイベントがそれぞれで同時発動して動きがおかしくなる、難しい
関連記事
次回は2日後
前回のWPF記事は一昨日
5年前
gogowaten.hatenablog.com
WPFだけどWindowsFormsの方法は煩雑、マウスのアップダウンイベントでマウスの移動量を測ってMargenプロパティで座標指定
gogowaten.hatenablog.com
ImageコントロールにDragDeltaイベントを移植?コピペコードで当時もわかってなかったけど、今見てもよくわからんw
gogowaten.hatenablog.com
Templateの変更をXAMLで行っている、動的な追加をしなければこっちのほうがラク