Anthony Brien
Anthony Brien

Reputation: 6166

How can I bind a DataGridTemplateColumn to column value instead of row value?

I have a property grid control that has many cell editors that automatically gets applied using a CellEditorTemplateSelector. Each property grid row is bound against a simple PropertyItemViewModel.

Now, I'm trying to reuse all these cell editors and present it in a DataGrid to be able to compare multiple object values side by side. So I added a PropertiesRow object that contains a list of PropertyItemViewModel (same as the above property grid).

To present each cell, I have a simple data template that uses the same template selector as the property grid.

<DataTemplate x:Key="CellDataTemplate">
    <ContentControl
         Content="{Binding Mode=OneWay}"
         ContentTemplateSelector="{StaticResource CellEditorTemplateSelector}" />            
</DataTemplate>

However, for this to work, the template expects a PropertyItemViewModel (not the PropertiesRow), so I have to somehow feed it through a binding that fetches the correct one from PropertiesRow.PropertyItems[columnIndex]. So when I add the columns through the code, I tried something like this:

void AddColumns()
{
    foreach (Property shownProperty in _ShownProperties)
    {
        _DataGrid.Columns.Add(new DataGridTemplateColumn()
        {
            Header = shownProperty.Name;
            Binding = new Binding("PropertyItems[" + index + "]");
            CellTemplate = (DataTemplate) FindResource("CellDataTemplate");
        });
    }
}

However, DataGridTemplateColumn does not have a Binding property! So I tried to generate an intermediate DataTemplate for each column, but that is starting to be very complex, and I feel there must be a simpler way of doing this.

Any suggestions?

Upvotes: 0

Views: 2081

Answers (2)

GregM
GregM

Reputation: 21

I was having trouble with the XAML above but I got this to work. Had to set Path='' or the compiler was unhappy.

Content="{Binding Mode=OneWay, Path='',
                  RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridCell, 
                                  AncestorLevel=1}, 
                  Converter={StaticResource CellToColumnValueConverter}}"

Upvotes: 2

Anthony Brien
Anthony Brien

Reputation: 6166

I found a way to do it, which is not clean by MVVM standards because it plays with the DataGridCells directly, but it works fine otherwise.

I left my cell template as is, except instead of leaving it bound to my PropertiesRow object, which has no indication of which column we are in, I bind using a relative source binding to the parent DataGridCell:

<DataTemplate x:Key="CellDataTemplate">
    <ContentControl
        Content="{Binding Mode=OneWay,
     RelativeSource={RelativeSource FindAncestor, 
                              AncestorType={x:Type Controls:DataGridCell}},
     Converter={StaticResource CellToColumnValueConverter}}}"
        ContentTemplateSelector="{StaticResource CellEditorTemplateSelector}" />            
</DataTemplate>

I then added a CellToColumnValueConverter which takes the DataGridCell and transforms it into a PropertyItem using the index of the column:

public class CellToColumnValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DataGridCell cell = (DataGridCell) value;
        int displayIndex = cell.Column.DisplayIndex;
        PropertiesRow r = (PropertiesRow) cell.DataContext;
        return r.PropertyItems[displayIndex];
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Upvotes: 0

Related Questions