1

I have a class derived from DataGrid which works very nicely when all the columns are DataGridTextColumns and allows me to extract the distinct strings in each column. However, I've had to expand it to allow DataGridComboBox columns and I can't find a way to get those strings.

If I have a DataGridTextColumn I can use the following code to get a sorted list of distinct values as strings. This code is in my subclassed DataGrid and it doesn't need to know the data source or the data source type at runtime.

        var type = this.Items[0].GetType();
        var propertyInfo = type.GetProperty(columnName);

        // Sorts the Items in the natural order for the property/column used
        // then gets just that property as strings
        // distinct etc.
        List<string> query = this.Items.Cast<object>()
            .OrderBy(i => propertyInfo.GetValue(i))
            .Select(i => $"{propertyInfo.GetValue(i)}")
            .Distinct()
            .ToList();
        return query;

The DataGridComboBoxColumn is linked to my object's SupplierId and I can't find a way of converting the SupplierId to a supplier Name. Here is the XAML for the column definition.

                <DataGridComboBoxColumn 
                Header="Supplier"
                SelectedValueBinding="{Binding SupplierId, Mode=TwoWay}" 
                DisplayMemberPath="Name"
                SelectedValuePath="Id">
                <DataGridComboBoxColumn.ElementStyle>
                    <Style TargetType="{x:Type ComboBox}">
                        <Setter Property="ItemsSource" Value="{Binding Path=DataContext.SuppliersList, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                    </Style>
                </DataGridComboBoxColumn.ElementStyle>
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="{x:Type ComboBox}">
                        <Setter Property="ItemsSource" Value="{Binding Path=DataContext.SuppliersList, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                    </Style>
                </DataGridComboBoxColumn.EditingElementStyle>
            </DataGridComboBoxColumn>


public class MyObject
{
     public int SupplierId { get; set; }
     // other properties
}

public class Supplier
{
    public int Id { get; set; }
    public string Name  { get; set; }
}

I've come to the point where I don't think it can be done and I'm going to have to redesign my DataGrid class but I'd really rather not.

Steve
  • 976
  • 5
  • 15
  • You should look into mvvm, Getting data back out of controls is a really bad way to work with wpf. Bind data, from a viewmodel. Work with the data in there. – Andy Feb 07 '19 at 16:55
  • @Andy I am using mvvm. With a DatGridTextColumn I'm able to use the binding to get to the data I need. Unfortunately the combobox column isn't as accommodating. I'm going to change my design to use a popup instead – Steve Feb 08 '19 at 10:18

1 Answers1

1

Your data item, i.e. the object in this.Items, has no name to be retrieved. It only has a SupplierId property.

So in order to get the name that is displayed in the ComboBox, you either need to get it from the SuppliersList, which is unknown to the DataGrid, or from the visual ComboBox element and this only works for rows that have been loaded into the visual tree.

So yes, you should probably reconsider your design.

If you do want to get a reference to the visual ComboBox element, here is an example of how you could to it:

foreach (object item in this.Items)
{
    DataGridRow row = this.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
    if (row != null)
    {
        ComboBox cmb = FindVisualChildren<ComboBox>(row)?.FirstOrDefault();
        if (cmb != null)
        {
            string name = cmb.Text;
        }
    }
}

Find all controls in WPF Window by type

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks for the answer, I wasn't familiar with ContainerFromItem. Sadly this only gets the items that are visible on screen, by scrolling down I get different set of strings returned. Looks like I'm breaking out the refactoring gun. – Steve Feb 07 '19 at 16:34