Search code examples
c#wpfdatagriddatatemplate

C# - FrameworkElement.FindName returns null


I spent the past one hour trying to figure this out. I have a CheckBox inside DataGrid as follows:

<DataGridTemplateColumn>
    <DataGridTemplateColumn.Header>
        <CheckBox Name="chkall" Content="Select All" Checked="chkall_Checked" Unchecked="chkall_Unchecked"/>
    </DataGridTemplateColumn.Header>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox Tag="{Binding Path=id}" x:Name="chksingle"  Checked="chksingle_Checked" Unchecked="chksingle_Unchecked"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

As you can see I am trying to check and uncheck the CheckBoxes inside DataGrid rows when the CheckBox in the header is checked or unchecked. This is the code where I am trying to retrieve the CheckBox and mark it as checked:

private void chkall_Checked(object sender, RoutedEventArgs e)
{
    foreach (var r in userDG.Items)
    {
        DataGridRow row =(DataGridRow)userDG.ItemContainerGenerator.ContainerFromItem(r);
        FrameworkElement FW_element = userDG.Columns[0].GetCellContent(row);
        FW_element.ApplyTemplate();
        var checkbox = FW_element.FindName("chksingle") as CheckBox;
        checkbox.IsChecked = false;
    }
}

I have already tried RegisterName() method and I've tried VisualTreeHelper but nothing works.

This line always returns null:

var checkbox = FW_element.FindName("chksingle") as CheckBox;

Here is a WPF visualizer screenshot for my FrameworkElement where I can clearly see the checkbox I am trying to find:

WPF visualizer screenshot of FrameworkElement

Please tell me what am I doing wrong? Thank you.


Solution

  • The thing is that a DataTemplate is a name scope boundary, that is the templated element (a ContentPresenter in this case) or any of it's ancestors are not aware of named elements defined inside the template. In order to find a named element inside the template you need to use the DataTemplate.FindName method instead (inherited from FrameworkTemplate). Notice that it takes two parameters instead of one, the second one being the templated element. This should do the trick for you:

    private void chkall_Checked(object sender, RoutedEventArgs e)
    {
        foreach (var r in userDG.Items)
        {
            DataGridRow row = (DataGridRow)userDG.ItemContainerGenerator.ContainerFromItem(r);
            FrameworkElement FW_element = userDG.Columns[0].GetCellContent(row);
            //We use the CellTemplate defined on the column to find the CheckBox
            var checkbox = ((DataGridTemplateColumn)userDG.Columns[0]).CellTemplate.FindName("chksingle", FW_element) as CheckBox;
            checkbox.IsChecked = true;
        }
    }