午後わてんのブログ

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

WPFとVB.NET、Canvasの中に回転表示したコントロールのドラッグ移動で気づいたこと

RenderTransformやLayoutTransformで
回転(変形)表示したコントロール見た目と中身が一致しないので扱いが難しい
以下はCanvasパネルに表示した場合だけで確認
 

f:id:gogowaten:20191025112217p:plain

サイズが100x100のThumbコントロール
プロパティ画面から右に10度回転の指定をすると
RotateTransform Angle = "10"がXAMLの方に挿入される
RenderTransformとRotateTransformで回転角度が指定されたことで
Thumbも回転表示される
でも回転したのは見た目だけで内部的には回転していない
 
 
イメージ 2
右に傾けた青色のThumb
赤枠が元の位置とサイズ
緑枠が回転後のThumbが収まる位置とサイズ
赤丸が元の左上の位置を表す
Canvasパネルの中でマウスドラッグでコントロールを移動させる場合は
緑枠の位置とサイズが必要になる
 
Canvas1の中のThumb1の見た目通りのRect(位置とサイズ)取得
        Dim gt As GeneralTransform = thumb1.TransformToVisual(canvas1)
        Dim s As Size = thumb1.RenderSize
        Dim r As Rect = gt.TransformBounds(New Rect(s))
これが緑枠になる
これはOK
納得
 
全く予想外だったのが
回転表示したコントロール上でのマウスの動きに対する位置の取得!
 
ThumbコントロールはDragDeltaイベントで
マウスドラッグの移動距離を簡単に取得できるので
これを使ってどんな値が取得できているのか見てみた
イメージ 3
回転前と回転後のThumbコントロール上で
マウスを移動させた時に取得できる値の変化
表示している数値がマイナスならそれぞれ左と上
回転前では普通に横の値が変化しているけど
右に90度回転した後だと縦の値が変化していて上に移動していることになっている
つまり回転角度に合わせてマウスの位置も変換されているみたい
うーん、これはある意味見た目通りの動きなのかも?
 
このわかりづらい罠みたいな動きはThumbのDragDeltaイベントだけじゃなくて
その他のコントロールでのMouseMoveイベントでも同じだった
 

f:id:gogowaten:20191025112320p:plain

 

f:id:gogowaten:20191025112332p:plain

 

Imports System.Windows.Controls.Canvas
Imports System.Windows.Controls.Primitives

Class MainWindow
    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        Dim gt1 As GeneralTransform = thumb1.TransformToVisual(canvas1)
        Dim r1 As Rect = gt1.TransformBounds(New Rect(New Size(thumb1.Width, thumb1.Height)))
        'Dim cp As Point = New Point(GetLeft(rb), GetTop(rb))

        Dim rg1 As New RectangleGeometry(r1)
        path1.Data = rg1

        Dim p1 As Point = gt1.Transform(New Point(0, 0))
        SetLeft(elli1, p1.X - (elli1.Width / 2))
        SetTop(elli1, p1.Y - (elli1.Height / 2))

    End Sub

    Private Sub kaiten_ValueChanged(sender As Object, e As RoutedPropertyChangedEventArgs(Of Double)) Handles kaiten.ValueChanged
        Call waku()
    End Sub

    Private Sub kakusyuku_ValueChanged(sender As Object, e As RoutedPropertyChangedEventArgs(Of Double)) Handles kakusyuku.ValueChanged
        Call waku()
    End Sub

    Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Call motonowaku()
    End Sub

    Private Sub waku()
        Dim rt As New RotateTransform(kaiten.Value)
        thumb1.RenderTransform = rt
        Dim st As New ScaleTransform(kakusyuku.Value, kakusyuku.Value)
        thumb1.RenderTransform = st


        Dim tg As New TransformGroup
        Dim tc As New TransformCollection
        tc.Add(rt)
        tc.Add(st)
        tg.Children = tc

        thumb1.RenderTransform = tg 'RenderTransform
        'thumb1.LayoutTransform = rt 'LayoutTransform    
        Dim gt As GeneralTransform = thumb1.TransformToVisual(canvas1)
        Dim s As Size = thumb1.RenderSize
        Dim r As Rect = gt.TransformBounds(New Rect(s))
        Dim rg As New RectangleGeometry(r)
        path1.Data = rg

        '○
        Dim topLeft As Point = gt.Transform(New Point(0, 0))
        SetLeft(elli1, topLeft.X)
        SetTop(elli1, topLeft.Y)

    End Sub
    Private Sub motonowaku()
        Dim r2 As New Rect(GetLeft(thumb1), GetTop(thumb1), thumb1.Width, thumb1.Height)
        Dim rg2 As New RectangleGeometry(r2)
        path2.Data = rg2

    End Sub

    Private Sub rb_DragDelta(sender As Object, e As Primitives.DragDeltaEventArgs) Handles thumb1.DragDelta
        textblock1.Text = $"マウスは横に{e.HorizontalChange:000}、縦に{e.VerticalChange:000}移動"
    End Sub

    Private Sub rb_DragCompleted(sender As Object, e As DragCompletedEventArgs) Handles thumb1.DragCompleted
        Cursor = Cursors.Arrow
        textblock1.Text = $"マウスは横に000、縦に000移動"
    End Sub

    Private Sub rb_DragStarted(sender As Object, e As DragStartedEventArgs) Handles thumb1.DragStarted
        Cursor = Cursors.Hand
    End Sub
