views:

47

answers:

3

Hi all. I am writing a function to recurse my XAML and add all the controls to a hashtable, with their names being the keys. Unfortunately it seems like I have to go through and list every possible type:

void Recurse_Controls(object start)
{
    string start_type = start.GetType().ToString();

    if (start_type == "StackPanel")
    {
        ControlsByName.Add(((StackPanel)start).Name, start);
        foreach (object item in ((StackPanel)start).Children)
        {
            Recurse_Controls(item);
        }
    }

    if (start_type == "Grid")
    {
        ControlsByName.Add(((Grid)start).Name, start);
        foreach (object item in ((Grid)start).Children)
        {
            Recurse_Controls(item);
        }
    }
}

Is there a simpler way of doing this?

A: 

You could loop through the base types, like Control for buttons etc. or ContainerControl to loop through the nested types. That would cut the amount of if clauses a great deal.

I wonder why you would need something like this though. Wat exactly are you trying to do that you need to list all controls?

Edit: Just realized you can't just use the base type like that; you need to replace start.GetType().ToString() with start.GetType().BaseType.ToString();

Jouke van der Maas
I have a form with around 200-300 controls, and they are named very uniformly and meaningfully. When I interact with them programmatically, it isn't known at compile time which ones I need to interact with, so I need a more dynamic way to refer to the controls.
Adam S
Don't you think storing 200-300 strings as keys in a dictionary is going to use an awful lot of memory? I would consider using shorts instead, giving ranges a special meaning (like 1-100 are buttons, 101-200 are checkboxes, or whatever you're doing).
Jouke van der Maas
A: 

It won't get much better than you did it. You should however use the is clause instead of comparing strings. This is much safer and much faster. Also you might be able to use baseclasses in the check. For example if (start is ButtonBase) will be true for all derived classes too (if that is suitable for your needs).

bitbonk
+4  A: 

What you really want to do is get all the objects in the logical tree. The logical tree will include other things than just controls (RowDefinitions for instance), so you should check to ensure the name actually exists on the object before adding it to the dictionary.

Since the Name property is defined on FrameworkElement (and FrameworkContentElement, but they use AddOwner so that these two properties are actually the same instance), you can just use GetValue to retrieve the value. This should do what you need:

void Recurse_Controls(DependencyObject start)
{
     if (start == null) return;

     var name = (string)start.GetValue(FrameworkElement.NameProperty);
     if (!String.IsNullOrEmpty(name))
         ControlsByName.Add(name, start);

     foreach (var child in LogicalTreeHelper.GetChildren(start))
         Recurse_Controls(child);
}

Side note: this is easily made iterative by using a Queue instead of recursion:

void Add_Controls(DependencyObject start)
{
     if (start == null) return;
     var items = new Queue<DependencyObject>();
     items.Enqueue(start);

     while (items.Count > 0)
     {
         var item = items.Dequeue();
         var name = (string)item.GetValue(FrameworkElement.NameProperty);
         if (!String.IsNullOrEmpty(name))
             ControlsByName.Add(name, item);

         foreach (var child in LogicalTreeHelper.GetChildren(item))
             items.Enqueue(child);
     }
}
Abe Heidebrecht
@Abe - Nice solution. This is exactly what I wanted to recommend, so now I won't bother. :)
Venemo