Always in the last place you look (programmatically finding controls in a C# / XAML store app)

March 04, 2013

I’ve seen a few articles on this, but none dealing directly with a Windows 8 Store App.  Not that much changes in the general approach, but this article is more about working out why the wheel is round than trying to re-invent it.

The Problem

So, the basis for the article is a standard sample Windows 8 Grid App in C# / XAML.  Here’s the XAML as it stands - it should fit neatly into the template Grid app with a bit of adaptation:

[sourcecode language=“xml”]

        ;
    



So to quickly summarise, I have a media element and a play button.  So, the actual media file will, in real life, be bound to the data source, but the play button should have the same effect regardless.  The problem that I have it that I can't reference the MediaElement "media", because the code behind doesn't know which "media" I mean.

**The Solution**

To start off with, let's have a look at the visual tree (this should also guide the way to the solution).  This function tells me what the XAML visual tree looks like:

``` csharp

private void DisplayVisualTree(DependencyObject control, int indent)
{
    string tab = "";
    for (int i = 1; i <= indent; i++)
        tab += "t";
    
    System.Diagnostics.Debug.WriteLine(string.Format("{0}{1}",
        tab, control.ToString()));

    int childNumber = VisualTreeHelper.GetChildrenCount(control);

    for (int i = 0; i < childNumber; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(control, i);

        DisplayVisualTree(child, indent + 1);
    }    
}

It’s not the finest piece of code I’ve ever written, but if we wire into the play button, it’ll show what the tree looks like in the output window:


private void playVideo\_Click(object sender, RoutedEventArgs e)
{
     DisplayVisualTree(this, 0);
}

Okay - so now we know what the visual tree looks like, so let’s see if we can work out what each element is called.  The trick to this is just casting the FrameworkElement (change the DisplayVisualTree function as follows):


    string tab = "";
    for (int i = 1; i <= indent; i++)
        tab += "t";

    FrameworkElement ctrl = control as FrameworkElement;
    System.Diagnostics.Debug.WriteLine(string.Format("{0}, {1}, {2}",
        tab, ctrl.Name, control.ToString()));

    int childNumber = VisualTreeHelper.GetChildrenCount(control);

So now when you run it, you can see each element’s name.  You can simply search the output window and see the “media” element there.  Now we want to return that.  Basically, we want the DisplayVisualTree function to find the control and return it; something like this would do that:


private DependencyObject FindChildControl;(DependencyObject control, string ctrlName)
{
    int childNumber = VisualTreeHelper.GetChildrenCount(control);
    for (int i = 0; i < childNumber; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(control, i);
        FrameworkElement fe = child as FrameworkElement;
        // Not a framework element or is null
        if (fe == null) return null;

        if (child is T && fe.Name == ctrlName)
        {
            // Found the control so return
            return child;
        }
        else
        {
            // Not found it - search children
            DependencyObject nextLevel = FindChildControl;(child, ctrlName);
            if (nextLevel != null)
                return nextLevel;
        }
    }
    return null;
}

If you call this in the Play button, you’ll be able to get a handle to the control:


private void playVideo\_Click(object sender, RoutedEventArgs e)
{
    MediaElement media = FindChildControl;(this, "media") as MediaElement;
    media.Play();
        
    //DisplayVisualTree(this, 0));
}

Conclusion

This does look like a lot of trouble to go to to simply control a media element (especially since sinple clicking the element itself will start the play), however, once you start dealing with databound XAML, it’s only a matter of time before you decide you need to access and control an element inside a databound template.



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024