質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

1802閲覧

WPF C# Canvas.GetLeft(Rectangle)がNANになってしまう

nodoita

総合スコア7

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2023/06/21 07:04

お世話になります。
調べたのですが、分からないため教えてください。

実現したいこと

Rectangleのどの位置にカーソルが居るのかでカーソルを変化させようとしています。
しかし、RectangleのCanvasLeft,Topが取得できなくて困ってます。

実現したいこととしては、以下です。
①作成済みのRectangleをクリック→Rectangleにフォーカスが当たる
②フォーカスの当たっているRectangleの中心は移動カーソル、端は拡縮カーソル表示
③カーソルの状態でRectangleを編集する
とりあえず、フォーカスよりも前にRectangleのCanvasLeft/Topを取得したいです。

前提

BindingとかMVVMの理解が浅いです。
色々と手探りで作りながら勉強をしていますが
1つずつコツコツ自分のやりたいことを実現しながら学んでいきたいと思ってます。
継ぎはぎで見づらいソースで申し訳ありません。
このソースの改良点等もあれば教えていただきたいです。

該当のソースコード

MainWindow.xaml

1 <Window.Resources> 2 <DataTemplate DataType="{x:Type local:RectangleItem}"> 3 <Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Fill}" StrokeThickness="{Binding StrokeThickness}" Stroke="{Binding Stroke}" Canvas.ZIndex="0" 4 Focusable="True" MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" MouseLeftButtonUp="Rectangle_MouseLeftButtonUp" MouseMove="Rectangle_MouseMove"/> 5 </DataTemplate> 6 </Window.Resources> 7 <DockPanel LastChildFill="False"> 8 <ToolBar VerticalAlignment="Top" DockPanel.Dock="Top" > 9 <RadioButton x:Name="DefaultButton" Width="32" Height="32" 10 Content="?" IsChecked="True" /> 11 <RadioButton x:Name="RectButton" Width="32" Height="32"> 12 <Rectangle Width="24" Height="16" Stroke="Black"/> 13 </RadioButton> 14 </ToolBar> 15 <Canvas x:Name="MyCanvas" Width="1000" Height="800" Background="Transparent" MouseLeftButtonDown="MyCanvas_MouseLeftButtonDown" MouseLeftButtonUp="MyCanvas_MouseLeftButtonUp" MouseMove="MyCanvas_MouseMove" MouseLeave="MyCanvas_MouseLeave"> 16 <ItemsControl ItemsSource="{Binding MyRects}" Panel.ZIndex="255"> 17 <ItemsControl.ItemsPanel> 18 <ItemsPanelTemplate> 19 <Canvas /> 20 </ItemsPanelTemplate> 21 </ItemsControl.ItemsPanel> 22 <ItemsControl.ItemContainerStyle> 23 <Style> 24 <Setter Property="Canvas.Left" Value="{Binding X}" /> 25 <Setter Property="Canvas.Top" Value="{Binding Y}" /> 26 </Style> 27 </ItemsControl.ItemContainerStyle> 28 </ItemsControl> 29 <Image MinWidth="1000" MinHeight="800" MaxWidth="1000" MaxHeight="800" Width="1000" Height="800" Source="1.png" StretchDirection="DownOnly" HorizontalAlignment="Left" VerticalAlignment="Top" /> 30 </Canvas> 31 </DockPanel> 32</Window>

MainWindow.xaml.cs

