Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing a control inside a ControlTemplate

This is the xaml:

<Page.Resources>
    <ControlTemplate x:Key="WeddingButtonBigTemplate" TargetType="Button">
        <Grid>
            <Image x:Name="imgNormal" Source="../Images/Married_button2.png"/>
            <TextBlock x:Name="textBlock2" Style="{StaticResource RegularBlueSpecialBoldText}" LineHeight="28" LineStackingStrategy="BlockLineHeight" HorizontalAlignment="Center" Margin="10,30,10,70" TextWrapping="Wrap" TextAlignment="Center" VerticalAlignment="Stretch" >
                <Run FontSize="20" Text="The event of"></Run>
                <Run FontSize="28" Text="{DynamicResource strBride}"></Run>
            </TextBlock>
        </Grid>
    </ControlTemplate>
</Page.Resources>

<Grid HorizontalAlignment="Center" VerticalAlignment="Top" Width="1000">
    <Button x:Name="btnWedding" HorizontalAlignment="Left" Margin="10,20,0,-49" VerticalAlignment="Top" Template="{StaticResource WeddingButtonBigTemplate}" Foreground="#FF2B4072" Width="380" Click="btnClick" />
</Grid>

I'm tring to get access to the TextBlock named textBlock2.
I've tried to override OnApplyTemplate but got null.

I've tried:

Grid gridInTemplate = (Grid)btnWedding.Template.FindName("grid", btnWedding);
var ct0 = btnWedding.Template.FindName("textBlock2", btnWedding);
var ct1 = btnWedding.FindName("textBlock2");
var ct2 = btnWedding.FindResource("textBlock2");

The gridInTemplate is null (sample taken from MSDN).
The ct# are all null, of course.

What am I missing here?

like image 557
toy4fun Avatar asked Oct 01 '13 12:10

toy4fun


4 Answers

If you have overriden OnApplyTemplate then do not use FindResource() or Template.FindName() or any hacks with VisualTreeHelper. Just use this.GetTemplateChild("textBlock2");

Templates in WPF have a self-contained namescope. This is because templates are re-used, and any name defined in a template cannot remain unique when multiple instances of a control each instantiate its template. Call the GetTemplateChild method to return references to objects that come from the template after it is instantiated. You cannot use the FrameworkElement.FindName method to find items from templates because FrameworkElement.FindName acts in a more general scope, and there is no connection between the ControlTemplate class itself and the instantiated template once it is applied.

Check this link out:

http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.gettemplatechild.aspx

If your example is microsoft example then I suggest you to read it again. You might have skipped something.

http://msdn.microsoft.com/en-us/library/bb613586.aspx

To sum up - Use GetTemplateChild() when authoring custom control e.g. OnApplyTemplate, and use Template.FindName in other situations.

like image 93
dev hedgehog Avatar answered Oct 02 '22 14:10

dev hedgehog


Try the following code. This will return the templated element.

this.GetTemplateChild("ControlName");
like image 36
Ramesh Avatar answered Oct 02 '22 15:10

Ramesh


Your code is correct, but probably not in the right place... FindName will only work after the template has been applied. Typically, you use it when you override OnApplyTemplate in a custom control. Since you're not creating a custom control, you can do it in the Loaded event of the button.

like image 33
Thomas Levesque Avatar answered Oct 02 '22 15:10

Thomas Levesque


you can use VisualTreeHelper to iterate the visual tree of button to get any child. You can use this basic generic function to get it

private static DependencyObject RecursiveVisualChildFinder<T>(DependencyObject rootObject)  
{  
    var child = VisualTreeHelper.GetChild(rootObject, 0);  
    if (child == null) return null;  

    return child.GetType() == typeof (T) ? child : RecursiveVisualChildFinder<T>(child);  
}

you can use it like

TextBlock textblock = RecursiveVisualChildFinder<TextBlock>(btnWedding);
if(textblock.Name == "textBlock2")
{// Do your stuff here
}
like image 33
Nitin Avatar answered Oct 02 '22 15:10

Nitin