Combobox - lucyberryhub/WPF.Tutorial GitHub Wiki

Creating a ComboBox Inside a DataGrid with Hardcoded and DB-Driven Data

private List<DataGridColumn> CreateColumns(Style textStyle, Style editTextStyle, List<ComboBoxItemModel> sortedCategories, bool isDbData)
{
    // Generate the ComboBox data based on whether we need hardcoded values or fetched data from the DB
    var comboBoxData1 = isDbData
        ? sortedCategories // Use DB data (list of ComboBoxItemModel)
        : new List<ComboBoxItemModel> // Hardcoded values in the same format
        {
            new ComboBoxItemModel { DisplayText = "---", ActualValue = "---_---" },
            new ComboBoxItemModel { DisplayText = "Electronics", ActualValue = "ELEC_001" },
            new ComboBoxItemModel { DisplayText = "Clothing", ActualValue = "CLO_002" },
            new ComboBoxItemModel { DisplayText = "Furniture", ActualValue = "FURN_003" },
            new ComboBoxItemModel { DisplayText = "Groceries", ActualValue = "GROC_004" },
            new ComboBoxItemModel { DisplayText = "Books", ActualValue = "BOOK_005" }
        };

    // Use the same comboBoxData for both ComboBox columns if needed
    var comboBoxData2 = sortedCategories;

    return new List<DataGridColumn>
    {
        // Hidden column for ProductId (Invisible to the user but stored)
        new DataGridTextColumn
        {
            Header = "ProductId",
            Binding = new Binding("ProductId") { Mode = BindingMode.OneWay },
            Visibility = Visibility.Collapsed
        },

        // First ComboBox column for selecting the category of the product
        new DataGridComboBoxColumn
        {
            Header = "Category", // Display header text
            ItemsSource = comboBoxData1, // Set the ComboBox items from either DB or hardcoded list
            DisplayMemberPath = "DisplayText", // This will be shown to the user
            SelectedValuePath = "ActualValue", // The value bound to the selected item
            SelectedValueBinding = new Binding("CategoryCode") { Mode = BindingMode.TwoWay }, // Binding to the CategoryCode property of the product
            Width = 150, // Set the column width
            ElementStyle = Application.Current.FindResource("ComboBoxStyle") as Style, // Apply style from resources
            EditingElementStyle = TemplateHelper.CreateComboBoxEditingElementStyle(ComboBox_SelectionChanged) // Style for when editing the ComboBox
        },

        // Second ComboBox column for selecting sub-category of the product (useful in more complex scenarios)
        new DataGridComboBoxColumn
        {
            Header = "Sub-Category", // Display header text
            ItemsSource = comboBoxData2, // Set the ComboBox items from either DB or hardcoded list
            DisplayMemberPath = "DisplayText", // This will be shown to the user
            SelectedValuePath = "ActualValue", // The value bound to the selected item
            SelectedValueBinding = new Binding("SubCategoryCode") { Mode = BindingMode.TwoWay }, // Binding to the SubCategoryCode property of the product
            Width = 150, // Set the column width
            ElementStyle = Application.Current.FindResource("ComboBoxStyle") as Style, // Apply style from resources
            EditingElementStyle = TemplateHelper.CreateComboBoxEditingElementStyle(ComboBox_SelectionChanged) // Style for when editing the ComboBox
        },

        // Additional columns (e.g., product images or other properties) can be added here
        ObjectsHelper.CreateImageColumn("ProductImage", 100), // Custom column for product images
        ObjectsHelper.CreateImageColumn("ProductPreview", 100) // Another custom column for product preview images
    };
}

Explanation of Each Section:

1. ComboBox Data Generation:

We begin by determining the source of the data for the ComboBox columns. This is controlled by the isDbData flag:

  • If isDbData is true, we fetch the list from the database (sortedCategories).
  • If isDbData is false, we use hardcoded values like product categories such as "Electronics", "Clothing", and so on.

This is done using the ternary operator to decide the list of ComboBoxItemModel objects:

var comboBoxData1 = isDbData
    ? sortedCategories // Use DB data (list of ComboBoxItemModel)
    : new List<ComboBoxItemModel> // Hardcoded values in the same format
    {
        new ComboBoxItemModel { DisplayText = "---", ActualValue = "---_---" },
        new ComboBoxItemModel { DisplayText = "Electronics", ActualValue = "ELEC_001" },
        new ComboBoxItemModel { DisplayText = "Clothing", ActualValue = "CLO_002" },
        new ComboBoxItemModel { DisplayText = "Furniture", ActualValue = "FURN_003" },
        new ComboBoxItemModel { DisplayText = "Groceries", ActualValue = "GROC_004" },
        new ComboBoxItemModel { DisplayText = "Books", ActualValue = "BOOK_005" }
    };

2. DataGridTextColumn for Hidden ProductId:

