-
Notifications
You must be signed in to change notification settings - Fork 0
/
ControlExtension.cs
388 lines (345 loc) · 13.2 KB
/
ControlExtension.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
namespace WPFMediaTest
{
public static class ControlExtension
{
#region BindCommand
/// <summary>
/// 绑定命令和命令事件到宿主UI
/// </summary>
public static void BindCommand(this UIElement @ui, ICommand com, Action<object, ExecutedRoutedEventArgs> call)
{
var bind = new CommandBinding(com);
bind.Executed += new ExecutedRoutedEventHandler(call);
@ui.CommandBindings.Add(bind);
}
/// <summary>
/// 绑定RelayCommand命令到宿主UI
/// </summary>
public static void BindCommand(this UIElement @ui, ICommand com)
{
var bind = new CommandBinding(com);
@ui.CommandBindings.Add(bind);
}
#endregion
#region Storyboard
/// <summary>
/// 释放动画资源,containingObject为Storyboard关联的容器
/// </summary>
/// <param name="this"></param>
/// <param name="containingObject"></param>
public static void Dispose(this Storyboard @this, FrameworkElement containingObject = null)
{
if (@this == null)
return;
@this.Stop();
if (containingObject == null)
@this.Remove();
else
@this.Remove(containingObject);
@this.Children.Clear();
}
#endregion
#region FindParent,FindChildren
/// <summary>
/// 获取指定条件的父元素并转换为指定泛型参数的类型,若没找到,则返回null.
/// </summary>
public static T FindParent<T>(this DependencyObject obj, Func<DependencyObject, bool> predicate) where T : FrameworkElement
{
if (!(obj is Visual) && !(obj is Visual3D))
return null;
DependencyObject parent = VisualTreeHelper.GetParent(obj);
while (parent != null)
{
if (parent is T && predicate(parent))
return parent as T;
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
/// <summary>
/// 获取指定条件的父元素并转换为指定泛型参数的类型,若没找到,则返回null.
/// </summary>
public static T FindFirstParent<T>(this DependencyObject obj) where T : FrameworkElement
{
DependencyObject parent = VisualTreeHelper.GetParent(obj);
while (parent != null)
{
if (parent is T)
return parent as T;
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
/// <summary>
/// 查找指定条件的子控件,找到第一个就返回,如果没有找到,则返回null
/// </summary>
public static T FindChild<T>(this DependencyObject obj, Func<DependencyObject, bool> predicate) where T : FrameworkElement
{
if (obj == null) return null;
T t = null;
DependencyObject child = null;
int count = VisualTreeHelper.GetChildrenCount(obj);
//尝试从content获取
if (count == 0 && obj is ContentControl)
{
var objc = obj as ContentControl;
t = DoPredicate<T>(objc.Content, predicate);
}
if (count == 0 && obj is ContentPresenter)
{
var objc = obj as ContentPresenter;
t = DoPredicate<T>(objc.Content, predicate);
}
if (t != null) return t;
for (int i = 0; i < count; i++)
{
child = VisualTreeHelper.GetChild(obj, i);
t = DoPredicate<T>(child, predicate);
if (t != null) return t;
}
return null;
}
private static T DoPredicate<T>(object obj, Func<DependencyObject, bool> predicate) where T : FrameworkElement
{
if (obj == null) return null;
var cc = obj as DependencyObject;
if (cc == null) return null;
if (cc is T && predicate(cc))
return cc as T;
return cc.FindChild<T>(predicate);
}
/// <summary>
/// 查找指定条件的所有子控件集合,没找到则返回空元素
/// </summary>
public static List<T> FindChildren<T>(this DependencyObject obj, Func<DependencyObject, bool> predicate) where T : FrameworkElement
{
T t = null;
List<T> children = new List<T>();
int count = VisualTreeHelper.GetChildrenCount(obj);
//尝试从content获取
if (count == 0 && obj is ContentControl)
{
var objc = obj as ContentControl;
DoPredicate<T>(objc.Content, predicate, children);
}
if (count == 0 && obj is ContentPresenter)
{
var objc = obj as ContentPresenter;
DoPredicate<T>(objc.Content, predicate, children);
}
DependencyObject child = null;
for (int i = 0; i < count; i++)
{
child = VisualTreeHelper.GetChild(obj, i);
DoPredicate(child, predicate, children);
}
return children;
}
private static void DoPredicate<T>(object obj, Func<DependencyObject, bool> predicate, List<T> list) where T : FrameworkElement
{
if (obj == null) return;
var cc = obj as DependencyObject;
if (cc == null) return;
if (cc is T && predicate(cc))
list.Add(cc as T);
list.AddRange(cc.FindChildren<T>(predicate));
}
#endregion
/// <summary>
/// 判断依赖对象是否包含在 <see cref="Popup"/> 中。
/// </summary>
/// <param name="visual">要判断的依赖对象。</param>
/// <returns></returns>
public static bool IsPopupChild(this DependencyObject visual)
{
return IsPopupChild(visual, out Popup popup);
}
/// <summary>
/// 获取特定依赖对象所在的 <see cref="Popup"/>。
/// </summary>
/// <param name="visual">依赖对象。</param>
/// <param name="popup">依赖对象所在的Popup。</param>
/// <returns></returns>
public static bool IsPopupChild(this DependencyObject visual, out Popup popup)
{
popup = null;
var presentationSource = PresentationSource.FromDependencyObject(visual);
if (presentationSource == null)
return false;
var rootVisual = presentationSource.RootVisual;
if (rootVisual is FrameworkElement fe && fe.Parent is Popup parentPopup)
{
popup = parentPopup;
return true;
}
return false;
}
/// <summary>
/// 获取特定依赖对象所在的 <see cref="Popup"/>。
/// </summary>
/// <param name="visual">依赖对象。</param>
/// <returns>若依赖对象在Popup中,则返回依赖对象所在的Popup,否则返回Null。</returns>
public static Popup FindPopup(this DependencyObject visual)
{
IsPopupChild(visual, out Popup popup);
return popup;
}
/// <summary>
/// Disconnect a visual from it's parent.
/// </summary>
/// <param name="parent">parent visual in the visual tree.</param>
/// <param name="visual">visual which need to disconnect.</param>
/// <remarks>
/// Be aware,disconnect a visual from it's parent also break any exist binding.
/// </remarks>
public static void Disconnect(this DependencyObject visual, DependencyObject parent)
{
DisconnectImpl(visual, parent, out DependencyObject realParent);
}
/// <summary>
/// Disconnect a visual from it's parent.
/// </summary>
/// <param name="parent">parent visual in the visual tree.</param>
/// <param name="visual">visual which need to disconnect.</param>
/// <param name="disconnectMethod">自定义的视图树移除方法,参数1为要移除的视图,参数2为要移除视图的父视图。</param>
/// <remarks>
/// Be aware,disconnect a visual from it's parent also break any exist binding.
/// </remarks>
public static void Disconnect(this DependencyObject visual, DependencyObject parent, Action<DependencyObject, DependencyObject> disconnectMethod)
{
if (!DisconnectImpl(visual, parent, out DependencyObject realParent))
{
disconnectMethod?.Invoke(visual, realParent);
}
}
private static bool DisconnectImpl(DependencyObject visual, DependencyObject parent, out DependencyObject realParent)
{
if (visual == null)
{
throw new ArgumentNullException(nameof(visual));
}
if (parent == null)
{
parent = VisualTreeHelper.GetParent(visual);
}
realParent = parent;
if (parent == null)
return true;
if (parent is Popup popup)
{
popup.Child = null;
return true;
}
if (parent is Panel panel)
{
panel.Children.Remove(visual as UIElement);
return true;
}
if (parent is Decorator decorator)
{
if (decorator.Child == visual)
{
decorator.Child = null;
}
return true;
}
if (parent is ContentPresenter contentPresenter)
{
if (contentPresenter.Content == visual)
{
contentPresenter.Content = null;
}
return true;
}
if (parent is ContentControl contentControl)
{
if (contentControl.Content == visual)
{
contentControl.Content = null;
}
return true;
}
if (parent is Viewport2DVisual3D viewport)
{
if (viewport.Visual == visual)
{
viewport.Visual = null;
}
return true;
}
return false;
}
/// <summary>
/// Disconnect a visual from it's parent then connect to a new parent.
/// </summary>
/// <param name="source">The old parent visual in the visual tree.</param>
/// <param name="target">The new parent visual in the visual tree.</param>
/// <param name="visual">visual which need to disconnect.</param>
public static void Connect(this DependencyObject visual, DependencyObject source, DependencyObject target)
{
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (visual == target)
{
return;
}
visual.Disconnect(source);
DependencyObject child = visual;
if (target is Panel panel)
{
var element = child as UIElement;
if (!panel.Children.Contains(element) && element != null)
{
panel.Children.Add(element);
}
else
{
throw new InvalidOperationException($"The {visual} is already a child of {target}!");
}
return;
}
if (target is Decorator decorator)
{
if (decorator.Child != child && decorator.Child != null)
{
throw new InvalidOperationException($"{target} has another child!");
}
decorator.Child = child as UIElement;
return;
}
if (target is ContentPresenter contentPresenter)
{
if (contentPresenter.Content != child && contentPresenter.Content != null)
{
throw new InvalidOperationException($"{target} contains other content!");
}
contentPresenter.Content = child;
return;
}
if (target is ContentControl contentControl)
{
if (contentControl.Content != child && contentControl.Content != null)
{
throw new InvalidOperationException($"{target} contains other content!");
}
contentControl.Content = child;
return;
}
}
}
}