We are working on WPF app development. And one of our dialogs has a DataGrid with custom columns. We have one custom column class that inherits a DataGridTemplateColumn:
public class MultiEditComboBoxColumn : DataGridTemplateColumn
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",
typeof(IEnumerable),
typeof(MultiEditComboBoxColumn),
new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
}
This column generates a dynamic template with combobox. Also, it has some customs events and a lot of custom backend logic. So that's why we don't use a default DataGridComboboxColumn.
To set some collection for this dynamic combobox we created a Dependency Property "ItemsSource" and bind it to "ItemsSource" property of the internal dynamic combobox.
So when we have some DataGrid and use our MultiEditComboBoxColumn inside - we can pass some collection to this property.
In our project, we have one dialog "MainView" and "MainViewModel" for it. This dialog has a DataGrid with MultiEditComboBoxColumn column. "MainViewModel" has some collection that are related to DataGrid. This collection contains a few objects of "PointViewModel" type that represent each row in DataGrid. "PointViewModel" has the property "AvailablePointTypes" - a list of strings. Each "PointViewModel" can have different values in this list. And I want to set this list to "ItemsSource" property of MultiEditComboBoxColumn:
public class PointViewModel : ViewModelBase
{
public List<string> AvailablePointTypes { get; }
public PointViewModel()
{
AvailablePointTypes = GetAvailablePointTypes();
}
private List<string> GetAvailablePointTypes()
{
// Get a list of available point types with different values for each PointViewModel object.
...
}
}
public class MainViewModel : ViewModelBase
{
public List<PointViewModel> Points { get; }
public MainViewModel()
{
Points = ReadPoints();
}
private List<PointViewModel> ReadPoints()
{
// Get a collection of points to display in DataGrid.
...
}
}
And here is how I try to set this collection to our custom column:
<DataGrid ItemsSource="{Binding Points}">
<DataGrid.Columns>
// Other columns ...
<MultiEditComboBoxColumn ItemsSource="{Binding AvailablePointTypes}"/>
</DataGrid.Columns>
</DataGrid>
But it doesn't work. When I run the app I see an error:
Cannot find governing FrameworkElement or FrameworkContentElement for the target element. BindingExpression:Path=AvailablePointTypes; DataItem=null; target element is 'MultiEditComboBoxColumn' (HashCode=29829272); target property is 'ItemsSource' (type 'IEnumerable')
It seems that it is unable to detect a DataContext of row in this column where I clicked on dynamic combobox. So that's why I see my combobox in the UI but it's always empty.
How can I fix that correctly?
A DataGridColumn
is not part of the visual tree and doesn't inherit any DataContext
.
A common solution to this issue is to use a Freezable
that captures the DataContext
as explained and exemplified here:
<DataGridTextColumn ... Visibility="{Binding Data.SomeProperty, Source={StaticResource proxy}}"/>
Or you could create a default CellTemplate
that simply binds a ComboBox
to some property that you specify dynamically.