C# 访问 ControlTemplate 内的控件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19116327/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:08:37  来源:igfitidea点击:

Accessing a control inside a ControlTemplate

c#wpfc#-4.0controltemplate

提问by toy4fun

This is the xaml:

这是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 OnApplyTemplatebut got null.

我正在尝试访问名为textBlock2.
我试图覆盖OnApplyTemplate但为空。

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.

gridInTemplate 为空(样本取自 MSDN)。
当然,ct# 都是空的。

What am I missing here?

我在这里缺少什么?

回答by RonakThakkar

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

如果您可以获得网格控制,请尝试使用以下代码

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

回答by Nitin

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

您可以使用 VisualTreeHelper 迭代按钮的可视化树以获取任何子项。你可以使用这个基本的通用函数来获取它

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
}

回答by Thomas Levesque

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

您的代码是正确的,但可能不在正确的位置......FindName只有在应用模板后才能工作。通常,OnApplyTemplate在自定义控件中覆盖时会使用它。由于您不是在创建自定义控件,因此可以在按钮的 Loaded 事件中进行。

回答by dev hedgehog

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

如果您已覆盖 OnApplyTemplate,则不要使用 FindResource() 或 Template.FindName() 或任何与 VisualTreeHelper 的技巧。只需使用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.

WPF 中的模板具有独立的名称范围。这是因为模板是重复使用的,当一个控件的多个实例每个实例化其模板时,模板中定义的任何名称都不能保持唯一。调用 GetTemplateChild 方法以返回对模板实例化后来自模板的对象的引用。您不能使用 FrameworkElement.FindName 方法从模板中查找项目,因为 FrameworkElement.FindName 在更一般的范围内起作用,并且 ControlTemplate 类本身与应用后的实例化模板之间没有任何联系。

Check this link out:

看看这个链接:

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

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

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.

总结 - 在创作自定义控件(例如 OnApplyTemplate)时使用 GetTemplateChild(),在其他情况下使用 Template.FindName。

回答by Karthik

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)

方法“FrameworkElement.FindName(string name)”使用布局的名称范围,其中按钮/控件用于解析名称。简而言之,您可以使用它在应用程序布局的网格或堆栈面板中查找子项。但是您不能使用它来查找您在应用程序布局中使用的控件的子项(因为模板化的子项名称在不同的范围内)

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>

回答by Ramesh

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

试试下面的代码。这将返回模板化元素。

this.GetTemplateChild("ControlName");