1
votes

Is it possible to use 2 different collections to DataGrid?

Collection Days - Datagrid rows, Collection Headers - Datagrid headers.

ViewModel:

public class MonthViewModel : ViewModel
{
    public ObservableCollection<DayViewModel> Days { get; set; }
    public ObservableCollection<string> Headers { get; set; }

    public MonthViewModel()
    {
        Days = new ObservableCollection<DayViewModel>
        {
            new DayViewModel { Value1 = 1, Value2 = 2 },
            new DayViewModel { Value1 = 3, Value2 = 4 },
        };

        Headers = new ObservableCollection<string>
        {
            "Header1",
            "Header2"
        };
    }
}

public class DayViewModel : ViewModel
{
    private int _value1;
    public int Value1
    {
        get
        {
            return _value1;
        }
        set
        {
            _value1 = value;
            OnPropertyChanged();
        }
    }

    private int _value2;
    public int Value2
    {
        get
        {
            return _value2;
        }
        set
        {
            _value2 = value;
            OnPropertyChanged();
        }
    }
}

View:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Days}">

    <DataGrid.Columns>
        <DataGridTextColumn Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.Headers[0]}" Binding="{Binding Value1}"/>
        <DataGridTextColumn Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.Headers[1]}" Binding="{Binding Value2}"/>
    </DataGrid.Columns>

</DataGrid>

Error - Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=DataContext.Headers[0]; DataItem=null; target element is 'DataGridTextColumn' (HashCode=7245576); target property is 'Header' (type 'Object')

What's the problem?

1

1 Answers

1
votes

The problem is that your RelativeSource binding ends up looking for a property called "Headers" on your DataGrid's data context (which is set by ItemsSource, so is a collection of DayViewModel, clearly not something with a Headers property.

The smallest change would be to look at the Window's DataContext instead of the DataGrid's.

<DataGrid.Columns>
        <DataGridTextColumn Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Headers[0]}" Binding="{Binding Value1}"/>
        <DataGridTextColumn Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Headers[1]}" Binding="{Binding Value2}"/>
</DataGrid.Columns>

Given the declarative nature of XAML, and the fact that your headers probably shouldn't be changing at runtime, I would reconsider binding the "Header" text at all, but the above solution should fix your problem.