teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

2

見直しキャンペーン中

2023/07/22 08:48

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,190 +1,191 @@
1
- `DragDelta`はクリックした位置からの差分なので、毎回足していくとどんどん伸びることになります。
2
- 前回の`HorizontalChange`を引くか、クリックした時の`Width`を覚えておくかになると思います。
3
- そうなると`DragStarted`や`DragCompleted`の監視が必要になりそうです。
4
-
5
- そもそもとして左端の`Thumb`はいいですが、右のほうの`Thumb`はサイズが変わったことで移動してしまい`DragDelta`が大きく暴れてしまいます。
6
- `DragDelta`が使えないとなると、`Mouse.GetPosition(Application.Current.MainWindow).X`とかになるでしょうが「どうなの?」って気はします。
7
-
8
- もうすこし用途というかシナリオが分かればいい方法もありそうな気がしますが、かなり凝ったことをされているようなので説明しづらいでしょうね^^;
9
-
10
- ---
11
-
12
- 検証中はMVVMにこだわらずコードビハインドに書いていいと思います。
13
- 希望の動作になったら移せばいいので。
14
-
15
- 追記
16
- ---
17
-
18
- 一応それっぽくはなったのですが、非常に泥臭くなってしまいました^^;
19
- これはビヘイビアにするのが正攻法でしょうね(ViewModelでやる処理じゃないですね)
20
-
21
- **注意**
22
- * Prism.Coreだけ入れました
23
- * 構造は全部決め打ちです
24
- * 画像のサイズが同じという前提です(違うならサイズでなくてマージンで調整か?)
25
- * 縦線や半透明ピンクは確認用なので深い意味はないです
26
- * Rectangleなのも特に意味はないです
27
- * 画像も確認用なので特に意味はないです
28
-
29
- Width Heightのバインディングが必要ないなら、`ItemsControl`自体のドラッグで全ての`ContentPresenter`をリサイズするほうが簡単そうな気もします(位置補正まですると大差ないかもしれない)
30
-
31
- ```xaml
32
- <Window
33
- x:Class="Questions276037.MainWindow"
34
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
35
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
36
- Width="525"
37
- Height="350">
38
- <Window.Resources>
39
- <!-- 個々のアイテム -->
40
- <DataTemplate x:Key="itemTemplate">
41
- <Grid>
42
- <Image
43
- x:Name="image"
44
- HorizontalAlignment="Center"
45
- VerticalAlignment="Center"
46
- Source="{Binding Source}" />
47
- <Rectangle
48
- Width="{Binding Width}"
49
- Height="{Binding Height}"
50
- MinWidth="{Binding ActualWidth, ElementName=image}"
51
- MinHeight="{Binding ActualHeight, ElementName=image}"
52
- Fill="#4CFF0000"
53
- MouseDown="Rectangle_MouseDown"
54
- MouseMove="Rectangle_MouseMove"
55
- MouseUp="Rectangle_MouseUp" />
56
- </Grid>
57
- </DataTemplate>
58
-
59
- <!-- 目印用グリッド線 -->
60
- <VisualBrush
61
- x:Key="lineBrush"
62
- TileMode="Tile"
63
- Viewbox="0,0,25,25"
64
- ViewboxUnits="Absolute"
65
- Viewport="0,0,25,25"
66
- ViewportUnits="Absolute">
67
- <VisualBrush.Visual>
68
- <Path Data="M0,0L0,25" Stroke="Gray" />
69
- </VisualBrush.Visual>
70
- </VisualBrush>
71
- </Window.Resources>
72
-
73
- <ItemsControl ItemTemplate="{StaticResource itemTemplate}" ItemsSource="{Binding List}">
74
- <ItemsControl.Template>
75
- <ControlTemplate>
76
- <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
77
- <ItemsPresenter />
78
- </ScrollViewer>
79
- </ControlTemplate>
80
- </ItemsControl.Template>
81
- <ItemsControl.ItemsPanel>
82
- <ItemsPanelTemplate>
83
- <StackPanel Background="{StaticResource lineBrush}" Orientation="Horizontal" />
84
- </ItemsPanelTemplate>
85
- </ItemsControl.ItemsPanel>
86
- </ItemsControl>
87
- </Window>
88
- ```
89
-
90
- ```C#
91
- using System;
92
- using System.Collections.ObjectModel;
93
- using System.Windows;
94
- using System.Windows.Controls;
95
- using System.Windows.Input;
96
- using System.Windows.Media;
97
- using System.Windows.Media.Imaging;
98
- using System.Windows.Shapes;
99
- using Prism.Mvvm;
100
-
101
- namespace Questions276037
102
- {
103
- public partial class MainWindow : Window
104
- {
105
- private readonly BitmapImage i1 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/KnkDDC5A_thumbnail.jpg"));
106
- private readonly BitmapImage i2 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u10/105091/d8tKNb7D_thumbnail.jpg"));
107
-
108
- public ObservableCollection<Item> List { get; } = new ObservableCollection<Item>();
109
-
110
- public MainWindow()
111
- {
112
- InitializeComponent();
113
-
114
- for(var i = 0; i < 5; i++)
115
- {
116
- List.Add(new Item() { Width = 95, Height = 95, Source = i1, });
117
- List.Add(new Item() { Width = 95, Height = 95, Source = i2, });
118
- }
119
- DataContext = this;
120
- }
121
-
122
- private Point oldPos;
123
- private bool dragging;
124
-
125
- private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
126
- {
127
- dragging = true;
128
- var rectangle = sender as Rectangle;
129
- rectangle.CaptureMouse();
130
- oldPos = Mouse.GetPosition(null);
131
- }
132
- private void Rectangle_MouseMove(object sender, MouseEventArgs e)
133
- {
134
- if(!dragging) return;
135
-
136
- var rectangle = sender as Rectangle;
137
- var newPos = Mouse.GetPosition(null);
138
- var deltaX = newPos.X - oldPos.X;
139
- oldPos = newPos;
140
-
141
- if(rectangle.Width + deltaX < rectangle.MinWidth) return; // 画像より小さくしない
142
-
143
- var contentPresenter = rectangle.FindAncestor<ContentPresenter>();
144
- var stackPanel = rectangle.FindAncestor<StackPanel>();
145
- var scrollViewer = rectangle.FindAncestor<ScrollViewer>();
146
- var itemsControl = rectangle.FindAncestor<ItemsControl>();
147
-
148
- foreach(Item item in itemsControl.Items)
149
- item.Width += deltaX;
150
-
151
- var offset = scrollViewer.TranslatePoint(new Point(), stackPanel); // StackPanelがScrollViewerからはみ出た長さ
152
- var index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter);
153
- var total = (index + 1 - 0.5) * deltaX; // カーソル左側の伸びた分合計
154
-
155
- scrollViewer.ScrollToHorizontalOffset(offset.X + total); // スクロールバー調整
156
- }
157
-
158
- private void Rectangle_MouseUp(object sender, MouseButtonEventArgs e)
159
- {
160
- dragging = false;
161
- var rectangle = sender as Rectangle;
162
- rectangle.ReleaseMouseCapture();
163
- oldPos = new Point();
164
- }
165
- }
166
-
167
- public class Item : BindableBase
168
- {
169
- private double _width = 0;
170
- private double _height = 0;
171
- public double Width { get => _width; set => SetProperty(ref _width, value); }
172
- public double Height { get => _height; set => SetProperty(ref _height, value); }
173
- public BitmapImage Source { get; set; }
174
- }
175
-
176
- internal static class DependencyObjectExtensions
177
- {
178
- public static T FindAncestor<T>(this DependencyObject obj) where T : DependencyObject
179
- {
180
- do
181
- {
182
- obj = VisualTreeHelper.GetParent(obj);
183
- if(obj == null) return null;
184
- } while(!(obj is T));
185
-
186
- return obj as T;
187
- }
188
- }
189
- }
190
- ```
1
+ `DragDelta`はクリックした位置からの差分なので、毎回足していくとどんどん伸びることになります。
2
+ 前回の`HorizontalChange`を引くか、クリックした時の`Width`を覚えておくかになると思います。
3
+ そうなると`DragStarted`や`DragCompleted`の監視が必要になりそうです。
4
+
5
+ そもそもとして左端の`Thumb`はいいですが、右のほうの`Thumb`はサイズが変わったことで移動してしまい`DragDelta`が大きく暴れてしまいます。
6
+ `DragDelta`が使えないとなると、`Mouse.GetPosition(Application.Current.MainWindow).X`とかになるでしょうが「どうなの?」って気はします。
7
+
8
+ もうすこし用途というかシナリオが分かればいい方法もありそうな気がしますが、かなり凝ったことをされているようなので説明しづらいでしょうね^^;
9
+
10
+ ---
11
+
12
+ 検証中はMVVMにこだわらずコードビハインドに書いていいと思います。
13
+ 希望の動作になったら移せばいいので。
14
+
15
+ ---
16
+
17
+ 追記
18
+ 一応それっぽくはなったのですが、非常に泥臭くなってしまいました^^;
19
+ これはビヘイビアにするのが正攻法でしょうね(ViewModelでやる処理じゃないですね)
20
+
21
+ **注意**
22
+ * Prism.Coreだけ入れました
23
+ * 構造は全部決め打ちです
24
+ * 画像のサイズが同じという前提です(違うならサイズでなくてマージンで調整か?)
25
+ * 縦線や半透明ピンクは確認用なので深い意味はないです
26
+ * `Rectangle`なのも特に意味はないです
27
+ * 画像も確認用なので特に意味はないです
28
+
29
+ `Width`・`Height`のバインディングが必要ないなら、`ItemsControl`自体のドラッグで全ての`ContentPresenter`をリサイズするほうが簡単そうな気もします(位置補正まですると大差ないかもしれない)
30
+
31
+ ```xml
32
+ <Window
33
+ x:Class="Questions276037.MainWindow"
34
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
35
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
36
+ Width="525"
37
+ Height="350">
38
+ <Window.Resources>
39
+ <!-- 個々のアイテム -->
40
+ <DataTemplate x:Key="itemTemplate">
41
+ <Grid>
42
+ <Image
43
+ x:Name="image"
44
+ HorizontalAlignment="Center"
45
+ VerticalAlignment="Center"
46
+ Source="{Binding Source}" />
47
+ <Rectangle
48
+ Width="{Binding Width}"
49
+ Height="{Binding Height}"
50
+ MinWidth="{Binding ActualWidth, ElementName=image}"
51
+ MinHeight="{Binding ActualHeight, ElementName=image}"
52
+ Fill="#4CFF0000"
53
+ MouseDown="Rectangle_MouseDown"
54
+ MouseMove="Rectangle_MouseMove"
55
+ MouseUp="Rectangle_MouseUp" />
56
+ </Grid>
57
+ </DataTemplate>
58
+
59
+ <!-- 目印用グリッド線 -->
60
+ <VisualBrush
61
+ x:Key="lineBrush"
62
+ TileMode="Tile"
63
+ Viewbox="0,0,25,25"
64
+ ViewboxUnits="Absolute"
65
+ Viewport="0,0,25,25"
66
+ ViewportUnits="Absolute">
67
+ <VisualBrush.Visual>
68
+ <Path Data="M0,0L0,25" Stroke="Gray" />
69
+ </VisualBrush.Visual>
70
+ </VisualBrush>
71
+ </Window.Resources>
72
+
73
+ <ItemsControl ItemTemplate="{StaticResource itemTemplate}" ItemsSource="{Binding List}">
74
+ <ItemsControl.Template>
75
+ <ControlTemplate>
76
+ <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
77
+ <ItemsPresenter />
78
+ </ScrollViewer>
79
+ </ControlTemplate>
80
+ </ItemsControl.Template>
81
+ <ItemsControl.ItemsPanel>
82
+ <ItemsPanelTemplate>
83
+ <StackPanel Background="{StaticResource lineBrush}" Orientation="Horizontal" />
84
+ </ItemsPanelTemplate>
85
+ </ItemsControl.ItemsPanel>
86
+ </ItemsControl>
87
+ </Window>
88
+ ```
89
+
90
+ ```cs
91
+ using System;
92
+ using System.Collections.ObjectModel;
93
+ using System.Windows;
94
+ using System.Windows.Controls;
95
+ using System.Windows.Input;
96
+ using System.Windows.Media;
97
+ using System.Windows.Media.Imaging;
98
+ using System.Windows.Shapes;
99
+ using Prism.Mvvm;
100
+
101
+ namespace Questions276037
102
+ {
103
+ public partial class MainWindow : Window
104
+ {
105
+ private readonly BitmapImage i1 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/KnkDDC5A_thumbnail.jpg"));
106
+ private readonly BitmapImage i2 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u10/105091/d8tKNb7D_thumbnail.jpg"));
107
+
108
+ public ObservableCollection<Item> List { get; } = new ObservableCollection<Item>();
109
+
110
+ public MainWindow()
111
+ {
112
+ InitializeComponent();
113
+
114
+ for(var i = 0; i < 5; i++)
115
+ {
116
+ List.Add(new Item() { Width = 95, Height = 95, Source = i1, });
117
+ List.Add(new Item() { Width = 95, Height = 95, Source = i2, });
118
+ }
119
+ DataContext = this;
120
+ }
121
+
122
+ private Point oldPos;
123
+ private bool dragging;
124
+
125
+ private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
126
+ {
127
+ dragging = true;
128
+ var rectangle = sender as Rectangle;
129
+ rectangle.CaptureMouse();
130
+ oldPos = Mouse.GetPosition(null);
131
+ }
132
+ private void Rectangle_MouseMove(object sender, MouseEventArgs e)
133
+ {
134
+ if(!dragging) return;
135
+
136
+ var rectangle = sender as Rectangle;
137
+ var newPos = Mouse.GetPosition(null);
138
+ var deltaX = newPos.X - oldPos.X;
139
+ oldPos = newPos;
140
+
141
+ if(rectangle.Width + deltaX < rectangle.MinWidth) return; // 画像より小さくしない
142
+
143
+ var contentPresenter = rectangle.FindAncestor<ContentPresenter>();
144
+ var stackPanel = rectangle.FindAncestor<StackPanel>();
145
+ var scrollViewer = rectangle.FindAncestor<ScrollViewer>();
146
+ var itemsControl = rectangle.FindAncestor<ItemsControl>();
147
+
148
+ foreach(Item item in itemsControl.Items)
149
+ item.Width += deltaX;
150
+
151
+ var offset = scrollViewer.TranslatePoint(new Point(), stackPanel); // StackPanelがScrollViewerからはみ出た長さ
152
+ var index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter);
153
+ var total = (index + 1 - 0.5) * deltaX; // カーソル左側の伸びた分合計
154
+
155
+ scrollViewer.ScrollToHorizontalOffset(offset.X + total); // スクロールバー調整
156
+ }
157
+
158
+ private void Rectangle_MouseUp(object sender, MouseButtonEventArgs e)
159
+ {
160
+ dragging = false;
161
+ var rectangle = sender as Rectangle;
162
+ rectangle.ReleaseMouseCapture();
163
+ oldPos = new Point();
164
+ }
165
+ }
166
+
167
+ public class Item : BindableBase
168
+ {
169
+ private double _width = 0;
170
+ private double _height = 0;
171
+ public double Width { get => _width; set => SetProperty(ref _width, value); }
172
+ public double Height { get => _height; set => SetProperty(ref _height, value); }
173
+ public BitmapImage Source { get; set; }
174
+ }
175
+
176
+ internal static class DependencyObjectExtensions
177
+ {
178
+ public static T FindAncestor<T>(this DependencyObject obj) where T : DependencyObject
179
+ {
180
+ do
181
+ {
182
+ obj = VisualTreeHelper.GetParent(obj);
183
+ if(obj == null) return null;
184
+ } while(!(obj is T));
185
+
186
+ return obj as T;
187
+ }
188
+ }
189
+ }
190
+ ```
191
+ ![アプリ動画](https://ddjkaamml8q8x.cloudfront.net/questions/2023-07-22/982627ad-5a92-4d08-ae0f-17b33d79675a.gif)

1

コード追記

2020/07/08 23:23

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -10,4 +10,181 @@
10
10
  ---
11
11
 
12
12
  検証中はMVVMにこだわらずコードビハインドに書いていいと思います。
13
- 希望の動作になったら移せばいいので。
13
+ 希望の動作になったら移せばいいので。
14
+
15
+ 追記
16
+ ---
17
+
18
+ 一応それっぽくはなったのですが、非常に泥臭くなってしまいました^^;
19
+ これはビヘイビアにするのが正攻法でしょうね(ViewModelでやる処理じゃないですね)
20
+
21
+ **注意**
22
+ * Prism.Coreだけ入れました
23
+ * 構造は全部決め打ちです
24
+ * 画像のサイズが同じという前提です(違うならサイズでなくてマージンで調整か?)
25
+ * 縦線や半透明ピンクは確認用なので深い意味はないです
26
+ * Rectangleなのも特に意味はないです
27
+ * 画像も確認用なので特に意味はないです
28
+
29
+ Width Heightのバインディングが必要ないなら、`ItemsControl`自体のドラッグで全ての`ContentPresenter`をリサイズするほうが簡単そうな気もします(位置補正まですると大差ないかもしれない)
30
+
31
+ ```xaml
32
+ <Window
33
+ x:Class="Questions276037.MainWindow"
34
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
35
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
36
+ Width="525"
37
+ Height="350">
38
+ <Window.Resources>
39
+ <!-- 個々のアイテム -->
40
+ <DataTemplate x:Key="itemTemplate">
41
+ <Grid>
42
+ <Image
43
+ x:Name="image"
44
+ HorizontalAlignment="Center"
45
+ VerticalAlignment="Center"
46
+ Source="{Binding Source}" />
47
+ <Rectangle
48
+ Width="{Binding Width}"
49
+ Height="{Binding Height}"
50
+ MinWidth="{Binding ActualWidth, ElementName=image}"
51
+ MinHeight="{Binding ActualHeight, ElementName=image}"
52
+ Fill="#4CFF0000"
53
+ MouseDown="Rectangle_MouseDown"
54
+ MouseMove="Rectangle_MouseMove"
55
+ MouseUp="Rectangle_MouseUp" />
56
+ </Grid>
57
+ </DataTemplate>
58
+
59
+ <!-- 目印用グリッド線 -->
60
+ <VisualBrush
61
+ x:Key="lineBrush"
62
+ TileMode="Tile"
63
+ Viewbox="0,0,25,25"
64
+ ViewboxUnits="Absolute"
65
+ Viewport="0,0,25,25"
66
+ ViewportUnits="Absolute">
67
+ <VisualBrush.Visual>
68
+ <Path Data="M0,0L0,25" Stroke="Gray" />
69
+ </VisualBrush.Visual>
70
+ </VisualBrush>
71
+ </Window.Resources>
72
+
73
+ <ItemsControl ItemTemplate="{StaticResource itemTemplate}" ItemsSource="{Binding List}">
74
+ <ItemsControl.Template>
75
+ <ControlTemplate>
76
+ <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
77
+ <ItemsPresenter />
78
+ </ScrollViewer>
79
+ </ControlTemplate>
80
+ </ItemsControl.Template>
81
+ <ItemsControl.ItemsPanel>
82
+ <ItemsPanelTemplate>
83
+ <StackPanel Background="{StaticResource lineBrush}" Orientation="Horizontal" />
84
+ </ItemsPanelTemplate>
85
+ </ItemsControl.ItemsPanel>
86
+ </ItemsControl>
87
+ </Window>
88
+ ```
89
+
90
+ ```C#
91
+ using System;
92
+ using System.Collections.ObjectModel;
93
+ using System.Windows;
94
+ using System.Windows.Controls;
95
+ using System.Windows.Input;
96
+ using System.Windows.Media;
97
+ using System.Windows.Media.Imaging;
98
+ using System.Windows.Shapes;
99
+ using Prism.Mvvm;
100
+
101
+ namespace Questions276037
102
+ {
103
+ public partial class MainWindow : Window
104
+ {
105
+ private readonly BitmapImage i1 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u13/132786/KnkDDC5A_thumbnail.jpg"));
106
+ private readonly BitmapImage i2 = new BitmapImage(new Uri("https://teratail-v2.storage.googleapis.com/uploads/avatars/u10/105091/d8tKNb7D_thumbnail.jpg"));
107
+
108
+ public ObservableCollection<Item> List { get; } = new ObservableCollection<Item>();
109
+
110
+ public MainWindow()
111
+ {
112
+ InitializeComponent();
113
+
114
+ for(var i = 0; i < 5; i++)
115
+ {
116
+ List.Add(new Item() { Width = 95, Height = 95, Source = i1, });
117
+ List.Add(new Item() { Width = 95, Height = 95, Source = i2, });
118
+ }
119
+ DataContext = this;
120
+ }
121
+
122
+ private Point oldPos;
123
+ private bool dragging;
124
+
125
+ private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
126
+ {
127
+ dragging = true;
128
+ var rectangle = sender as Rectangle;
129
+ rectangle.CaptureMouse();
130
+ oldPos = Mouse.GetPosition(null);
131
+ }
132
+ private void Rectangle_MouseMove(object sender, MouseEventArgs e)
133
+ {
134
+ if(!dragging) return;
135
+
136
+ var rectangle = sender as Rectangle;
137
+ var newPos = Mouse.GetPosition(null);
138
+ var deltaX = newPos.X - oldPos.X;
139
+ oldPos = newPos;
140
+
141
+ if(rectangle.Width + deltaX < rectangle.MinWidth) return; // 画像より小さくしない
142
+
143
+ var contentPresenter = rectangle.FindAncestor<ContentPresenter>();
144
+ var stackPanel = rectangle.FindAncestor<StackPanel>();
145
+ var scrollViewer = rectangle.FindAncestor<ScrollViewer>();
146
+ var itemsControl = rectangle.FindAncestor<ItemsControl>();
147
+
148
+ foreach(Item item in itemsControl.Items)
149
+ item.Width += deltaX;
150
+
151
+ var offset = scrollViewer.TranslatePoint(new Point(), stackPanel); // StackPanelがScrollViewerからはみ出た長さ
152
+ var index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter);
153
+ var total = (index + 1 - 0.5) * deltaX; // カーソル左側の伸びた分合計
154
+
155
+ scrollViewer.ScrollToHorizontalOffset(offset.X + total); // スクロールバー調整
156
+ }
157
+
158
+ private void Rectangle_MouseUp(object sender, MouseButtonEventArgs e)
159
+ {
160
+ dragging = false;
161
+ var rectangle = sender as Rectangle;
162
+ rectangle.ReleaseMouseCapture();
163
+ oldPos = new Point();
164
+ }
165
+ }
166
+
167
+ public class Item : BindableBase
168
+ {
169
+ private double _width = 0;
170
+ private double _height = 0;
171
+ public double Width { get => _width; set => SetProperty(ref _width, value); }
172
+ public double Height { get => _height; set => SetProperty(ref _height, value); }
173
+ public BitmapImage Source { get; set; }
174
+ }
175
+
176
+ internal static class DependencyObjectExtensions
177
+ {
178
+ public static T FindAncestor<T>(this DependencyObject obj) where T : DependencyObject
179
+ {
180
+ do
181
+ {
182
+ obj = VisualTreeHelper.GetParent(obj);
183
+ if(obj == null) return null;
184
+ } while(!(obj is T));
185
+
186
+ return obj as T;
187
+ }
188
+ }
189
+ }
190
+ ```