Home > OS >  How to find a DataTemplate control from inside the Click event
How to find a DataTemplate control from inside the Click event

Time:05-18

I have a GridView with a ColumnHeaderTemplate

The template contains a path with the name arrow:

<DataTemplate x:Key="HeaderTemplate">
  <DockPanel>
    <Path DockPanel.Dock="Right" Margin="5,0,5,0" x:Name="arrow" StrokeThickness="1" Fill="Gray" Data="M 5,5 L 10,10 L 15,5 L 5,5" SnapsToDevicePixels="True"/>
  </DockPanel>
</DataTemplate>

The template is assigned in the view like this:

<GridView ColumnHeaderTemplate="{StaticResource HeaderTemplate}">

The GridView is inside a ListView that manages the events

GridViewColumnHeader.Click="ListView_ColumnHeaderClick"

private void ListView_ColumnHeaderClick(object sender, RoutedEventArgs e)

When the event is triggered I want to be able to find the arrow control.

According to my research I should use the Template.FindName method, but so far I have not been able to make this work. I cant seem to find the correct objects to use with the function and so I never find the control I am looking for.

CodePudding user response:

No, the FindName method you mean would apply to ControlTemplate, not DataTemplate.

For DataTemplate you have to iterate the children maually using VisualTreeHelper.

I do not know how you have attached the column header event handler, so I assume this:

<ListView ItemsSource="{Binding YourItemsSource}">
    <ListView.Resources>
        <DataTemplate x:Key="HeaderTemplate">
            <DockPanel>
                <Path DockPanel.Dock="Right" Margin="5,0,5,0" x:Name="arrow" StrokeThickness="1" Fill="Gray" Data="M 5,5 L 10,10 L 15,5 L 5,5" SnapsToDevicePixels="True"/>
            </DockPanel>
        </DataTemplate>
        <Style x:Key="HeaderContainerStyle" TargetType="{x:Type GridViewColumnHeader}" BasedOn="{StaticResource {x:Type GridViewColumnHeader}}">
            <EventSetter Event="Click" Handler="ListView_ColumnHeaderClick"/>
        </Style>
    </ListView.Resources>
    <ListView.View>
        <GridView ColumnHeaderTemplate="{StaticResource HeaderTemplate}"
                  ColumnHeaderContainerStyle="{StaticResource HeaderContainerStyle}">
            <!-- ...your column definitions. -->
        </GridView>
    </ListView.View>
</ListView>

You have to create a custom method to recursively traverse the visual tree of the the grid view column header that checks the type and the name of the child elements to get the right one.

public T GetChild<T>(DependencyObject dependencyObject, string name) where T : FrameworkElement
{
   if (dependencyObject == null)
      return null;

   for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i  )
   {
      var child = VisualTreeHelper.GetChild(dependencyObject, i);
      if (child is T frameworkElement && frameworkElement.Name.Equals(name))
         return frameworkElement;

      var nextChild = GetChild<T>(child, name);
      if (nextChild != null)
         return nextChild;
   }

   return null;
}

Then in the event handler, you can pass the sender, which is the column header.

private void ListView_ColumnHeaderClick(object sender, RoutedEventArgs e)
{
   var gridViewColumnHeader = (GridViewColumnHeader)sender;
   var arrow = GetChild<Path>(gridViewColumnHeader, "arrow");

   // ... do something with arrow.

   return;
}

Although this solution works and is a legitimate and officially documented way to solve your issue, you should usually not have to traverse the visual tree this way. In most cases it is not necessary as a lot of issues can be solved more elegantly and easier using data binding.

  • Related