要素を指定した位置に移動させる
水色の四角枠は目印、サイズ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
用意したPriorityは全部NotifyPriority通知プロパティにした
MyLeft X座標、左位置
MyTop Y座標、上位置
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のところで
x,yだけ差分を足して、サイズはそのまま
指定された値に差分を足した値を指定
なのでMainWindowからは単純に
MyExThumb.SetPoint2(100, 100)
だけで見た目上の100,100の位置に移動させることができる
デザイン画面
、XAMLを書くと投稿エラーになるから画像で
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
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 btn2.Click, AddressOf MyCheck2
AddHandler btn4.Click, AddressOf MyMove
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
Dim b As Binding
b = New Binding(NameOf(ExThumb.MyAngle)) With {.Source = MyExThumb, .Mode = BindingMode.TwoWay}
sldAngle.SetBinding(Slider.ValueProperty, b)
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyAngle), tbAngle)
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.DiffPoint), tbRect)
Call SetTextBlockBinding(MyExThumb, NameOf(ExThumb.MyLeft), tbLeft)
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
Implements ComponentModel.INotifyPropertyChanged
Private RootCanvas As Canvas
Private RootRotate As RotateTransform
Public testRootCanvas As Canvas
Public Sub SetPoint2(x As Double, y As Double)
MyLeft = x + (-DiffPoint.X)
MyTop = y + (-DiffPoint.Y)
End Sub
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
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
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
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
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
Public Sub New(elm As FrameworkElement)
Template = CreateTemplate()
ApplyTemplate()
RootCanvas = Me.Template.FindName("RootCanvas", Me)
With RootCanvas
.Children.Add(elm)
.Height = elm.Height
.Width = elm.Width
End With
testRootCanvas = RootCanvas
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を求めるところ、こんなふうに書いてみたけど
DependencyPropertyのPropertyMetadataのPropertyChangedCallbackのところでTransformToVisualを使ってみたんだけどこのタイミングだと変形する直前みたいで変形前のRectしか取得できなかった
なので今回のようにNotifyPropertyって言う値の変更時に通知を出せる通知プロパティを使った、これは一年前の方法とほとんど変わらないけど少しうまく書けた気がする
参照したところ
特に紫の文字のコトロがすごい便利だった
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
Call NotifyPropertyChanged()
End Set
End Property
赤文字のところを
Call NotifyPropertyChanged("DiffPoint")
って書くか
Call NotifyPropertyChanged(NameOf(DiffPoint))
って書くことになる
これがなんにも書かなくても良くなるから打ち間違えることもないし
ラク
関連記事
前回の記事 2017/6/24
次の記事 2017/07/01は2日後
昔の関連記事
2016/3/1は1年4ヶ月前
こんなふうに回転させるとその上で動くマウスの位置情報も回転されてめんどくさいので
ThumbのTemplateの中に入れた
Canvasを回転させているのが今回の記事