Accessing a control inside a ControlTemplate

北城余情 提交于 2019-12-09 14:27:19

问题


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?


回答1:


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.




回答2:


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.




回答3:


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

this.GetTemplateChild("ControlName");



回答4:


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
}



回答5:


If you can get grid control, then try using below code

TextBlock textBlock2 = (TextBlock)gridInTemplate.Children[1];



回答6:


The method "FrameworkElement.FindName(string name)" uses the namescope of the layout where the button/control is used to resolve the names. In short you can use this to find children in a grid or stack panel in your application layout. But you cannot use the same to find the children of a control which you have used in your application layout(because the templated chidren names are in a different scope)

One way you can get the children in your situation is to inherit the button. Since you will not be modifying any other property or behavior of the button the new button would work as normal one. Practically I have never used such a method of accessing the templated children as I never had the need to use them outside the scope of the control's class.

public class WeddingButton : Button
{
    public override void OnApplyTemplate()
    {
        TextBlock textBlock = this.GetTemplateChild("textBlock2") as TextBlock;
        base.OnApplyTemplate();
    }
}

<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">
    <WeddingButton x:Name="btnWedding" HorizontalAlignment="Left" Margin="10,20,0,-49" VerticalAlignment="Top" Template="{StaticResource WeddingButtonBigTemplate}" Foreground="#FF2B4072" Width="380" Click="btnClick" />
</Grid>


来源:https://stackoverflow.com/questions/19116327/accessing-a-control-inside-a-controltemplate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!