WPF DataGridTemplateColumn 내부에서 RelativeSource FindAncestor 사용 시 에러

아래와 같은 WPF XAML이 있습니다.

아래 소스에 대해서 현재 RelativeSource, FindAncestor, AncesstorType=‘System.Windows.Controls.DataGrid’, AncestorLevel=‘1’ 소스를 찾을 수 없습니다.

라는 에러가 발생하네요.

<DataGridTemplateColumn x:Name="dcChecked"
                        Width="Auto"
                        MinWidth="50">
    <DataGridTemplateColumn.Header>
        <unit:GdtCheckBox IsThreeState="False">
            <unit:GdtCheckBox.IsChecked>
                <Binding FallbackValue="False"
                         Path="DataContext.IsAllChecked"
                         UpdateSourceTrigger="PropertyChanged">
                    <Binding.RelativeSource>
                        <RelativeSource AncestorType="{x:Type unit:GdtDataGrid}" Mode="FindAncestor" />
                    </Binding.RelativeSource>
                </Binding>
            </unit:GdtCheckBox.IsChecked>
        </unit:GdtCheckBox>
    </DataGridTemplateColumn.Header>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <unit:GdtCheckBox HorizontalAlignment="Center"
                              VerticalAlignment="Center"
                              IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

GdtCheckBox는 기존 CheckBox를 상속받는 CustomControl입니다.
DataGrid에 데이터가 있을 때는 에러가 발생하지 않지만, DataGrid에 바인딩된 ItemsSource의 Count가 0일 경우, 데이터가 없으니까 랜더링할 Column이 없어서 그런지 RelativeSource 바인딩 에러가 발생하는데요.

이럴 때 어떤 솔루션이 있을까요?

1개의 좋아요

올려주신 코드 내에서는 RelativeSource 바인딩으로 인한 오류가 발생할 부분은 없어 보입니다. GdtCheckBox의 바인딩에 의해 오류가 났다면 AncesstorTypeunit:GdtDataGrid으로 표시됐을 겁니다.
AncestorType={x:Type DataGrid}으로 설정된 바인딩이 있는지 확인해보시죠ㅎ

2개의 좋아요

이미 질문을 올리기 전에 DataGrid와 GdtDataGrid 둘 다 AncestorType으로 지정했었습니다.

둘 다 ItemsSource에 데이터가 없을 경우 RelativeSource 바인딩 에러가 발생합니다.

1개의 좋아요

재현되는 프로젝트 올려주실 수 있을까요 ?

1개의 좋아요

DataGrid에서 DataGridColumnHeader 컨트롤은 VisualTree 상 DataGrid컨트롤의 하위에 존재하고 ItemsSource에 상관없이 항상 표시되므로 데이터의 유무에 따라 오류가 발생할 수 없습니다.

Imgur

프로젝트 내 Style 또는 ControlTemplate의 재정의나 IsCheckedAll 등의 구현 등 RelativeSource를 지정한 다른 부분에서 오류가 발생한 것으로 추측됩니다.

1개의 좋아요

아마 템플릿을 리소스로 빼면 괜찮을껄요?ㅎㅎ

1개의 좋아요

맞는 솔루션인지 모르겠지만 해결이 되어서 업데이트합니다.

<unit:GdtDataGrid Margin="{StaticResource DataGridBorderInterval}"
                  AutoGenerateColumns="False"
                  Background="{StaticResource DataGridAreaBackgroundBrush}"
                  BorderBrush="{StaticResource GdtDataGridBorderBrush}"
                  BorderThickness="1"
                  ColumnHeaderBackgroundBrush="{DynamicResource Theme_PopupDataViewColumnHeaderColorBrush}"
                  CornerRadius="{DynamicResource Theme_AllCornerRadius}"
                  ItemsSource="{Binding Source={StaticResource ItemsSort}}"
                  Visibility="{Binding Items.Count, Converter={cnvt:EmptyCollectionToVisibilityConverter}}">
    ...
    <DataGrid.Columns>
        <DataGridTemplateColumn x:Name="dcChecked"
                                Width="Auto"
                                MinWidth="50">
            <DataGridTemplateColumn.Header>
                <unit:GdtCheckBox IsThreeState="False">
                    <unit:GdtCheckBox.IsChecked>
                        <Binding FallbackValue="False"
                                 Path="DataContext.IsAllChecked"
                                 UpdateSourceTrigger="PropertyChanged">
                            <Binding.RelativeSource>
                                 <RelativeSource AncestorType="{x:Type unit:GdtDataGrid}" Mode="FindAncestor" />
                            </Binding.RelativeSource>
                        </Binding>
                    </unit:GdtCheckBox.IsChecked>
                </unit:GdtCheckBox>
            </DataGridTemplateColumn.Header>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <unit:GdtCheckBox HorizontalAlignment="Center"
                                      VerticalAlignment="Center"
                                      IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" />
                 </DataTemplate>
             </DataGridTemplateColumn.CellTemplate>
         </DataGridTemplateColumn>
         ...
     </DataGrid.Columns>
 </unit:GdtDataGrid>

형태는 위처럼 생겼었습니다.
이 DataGrid의 Items.Count가 0일 경우 보기 싫어서 아래 컨버터로 Items.Count가 0일 때 Visibility를 바꿨었습니다.

public sealed class EmptyCollectionToVisibilityConverter : ConverterMarkupExtension<EmptyCollectionToVisibilityConverter>
{
    public override object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int count = System.Convert.ToInt32(value);
        Visibility result = count == 0 ? Visibility.Collapsed : Visibility.Visible;

        return result;
    }

    public override object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

여기서 아래 부분이 틀렸었네요.

Visibility result = count == 0 ? Visibility.Collapsed : Visibility.Visible;

저걸 Hidden으로 바꿔주면 바인딩에러가 발생하지 않고 정상 동작합니다.

Visibility result = count == 0 ? Visibility.Hidden : Visibility.Visible;

지금 프로젝트 마감 기간이라서 막히는 것을 제껴두고, 다른 일을 하다가… 혹시 몰라 바꿔본게 얻어 걸리게 되었습니다.

테스트 소스를 재현해서 올리려고 했었는데 그 전에 해결되었네요.

답변 주신 분들 모두 감사드립니다.

4개의 좋아요