0
votes

I have two questions about binding ComboBox to lists objects, when the ComboBox are implemented in DataGrid. But they are so interrelated, that I think two threads are not constructive.

I have a fistful of classes, and I want show their data in a xceed DataGrid. My DataContext is set to ViewModelClass. It has a list of class X objects:

public class ViewModelClass
{
    public IList<X> ListX { get; set; }
}

The class X looks something like this. It has a property Id, and a list list of class Y objects. The list should be my ItemsSource for the ComboBoxes (in DataGrid).

public class X
{
     public int Id { get; set; }

     // this should be my ItemsSource for the ComboBoxes
     public IList<Y> ListY { get; set; }
}

The class Y and Z look something like this. They are some kinds of very simple classes:

public class Y
{
     public Z PropZ { get; set; }
}

public class Z
{
     public string Name { get; set; }
}

My XAML-Code looks something like this.

<Grid.Resources>
<xcdg:DataGridCollectionViewSource x:Key="ListX" AutoCreateItemProperties="False"
                                   Source="{Binding Path=ListX,
                                                    UpdateSourceTrigger=PropertyChanged,
                                                    Mode=TwoWay}" />
</Grid.Resources>

<p:DataGrid AutoCreateColumns="False"
            ItemsSource="{Binding Source={StaticResource ListX},
                                  UpdateSourceTrigger=PropertyChanged}">

    <xcdg:Column Title="Id" FieldName="Id" />

    <xcdg:Column Title="Functions" **FieldName="ListY"**>
        <xcdg:Column.CellContentTemplate>
            <DataTemplate>
                <ComboBox DisplayMemberPath="PropZ.Name"
                          **ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type xcdg:DataGridControl}}, Path=ItemsSource.ListY**}"                                      SelectedValuePath="Funktion.FunktionId" />
            </DataTemplate>
        </xcdg:Column.CellContentTemplate>
    </xcdg:Column>
  1. Now I dont know, how can I bind the ItemsSource of the ComboBox, so that I can read the list values of ListY in my X class?

  2. Then I dont know what is in fact my FieldName for the Functions column? I entered ListY, because it represents the property (IList<>) in my X class. But I think it is probably not right.

Thanks a lot for your help!

3

3 Answers

0
votes

To answer your first question - try this

<ComboBox ItemsSource="{Binding Path=DataContext.ListY, 
 RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"

For your seconds question I am not too sure (it is up to you) but the field name would probably be SelectedFunction or something along those lines

0
votes

Let's break down your problem into bite sized pieces. You have a ListX collection that is data bound to a DataGrid.ItemsSource property:

<DataGrid ItemsSource="{Binding ListX}" ... />

One thing to note about your code at this stage is that it is pointless setting the Binding.UpdateSourceTrigger property to PropertyChanged on the ItemsSource property. From the linked page:

Bindings that are TwoWay or OneWayToSource listen for changes in the target property and propagate them back to the source. This is known as updating the source. Usually, these updates happen whenever the target property changes. This is fine for check boxes and other simple controls, but it is usually not appropriate for text fields. Updating after every keystroke can diminish performance and it denies the user the usual opportunity to backspace and fix typing errors before committing to the new value. Therefore, the default UpdateSourceTrigger value of the Text property is LostFocus and not PropertyChanged.

You really should know what the code does before you use it.

So anyway, back to your problem... we have a data bound DataGrid and one of its columns has a ComboBox in it. I'm not really sure why you're not using the DataGridComboBoxColumn Class or equivalent, but no matter. Now, you need to understand something about all collection controls:

If a collection of type A is data bound to the ItemsSource property of a collection control, then each item of the collection control will be an instance of type A. This means that the DataContext of each item will be set to that instance of type A. This means that we have access to all of the properties defined in class A from within any DataTemplate that defines what each item should look like.

That means that you have direct access to the ListY property of the X class from within the DataTemplate that defines what your items should look like. Therefore, you should be able to do this:

<DataTemplate>
    <ComboBox DisplayMemberPath="PropZ.Name" ItemsSource="{Binding ListY}" 
        SelectedValuePath="Funktion.FunktionId" />
</DataTemplate>

I can't confirm whether the SelectedValuePath that you set will work, because you didn't mention it anywhere, but if your class Y doesn't have a property named Funktion in it, then it will not work. You'll also have to explain your second problem better, as I didn't really understand it.

0
votes

I have found a solution, but even that has not proven to be productive. Because the allocation of cell.Content to the comboBox.ItemsSource shows no effect in my View :-(

In XAML, I have the following code

<xcdg:Column.CellContentTemplate>
    <DataTemplate>
        <p:XDataGridComboBox 
            DataRow="{Binding RelativeSource={RelativeSource AncestorType={x:Type xcdg:DataRow}}}" 
            ItemsFieldName="Functions" />
    </DataTemplate>
</xcdg:Column.CellContentTemplate>

I have written a custom control in which I explicitly set the data source for each ComboBox:

static XDataGridComboBox()
{
    DataRowProperty = DependencyProperty.RegisterAttached(
        "DataRow",
        typeof(DataRow),
        typeof(XDataGridComboBox),
        new FrameworkPropertyMetadata(OnChangeDataRow));

        ItemsFieldNameProperty = DependencyProperty.RegisterAttached(
            "ItemsFieldName",
            typeof(string),
            typeof(XDataGridComboBox),
            new FrameworkPropertyMetadata(OnChangeItemsFieldName));
}

private static void OnChangeDataRow(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var comboBox = d as XDataGridComboBox;
    if (comboBox == null)
    {
        return;
    }

    var cell =
        (from DataCell c in comboBox.DataRow.Cells where c.FieldName == comboBox.ItemsFieldName select c)
            .FirstOrDefault();

    if (cell == null)
    {
        return;
    }

    comboBox.ItemsSource = cell.Content as IEnumerable;
}

The data that I need are available, but the view does not show it. I do not know what I have not considered.