I created an helper method to return a List
of UIElements
where the returned elements have to match with one of the Types
which I pass to the method.
First I used this method to get all the children of one type:
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject dependencyObj) where T : DependencyObject
{
if (dependencyObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(dependencyObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
I called this method like:
var textBoxes = UIHelper.FindVisualChildren<TextBox>(currentView);
But I need to find find elements of multiple types, so I rewrote the helper method to accept a List
of Types
.
public static IEnumerable<UIElement> FindVisualChildrenOfMultipleTypes(List<Type> types, DependencyObject dependencyObj)
{
if (dependencyObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(dependencyObj, i);
if (child != null && types.Contains(child.GetType()))
{
yield return child as UIElement;
}
foreach (var childOfChild in FindVisualChildrenOfMultipleTypes(types, child))
{
yield return childOfChild;
}
}
}
}
Called like:
List<Type> types= new List<Type>()
{
typeof(CheckBox),
typeof(RadioButton),
typeof(TextBox)
};
var children = UIHelper.FindVisualChildrenOfMultipleTypes(types, currentView);
This works, but I think there a some improvements possible. For example the signature of my method, I think Generics can be used in the method signature, but I'm not very experienced with Generics, so advice on this would be nice. I'm also wondering if the performance of the method can be improved.
1 Answer 1
SRP
Your current soulution violates the Single Responsibility Principle because it does two things:
- it enumerates all children and
- it checks if a child is of the specified type
This can and should be splitted into two methods that you can implements as extensions.
The Children
extension will only know how to get all children, nothing more. This means I removed the type checking and I reduced nesting by inverting the first condition.
public static IEnumerable<UIElement> Children(this DependencyObject dependencyObj)
{
if (dependencyObj == null)
{
yield break;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObj); i++)
{
var child = VisualTreeHelper.GetChild(dependencyObj, i);
if (child != null)
{
yield return child as UIElement;
}
foreach (var childOfChild in child.Children())
{
yield return childOfChild;
}
}
}
The second extension OfTypes
will be your filter. Here I changed the order of the parameters and decorated the last one with params
which will allow you to easier specify the types without having to create an extra list for them.
public static IEnumerable<UIElement> OfTypes(this IEnumerable<UIElement> uiElements, params Type[] types)
{
return uiElements.Where(x => types.Contains(x.GetType()));
}
Example:
var result = currentView.Children().OfTypes(
typeof(CheckBox),
typeof(RadioButton),
typeof(TextBox)
);
The separation of those two concerns allows you to reuse the Children
extension for other purposes should you need it somewhere else.
FindVisualChildrenOfMultipleTypes
By using extensions like the two I've just suggested, you can get rid of the very long method name because you splitted it in multiple parts.
static
utility or helper methods. Add thethis
keyword to the front of the first parameter, and then they are extension methods. \$\endgroup\$