End Class
 
 
 
 
回転させたものをドラッグ移動させると
ウィンドウの外に勢い良くすっ飛んでいく原因を探っていて気づいたけど
1週間くらいかかったYO!
気づいたのはいいけど今度は期待通りの値を取得する方法がわからない
思いついたのが

gogowaten.hatenablog.com

 
WPFVBでアプリ作る準備その2、ControlTemplateの中のControlを取得する ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/13906217.html
この時に使ったControlTemplateを使う方法
 
イメージ 4
XAMLのほうで
Thumb.Template
ControlTemplate
Image
ってThumbのControlTemplateにImageを入れておいてImageだけを回転させれば
Thumbから取得できるマウスの値は期待したものになるし
画像も回転表示できる
 
中のImageを回転させるためには
対象となるThumbのTemplateの中にあるimage1と名前を付けておいたImageを取得、tがThumb
 
  'テンプレートの中のコントロールを取得するTemplate.FindName
        Dim img As Image = t.Template.FindName("image1", t)
 
あとはImageに対してRotateTransformを使って回転させればいい
 
Imageを30度回転するとき
 
Dim rt As New RotateTransform(30)
Image.RenderTransform = rt
 
ふつうにできる
回転後はImageのサイズが変わるけどThumbも自動で変わるので
指定する必要はないみたい?
ただし両方共見た目だけが変化しただけで内部では回転前の位置とサイズ
なのはプロパティのRenderSizeとか見るとわかる
 
 
 
回転後の見た目通りのRect(位置とサイズ)を取得、上の例だと緑枠の取得するには
Imageを対象にする
 
tがThumb、canvas1がCanvas
        Dim img As Image = t.Template.FindName("image1", t)
        Dim gt As GeneralTransform = img.TransformToVisual(canvas1)
        Dim s As Size = img.RenderSize
        Dim r As Rect = gt.TransformBounds(New Rect(s))
 
そんなこんなで回転や拡大をできるようになった
イメージ 5
動かすコントロールをImageからImageを中に入れたThumbに変更することになって、大幅書き換えになったけど満足の行くできになった
画像の回転はWindowsフォームアプリのときは結構手間がかかったんだけど
WPFでは実質2行でできるから簡単にできそうだなって思って作り始めたら
思わぬところで時間がかかったw
でももっと作りこんでから気づいた場合は書き直しがもっと大変だろうから
今回気づけてよかった
 
 
アプリとソース一式ダウンロード先
ヤフーボックス
 
2016年5月22日追記
関連記事、3ヶ月後

gogowaten.hatenablog.com

 
WPFVB.NET、ControlTemplateを使ったThumbを回転表示する時に回転させるのはどれがいいのか ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14157487.html
 
2017年6月29日は4ヶ月後

gogowaten.hatenablog.com

 
WPF、変形した要素を指定位置に移動、NotifyProperty ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14998511.html
 
7年後
WPF、マウスでTextBoxのサイズ変更するのにAdorner(装飾者)を使ってみた - 午後わてんのブログ

gogowaten.hatenablog.com