0

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

Community
  • 1
  • 1
user3146472
  • 383
  • 1
  • 5
  • 16

2 Answers2

1

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>
BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • Note that "pisList" and "personList" need to be public properties on the window's data context object (ideally a view model) for the above code sample to work. The concept is the same for programmatically setting them though. – BradleyDotNET Dec 30 '13 at 17:11
  • `CollectionContainer` and `CollectionViewSource` are not valid values for `ItemsSource DP`. – Rohit Vats Dec 30 '13 at 17:23
  • You are correct, see updated answer (my binding wasn't complete) – BradleyDotNET Dec 30 '13 at 18:27
  • @LordTakkera Tried to run this code and Comboboxes are still empty. Made `pisList` and `personList` public. Problem seems to be way of binding. `dgPlayers.ItemsSource = (...).ToList();` in Window_Loaded populates main datagrid corectly. `"` does not on the other hand. Alos giving break point at the end of Window_Loaded, `Collection="{Binding personList}" />` shows up as list of 14 Person elements, no result on ComboboxColumn though. – user3146472 Dec 31 '13 at 08:59
  • are pisList and personList public properties of the Data Context class? Also, you need set the datacontext of the MainWindow class to whatever object has those lists as public properties. You will see binding exceptions in the output window at runtime if you don't have a piece correct. – BradleyDotNET Dec 31 '13 at 20:06
0

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>
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • I've started with second method becuase it seems easier for me. Even though I think I understand it, seems like I am missing something with implementation. Done everything exactly as stated above, but got this error: `Object reference not set to an instance of an object.` Textblock was added above DataGrid and I also added DataContext in code behind. Any hint? – user3146472 Dec 30 '13 at 14:40
  • The second method is better, but a better way of doing it would be through a resource, not a hidden UI element. – BradleyDotNET Dec 30 '13 at 16:59
  • @LordTakkera - Resource doesn't inherit DataContext until it's applied on some element. – Rohit Vats Dec 30 '13 at 17:09
  • Notice in my answer I use the Windows' resources, so it would have the window's data context (which is what i am assuming he wants) – BradleyDotNET Dec 30 '13 at 17:12
  • Have you tried running it? `CollectionContainer` and `CollectionViewSource` are not valid values for ItemsSource DP. – Rohit Vats Dec 30 '13 at 17:22
  • You are correct, you use that source as a source in the ItemsSouce binding. I have updated my answer, thank you for catching the problem. – BradleyDotNET Dec 30 '13 at 18:26
  • @RohitVats in point 2 error goes on `ItemsSource="{Binding Source={x:Reference txt}}` If it comes to first - I've added told class, and tried to run it, no effect on ComboboxColumn. I've only added `personList' in 'Data="{Binding personList}"'. Am I missing something, or that's all I should add to this? – user3146472 Dec 31 '13 at 09:57