午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPF、Image(画像)をマウスドラッグ移動、ThumbのTemplateを変更して作成

結果

f:id:gogowaten:20210227000115g:plain
今回の結果



コード

github.com




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

こんな感じかな

今回はTemplateにCanvasを使ったけど、要はImageを追加できればいいので、GridとかStackPanelとかのパネル系コントロールならできると思う、Gridはできた



Thumbを継承させたImageThumbクラス作成

f:id:gogowaten:20210226220137p:plain
ImageThumb

74~77行目
ControlTemplateを使ってTemplate変更する
Canvasだけで構成されたTemplateを作成して、これをThumbのTemplateに指定する

80行目
Templateの中のCanvasを取得しておく

84~89行目
CanvasにImageを追加すれば画像が表示されたThumbになる


ImageThumbを使う

追加先はCanvas限定

f:id:gogowaten:20210226232910p:plain
xaml
ImageThumbの座標指定はCanvas.SetLeftとCanvas.Topで行ったので、追加(表示)するパネルはCanvas限定になる、他のパネル系(Gridとか)だと動かない


作成してCanvasに追加

f:id:gogowaten:20210226232639p:plain
使う
23、24行目
表示位置の指定。指定しなくても(0,0)に表示されるけど、移動できなくなるので必要


DragDeltaイベントでドラッグ移動

f:id:gogowaten:20210226233826p:plain
DragDeltaイベント
このイベントがImageコントロールにも、あればいいんだけどねえ


感想

5年前から同じことしてる
前よりは簡潔にかけたような気がするけどどうかなあ
あと今回はImageだけに絞ったけど、CanvasがTemplateなので基本的には他のコントロールも追加できて、TextBlockやBorderは普通に追加できて移動もできた。ButtonやTextBoxは追加表示はできるけど移動ができなかったけど、IsEnableをfalseにしたら動いたので、IsEnableがtrueだとクリックとかキー入力が優先されてDragDeltaイベントがスルーされるみたい
エクセルの図形のグループ化を真似したいってのがあって、今回のだとImageThumbの中にImageThumbを入れる形にして、それぞれを移動できるようになればできそうなんだけど、そうしてみたらDragDeltaイベントがそれぞれで同時発動して動きがおかしくなる、難しい




関連記事

次回は2日後

gogowaten.hatenablog.com


前回のWPF記事は一昨日

gogowaten.hatenablog.com

5年前

gogowaten.hatenablog.com WPFだけどWindowsFormsの方法は煩雑、マウスのアップダウンイベントでマウスの移動量を測ってMargenプロパティで座標指定

gogowaten.hatenablog.com ImageコントロールにDragDeltaイベントを移植?コピペコードで当時もわかってなかったけど、今見てもよくわからんw

gogowaten.hatenablog.com Templateの変更をXAMLで行っている、動的な追加をしなければこっちのほうがラク

gogowaten.hatenablog.com

gogowaten.hatenablog.com