We create a hidden column that contains the ProductId. This column is invisible to the user but will still hold data for internal processing.

new DataGridTextColumn
{
    Header = "ProductId",
    Binding = new Binding("ProductId") { Mode = BindingMode.OneWay },
    Visibility = Visibility.Collapsed
}
  • Binding: We bind this column to the ProductId property, but since the visibility is set to Collapsed, the column is not shown in the UI.

3. DataGridComboBoxColumn for Category:

This is the first ComboBox column where the user can select the product's category:

new DataGridComboBoxColumn
{
    Header = "Category",
    ItemsSource = comboBoxData1, // Set ComboBox items from DB or hardcoded data
    DisplayMemberPath = "DisplayText", // This shows the category name
    SelectedValuePath = "ActualValue", // This stores the category code
    SelectedValueBinding = new Binding("CategoryCode") { Mode = BindingMode.TwoWay }, // Bind to product's CategoryCode
    Width = 150,
    ElementStyle = Application.Current.FindResource("ComboBoxStyle") as Style, // Apply ComboBox style
    EditingElementStyle = TemplateHelper.CreateComboBoxEditingElementStyle(ComboBox_SelectionChanged) // Apply editing style
}
  • ItemsSource: This is where we provide the data (comboBoxData1), either from the database or hardcoded.
  • DisplayMemberPath: This is the property (DisplayText) that will be shown in the ComboBox dropdown.
  • SelectedValuePath: This is the property (ActualValue) that will be stored once an item is selected.
  • SelectedValueBinding: The binding to the CategoryCode property allows the ComboBox to update the product’s category code when the user selects a new value.

4. DataGridComboBoxColumn for Sub-Category:

The second ComboBox column allows the user to select a sub-category for the product:

new DataGridComboBoxColumn
{
    Header = "Sub-Category",
    ItemsSource = comboBoxData2,
    DisplayMemberPath = "DisplayText",
    SelectedValuePath = "ActualValue",
    SelectedValueBinding = new Binding("SubCategoryCode") { Mode = BindingMode.TwoWay },
    Width = 150,
    ElementStyle = Application.Current.FindResource("ComboBoxStyle") as Style,
    EditingElementStyle = TemplateHelper.CreateComboBoxEditingElementStyle(ComboBox_SelectionChanged)
}
  • This column works similarly to the Category column but uses a different binding for SubCategoryCode.

5. Additional Columns (Images):

We have extra image columns that can display product images or other visual content:

ObjectsHelper.CreateImageColumn("ProductImage", 100),
ObjectsHelper.CreateImageColumn("ProductPreview", 100)
  • These custom helper methods create image columns, which could be used to display product images or previews.

Difference Between SelectedValueBinding and SelectedItemBinding:

In ComboBox columns inside a DataGrid, you can bind data to the selected item in two primary ways: using SelectedValueBinding or SelectedItemBinding. Let's break down the differences:

1. SelectedValueBinding:

  • Purpose: SelectedValueBinding binds the value of the selected ComboBox item to a property in your data model.
  • How It Works: You bind to a specific property of the selected item that you want to be stored or displayed. The SelectedValuePath property is used to determine which value to use for binding.
  • Example:
    SelectedValueBinding = new Binding("CategoryCode") { Mode = BindingMode.TwoWay }
    
    • In this case, the CategoryCode property in the data model is updated with the value of ActualValue from the selected ComboBox item, thanks to the SelectedValuePath property.

2. SelectedItemBinding:

  • Purpose: SelectedItemBinding binds the entire selected item to the data model, allowing you to access all properties of the selected item, not just a single value.
  • How It Works: You bind directly to the entire object (the selected ComboBox item) rather than just one of its properties.
  • Example:
    SelectedItemBinding = new Binding("SelectedCategory") { Mode = BindingMode.TwoWay }
    
    • In this case, the entire selected ComboBoxItemModel (which includes DisplayText and ActualValue) is bound to the SelectedCategory property in the data model.

Key Differences:

  • SelectedValueBinding: Binds a specific property of the selected item (like ActualValue) to a property in your data model.
  • SelectedItemBinding: Binds the entire selected object (e.g., the whole ComboBoxItemModel) to a property in your data model, giving you access to all properties of the selected item.

Use SelectedValueBinding when you need to bind only a specific value from the ComboBox item, and use SelectedItemBinding when you need the full object to access its properties in your model.


Conclusion:

In this tutorial, we learned how to create a ComboBox in a DataGrid with dynamic data (either fetched from a database or hardcoded). We also learned how to manage hidden columns (like ProductId) and display user-friendly names (like category names) while storing useful codes (like category codes).

Additionally, we explored the difference between SelectedValueBinding and SelectedItemBinding, clarifying when to use each depending on whether you need a single property value or the entire selected item.

By following this example, you can extend this technique to more complex scenarios, where you need to present data in a user-friendly way while managing the actual data behind the scenes.