WPF Template.FindName return always null

Enzojz picture Enzojz · May 30, 2013 · Viewed 16k times · Source

Template

<Style TargetType="{x:Type local:Viewport}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:Viewport}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsPresenter/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <Canvas x:Name="PART_Canvas" IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

And the code in OnApplyTemplate

content = this.Template.FindName("PART_Canvas", this) as FrameworkElement;

the content returns always null, why it doesn't work?

if I replace with this, the program quits directly

content = this.ItemsPanel.FindName("PART_Canvas", this) as FrameworkElement;

Answer

dkozl picture dkozl · May 30, 2013

With FindName you can find only elements declared in a Template. ItemsPanel is not part of that template. ItemsControl puts ItemsPanel into ItemsPresenter place holder via which you can access your Canvas but first you need to name ItemsPresenter in your template:

<ControlTemplate TargetType="{x:Type local:Viewport}">
   <Border>
      <ItemsPresenter x:Name="PART_ItemsPresenter"/>
   </Border>
</ControlTemplate>

then, using VisualTreeHelper get your Canvas, but I think earliest place when you can call code below is when FrameWorkElement is Loaded. This is my example:

public class MyListBox : ListBox
{
  public MyListBox()
  {
      AddHandler(FrameworkElement.LoadedEvent, new RoutedEventHandler(ControlIsLoaded));
  }

  private void ControlIsLoaded(object sender, RoutedEventArgs e)
  {
      var canvas = VisualTreeHelper.GetChild(this.Template.FindName("PART_ItemsPresenter", this) as DependencyObject, 0);
  }
}