Search code examples
c#uwpwindows-community-toolkit

How to I bind controls in a UWP DataGrid Column Header template to instances of a class?


I'm trying to customise the header content of a UWP DataGrid. I can add the necessary controls by changing the header style like this, however I want to be able to bind the controls inside the template to instances of a class that are only available when the grid columns are (dynamically) created.

How do I make the binding in the code below valid (or another workaround)?

DataGrid example XAML:

<controls:DataGrid    
    x:Name="DataGrid"
    ItemsSource="{x:Bind ItemsSource}"
    AutoGenerateColumns="False"
    ColumnHeaderHeight="80">

    <controls:DataGrid.ColumnHeaderStyle>
        <Style TargetType="controlsprimitives:DataGridColumnHeader">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="controlsprimitives:DataGridColumnHeader">

                        <StackPanel Orientation="Vertical">

                            <TextBox Text="{x:bind PropertyOnMyClass, Mode=TwoWay}"/>

                            <!-- Some other controls -->

                        </StackPanel>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </controls:DataGrid.ColumnHeaderStyle>

</controls:DataGrid>

Example of a column being added:

private void MakeColumn(MyClass classInstance)
{
    DataGridTextColumn column = new();
    column.Header = classInstance;
    DataGrid.Columns.Add(column);
}

I can add controls in the way above with no issue, and I can set the DataGridTextColumn Header property to the class instance and set the header text using a GetString() override on the class, but I can't find a way to bind to multiple properties.

A solution that creates the header controls in codebehind would also be fine, however you can't create Templates in codebehind.

Thanks in advance.


Solution

  • So my workaround was to call a method the first time the control loads to set the necessary links between the controls and the class:

    XAML:

            <controls:DataGrid.ColumnHeaderStyle>
                <Style
                TargetType="controlsprimitives:DataGridColumnHeader">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="controlsprimitives:DataGridColumnHeader">
                                <ContentPresenter Loaded="HeaderLoaded"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </controls:DataGrid.ColumnHeaderStyle>
    

    and code behind:

            private void HeaderLoaded(object sender, RoutedEventArgs e)
            {
                var presenter = sender as ContentPresenter;
                if (presenter.DataContext is MyClassType myClassInstance)
                {
                    presenter.Content = new ColumnHeader(myClassInstance, this);                
                }
                presenter.Loaded -= HeaderLoaded;
            }