Я ищу способ найти все элементы управления в Window по их типу,
например: найти все TextBoxes
, найти все элементы управления, реализующие определенный интерфейс и т. д.
Я ищу способ найти все элементы управления в Window по их типу,
например: найти все TextBoxes
, найти все элементы управления, реализующие определенный интерфейс и т. д.
Ответы:
Это должно сделать свое дело
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 соответственно.) (Также следовал совету @ 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
a 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. Если вы повторно инициализируете объект «TextBlock» путем записи в параметр Textblock.Text, вы больше не сможете изменять текст Button с помощью параметра Button.Content. Кнопка будет постоянно показывать текст, записанный в нее из действия записи 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» и добавить методы (или события) для имитации кнопки XAMAL. XAML «TextBox» не собирается поиском «TextBlock».
Моя версия для 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;
};
По какой-то причине ни один из ответов, опубликованных здесь, не помог мне получить все элементы управления данного типа, содержащиеся в данном элементе управления в моем главном окне. Мне нужно было найти все пункты меню в одном меню, чтобы перебрать их. Они не все были прямыми потомками меню, поэтому мне удалось собрать только первые из них, используя любой из приведенного выше кода. Этот метод расширения является моим решением проблемы для всех, кто будет продолжать читать здесь.
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
Использование (это отключает все текстовые поля в окне):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Для этого и других случаев вы можете добавить текущий метод расширения в вашу библиотеку:
public static List<DependencyObject> FindAllChildren(this DependencyObject dpo, Predicate<DependencyObject> predicate)
{
var results = new List<DependencyObject>();
if (predicate == null)
return results;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dpo); i++)
{
var child = VisualTreeHelper.GetChild(dpo, i);
if (predicate(child))
results.Add(child);
var subChildren = child.FindAllChildren(predicate);
results.AddRange(subChildren);
}
return results;
}
Пример для вашего случая:
var children = dpObject.FindAllChildren(child => child is TextBox);
Мне было проще без Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};