views:

5078

answers:

5

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 binded to Data?


Well it has been a while since I asked this question and I am slowly beginning to think that it is impossible to do what I wish to accomplish. Just for the sake of it and to make this question alive again, I will create a bounty for it!

+1  A: 

Hi Partial.

EDIT I'm sorry, I'm little slow at midnights :). Here is an updated answer. It looks like great article from Vincent Sibal WPF DataGrid - DataGridComboBoxColumn v1 Intro answers your question. Does it?

Anvaka
It is a DataGridComboBoxColumn and not a DataGridCheckBoxColumn. What I wish to bind this DataGridComboBoxColumn to a Collection of collections of strings so that each row contains a ComboBox that is binded to different collections of strings.
Partial
Ooops. Sorry. I've updated the answer.
Anvaka
No, problem! In fact, I've read his article. It works great when you want to have the same data in each ComboBox, but what I am trying to achieve is to have different data in the ComboBox in each row.
Partial
+2  A: 

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!

AndyG
I also think that my binding is not right. I am looking for something like ItemsSource = new Binding("Data") but doing that gives an error... "Cannot implicitly convert type 'System.Windows.Data.Binding' to 'System.Collections.IEnumerable'." And thank you for finding that typo!
Partial
Oh and I believe that it should be SelectedValueBinding = new Binding("Data"), TextBinding = new Binding("Data")
Partial
Sorry about that I usually define my columns in XAML. Perhaps using SetBinding would work:comboBoxCol = new DataGridComboBoxColumn() {...properties but not itemsource...};comboBoxCol.SetBinding(DataGridComboBoxColumn.ItemsSourceProperty, new Binding() { Source = Data });DG_Operations.Columns.Add(comboBoxCol);Is the selected value you are looking for a string or collection of string?
AndyG
A string and the .SetBinding does not work with a DataGridComboBoxColumn :S
Partial
Hmm, well as mentioned i've always defined my columns in XAML. Let me know if you'd like help setting it up like that and i'll update my answer.
AndyG
Can you add an example in XAML?
Partial
This answer really seems to solve the problem
arconaut
+1  A: 

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.

John Bowen
+3  A: 

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

Timothy Khouri
Because I wanted to bind multiple rows of a DataGridComboBoxColumn with different ObservableCollections.
Partial
Your solution works, but I wanted to know if it was possible with a WPF DataGrid.
Partial
Yes, absolutely, 100%... a "DataGrid" is 100% like a GridView in that each row has the DataContext of the item that it is representing, so in the same way "Choices" would perfectly be available to bind to a ComboBox :)
Timothy Khouri
A: 

Partial - I think there is a confusion in what you are saying. You said you need a collection of collection of strings in each row so that the combo box could show different strings for different rows. However, for a combo box to show a set of strings, you only need a collection of strings, per row, not a collection of collection of strings.

Now since you need the collection of strings per row you might be thinking that you would need collection of collection of strings.

Is my understanding of your question correct? If so, then your mention of collection of collection of strings is wrong.

What you actually need is collection of StatOperations in which each StatOperation should have a collection of strings. This is exactly how you had as shown in your classes above.

To make progress, I suggest you edit your question and point out where exactly you were stuck after fixing the binding as suggested by AndyG.

Charles Prakash Dasari
Each column is a property in a class that I have created and I am using an ObservableCollection in order to keep the data of each row in memory and to show that data in the DataGrid. What I mean by Collection of a Collection is that for each row I need a different collection of strings for the DataGridComboBoxColumn.
Partial