WPF DataGrid: DataGridComboxBox ItemsSource Binding to a Collection of Collections
Situation:
I've created a DataGrid in XAML and the ItemsSource is binded to an ObservableCollection of a certain class that contains properties. Then in C#, I create a DataGridTextColumn and a DataGridComboBoxColumn and binded these to the properties of the objects inside the ObservableCollection. I can bind the DataGridComboBoxColumn to a simple Collection but what I want to do is bind it to a collection of collections of strings so that for each row the ComboBox inside the DataGrid has a different collection of string. I have failed to do so...
Question:
How can I bind the DataGridCombBoxColumn so that I can have a different collection of strings for each row of this type of column?
Code Sample:
XAML:
<Window>
<!-- ... -->
WPFToolkit:DataGrid
x:Name="DG_Operations"
Margin="10,5,10,5"
Height="100"
HorizontalAlignment="Stretch"
FontWeight="Normal"
ItemsSource="{Binding Path=OperationsStats}"
AlternatingRowBackground="{DynamicResource SpecialColor}"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Visible"
SelectionMode="Extended"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeRows="True"
CanUserSortColumns="True"
AutoGenerateColumns="False"
IsReadOnly="False"
IsEnabled="True"
BorderThickness="1,1,1,1"
VerticalAlignment="Stretch"/>
<!-- ... -->
</Window>
C#:
public class DataModelStatsOperations
{
public ObservableCollection<IStatsOperation> OperationsStats { get; set; }
}
public interface IStatsOperation
{
string Operation { get; set; }
Collection<string> Data{ get; set; }
}
public class StatsOperation : IStatsOperation
{
public StatsOperation(string operation, Collection<string> data)
{
Operation = operation;
Data = data;
}
public string Operation { get; set; }
public Collection<string> Data{ get; set; }
}
private ObservableCollection<IStatsOperation> dataOperations_ =
new ObservableCollection<IStatsOperation>();
//...
Binding items = new Binding();
PropertyPath path = new PropertyPath("Operation");
items.Path = path;
DG_Operations.Columns.Add(new DataGridTextColumn()
{
Header = "Operations",
Width = 133,
Binding = items
});
DG_Operations.Columns.Add(new DataGridComboBoxColumn()
{
Header = "Data",
Width = 190,
ItemsSource = /*???*/,
SelectedValueBinding = new Binding("Data"),
TextBinding = new Binding("Data")
});
dataOperations_.Add(new StatsOperation(CB_Operation.SelectedItem.ToString(),
dataCollection));
DG_Operations.DataContext = new DataModelStatsOperations
{
OperationsStats = dataOperations_
};
//...
Any help would be greatly appreciated!
Notes:
Okay, so after reading the two first answers I noticed something. My binding is really not right! Now, what I want to do is something similar to what AndyG proposed:
DG_Operations.Columns.Add(new DataGridComboBoxColumn()
{
Header = "Data",
Width = 190,
ItemsSource = new Binding("Data"), //notice this here does not work (have a look at the following error)
SelectedValueBinding = new Binding("Operation"),
TextBinding = new Binding("Operation")
});
Error: "Cannot implicitly convert type 'System.Windows.Data.Binding' to 'System.Collections.IEnumerable'."
How can the ItemsSource be bound to Data?
Firstly, this should be easy... secondly, why are you building (and binding) columns in C#? Eek.
XAML (I'm using a regular grid because I'm lazy):
<ListView Name="MyListView">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="{Binding Operation}" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Choices}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
C#:
void Window1_Loaded(object sender, RoutedEventArgs e)
{
var dahList = new List<StatsOperation>();
dahList.Add(new StatsOperation
{
Operation = "Op A",
Choices = new string[] { "One", "Two", "Three" },
});
dahList.Add(new StatsOperation
{
Operation = "Op B",
Choices = new string[] { "4", "5", "6" },
});
this.MyListView.ItemsSource = dahList;
}
The Results:
WPF grid with dynamic combo box choices http://www.singingeels.com/Articles/Articles/UserImage.aspx?ImageID=b1e3f880-c278-4d2b-bcc2-8ad390591200
I think the mistake is in how you've done your binding. When you define a column, the binding is related to the object that is represented by a particular row. So as I understand, you have a StatsOperation for each row, so the TextBox column is bound to operation, which is how you have it, and the ComboBox column ItemsSource should be bound to a Collection. Right now it looks like it's bound to a Collection<Collection<string>>
.
I've not defined columns in code-behind before so here is an example in XAML. I've found ComboBoxColumn can be tricky sometimes so i've shown how you can have a combobox in the column by using either a TemplateColumn or a ComboBoxColumn. I've copy pasted from my own code so just replace 'dg' with 'WPFToolkit' in your case:
<dg:DataGrid
...
...>
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Binding="{Binding Operation}" CanUserReorder="True" CanUserResize="True" Header="Operation" />
<dg:DataGridTemplateColumn CanUserReorder="True" CanUserResize="True" Header="Template Column">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Data}" SelectedItem="{Binding Operation}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
<dg:DataGridComboBoxColumn
Header="ComboBox Column"
SelectedValueBinding="{Binding Operation}"
SelectedItemBinding="{Binding Operation}">
<dg:DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
<Setter Property="ItemsSource" Value="{Binding Data}" />
</Style>
</dg:DataGridComboBoxColumn.ElementStyle>
<dg:DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Data}" />
<Setter Property="IsDropDownOpen" Value="True" />
</Style>
</dg:DataGridComboBoxColumn.EditingElementStyle>
</dg:DataGridComboBoxColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
I'm assuming that Operation is the selected item, Data is the items to select from, and that your DataGrid is bound to a collection of StatsOperation. Good luck!
To fix your ItemsSource Binding Error use the form:
BindingOperations.SetBinding(new DataGridComboBoxColumn(), DataGridComboBoxColumn.ItemsSourceProperty, new Binding("Data"));
You obviously can't do this in the intializer so you'll have to move your declarations around a bit but that should take care of that error in your update.