9

I have dynamic added controls in my XAML UI. How I can find a specific control with a name.

visc
  • 4,794
  • 6
  • 32
  • 58
CorneDompelaar
  • 121
  • 1
  • 1
  • 3

4 Answers4

16

There is a way to do that. You can use the VisualTreeHelper to walk through all the objects on the screen. A convenient method I use (obtained it somewhere from the web) is the FindControl method:

public static T FindControl<T>(UIElement parent, Type targetType, string ControlName) where T : FrameworkElement
{

    if (parent == null) return null;

    if (parent.GetType() == targetType && ((T)parent).Name == ControlName)
    {
        return (T)parent;
    }
    T result = null;
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < count; i++)
    {
        UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);

        if (FindControl<T>(child, targetType, ControlName) != null)
        {
            result = FindControl<T>(child, targetType, ControlName);
            break;
        }
    }
    return result;
}

You can use it like this:

var combo = ControlHelper.FindControl<ComboBox>(this, typeof(ComboBox), "ComboBox123");
Martin Tirion
  • 1,246
  • 1
  • 7
  • 10
  • Would [FindName](https://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.findname(v=vs.110).aspx) also work? – Bill Hoag Nov 15 '17 at 19:35
  • Thank you, Martin! For someone who needs get access from code to any `DataTemplate controls` just use `Tag` to keep dynamicly created name and modify code above for check the control `Tag` property. – NoWar Oct 06 '21 at 05:49
4

I have extended @Martin Tirion version, to make it comfortable:

  • Eliminate the type parameter
  • Make UIElement extension for better use

Here is the changed code:

namespace StackOwerflow.Sample.Helpers
{
    public static class UIElementExtensions
    {
        public static T FindControl<T>( this UIElement parent, string ControlName ) where T : FrameworkElement
        {
            if( parent == null )
                return null;

            if( parent.GetType() == typeof(T) && (( T )parent).Name == ControlName )
            {
                return ( T )parent;
            }
            T result = null;
            int count = VisualTreeHelper.GetChildrenCount( parent );
            for( int i = 0; i < count; i++ )
            {
                UIElement child = ( UIElement )VisualTreeHelper.GetChild( parent, i );

                if( FindControl<T>( child, ControlName ) != null )
                {
                    result = FindControl<T>( child, ControlName );
                    break;
                }
            }
            return result;
        }
    }
}

After the modification, I am able to use like this:

var combo = parent.FindControl<ComboBox>("ComboBox123");

or when the parent is the current dialog it just like this:

var combo = FindControl<ComboBox>("ComboBox123");

Thank you @Martin Tirion again!

György Gulyás
  • 1,290
  • 11
  • 37
1

When you create the Control in XAML you can give it a x:Name="..." Tag. In your corresponding C# Class the Control will be available under that name.
Some Container Views like Grid got a Children Property, you can use this to search for Controls inside them.

Thommy
  • 5,070
  • 2
  • 28
  • 51
0

I liked the previous answers, but I wanted some control if it would walk the tree and allow for it not to find the control.

Usage:

/// <summary>
/// Finds a grid panel with the name "GridVariables" and toggles it's visibility to and from visible
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public sealed partial class MainPage : Page
{
    private void btnShowVariables_Click(object sender, RoutedEventArgs e)
    {
        if (this.TryFindChildControl<Grid>("gridVariables", out var grid))
        {
            grid.Visibility = grid.Visibility.HasFlag(Visibility.Collapsed) ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}

The extension class:

public static class UIElementExtensions
{
    /// <summary>
    /// Returns the first FrameworkElement with the type and name
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sourceControl"></param>
    /// <param name="name"></param>
    /// <param name="control"></param>
    /// <returns></returns>
    public static bool TryFindChildControl<T>(this UIElement sourceControl, string name, out T control, bool recursiveSearch = true)
        where T : FrameworkElement
    {
        var childCount = VisualTreeHelper.GetChildrenCount(sourceControl);
        
        for (var c = 0; c < childCount; c++)
        {
            var child = VisualTreeHelper.GetChild(sourceControl, c) as FrameworkElement;

            if (child == null) continue;

            var castChild = child as T;
            var found = castChild != null && castChild.Name.ToLower() == name.ToLower();

            if (!found)
            {
                if (recursiveSearch && TryFindChildControl<T>(child, name, out var innerChild, recursiveSearch))
                {
                    castChild = innerChild;
                }
                else
                {
                    continue;
                }
            }

            control = castChild;
            return true;
        }

        control = null;
        return false;
    }
}
Monofuse
  • 735
  • 6
  • 14