The XAML of my UserControl with name "Clinical_Protocol":
<Grid>
<DataGrid Name="ClinicalProtocolDataGrid"
ItemsSource="{Binding DataGridItems, ElementName=Clinical_Protocol}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Structure ID"
ItemsSource="{Binding ComboBoxItems, ElementName=Clinical_Protocol, Mode=TwoWay}"
SelectedItemBinding="{Binding SelectedStructureId, Mode=TwoWay}"
/>
<DataGridTextColumn Header="RT ROI Type Code"
Binding="{Binding RtRoiInterpretedTypeCode}"/>
<DataGridTextColumn Header="RT ROI Type Description"
Binding="{Binding RtRoiInterpretedTypeDescription}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
The dependency property for ItemsSource
of the DataGrid in the code behind:
internal ObservableCollection<ClinicalProtocolDataGridItem> DataGridItems
{
get { return (ObservableCollection<ClinicalProtocolDataGridItem>)GetValue(DataGridItemsProperty); }
set { SetValue(DataGridItemsProperty, value); }
}
internal static readonly DependencyProperty DataGridItemsProperty =
DependencyProperty.Register("DataGridItems", typeof(ObservableCollection<ClinicalProtocolDataGridItem>),
typeof(ClinicalProtocolView), new PropertyMetadata(null, DataGridItemsPropertyChangedCallback));
private static void DataGridItemsPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (ClinicalProtocolView)d;
control.DataGridItems = (ObservableCollection<ClinicalProtocolDataGridItem>)e.NewValue;
}
The according ClinicalProtocolDataGridItem
class:
public class ClinicalProtocolDataGridItem
{
public string RtRoiInterpretedTypeCode { get; }
public string RtRoiInterpretedTypeDescription { get; }
public object SelectedStructureId { get; set; }
public ClinicalProtocolDataGridItem(string rtRoiInterpretedTypeCode, string rtRoiInterpretedTypeDescription)
{
RtRoiInterpretedTypeCode = rtRoiInterpretedTypeCode ??
throw new ArgumentNullException(nameof(rtRoiInterpretedTypeCode));
RtRoiInterpretedTypeDescription = rtRoiInterpretedTypeDescription ??
throw new ArgumentNullException(nameof(rtRoiInterpretedTypeDescription));
}
}
And finally the dependency property ComboBoxItems
:
public ObservableCollection<ComboBoxItem> ComboBoxItems
{
get { return (ObservableCollection<ComboBoxItem>)GetValue(ComboBoxItemsProperty); }
set { SetValue(ComboBoxItemsProperty, value); }
}
public static readonly DependencyProperty ComboBoxItemsProperty =
DependencyProperty.Register("ComboBoxItems", typeof(ObservableCollection<ComboBoxItem>),
typeof(ClinicalProtocolView), new PropertyMetadata(new ObservableCollection<ComboBoxItem>(),
PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (ClinicalProtocolView)d;
control.ComboBoxItems = (ObservableCollection<ComboBoxItem>)e.NewValue;
}
I try to populate both dependency properties with the following:
private void AddSomeComboBoxItems()
{
ComboBoxItems = new ObservableCollection<ComboBoxItem>
{
new ComboBoxItem(){Content = "S1", HorizontalContentAlignment = HorizontalAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center},
new ComboBoxItem(){Content = "S2", HorizontalContentAlignment = HorizontalAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center},
new ComboBoxItem(){Content = "S3", HorizontalContentAlignment = HorizontalAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center},
}
private void AddSomeRows()
{
var items = new List<ClinicalProtocolDataGridItem>
{
new ClinicalProtocolDataGridItem("PTV", "Description of PTV, and it is a very long description!"),
new ClinicalProtocolDataGridItem("OAR", "Description of OAR, and it is a very long description!"),
};
DataGridItems = new ObservableCollection<ClinicalProtocolDataGridItem>(items);
}
When I import it into a WPF application I get the following:
And the error message:
DataGridComboBoxColumn.ItemsSource IEnumerable Cannot find governing FrameworkElement or FrameworkContentElement for target element.
Microsoft documentation says that it is possible to bind a collection of ComboBoxItem
. What am I missing? I don't want to have a static resource, because the combo box items might change during runtime.
CodePudding user response:
The DataGridColumn is a simple DependencyObject, not a UI element. It is not embedded in the visual tree and cannot get values from it. The ElementName and FindAncestor bindings will not work in it.
You need to get the collection into a static resource and only then get it from there for the DataGridColumn.
<DataGrid Name="ClinicalProtocolDataGrid"
ItemsSource="{Binding DataGridItems, ElementName=Clinical_Protocol}"
AutoGenerateColumns="False">
<FrameworkElement.Resources>
<!--Using a CollectionViewSource as a proxy to create a "static link".-->
<CollectionViewSource
x:Key="comboBoxItems"
Source="{Binding ComboBoxItems, ElementName=Clinical_Protocol}"/>
</FrameworkElement.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn
Header="Structure ID"
ItemsSource="{Binding Source={StaticResource comboBoxItems}}"
SelectedItemBinding="{Binding SelectedStructureId, Mode=TwoWay}"/>
P.S. Please note my comment on your question. I explain there why even after a successful binding to such a source, the ComboBoxs may still work incorrectly.
CodePudding user response:
Remove dependency property ComboBoxItems
you don't need it, but add to the code behind a data provider method for your combo boxes:
public ObservableCollection<string> GetCbxSource()
{
return new ObservableCollection<string>
{
"S1",
"S2",
"S3",
};
}
Then in XAML you can declare a data provider and later use it like:
<Grid>
<Grid.Resources>
<ObjectDataProvider x:Key="cbxData" MethodName="GetCbxSource" ObjectInstance="{x:Reference Clinical_Protocol}"/>
</Grid.Resources>
<DataGrid Name="ClinicalProtocolDataGrid"
ItemsSource="{Binding DataGridItems, ElementName=Clinical_Protocol}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Structure ID"
ItemsSource="{Binding Source= {StaticResource cbxData}}"
SelectedItemBinding="{Binding SelectedStructureId, Mode=TwoWay}"
/>
<DataGridTextColumn Header="RT ROI Type Code"
Binding="{Binding RtRoiInterpretedTypeCode}"/>
<DataGridTextColumn Header="RT ROI Type Description"
Binding="{Binding RtRoiInterpretedTypeDescription}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>