按types查找WPF窗口中的所有控件
我正在寻找一种方式来find窗口上的所有控件的types,
例如:find所有的TextBoxes
,find实现特定接口的所有控件等
这应该做的伎俩
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { yield return (T)child; } foreach (T childOfChild in FindVisualChildren<T>(child)) { yield return childOfChild; } } } }
然后你列举了像这样的控件
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window)) { // do something with tb here }
这是最简单的方法:
IEnumerable<myType> collection = control.Children.OfType<myType>();
其中控制是窗口的根元素。
我改编了@Bryce Kahle的答案,按照@Mathias Lykkegaard Lorenzen的build议使用LogicalTreeHelper。
似乎工作没关系。 ;)
public static IEnumerable<T> FindLogicalChildren<T> ( DependencyObject depObj ) where T : DependencyObject { if( depObj != null ) { foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) ){ if( rawChild is DependencyObject ) { DependencyObject child = (DependencyObject)rawChild; if( child is T ) { yield return (T)child; } foreach( T childOfChild in FindLogicalChildren<T>( child ) ) { yield return childOfChild; } } } } }
(它仍然不会像@Benjamin Berry&@David R那样检查combobox内的选项卡控件或网格。)(也遵循@ noonand的build议&删除多余的孩子!= null)
根据你感兴趣的树使用助手类VisualTreeHelper
或LogicalTreeHelper
。它们都提供了获取元素的子元素的方法(虽然语法有些不同)。 我经常使用这些类来查找特定types的第一次出现,但是您可以轻松修改它以查找所有types的对象:
public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type) { if (obj != null) { if (obj.GetType() == type) { return obj; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type); if (childReturn != null) { return childReturn; } } } return null; }
我发现上面几个示例中使用的VisualTreeHelper.GetChildrenCount(depObj)行不会返回GroupBoxes的非零计数,特别是在GroupBox包含Grid的情况下,Grid包含子元素。 我相信这可能是因为GroupBox不允许包含多个子元素,并且存储在其Content属性中。 没有GroupBox.Childrentypes的属性。 我确信我没有这么做,但是我修改了这个链中的第一个“FindVisualChildren”示例,如下所示:
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { int depObjCount = VisualTreeHelper.GetChildrenCount(depObj); for (int i = 0; i <depObjCount; i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { yield return (T)child; } if (child is GroupBox) { GroupBox gb = child as GroupBox; Object gpchild = gb.Content; if (gpchild is T) { yield return (T)child; child = gpchild as T; } } foreach (T childOfChild in FindVisualChildren<T>(child)) { yield return childOfChild; } } } }
要获取特定types的所有孩子的列表,您可以使用:
private static IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, Type type) { if (obj != null) { if (obj.GetType() == type) { yield return obj; } for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type)) { if (child != null) { yield return child; } } } } yield break; }
recursion的小改变,以便您可以例如查找选项卡控件的子选项卡控件。
public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type) { if (obj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child.GetType() == type) { return child; } DependencyObject childReturn = FindInVisualTreeDown(child, type); if (childReturn != null) { return childReturn; } } } return null; }
这是如何向上的
private T FindParent<T>(DependencyObject item, Type StopAt) where T : class { if (item is T) { return item as T; } else { DependencyObject _parent = VisualTreeHelper.GetParent(item); if (_parent == null) { return default(T); } else { Type _type = _parent.GetType(); if (StopAt != null) { if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt)) { return null; } } if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T))) { return _parent as T; } else { return FindParent<T>(_parent, StopAt); } } } }
请注意,使用VisualTreeHelper只适用于从Visual或Visual3D派生的控件。 如果您还需要检查其他元素(例如TextBlock,FlowDocument等),则使用VisualTreeHelper将会引发exception。
如果需要的话,这里有一个可以回到逻辑树的select:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
我的C ++ / CLI版本
template < class T, class U > bool Isinst(U u) { return dynamic_cast< T >(u) != nullptr; } template <typename T> T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name) { if (Isinst<T>(element) && dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name == name) { return dynamic_cast<T>(element); } int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element); for (int i = 0; i < childcount; ++i) { auto childElement = FindVisualChildByType<T>(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name); if (childElement != nullptr) { return childElement; } } return nullptr; };
出于某种原因,这里发布的答案都没有帮助我在MainWindow中获得给定控件中包含的给定types的所有控件。 我需要在一个菜单中find所有菜单项来迭代它们。 他们并不都是菜单的直接后代,所以我设法只用上面的任何一个代码来收集他们的第一个lilne。 这个扩展方法是我的问题的解决scheme,任何人将继续阅读一路下来。
public static void FindVisualChildren<T>(this ICollection<T> children, DependencyObject depObj) where T : DependencyObject { if (depObj != null) { var brethren = LogicalTreeHelper.GetChildren(depObj); var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType<T>(); foreach (var childOfType in brethrenOfType) { children.Add(childOfType); } foreach (var rawChild in brethren) { if (rawChild is DependencyObject) { var child = rawChild as DependencyObject; FindVisualChildren<T>(children, child); } } } }
希望它有帮助。
这是另一个紧凑版本,具有generics语法:
public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject { if (obj != null) { if (obj is T) yield return obj as T; foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>()) foreach (T c in FindLogicalChildren<T>(child)) yield return c; } }
我想添加一个评论,但我不到50分,所以我只能“回答”。 请注意,如果使用“VisualTreeHelper”方法检索XAML“TextBlock”对象,那么它也将抓取XAML“Button”对象。 如果通过写入Textblock.Text参数来重新初始化“TextBlock”对象,则不再能够使用Button.Content参数更改Button文本。 该button将永久显示从Textblock.Text写操作写入的文本(从检索时 –
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window)) { // do something with tb here tb.Text = ""; //this will overwrite Button.Content and render the //Button.Content{set} permanently disabled. }
要解决这个问题,可以尝试使用XAML“TextBox”并添加方法(或事件)来模仿XAMALbutton。 XAML“TextBox”不是通过search“TextBlock”来收集的。
真的很好的答案。
VB.NET版本:
Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T) If depObj IsNot Nothing Then For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1 Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i) If child IsNot Nothing AndAlso TypeOf child Is T Then Yield DirectCast(child, T) End If For Each childOfChild As T In FindVisualChildren(Of T)(child) Yield childOfChild Next Next End If End Function
用法(这将禁用窗口中的所有文本框):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me) tb.IsEnabled = False Next
没有Visual Tree Helpers,我发现它更容易:
foreach (UIElement element in MainWindow.Children) { if (element is TextBox) { if ((element as TextBox).Text != "") { //Do something } } };