0
votes

I have problem with my app. I use 2 TabControl (main and sub). On my sub control I have 2 TabItems. All it's working but DataGridTextColumn.Header Binding working only for first TabItem. Window DataContext is set to ViewModelClass when window is showed (after click button)

Second TabItem has no column name. Binding error is :

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=DataContext.Translations.NumberText; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

Here is code snippet:

    <TabControl x:Name="ovTcMain">
    <TabItem Header="{Binding Translations.CodeBooksText }" TabIndex="0">
        <Grid Style="{StaticResource MainGrid}">
            <Grid.RowDefinitions>
                <RowDefinition Height="6*" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>

            <TabControl x:Name="ovTcOptions" Grid.Row="0" SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >

                <TabItem Header="{Binding Translations.ResortsText}" TabIndex="0" >
                    <DataGrid x:Name="ovTiCostCenters"
                ItemsSource="{Binding CodeBooks[CostCenter], Mode=TwoWay}"
                SelectedItem="{Binding SelectedCostCenter, Mode=TwoWay}"
                ColumnWidth="*"  AutoGenerateColumns="False" HorizontalAlignment="Stretch"
                Visibility="Visible" IsReadOnly="True">
                        <DataGrid.Columns>
                            <DataGridTextColumn Binding="{Binding Path=Name}" Width="auto">
                                <DataGridTextColumn.Header>
                                    <TextBlock Text="{Binding DataContext.Translations.NumberText, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>
                            <DataGridTextColumn Binding="{Binding Path=DescriptionTerm}" Width="auto">
                                <DataGridTextColumn.Header>
                                    <TextBlock Text="{Binding DataContext.Translations.TermText, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>
                        </DataGrid.Columns>
                    </DataGrid>
                </TabItem>

                <TabItem Header="{Binding Translations.BuildingsText}" TabIndex="1" >
                    <DataGrid x:Name="ovTiBuildings"
                ItemsSource="{Binding CodeBooks[Building], Mode=TwoWay}"
                SelectedItem="{Binding SelectedBuilding, Mode=TwoWay}"
                ColumnWidth="*" AutoGenerateColumns="False" HorizontalAlignment="Stretch"
                Visibility="Visible" IsReadOnly="True">
                        <DataGrid.Columns>
                            <DataGridTextColumn Binding="{Binding Path=Name}" Width="auto">
                                <DataGridTextColumn.Header>
                                    <TextBlock Text="{Binding DataContext.Translations.NumberText, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>
                            <DataGridTextColumn Binding="{Binding Path=DescriptionTerm}" Width="auto">
                                <DataGridTextColumn.Header>
                                    <TextBlock Text="{Binding DataContext.Translations.TermText, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
                                </DataGridTextColumn.Header>
                            </DataGridTextColumn>
                        </DataGrid.Columns>
                    </DataGrid>
                </TabItem>

Can you help me how to correctly bind HeaderText?

2

2 Answers

0
votes

In wpf, in controls that have items, especially if they use virtualization, sometimes the wpf engine is not reliable and cannot find the item's ancestor, (I think because the binding for the item may be evaluated before it is even added to the VisualTree). I encountered this behaviour a few times while working with TabControls (which I think is at fault in your case),DataGrids, ListBoxes and ListViews, even ComboBoxes. This is some sort of a bug in the framework. Sometimes it only shows you the warning and the binding works anyway (as is the case for your first item). Point is, sometimes RelativeSource binding is not reliable.

One of the workarounds is described here:

https://stackoverflow.com/a/15494510/891715

It works with the code you provided and binds correctly:

<UserConttrol.Resources>
        <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</UserConttrol.Resources>
...
 <TextBlock Text="{Binding Data.NumberText, Source={StaticResource proxy}}"/>
...
public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
        typeof(BindingProxy), new UIPropertyMetadata(null));
}
0
votes

Ok so the asnwer is: RelativeSource is not reliable...

Solution:

<UserControl.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</UserControl.Resources>

<DataGridTemplateColumn Visibility="{Binding Data.IsVisible, 
    Source={StaticResource proxy},
    Converter={StaticResource BooleanToVisibilityConverter}}">
public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), 
        typeof(BindingProxy), new UIPropertyMetadata(null));
}

Found this solution here