2
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ Neither of those are actually extension methods (in the C# sense), by the way. They are just static utility or helper methods. Add the this keyword to the front of the first parameter, and then they are extension methods. \$\endgroup\$ – Jesse C. Slicer Mar 25 '16 at 13:38
  • \$\begingroup\$ @JesseC.Slicer I edited my post with the correct naming. Any advice on how to improve the performance or how to improve the method signature (except what you already told about it)?. \$\endgroup\$ – Sybren Mar 25 '16 at 14:06
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.