我正在寻找一种通过其类型在Window上查找所有控件的方法,
例如: find all TextBoxes
,找到实现特定接口的所有控件等。
我正在寻找一种通过其类型在Window上查找所有控件的方法,
例如: find all TextBoxes
,找到实现特定接口的所有控件等。
Answers:
这应该可以解决问题
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
}
this
before DependencyObject
=>this DependencyObject depObj
这是最简单的方法:
IEnumerable<myType> collection = control.Children.OfType<myType>();
其中control是窗口的根元素。
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
,然后才能使用Anata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
我改编了@Bryce Kahle的回答,以遵循@Mathias Lykkegaard Lorenzen的建议和使用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分别提到的那样检查GroupBox中的选项卡控件或网格。)(也遵循@noonand的建议并删除了多余的孩子!= null)
使用帮助程序类VisualTreeHelper
或LogicalTreeHelper
取决于您感兴趣的树。它们都提供了获取元素子级的方法(尽管语法略有不同)。我经常使用这些类来查找特定类型的首次出现,但是您可以轻松地对其进行修改以查找该类型的所有对象:
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);
在上面的几个示例中使用的行不会为GroupBox
es 返回非零计数,尤其是其中GroupBox
包含Grid
,以及Grid
包含子元素的地方。我相信这可能是因为GroupBox
不允许包含多个孩子,并且此孩子存储在其Content
属性中。没有GroupBox.Children
属性类型。我敢肯定我做的效率不是很高,但是我修改了该链中的第一个“ 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;
}
}
}
}
要获取特定类型的所有子项的列表,可以使用:
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;
}
对递归进行小的更改,因此您可以例如找到选项卡控件的子选项卡控件。
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;
}
这是又一个紧凑的版本,具有泛型语法:
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;
}
}
这就是它向上工作的方式
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将引发异常。
如果需要的话,这是一种替代方案,它可以落到逻辑树上:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
我想添加评论,但我的分数不到50分,因此我只能“回答”。请注意,如果您使用“ VisualTreeHelper”方法检索XAML“ TextBlock”对象,那么它还将获取XAML“ Button”对象。如果通过写入Textblock.Text参数来重新初始化“ TextBlock”对象,则将不再能够使用Button.Content参数来更改Button文本。Button将永久显示从Textblock中写入的文本。Text write操作(从检索到-
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”并添加方法(或事件)来模仿XAMAL Button。搜索“ TextBlock”不会收集XAML“ TextBox”。
我的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中获取给定控件中包含的所有给定类型的控件。我需要在一个菜单中找到所有菜单项以对其进行迭代。它们不是菜单的全部直接后代,因此我设法使用上面的任何代码仅收集了它们的第一个列表。对于任何将继续阅读全文的人,此扩展方法都是我解决该问题的方法。
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);
}
}
}
}
希望能帮助到你。
该接受的答案返回发现的元素或多或少无序的,按照第一子分支尽可能深地,同时产生沿途发现的元素,回溯和重复的尚未解析树枝的步骤之前。
如果您需要按降序排列的后代元素,首先将产生直接子代,然后是其子代,依此类推,以下算法将起作用:
public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
where T : DependencyObject
{
if (parent == null || !(child is Visual || child is Visual3D))
yield break;
var descendants = new Queue<DependencyObject>();
descendants.Enqueue(parent);
while (descendants.Count > 0)
{
var currentDescendant = descendants.Dequeue();
if (applyTemplates)
(currentDescendant as FrameworkElement)?.ApplyTemplate();
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(currentDescendant); i++)
{
var child = VisualTreeHelper.GetChild(currentDescendant, i);
if (child is Visual || child is Visual3D)
descendants.Enqueue(child);
if (child is T foundObject)
yield return foundObject;
}
}
}
生成的元素将按从最近到最远的顺序排序。这将很有用,例如,如果您正在寻找某种类型和条件的最近子元素:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);
child
未定义。
@布莱斯,非常好的答案。
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
用法(这将禁用窗口中的所有TextBox):
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
}
}
};