要素を指定した位置に移動させる
水色の四角枠は目印、サイズ100x100、x,y=100,100
赤の四角が目的の要素、サイズ100x100
移動ボタン1と2どちらも100,100へ移動させるボタンだけど基準が違う
基準A:ボタン1は見た目上の位置
基準B:ボタン2は内部的な位置
変形後も内部の位置やサイズは変化しないから見た目とズレるから、このズレを計算しての位置指定
変形後の要素を他の要素にピッタリ重ねたいとか、横にくっつけたいとかしたいので基準Aの方法が必要だった
x,y=100,100のとき20度回転させると
x,y=85.9,85.9にズレる
これを100,100にするにはズレの分だけずらせばいい
ズレは-14.1なので
100+(-(-14.1))=114.1
100,100ぴったりになった
要素の中心を軸に回転させるとピッタリ収まる四角枠は位置もサイズも変化する
今回の赤の四角はThumbなんだけど
Canvasを入れたControlTemplateを作ってそれをThumbのTemplateに指定して、そのCanvasに赤のBorderを入れている
Thumb.Template
┗Canvas
┗Border
変形させているのはTemplateのCanvas
用意したPriorityは全部NotifyPriority通知プロパティにした
MyLeftX座標、左位置
MyTopY座標、上位置
MyAngle回転角度
DiffPoint見た目と中身の位置の差分
OutSize見た目の大きさ、ぴったり枠のサイズ
MyOutBoundsぴったり枠の位置とサイズ
TransformToVisualとTransformBoundsを使うと要素の変形後のRect(サイズや位置)を取得することができる
Dim gt As GeneralTransform = 要素.TransformToVisual(今回はThumb)
Dim r As Rect = gt.TransformBounds(New Rect(New Size(要素.Width, 要素.Height)))
この場合だと位置はズレの値(Thumbとの相対的な位置)になっている(-14.05…)
これをDiffPointに入れておいて
サイズ(128.17…)はOutSizeに入れておく
変形(回転)させたらぴったり枠も更新するので
MyAngleのSetのところでそれを実行する
RootRotate.Angle=Valueが実際に回転指定しているところ
DiffPoint見た目と中身の位置の差分
OutSize見た目の大きさ、ぴったり枠のサイズ
を更新して
MyOutBoundsぴったり枠の位置とサイズ
これも更新
移動させたときは
MyOutBoundsの位置の更新だけなので
MyLeftとMyTopのSetのところで
Canvas.SetLeft(Me, Value)が実際に位置指定しているところ
そのあとのCall SetOutBoundsで
x,yだけ差分を足して、サイズはそのまま
ぴったり枠基準(基準A)で指定位置に移動
指定された値に差分を足した値を指定
なのでMainWindowからは単純に
MyExThumb.SetPoint2(100, 100)
だけで見た目上の100,100の位置に移動させることができる
デザイン画面、XAMLを書くと投稿エラーになるから画像で
VBコード
MainWindowとThumbを継承したExThumb
Imports System.ComponentModel
Imports System.Windows.Controls.Primitives
Class MainWindow
Private WithEvents MyExThumb As ExThumb
Private Sub MyCheck()
Dim root = MyExThumb.testRootCanvas
End Sub
Private Sub MyCheck2()
MyExThumb.SetPoint2(100, 100)
End Sub
Private Sub MyMove()
MyExThumb.MyLeft = 100
MyExThumb.MyTop = 100
End Sub
Private Sub MyMove2()
MyExThumb.SetPoint2(0, 0)
End Sub
Private Sub MyMove3()
MyExThumb.MyLeft = 0
MyExThumb.MyTop = 0
End Sub
'数値確認用のTextBlockへのBinding
Private Sub SetTextBlockBinding(so As Object, sName As String, tb As TextBlock)
Dim b As New Binding(sName) With {.Source = so, .StringFormat = sName & " = {0:0.0}"}
tb.SetBinding(TextBlock.TextProperty, b)
End Sub
Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
AddHandler btnCheck.Click, AddressOf MyCheck
'AddHandler btn1.Click, AddressOf MyMove2
AddHandler btn2.Click, AddressOf MyCheck2
'AddHandler btn3.Click, AddressOf MyMove3
AddHandler btn4.Click, AddressOf MyMove
'ExThumbに100x100の赤Borderを追加してMyCanvasに表示
Dim ext As New ExThumb(New Border With {
.Width = 100, .Height = 100, .Background = Brushes.Red, .Opacity = 0.5})
Canvas.SetLeft(ext, 0) : Canvas.SetTop(ext, 0)
MyCanvas.Children.Add(ext)
MyExThumb = ext
'回転角度をSliderにBinding
Dim b As Binding
b = New Binding(NameOf(ExThumb.MyAngle)) With {.Source = MyExThumb, .Mode = BindingMode.TwoWay}
sldAngle.SetBinding(Slider.ValueProperty, b)
'数値確認用のTextBlockへのBinding
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyAngle), tbAngle) '角度
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.DiffPoint), tbRect) '差分座標
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyLeft), tbLeft) '実際のX座標
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyOutBounds), tbBounds) '見た目のピッタリ枠
End Sub
Private Sub MyExThumb_DragDelta(sender As Object, e As DragDeltaEventArgs) Handles MyExThumb.DragDelta
MyExThumb.MyLeft += e.HorizontalChange
MyExThumb.MyTop += e.VerticalChange
End Sub
End Class
Public Class ExThumb
Inherits Thumb 'Thumbを継承
Implements ComponentModel.INotifyPropertyChanged '通知プロパティ用
Private RootCanvas As Canvas
Private RootRotate As RotateTransform
Public testRootCanvas As Canvas
'OutBoundsの左上座標を指定
Public Sub SetPoint2(x As Double, y As Double)
MyLeft = x + (-DiffPoint.X)
MyTop = y + (-DiffPoint.Y)
End Sub
'DiffPointとOutBoundsの更新、変形時に実行する
Private Sub SetDiffPointAndOutSize()
Dim gt As GeneralTransform = RootCanvas.TransformToVisual(Me)
Dim r As Rect = gt.TransformBounds(New Rect(New Size(RootCanvas.Width, RootCanvas.Height)))
DiffPoint = r.Location
OutSize = r.Size
Call SetOutBounds()
End Sub
'OutBoundsの更新、移動時に実行する
Private Sub SetOutBounds()
Dim r As Rect = New Rect(New Point(DiffPoint.X + MyLeft, DiffPoint.Y + MyTop), OutSize)
MyOutBounds = r
End Sub
#Region "Property"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
'変形前後の左上座標の差分
Private Property _DiffPoint As Point
Public Property DiffPoint As Point
Get
Return _DiffPoint
End Get
Set(value As Point)
_DiffPoint = value
Call NotifyPropertyChanged()
End Set
End Property
'要素がピッタリ収まるサイズ
Private Property _OutSize As Size
Public Property OutSize As Size
Get
Return _OutSize
End Get
Set(value As Size)
_OutSize = value
Call NotifyPropertyChanged()
End Set
End Property
'回転角度
Private Property _MyAngle As Double
Public Property MyAngle As Double
Get
Return _MyAngle
End Get
Set(value As Double)
_MyAngle = value
RootRotate.Angle = value
Call NotifyPropertyChanged()
Call SetDiffPointAndOutSize()
End Set
End Property
'X座標
Private Property _MyLeft As Double
Public Property MyLeft As Double
Get
Return _MyLeft
End Get
Set(value As Double)
_MyLeft = value
Canvas.SetLeft(Me, value)
Call NotifyPropertyChanged()
Call SetOutBounds()
End Set
End Property
'Y座標
Private Property _MyTop As Double
Public Property MyTop As Double
Get
Return _MyTop
End Get
Set(value As Double)
_MyTop = value
Canvas.SetTop(Me, value)
Call NotifyPropertyChanged()
Call SetOutBounds()
End Set
End Property
'要素がピッタリ収まる四角枠
Private Property _MyOutBounds As Rect
Public Property MyOutBounds As Rect
Get
Return _MyOutBounds
End Get
Set(value As Rect)
_MyOutBounds = value
Call NotifyPropertyChanged()
End Set
End Property
#End Region
'ControlTemplate作成、Canvasを一個入れるだけ
Private Function CreateTemplate() As ControlTemplate
Dim ct As New ControlTemplate(GetType(Thumb))
Dim c As New FrameworkElementFactory With {.Name = "RootCanvas", .Type = GetType(Canvas)}
ct.VisualTree = c
Return ct
End Function
'コンストラクタ
'渡された要素をTemplateの中のCanvasに追加する
Public Sub New(elm As FrameworkElement)
Template = CreateTemplate()
ApplyTemplate() 'Templateを再構築、必要
'TemplateのCanvasを取得して渡された要素を追加
RootCanvas = Me.Template.FindName("RootCanvas", Me)
With RootCanvas
.Children.Add(elm)
.Height = elm.Height
.Width = elm.Width
End With
testRootCanvas = RootCanvas 'test
'各種TransformをGroupにしてTemplateのCanvasのRenderTransformに指定
RootRotate = New RotateTransform
Dim sc As New ScaleTransform
Dim sk As New SkewTransform
Dim tg As New TransformGroup
With tg.Children
.Add(sc) : .Add(sk) : .Add(RootRotate)
End With
With RootCanvas
.RenderTransformOrigin = New Point(0.5, 0.5)
.RenderTransform = tg
.Background = Brushes.Transparent
End With
'ピッタリ枠とかを更新するために角度指定
MyAngle = 0.0
End Sub
End Class
前回までのDependencyPropertyを最初に使って試したけどうまく書けなくて
今回はNotifyPropertyっていう値が変更されたら通知を出せるプロパティを使った
うまくできなかったのは変形(Angleを変更)したときにTransformToVisualを使って変形後のRectを求めるところ、こんなふうに書いてみたけど
なので今回のようにNotifyPropertyって言う値の変更時に通知を出せる通知プロパティを使った、これは一年前の方法とほとんど変わらないけど少しうまく書けた気がする
参照したところ
INotifyPropertyChanged インターフェイス (System.ComponentModel)
https://msdn.microsoft.com/ja-jp/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
特に紫の文字のコトロがすごい便利だった
Private Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
これがないと例えば
'変形前後の左上座標の差分
Private Property _DiffPoint As Point
Public Property DiffPoint As Point
Get
Return _DiffPoint
End Get
Set(value As Point)
_DiffPoint = value
Call NotifyPropertyChanged()
End Set
End Property
赤文字のところを
Call NotifyPropertyChanged("DiffPoint")
って書くか
Call NotifyPropertyChanged(NameOf(DiffPoint))
って書くことになる
これがなんにも書かなくても良くなるから打ち間違えることもないしラク
関連記事
前回の記事 2017/6/24
WPF、CanvasLeftTopとSliderValueをBinding ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14988621.html
2016/3/1
WPFとVB.NET、Canvasの中に回転表示したコントロールのドラッグ移動で気づいたこと ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/13947862.html
ThumbのTemplateの中に入れたCanvasを回転させているのが今回の記事