WPF、回転した子要素の中の回転した複数子要素がピッタリ収まるBoundsの取得
昨日の続き
複数個要素編
結果
灰色枠は要素それぞれのピッタリBoundsで、これは昨日の方法で取得したもの
これをRectクラスのUnionメソッドで合成すれば黒枠になる
要素が2個以上になっても同じ方法でできるはず
状態
テストアプリのコード
2025WPF/20250402 at main · gogowaten/2025WPF
github.com
環境
- Windows 10 Home バージョン 22H2
- Visual Studio Community 2022 Version 17.13.5
- WPF
- C#
- .NET 8.0
MainWindow.xaml
<Window x:Class="_20250402.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:_20250402" mc:Ignorable="d" Title="MainWindow" Height="367" Width="602"> <Grid UseLayoutRounding="True"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Canvas x:Name="MyCanvas1" DataContext="{Binding ElementName=MyBlackWaku12}"> <Rectangle Fill="Khaki" Width="160" Height="120"/> <Rectangle x:Name="MyRed10" Fill="Red" Canvas.Left="40" Canvas.Top="20" Width="40" Height="40"> <Rectangle.RenderTransform> <RotateTransform Angle="40"/> </Rectangle.RenderTransform> </Rectangle> <Rectangle x:Name="MyRed11" Fill="Red" Canvas.Left="100" Canvas.Top="50" Width="40" Height="40"> <Rectangle.RenderTransform> <RotateTransform Angle="20"/> </Rectangle.RenderTransform> </Rectangle> <Rectangle x:Name="MyBlackWaku10" Stroke="Black" StrokeThickness="2" Opacity="0.2"/> <Rectangle x:Name="MyBlackWaku11" Stroke="Black" StrokeThickness="2" Opacity="0.2"/> <Rectangle x:Name="MyBlackWaku12" Stroke="Black" StrokeThickness="2" Opacity="0.5"/> <StackPanel Margin="10,200,10,10"> <TextBlock Text="{Binding (Canvas.Left), StringFormat=Left {0:0.0}}"/> <TextBlock Text="{Binding (Canvas.Top), StringFormat=Top {0:0.0}}"/> <TextBlock Text="{Binding Width, StringFormat=Width {0:0.0}}"/> <TextBlock Text="{Binding Height, StringFormat=Height {0:0.0}}"/> </StackPanel> </Canvas> <Canvas Grid.Column="1" DataContext="{Binding ElementName=MyBlackWaku22}"> <Canvas x:Name="MyCanvas2" Width="160" Height="120" Background="Khaki"> <Canvas.RenderTransform> <RotateTransform Angle="10"/> </Canvas.RenderTransform> <Rectangle x:Name="MyRed20" Fill="Red" Canvas.Left="40" Canvas.Top="20" Width="40" Height="40"/> <Rectangle x:Name="MyRed21" Fill="Red" Canvas.Left="100" Canvas.Top="50" Width="40" Height="40"/> </Canvas> <Rectangle x:Name="MyBlackWaku20" Stroke="Black" StrokeThickness="2" Opacity="0.2"/> <Rectangle x:Name="MyBlackWaku21" Stroke="Black" StrokeThickness="2" Opacity="0.2"/> <Rectangle x:Name="MyBlackWaku22" Stroke="Black" StrokeThickness="2" Opacity="0.5"/> <StackPanel Margin="10,200,10,10"> <TextBlock Text="{Binding (Canvas.Left), StringFormat=Left {0:0.0}}"/> <TextBlock Text="{Binding (Canvas.Top), StringFormat=Top {0:0.0}}"/> <TextBlock Text="{Binding Width, StringFormat=Width {0:0.0}}"/> <TextBlock Text="{Binding Height, StringFormat=Height {0:0.0}}"/> </StackPanel> </Canvas> <Canvas Grid.Column="2" DataContext="{Binding ElementName=MyBlackWaku32}"> <Canvas x:Name="MyCanvas3" Width="160" Height="120" Background="Khaki"> <Canvas.RenderTransform> <RotateTransform Angle="10"/> </Canvas.RenderTransform> <Rectangle x:Name="MyRed30" Fill="Red" Canvas.Left="40" Canvas.Top="20" Width="40" Height="40"> <Rectangle.RenderTransform> <RotateTransform Angle="30"/> </Rectangle.RenderTransform> </Rectangle> <Rectangle x:Name="MyRed31" Fill="Red" Canvas.Left="100" Canvas.Top="50" Width="40" Height="40"> <Rectangle.RenderTransform> <RotateTransform Angle="10"/> </Rectangle.RenderTransform> </Rectangle> </Canvas> <Rectangle x:Name="MyBlackWaku30" Stroke="Black" StrokeThickness="2" Opacity="0.2"/> <Rectangle x:Name="MyBlackWaku31" Stroke="Black" StrokeThickness="2" Opacity="0.2"/> <Rectangle x:Name="MyBlackWaku32" Stroke="Black" StrokeThickness="2" Opacity="0.5"/> <StackPanel Margin="10,200,10,10"> <TextBlock Text="{Binding (Canvas.Left), StringFormat=Left {0:0.0}}"/> <TextBlock Text="{Binding (Canvas.Top), StringFormat=Top {0:0.0}}"/> <TextBlock Text="{Binding Width, StringFormat=Width {0:0.0}}"/> <TextBlock Text="{Binding Height, StringFormat=Height {0:0.0}}"/> </StackPanel> </Canvas> </Grid> </Window>
MainWindow.xaml.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Media; //WPF、子要素と孫要素ともに回転時の、孫要素がピッタリ収まるBounds(Rect)の取得 - 午後わてんのブログ //https://gogowaten.hatenablog.com/entry/2025/04/02/151945 namespace _20250402 { public partial class MainWindow : Window { //private Rect MyRedBounds; public MainWindow() { InitializeComponent(); Test11(); Test21(); Test31(); } //子要素、孫要素ともに回転しているとき //親要素から見た孫要素のBounds取得 private void Test31() { Rect r1 = GetRenderTransformBounds(MyCanvas3, MyRed30); Rect r2 = GetRenderTransformBounds(MyCanvas3, MyRed31); MySetBounds(MyBlackWaku30, r1); MySetBounds(MyBlackWaku31, r2); r1.Union(r2); MySetBounds(MyBlackWaku32, r1); } /// <summary> /// childの位置とサイズのRectを、parentのRenderTransformのTransformBoundsで返す /// </summary> /// <param name="parent"></param> /// <param name="child"></param> /// <returns></returns> private static Rect GetRenderTransformBounds(FrameworkElement parent, FrameworkElement child) { Rect rectZero = new(0, 0, child.Width, child.Height); Rect rect = new(Canvas.GetLeft(child), Canvas.GetTop(child), child.Width, child.Height); //位置の取得A、child自身のTransformを使ったBounds Rect boundsZero = child.RenderTransform.TransformBounds(rectZero); //位置の取得B、parentのTransformを使ったBounds Rect parentTFBounds = parent.RenderTransform.TransformBounds(rect); //最終的な位置の取得は、AとBの合成(offset) Point topLeft = parentTFBounds.TopLeft; topLeft.Offset(boundsZero.X, boundsZero.Y); //サイズ取得は、parentとchildのTransformを合成したTransformでのBounds MatrixTransform unionTF = UnionTransform(parent.RenderTransform, child.RenderTransform); Rect unionBounds = unionTF.TransformBounds(rect); //最終的な位置とサイズを返す return new Rect(topLeft, unionBounds.Size); } /// <summary> /// Transform1にTransform2を追加(Append)したTransformを作って返す /// </summary> /// <param name="transform1"></param> /// <param name="transform2"></param> /// <returns></returns> private static MatrixTransform UnionTransform(Transform transform1, Transform transform2) { Matrix union = transform1.Value; union.Append(transform2.Value); return new MatrixTransform(union); } //子要素の回転は無し、親要素のCanvasが回転しているとき //親要素から見た子要素のBounds取得 private void Test21() { //親CanvasのRenderTransformのTransformBoundsに、 //子要素の赤枠Boundsを入れるだけで取得できる Rect canvasTFBounds1 = MyCanvas2.RenderTransform.TransformBounds(new Rect(GetLeft(MyRed20), GetTop(MyRed20), MyRed20.Width, MyRed20.Height)); MySetBounds(MyBlackWaku20, canvasTFBounds1); Rect canvasTFBounds2 = MyCanvas2.RenderTransform.TransformBounds(new Rect(GetLeft(MyRed21), GetTop(MyRed21), MyRed21.Width, MyRed21.Height)); MySetBounds(MyBlackWaku21, canvasTFBounds2); //それぞれのRectを合成したものが目的のBoundsになる Rect unionR = canvasTFBounds1; unionR.Union(canvasTFBounds2); MySetBounds(MyBlackWaku22, unionR); //Rect neko = GetRenderTransformBounds(MyCanvas2, MyRed20); //Rect inu = GetRenderTransformBounds(MyCanvas2, MyRed21); } //図形だけ回転の場合 //零座標のRectで計算 private void Test11() { Rect r = MyRed10.RenderTransform.TransformBounds(new Rect(0, 0, MyRed10.Width, MyRed10.Height)); r.Offset(GetLeft(MyRed10), GetTop(MyRed10)); MySetBounds(MyBlackWaku10, r); Rect r2 = MyRed11.RenderTransform.TransformBounds(new Rect(0, 0, MyRed11.Width, MyRed11.Height)); r2.Offset(GetLeft(MyRed11), GetTop(MyRed11)); MySetBounds(MyBlackWaku11, r2); //それぞれのRectを合成したものが目的のBoundsになる Rect unionR = r; unionR.Union(r2); MySetBounds(MyBlackWaku12, unionR); } private double GetLeft(FrameworkElement element) => Canvas.GetLeft(element); private double GetTop(FrameworkElement element) => Canvas.GetTop(element); private void SetLeft(FrameworkElement element, double value) => Canvas.SetLeft(element, value); private void SetTop(FrameworkElement element, double value) => Canvas.SetTop(element, value); private void MySetBounds(FrameworkElement element, Rect bounds) { SetLeft(element, bounds.Left); SetTop(element, bounds.Top); element.Width = bounds.Width; element.Height = bounds.Height; } } }
感想
昨日の方法にRectクラスのUnionを使うだけだったので、簡単だった
これで直せるはず?
関連記事
次のWPF記事
WPF、回転した子要素の中の回転した複数子要素がピッタリ収まるBoundsの取得 - 午後わてんのブログ
gogowaten.hatenablog.com
前回のWPF記事
WPF、子要素と孫要素ともに回転時の、孫要素がピッタリ収まるBounds(Rect)の取得 - 午後わてんのブログ
gogowaten.hatenablog.com