How to create DataGrid with ComboBoxColumn using other DataContext than Grid itself?

839 Views Asked by At

I am pretty new to WPF and I am stuck on this one, I couldn't find anything that helped me solve my problem.

I am trying to create DataGrid where I would like to compare multpiple people from imported file to people in database. My idea of that was creating told DataGrid (of person data) with ComboboxColumn (with list of people from database). My main problem is that both people to compare and to fill combobox, may differ because of user choice in previous window. Having some database work I save both choices in Window_Loaded as:

        personList //list of Person objects to populate combobox
        pisList //list of Person objects to fill DataGrid

Now I have no idea how to get both those values to DataGrid so I populate ComboboxColumn as well. I tried do get one of those Values as DataContext od whole Window and second as datagrid ItemsSource.

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {        
        Database db = new Database();
        List<Person> personList = (...); 
        List<Person> pislist = (...);
        dgPlayers.ItemsSource = impTeam.PlaysInSquad.ToList();
        wPlayers.DataContext = personList;
    }

As I failed miserably to get those 2 in one DataGrid, I created 2 separate, but still can make it work.

    <DataGrid Name="dgPlayers" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Number" Binding="{Binding Number}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Name" Binding="{Binding Person.Name}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Surname" Binding="{Binding Person.Surname}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Role" Binding="{Binding IDRole}" IsReadOnly="True"/>
        </DataGrid.Columns>
    </DataGrid>

    <DataGrid Name="dgList" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridComboBoxColumn x:Name="List" ItemsSource="{Binding DataContext.Person}" DisplayMemberPath="Name"/>               
        </DataGrid.Columns>
    </DataGrid>

First DataGrid is ok, but still, I can't properly populate ComboboxColumn + I would love to see that in one DataGrid instead of two.

EDIT

Even though I already tried it before without success, solution from this thread worked for me after like 3rd try: How to Bind data to DataGridComboBoxColumn in DataGrid using MVVM

2

There are 2 best solutions below

7
On

DataGridColumns does not lies in same visual tree as that of DataGrid, so it doesn't inherit DataContext from its parent. That's why you are not able to bind it using simple binding syntax. However, there are many workarounds to achieve that.

I'll list two of them here:

First take advantage of Freezable class. Magic of Freezable objects is that it can inherit the DataContext even when they’re not in the visual or logical tree. You can read more about here Bind Data when DataContext is not inherited.

Add this class in your project:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    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));
}

XAML usage:

<DataGrid>
   <DataGrid.Resources>
      <local:BindingProxy x:Key="proxy" Data="{Binding}" />
   </DataGrid.Resources>
   <DataGrid.Columns>
      <DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource proxy}}"/>
   </DataGrid.Columns>
</DataGrid>

Second, if you are using WPF 4.0, you can take adavantage of x:Reference which allows you to bind to element name even if it does not exist in Visual tree.

Create TextBlock whose Visibility will be Collapsed and set its DataContext to personsList.

<TextBlock Visibility="Collapsed" x:Name="txt"/>

and in code behind

txt.DataContext = personList;

Now you can bind in DataGrid like this:

<TextBlock Visibility="Collapsed" x:Name="txt"/>
<DataGrid>
   <DataGrid.Columns>
      <DataGridComboBoxColumn ItemsSource="{Binding Source={x:Reference txt}}"/>
   </DataGrid.Columns>
</DataGrid>
5
On

To bind your combo box separately, just have the collection for the combo box set up as a resource. That way, your combo box doesn't need to worry about what the data grid's data context is.

<Window.Resources>
    <CollectionContainer x:Key="PeopleItemsSource" Collection="{Binding personList}" />
<!-- or <CollectionViewSource x:Key="PeopleItemsSource" Source="{Binding personList}" /> -->
</Window.Resources>
<Grid>
    <DataGrid ItemsSource="{Binding pisList}">
        <DataGrid.Columns>
            <DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource PeopleItemsSource}}"/>
        </DataGrid.Columns>
    </DataGrid>
</Grid>