Jaa


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>

Button ControlTemplate

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());

Grid in Template Message Box

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 to

  • Anonymous
    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 ActualWidth

  • Anonymous
    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.aspx

  • Anonymous
    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  :D

  • Anonymous
    July 30, 2008
    The comment has been removed

  • Anonymous
    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