4
\$\begingroup\$

The goal is to have a DataGrid of comboboxes, 10 rows, 3 columns.

Here's the XAML to create said grid:

<DataGrid Name="grid1"
          AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Product">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox Margin="2"
                              FontSize="25"
                              MinWidth="70"
                              ItemsSource="{Binding Product}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Amount">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox Margin="2"
                              FontSize="25"
                              MinWidth="70"
                              ItemsSource="{Binding Amount}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Units">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox Margin="2"
                              FontSize="25"
                              MinWidth="70"
                              ItemsSource="{Binding Units}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

Here's the code behind for the binding:

private void SetGrid()
{
    var materialList = new List<MudMaterial>();

    var newMaterial = new MudMaterial();

    for (var i = 0; i < 10; i++)
    {
        materialList.Add(newMaterial);
    }

    grid1.ItemsSource = materialList;
}

The MudMaterial Class:

class MudMaterial
{
    public ObservableCollection<string> Product { get; set; }
    public ObservableCollection<string> Units { get; set; }
    public ObservableCollection<string> Amount { get; set; }

    public MudMaterial()
    {
        Product = new ObservableCollection<string>() {  "                ",
            "Gel",
                                                        "Barrite",
                                                        "Detergent",
                                                        "Soap",
                                                        "X Gel",
                                                        "Bentonite",};

        Units = new ObservableCollection<string>() {"                ",
            "20 lb bag",
                                                    "40 lb bag",
                                                    "50 lb bag",
                                                    "80 lb bag",
                                                    "1 gal",
                                                    "2 gal",
                                                    "3 gal",};

        Amount = new ObservableCollection<string>() {"                ",
            "1",
                                                        "2",
                                                        "3",
                                                        "4",
                                                        "5",
                                                        "6",};
    }
}

The actual length of the ObservableCOllection<string>s ranges from 10-70 items.

Now, aside from a few quirks(extra empty row at the end, extra empty column in the beginning), this works. However, I feel dirty having come up with this. Surely there must be a more efficient way to do this?

Here's a picture of the grid:

enter image description here

I'm using a datagrid because it seemed easier to do than anything else. Every box under "Product" has the same exact items, every box under "Amount" has the same exact items, and every box under "Units" has the exact same items. Essentially, I'm just looking for a better way, if it exists, to populate those boxes than what I'm already doing.

\$\endgroup\$
  • 1
    \$\begingroup\$ What is your problem exactly? Please add more context. Is there a reason, you use strings? Is there a reason you use observable collections? Are those collections supposed to be unique per item? Stuff like that. Its hard to tell from your code, what behaviour you are trying to achieve, so its hard to give you a proper advice. \$\endgroup\$ – Nikita B Aug 8 '13 at 5:46
  • \$\begingroup\$ @Nik: There's no "problem" per se. If there was a problem, I'd have posted this on SO. This works the way I intend. I'm just looking for a better way to do this. Also, the first line of my post shows what I'm trying to achieve. \$\endgroup\$ – PiousVenom Aug 8 '13 at 12:35
  • \$\begingroup\$ do you have a viewable sample of what this looks like? I am not sure why you would use a Datagrid instead of a Table, if I could see it in action it might be more clear to me. \$\endgroup\$ – Malachi Aug 8 '13 at 13:17
  • 1
    \$\begingroup\$ @MyCodeSucks, if your first line was enough to understand your intent, i would not ask, would I? Its not like i'm asking for fun. :) As it is, all i can say for sure is: your design is wrong. But its hard to tell, how your code can be altered without knowing how are you going to use this grid. \$\endgroup\$ – Nikita B Aug 8 '13 at 14:12
  • 2
    \$\begingroup\$ @Malachi: It doesn't matter what it does. I'm asking if this is the most effective way to populate them, not store them. And if 1 row were good enough, I wouldn't be trying to do 10. My boss wants 10. \$\endgroup\$ – PiousVenom Aug 8 '13 at 14:52
3
\$\begingroup\$

If your items in the ComboBox are static, you don't need binding at all.

<DataGridTemplateColumn Header="Product">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <ComboBox>
                    <ComboBoxItem>Grapes</ComboBoxItem>
                    <ComboBoxItem>Apples</ComboBoxItem>
                    <ComboBoxItem>Oranges</ComboBoxItem>
                </ComboBox>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
\$\endgroup\$
1
\$\begingroup\$

Alright. This is how your code can be improved in my opinion:

1) You do not need observable collections for ComboBox itemsources. It is used to support changes to collection, and you have none. In your case, a simple array or List will do.

2) You should try to avoid using string arrays for storing entities which are not exactly strings. You should avoid hardcoding 70 string values as well. If you need to populate such array - this is the first sign that you need a database. You will be able to add new Products without recompiling your code, populating arrays will require a single query, you will operate using Product ID's or some complex object, etc, etc. There are many advantages. Amount can be made a List<int> and can be populated in a simple loop (you can implement IValueConverter to add an empty row, if you need one). Units should be a list of some objects, representing volume.

3) As for UI, you can improve your code using standart MVVM practices. Instead of setting up bindings in code behind, set your UserControl.DataContext to some viewmodel and use data binding.

ViewModel example (i left strings intact for simplicity):

class MyViewModel
{
    public IList<string> Products { get; private set; }
    public IList<string> Amounts { get; private set; }
    public IList<string> Units { get; private set; }

    public ObservableCollection<MudMaterial> Materials { get; private set; } 

    public MyViewModel()
    {
        //populate above collections
    }
}

class MudMaterial
{
    public string Product { get; set; }
    public string Unit { get; set; }
    public string Amount { get; set; }
}

xaml example:

<DataGrid ItemsSource="{Binding Materials}" AutoGenerateColumns="False">
  <DataGrid.Resources>
     <Style TargetType="ComboBox" x:Key="products">
         <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=DataContext.Products}" />
     </Style>
  </DataGrid.Resources>
  <DataGrid.Columns>
       <DataGridComboBoxColumn
                    Header="Product" 
                    MinWidth="70"  
                    ElementStyle="{StaticResource products}"
                    EditingElementStyle="{StaticResource products}"
                    SelectedItemBinding="{Binding Product}"/>

Dont forget to set DataContext by calling DataContext = new MyViewModel(); in constructor in code-behind or by setting it in xaml.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.