-1
<Window x:Class="WpfApp12.MainWindow"
    xmlns=...usual namespaces...
    Loaded="Window_Loaded"
    >

<Window.Resources>

    <DataTemplate x:Key="myHeaderTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{TemplateBinding Content}"/>
            <Button Margin="5,0,0,0" x:Name="MyButton">Press me</Button>
        </StackPanel>
    </DataTemplate>

</Window.Resources>

<!-- Just point the datacontext to the code behind -->
<Window.DataContext>
    <Binding RelativeSource="{RelativeSource Mode=Self}"/>
</Window.DataContext>

<DataGrid Name="DG" ItemsSource="{Binding People}"/>

</Window>

This, togheter with the code behind below gives just what I want: a DataGrid with a column whose header has been dynamically assigned a DataTemplate with a Button "Press me":

DataGrid with Column with DataTemplated header

The code behind:

public partial class MainWindow : Window
{

    public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
    }

    public MainWindow()
    {
        People.Add(new Person() { Name = "Isaac", Surname = "Newton" });
        People.Add(new Person() { Name = "Galileo", Surname = "Galilei" });

        InitializeComponent();
    }

    public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        DG.Columns[0].HeaderTemplate = (DataTemplate)FindResource("myHeaderTemplate");
        //how to access the button in the template in order to assign the click event?
    }

    private void MyButton_Click(object sender, RoutedEventArgs e)
    {
        //DO SOMETHING
    }

  }
}

Now i want to dynamically wire MyButton_Click event to the button in the template.

This kind of problems seem to have a lot of coverage, this one being one of the best: WPF How to access control from DataTemplate There there is something like:

ComboBox myCombo =  _contentPresenter.ContentTemplate.FindName("myCombo", _contentPresenter) as ComboBox;

I'm not very familiar with the templating, and I cannot find the starting point, the "content presenter" on which to call the FindName.

AgostinoX
  • 7,477
  • 20
  • 77
  • 137
  • To be honest, this implementation looks very weird to me and is simply doesn't fit the MVVM approach of WPF... – PythonNoob Dec 06 '19 at 18:56
  • Actually, there is very little beyond adding a button to a header; the code is in the codebehind only for keeping things simple. The problem is how to connect the button, have you any idea? – AgostinoX Dec 06 '19 at 23:39
  • Does this: https://learn.microsoft.com/de-de/dotnet/framework/wpf/data/how-to-find-datatemplate-generated-elements help you? – PythonNoob Dec 07 '19 at 19:39

1 Answers1

0

You could use a recursive helper method that finds the Button in the visual tree:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    DG.Columns[0].HeaderTemplate = (DataTemplate)FindResource("myHeaderTemplate");
    DG.Dispatcher.BeginInvoke(new Action(() =>
    {
        DataGridColumnHeadersPresenter presenter = FindVisualChild<DataGridColumnHeadersPresenter>(DG);
        DataGridCellsPanel dataGridCellsPanel = FindVisualChild<DataGridCellsPanel>(presenter);
        DataGridColumnHeader header = dataGridCellsPanel.Children[0] as DataGridColumnHeader;
        Button button = FindVisualChild<Button>(header);
        if (button != null)
            button.Click += MyButton_Click;
    }));
}

private void MyButton_Click(object sender, RoutedEventArgs e)
{
    //DO SOMETHING
}

private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is T)
            return (T)child;
        else
        {
            T childOfChild = FindVisualChild<T>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}
mm8
  • 163,881
  • 10
  • 57
  • 88