1namespace TEST 2{ 3 public partial class MainWindow : Window 4 { 5 bool isMouseDown = false; 6 Point StartPoint; 7 Point MovePoint; 8 public string MouseNow = ""; 9 System.Windows.Shapes.Rectangle rectangle; 10 private System.Windows.Shapes.Rectangle? activeRect; 11 public MainWindow() 12 { 13 InitializeComponent(); 14 DataContext = new ViewModel(); 15 } 16 17 private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 18 { 19 var point = e.GetPosition(MyCanvas); 20 21 var leftOffset = point.X % 10; 22 leftOffset = leftOffset < 5 ? -leftOffset : 10 - leftOffset; 23 var topOffset = point.Y % 10; 24 topOffset = topOffset < 5 ? -topOffset : 10 - topOffset; 25 26 StartPoint = new Point(point.X + leftOffset, point.Y + topOffset); 27 28 rectangle = new System.Windows.Shapes.Rectangle 29 { 30 Stroke = Brushes.Black, 31 Fill = Brushes.LightBlue, 32 HorizontalAlignment = HorizontalAlignment.Left, 33 VerticalAlignment = VerticalAlignment.Top, 34 Width = 0, 35 Height = 0, 36 Opacity = 0.3, 37 }; 38 39 Panel.SetZIndex(rectangle, 0);//最前面表示 40 Canvas.SetLeft(rectangle, StartPoint.X); 41 Canvas.SetTop(rectangle, StartPoint.Y); 42 MyCanvas.Children.Add(rectangle); 43 isMouseDown = true; 44 } 45 46 private void MyCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 47 { 48 var R_X = StartPoint.X; 49 var R_Y = StartPoint.Y; 50 if (MovePoint.X - StartPoint.X <= 0) 51 { 52 R_X = MovePoint.X; 53 } 54 if (MovePoint.Y - StartPoint.Y <= 0) 55 { 56 R_Y = MovePoint.Y; 57 } 58 59 var R_H = rectangle.Height; 60 var R_W = rectangle.Width; 61 62 MyCanvas.Children.Remove(rectangle); 63 MyRect NowRect = new RectangleItem { X = R_X, Y = R_Y, Width = R_W, Height = R_H }; 64 ((ViewModel)DataContext).MyRects.Add(NowRect); 65 66 isMouseDown = false; 67 rectangle.Opacity = 1; 68 } 69 70 private void MyCanvas_MouseMove(object sender, MouseEventArgs e) 71 { 72 if (!isMouseDown) return; 73 74 var point = e.GetPosition(MyCanvas); 75 76 var leftOffset = point.X % 10; 77 leftOffset = leftOffset < 5 ? -leftOffset : 10 - leftOffset; 78 var topOffset = point.Y % 10; 79 topOffset = topOffset < 5 ? -topOffset : 10 - topOffset; 80 81 MovePoint = new Point(point.X + leftOffset, point.Y + topOffset); 82 rectangle.Width = Math.Abs(MovePoint.X - StartPoint.X); 83 rectangle.Height = Math.Abs(MovePoint.Y - StartPoint.Y); 84 85 if (MovePoint.X - StartPoint.X <= 0) 86 { 87 Canvas.SetLeft(rectangle, MovePoint.X); 88 } 89 if (MovePoint.Y - StartPoint.Y <= 0) 90 { 91 Canvas.SetTop(rectangle, MovePoint.Y); 92 } 93 } 94 95 private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 96 { 97 activeRect = sender as System.Windows.Shapes.Rectangle; 98 if (activeRect == null) return; 99 100 if (e.LeftButton != MouseButtonState.Pressed) 101 { 102 return; 103 } 104 activeRect.Fill = Brushes.AliceBlue;//クリックしたら青色にする(確認のため) 105 } 106 107 private void Rectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 108 { 109 Cursor = Cursors.Arrow;//カーソルをデフォルトに 110 } 111 112 private void Rectangle_MouseMove(object sender, MouseEventArgs e) 113 { 114 //マウスポインタの座標を取得 115 var p = e.GetPosition(MyCanvas); 116 activeRect = sender as System.Windows.Shapes.Rectangle; 117 118 if (activeRect != null) { 119 HitTest(p, activeRect); 120 } 121 122 if (e.LeftButton == MouseButtonState.Pressed) 123 { 124 if (activeRect is null) 125 { return; } 126 } 127 else 128 { 129 130 } 131 } 132 133 public virtual bool HitTest(Point p, System.Windows.Shapes.Rectangle nowRect) 134 { 135 Point activeXY = new Point(Canvas.GetLeft(nowRect), Canvas.GetTop(nowRect)); 136 Point activeWH = new Point(activeXY.X + nowRect.ActualWidth, activeXY.Y + nowRect.ActualHeight); 137 138 if (nowRect.Fill is not null) 139 { 140 //図形内部の当たり判定 141 if (activeXY.X <= p.X && p.X <= activeXY.Y 142 && activeXY.Y <= p.Y && p.Y <= activeWH.Y) 143 { 144 MouseNow = "移動"; 145 return true; 146 } 147 } 148 if (nowRect.Stroke is not null) 149 { 150 if (p.X >= activeXY.X && p.X <= activeWH.X 151 && p.Y >= activeXY.Y - 2 && p.Y <= activeWH.Y + 2) 152 { 153 MouseNow = "上下"; 154 return true; 155 } 156 if (p.X >= activeXY.X && p.X <= activeXY.Y 157 && p.Y >= activeWH.Y - 2 && p.Y <= activeWH.Y + 2) 158 { 159 MouseNow = "上下"; 160 return true; 161 } 162 if (p.Y >= activeXY.Y && p.Y <= activeWH.Y 163 && p.X >= activeXY.X - 2 && p.X <= activeXY.X + 2) 164 { 165 MouseNow = "左右"; 166 return true; 167 } 168 if (p.Y >= activeXY.Y && p.Y <= activeWH.Y 169 && p.X >= activeXY.Y - 2 && p.X <= activeXY.Y + 2) 170 { 171 MouseNow = "左右"; 172 return true; 173 } 174 } 175 return false; 176 } 177 } 178} 179

MyRect.cs

1namespace TEST; 2 public class MyRect 3 { 4 public double X { get; set; } 5 public double Y { get; set; } 6 public double Width { get; set; } 7 public double Height { get; set; } 8 } 9 10 public class RectangleItem : MyRect 11 { 12 public double StrokeThickness { get; set; } = 3; 13 public Brush Stroke { get; set; } = Brushes.Red; 14 public Brush Fill { get; set; } = Brushes.Transparent; 15 } 16 17

ViewModel.cs

1namespace TEST; 2 3public partial class ViewModel : ObservableObject 4{ 5 [ObservableProperty] private bool isSubWindowShown; 6 public ObservableCollection<MyRect> MyRects { get; } = new(); 7 public ViewModel() 8 { 9 } 10 11 } 12

試したこと

mainWindow.xaml.cs

1 public virtual bool HitTest(Point p, System.Windows.Shapes.Rectangle nowRect) 2 { 3 Point activeXY = new Point(Canvas.GetLeft(nowRect), Canvas.GetTop(nowRect)); 4 Point activeWH = new Point(activeXY.X + nowRect.ActualWidth, activeXY.Y + nowRect.ActualHeight); 5 }

クリックしたものだけでもRectangleの値を取得したいと思い、
とりあえず上記のようにしていました。
しかし、「Canvas.GetLeft(nowRect)」がNaNになってしまいます。
Nanになってしまうため、「Point activeXY 」が取得できません。
ブレイクポイントをつけ、nowRectを確認するとRectangleの値は入っているのですが
どうもうまく取得できていないようです。
どうしたら現在選択しているRectのCanvasLeft,Topが取得できますか?

補足情報(FW/ツールのバージョンなど)

Visualstudio2022 V17.6.3
.NET 6.0 C#

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Canvas.GetLeft(Rectangle)がNANになってしまう

ItemsControl中の個々のアイテムは、直にItemsPanelに入っているわけではありません。

xml

1<ItemsControl.ItemContainerStyle> 2 <Style> 3 <Setter Property="Canvas.Left" Value="{Binding X}" /> 4 <Setter Property="Canvas.Top" Value="{Binding Y}" /> 5 </Style> 6</ItemsControl.ItemContainerStyle>

としてるようにItemContainerに包まれたうえで入っています。

「ライブ ビジュアル ツリー」ウィンドウを見るとわかりやすいです。
デバッグ中に XAML のプロパティを調べる - Visual Studio (Windows) | Microsoft Learn

つまりCanvas.LeftCanvas.Topは、ItemContainerItemsControlではContentPresenter)に付いています。

cs

1var cp = (UIElement)VisualTreeHelper.GetParent(nowRect); 2var activeXY = new Point(Canvas.GetLeft(cp), Canvas.GetTop(cp)); 3//Point activeXY = new Point(Canvas.GetLeft(nowRect), Canvas.GetTop(nowRect));

Rectangleのどの位置にカーソルが居るのかでカーソルを変化させようとしています。

カーソルを変えるならこんなのが簡単です(サイズ変更イベントも付けやすいかな??)

xml

1<DataTemplate DataType="{x:Type local:RectangleItem}"> 2 <Grid> 3 <Rectangle 4 Width="{Binding Width}" 5 Height="{Binding Height}" 6 Cursor="SizeAll" 7 Fill="{Binding Fill}" 8 Focusable="True" 9 MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" 10 MouseLeftButtonUp="Rectangle_MouseLeftButtonUp" 11 MouseMove="Rectangle_MouseMove" 12 Stroke="{Binding Stroke}" 13 StrokeThickness="{Binding StrokeThickness}" /> 14 <Line 15 Cursor="SizeNS" 16 Stroke="Transparent" 17 StrokeThickness="20" 18 X2="{Binding Width}" /> 19 </Grid> 20</DataTemplate>

投稿2023/06/21 10:22

TN8001

総合スコア10049

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nodoita

2023/06/23 01:56

TN8001様 回答ありがとうございます! なるほど、参照している個所が違ったのですね。 おぉお!カーソルを変更するためにLINEを透明にして重ねておくんですね!凄い! <Line x:Name="TopLine" Cursor="SizeNS" Stroke="Blue" StrokeThickness="20" X2="{Binding Width}" /> <Line x:Name="LeftLine" Cursor="SizeWE" Stroke="Black" StrokeThickness="20" Y2="{Binding Height}"/> <Line x:Name="BottomLine" Cursor="SizeNS" Stroke="Green" StrokeThickness="20" X2="{Binding Width}" Y1="{Binding Height}" Y2="{Binding Height}" /> <Line x:Name="RightLine" Cursor="SizeWE" Stroke="yellow" StrokeThickness="20" Y2="{Binding Height}" X1="{Binding Width}" X2="{Binding Width}" /> 色々と試行錯誤し、とりあえず色で見分けてみました。X1,X2などの考え方がピンとこず、凄く時間がかかりました…。ありがとうございました!本当に助かりました!!! また何かあれば相談させてください。よろしくお願いいたします。
TN8001

2023/06/23 03:00

> 色々と試行錯誤し、とりあえず色で見分けてみました。 あ、全部書いとけばよかったですか(コメントしていただけたらよかったのに^^; > X1,X2などの考え方がピンとこず、凄く時間がかかりました…。 始点(X1,Y1)・終点(X2,Y2)、省略するとゼロということですね。 [方法: 線を描画する - WPF .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/graphics-multimedia/how-to-draw-a-line)
nodoita

2023/06/23 04:37

試行錯誤はとても勉強になりました!ありがとうございました! Canvasの左上をゼロと考えたら、Widthとかが終点になるのは違和感がありました。 Canvasではなく、Rectangleの位置というのなら分かりますが、うーん。難しい。 すみません、もう1つ教えてください。 LINEでカーソルを変更することは出来たのですが 同じ階層にあるRectangleはどうやって取得したら良いのでしょうか? 兄弟要素と呼ぶっぽいですが、親子とかばかり出てきまして… 度々申し訳ありませんが、わかるようでしたらお手数ですが回答お願いできますでしょうか。
TN8001

2023/06/23 05:55 編集

> Canvasの左上をゼロと考えたら、Widthとかが終点になるのは違和感がありました。 > Canvasではなく、Rectangleの位置というのなら分かりますが、うーん。難しい。 「このDataTemplate(Grid)を起点として」ってことですね。 > 同じ階層にあるRectangleはどうやって取得したら良いのでしょうか? > 兄弟要素と呼ぶっぽいですが、親子とかばかり出てきまして… 一般的には親をとって子を探すことになりますかね。 private void TopLine_MouseDown(object sender, MouseButtonEventArgs e) { if (sender is FrameworkElement { Parent: FrameworkElement p }) { var rect = (Rectangle) p.FindName("rect"); // <Rectangle x:Name="rect" だったとして } } [FrameworkElement.FindName(String) メソッド (System.Windows) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.frameworkelement.findname) 「毎回探すのもどうなのよ?」って気もするのでこうとか?? <Line Tag="{Binding ElementName=rect}" private void TopLine_MouseDown(object sender, MouseButtonEventArgs e) { if (sender is FrameworkElement { Tag: Rectangle rect }) { } }
TN8001

2023/06/23 05:52

MyRectがObservablePropertyになっていれば、Canvas.GetLeftとかSetLeftとかそもそも不要なのですが。。。 前回回答での「バインドするパターン」のことです(とはいえCanvasなんかは取らざるを得ませんが^^; CaptureMouseなんかも結構重要です(マウスを素早く動かしたり、ウィンドウ外にもっていった場合の挙動等)
nodoita

2023/06/23 08:20

TS8001様 再び回答ありがとうございます! <Rectangle x:Name="rect" <Line Tag="{Binding ElementName=rect}" if (sender is FrameworkElement { Tag: Rectangle rect }) なるほど。 LINEはrectの名前がついているRectangleと紐づいていて、 それを直接引っ張っている感じですね!何となく分かりました! CaptureMouseを調べたら参考になりそうなサイトがあったので 参考にしつつ勉強しながら作ってみようと思います!ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問