How do I programmatically interact with template-generated elements? Part I
You may have a scenario that requires you to find an element within a ControlTemplate. To do that, you use Template.FindName.
Say you have a simple ControlTemplate for a Button, like the following:
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Margin="5" Name="grid">
<Ellipse Stroke="DarkBlue" StrokeThickness="1">
<Ellipse.Fill>
<RadialGradientBrush Center="0.3,0.2"
RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="Azure" Offset="0.1" />
<GradientStop Color="CornflowerBlue"
Offset="1.1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Name="content" Margin="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks to the TargetType attribute, the ControlTemplate is automatically applied to this Button:
<Button Name="myButton1" Margin="10"
Click="ControlTemplateFindElement">Find Grid in ControlTemplate</Button>
If you need to find a named element in the ControlTemplate of myButton1, like the Grid, you can use Template.FindName like so:
// Finding the grid generated by the ControlTemplate of the Button
Grid gridInTemplate = (Grid)myButton1.Template.FindName("grid", myButton1);
Just for fun, let’s create a message box that shows the actual width of that grid:
// Do something with the ControlTemplate-generated grid
MessageBox.Show("The actual width of the grid in the ControlTemplate:"
+ gridInTemplate.GetValue(Grid.ActualWidthProperty).ToString());
Calling Template.FindName works here, but in some cases you may have to write a bit more code. In Part II, I’ll show you how to find an element that’s generated by the DataTemplate of a ListItem (which is also generated). A sample that shows both scenarios will also be in the next refresh of the SDK docs.
Comments
Anonymous
June 01, 2007
What did you people do what that horrible ellipse? It has no antialiasing even on Windows Vista. Terrible fringes.Anonymous
June 04, 2007
Hi Tanveer, thanks for the feedback! First of all, we're looking at the PNG of a screenshot here. Also, anti-aliasing is actually in place. For more information regarding rendering and anti-aliasing in WPF, please see http://msdn2.microsoft.com/En-US/library/aa970908.aspx.Anonymous
June 05, 2007
This post shows you how to find a named element within a DataTemplate. In Part I , we discussed how toAnonymous
June 05, 2007
Well, how can I do this from XAML? <ControlTemplate x:Key="Expanded" TargetType="{x:Type HeaderedContentControl}"> <StackPanel x:Name="contentPanel"> <ContentPresenter Name="HeaderKey" ContentSource="Header"/> <ContentPresenter ContentSource="Content"/> </StackPanel> </ControlTemplate> <Style x:Key="{x:Type HeaderedContentControl}" TargetType="{x:Type HeaderedContentControl}"> <Setter Property="Template" Value="{StaticResource Expanded}"/> <Setter Property="Height" Value="{Binding ElementName=HeaderKey,Path=ActualHeight}" /> <Setter Property="Width" Value="{Binding ElementName=HeaderKey,Path=ActualWidth}" /> ...(rest of the code omitted) This code not working because of bad databinding. How can I access ContentPresenter's ActualHeight and ActualWidthAnonymous
June 05, 2007
Hi Jelical, It seems like you are trying to bind the ActualWidth and ActualHeight of the ContentPresenter inside the template to the Width and Height values on the HeaderedContentControl (that the template is applied to). If that's the case, then you should use TemplateBinding. It would be something like: <ContentPresenter Name="HeaderKey" ActualWidth="{TemplateBinding Width}" ContentSource="Header"/> There's a brief discussion of TemplatingBinding here: http://msdn2.microsoft.com/en-us/library/ms745683.aspx#styling_templatingbinding Hope that help, please let me know if that doesn't work for you!Anonymous
June 05, 2007
Thanks, but no :) It is upside down. I want to bind HeaderedContentControl size to actual size of its header.Anonymous
June 07, 2007
Hi Jelical, if what you're trying to do is to let the Header determine the width and height of the control, then you can do this: <Style x:Key="{x:Type HeaderedContentControl}" TargetType="HeaderedContentControl"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="HeaderedContentControl"> <StackPanel Height="{Binding ElementName=HeaderPresenter, Path=ActualHeight}" Width="{Binding ElementName=HeaderPresenter, Path=ActualWidth}"> <ContentPresenter ContentSource="Header" Name="HeaderPresenter"/> <ContentPresenter ContentSource="Content"/> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> (I combined your template and style to make it more concise here.) You may already know, but in case you’re interested, there’s also an Expander control: http://msdn2.microsoft.com/en-us/library/system.windows.controls.expander.aspx. Hope that helps.Anonymous
November 14, 2007
Can we do the same for Datatemplates as well?Anonymous
November 15, 2007
The following post discusses how to do this with DataTemplates: http://blogs.msdn.com/wpfsdk/archive/2007/03/16/how-do-i-programmatically-interact-with-template-generated-elements-part-ii.aspxAnonymous
July 28, 2008
I need some help. <Style x:Key="{x:Type local:HSChart}" TargetType="{x:Type local:HSChart}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:HSChart}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid x:Name="grid"> <Viewbox> <Canvas Width="8600" Height="4200" x:Name="drawingArea"> <Polygon Points="700,150 7900,150 7900,3750 700,3750 700,150" Fill="#FFFFFFF1" /> ....... I need suggestion on how to access "drawingArea" from the public class HSChart : Control{ ..... private void doSomething(){ Canvas c = this.Template.FindName("drawingArea", this); } } my c is always null. How do I access the named element in xaml? Thanks and Best Regards, Zuff :DAnonymous
July 30, 2008
The comment has been removedAnonymous
July 31, 2008
Hi Carole, Thanks for your help. I managed to get it working after I moved the doSomething() from the constructor. :D Hope you have a nice weekend :D Best Regards, Zuff