Tuesday, August 2, 2011

Access Templated Child in Silverlight and Windows Phone 7



Accessing a templated child is way too simple in WPF but for SilverLight which is even being used in Windows Phone 7 needs a little extra work to the work being done. Well there are two ways to achieve it. I'll try to explain both of them one by one. Before getting into explanations of these methods we need a sample template to get a better picture.

A sample template for a ListBoxItem :


So, now when we have custom style :-)  we can start looking into the options to retrieve an element (myTextBox) from Style at run time.

First Approach
  1.  Make sure you have defined names for the items you are going to acess in your template let say it "myTextBox".
  2. Now you must have ListBoxItem for which you are going to find your TemplateChild lets say it "lbi".
After going through these initial check now use the following code to find element:

//Enforce Style to apply in case if it's not yet applied
lbi.ApplyTemplate();
FirstApproach(lbi);

void FirstApproach(ListBoxItem lbi)
{
    StackPanel stkTemplatedChild = VisualTreeHelper.GetChild((DependencyObject)lbi, 0) as StackPanel;
    TextBlock txtBlk = stkTemplatedChild.FindName("myTextBlk") as TextBlock;
    txtBlk.Text = "First Approach CLicked";
}

So, "txtBlk " is your desired element 

Second Approach:

This approach is to itrerate through all the elements in style's template. which can be done by using this code:

//Enforce Style to apply in case if it's not yet applied
lbi.ApplyTemplate();
SecondApproach(lbi);

void SecondApproach(ListBoxItem lbi)
{
    TextBlock templatedTextBlk;
    int childCount = VisualTreeHelper.GetChildrenCount((DependencyObject)lbi);
    for (int i = 0; i < childCount; i++)
    {
        DependencyObject childObj = VisualTreeHelper.GetChild(lbi, i);
        int subChild = VisualTreeHelper.GetChildrenCount(childObj);
        for (int n = 0; n < subChild; n++)
        {
            DependencyObject dpChild = VisualTreeHelper.GetChild(childObj, n);
            if (dpChild is TextBlock && (dpChild as TextBlock).Name == "myTextBlk")
            {
                templatedTextBlk = dpChild as TextBlock;
                templatedTextBlk.Text = "Second Approach Clicked";
                break;
            }
        }
    }
}
"templatedTextBx" is your result.

Third Approach (using helper function):  This approach may look catchy but it has some down sides. In this approach you have to iterate through VisualTree which could be a resource intensive. But this one is good if you have a inline template.

//Enforce Style to apply in case if it's not yet applied
lbi.ApplyTemplate();
TextBlock txtbx = FindVisualChildByType(lbi, "myTextBlk");
if (txtbx != null)
    txtbx.Text = "Third Approach Clicked";

T FindVisualChildByType(DependencyObject element, String name) where T : class     
{
    if (element is T && (element as FrameworkElement).Name == name)
        return element as T;
    int childcount = VisualTreeHelper.GetChildrenCount(element);
    for (int i = 0; i < childcount; i++)
    {
        T childElement = FindVisualChildByType(VisualTreeHelper.GetChild(element, i), name);
        if (childElement != null)
            return childElement;
    }
    return null;
}
So, txtbx is your desired result.
Source Code