如何以编程方式访问在ContentTemplate中定义的元素?

4
假设我已经创建了一个UserControl,并在XAML中定义了以下ContentTemplate:
<UserControl.ContentTemplate>
    <DataTemplate> 
        <Ellipse Name="myEllipse" Stroke="White"/>
        <ContentPresenter Content="{TemplateBinding Content}"/>
    </DataTemplate>
</UserControl.ContentTemplate>

我该如何在代码中访问“myEllipse”元素,以便例如通过“myEllipse.Height”查找其高度?我无法直接通过名称访问它。我尝试创建一个引用来访问它:

Ellipse ellipse = ContentTemplate.FindName("myEllipse",this) as Ellipse;  

当我运行程序时,它会崩溃,并说它无法创建我的类的实例。也许我没有正确使用FindName函数。如果有人能帮我解决这个问题,我将非常感激。

谢谢,

Dalal

2个回答

6
为了在DataTemplate中使用FindName,您需要引用ContentPresenter。请参考Josh Smith的文章如何在ContentControl中使用FindName
实际上,您可能想要使用ControlTemplate而不是DataTemplate。这样做应该更容易使用,并且可以让控件的用户应用自己的内容模板或使用隐式模板。如果您像这样做:
<UserControl.Template>
    <ControlTemplate TargetType="UserControl">
        <Grid>
            <ContentPresenter/>
            <Ellipse Name="myEllipse" Stroke="White"/>
        </Grid>
    </ControlTemplate>
</UserControl.Template>

然后在代码中(可能是在OnApplyTemplate重写中),您将能够做到这一点:
var ellipse = Template.FindName("myEllipse", this) as Ellipse;

您还需要像这样使用TemplatePartAttribute修饰您的类:

[TemplatePart(Name="myEllipse", Type = typeof(Ellipse))]

因此,如果有人重新制定您的控件模板,则需要使用该名称提供一个Ellipse元素。 (如果该类仅在内部使用,则此项较不重要。)

最后,如果您只想更改Ellipse的颜色,则可以使用数据绑定。 您可以在控件上创建一个EllipseColor依赖属性,然后仅设置Stroke =“{TemplateBinding EllipseColor}”


感谢您提供如此详细的答案。我尝试使用ControlTemplate替换我的DataTemplate,但是我的UserControl看起来不同了,并且某些功能受到了影响,因此我宁愿尝试在控件开发中走得更远,使用DataTemplate让它正常工作。阅读Josh Smith的文章后,我尝试在控件的构造函数中添加以下行:ContentPresenter contentPresenter = VisualTreeHelper.GetChild(this,0) as ContentPresenter;不幸的是,在那一行失败了。我收到了一个XAML解析异常。有什么想法吗? - Dalal
1
@Dalal:在构造函数中,可视树尚未填充,因此在调用GetChild时会出现范围外异常。这被包装在XAML解析异常中,因为它无法实例化您的对象。您需要将代码移动到Load事件处理程序或OnApplyTemplate重写中。此外,UserControl的默认控件模板具有一个Border中的ContentPresenter,因此VisualTreeHelper.GetChild(this, 0)将返回一个Border。您需要执行类似于VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0)的操作。 - Quartermeister
在我的OnApplyTemplate重写中,我成功地使用以下代码行获取了ContentPresenter:VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0) as ContentPresenter;。现在我需要引用我的一个名为'headerLabel'的对象,所以我放置了headerLabel = ContentTemplate.FindName("headerLabel", contentPresenter) as Label;。但是我收到了InvalidOperationException: This operation is valid only on elements that have this template applied.的错误提示。好吧,教程说要传入ContentPresenter。我已经这样做了。我还尝试将此代码放置在MouseDown事件中,但无济于事。 - Dalal
1
@Dalal:我很惊讶MouseDown没有起作用,但也许OnApplyTemplate还为时过早。您能否确认contentPresenter.ContentTemplate == ContentTemplate,即ContentPresenter是否正在使用您提供的ContentTemplate?在调用FindName之前尝试调用contentPresenter.ApplyTemplate()以强制ContentPresenter实例化其模板。 - Quartermeister
你说得对,我之前做错了,但是你的建议解决了问题。我使用了 contentPresenter.ContentTemplate 并且加上了 contentPresenter.ApplyTemplate(),然后它就可以工作了!太好了!非常感谢你。我当时非常沮丧。如果没有 ApplyTemplate(),它是无法工作的,所以感谢你提到这一点,否则我可能会放弃并使用一个变通方法,即通过它们的加载事件处理程序获取我的对象引用。 - Dalal

0

我的问题是获取对象的引用,而不是改变它的描边。我只是举了描边作为例子,但现在我会编辑它,因为有点不清楚。谢谢。 - Dalal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接