How can i filter a ComboBox's dropdown in a datagridcolumn based on the selection of the previous cell?

Mesh Ka 345 Reputation points
2024-01-31T05:10:17.2666667+00:00

I have 3 ComboBoxes in a DevExpress Datagrid Columns namely; Country, Provinces Districts respectively. The Country Column has a dropdown that lists all of the Countries in the World, the Province Dropdown has all of the Provinces in all the Countries, the District dropdown has all of the districts in all the Provinces. Now I want to make it work properly, What I wanted is to display only the the provinces of the selected Country in the Provinces dropdowns and to display only the districts of the selected province in the districts dropdowns. How can I achieve that?

Here is my DATABASE SCRIPT i used and
Here are my codes: MainWindow.xaml:

<dx:ThemedWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
    xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:DataGridBindingExampleCore"
    xmlns:conveters="clr-namespace:DataGridBindingExampleCore.Converters"
    x:Class="DXApplication2.MainWindow"
    Title="MainWindow" Height="800" Width="1000" DataContext="{DynamicResource vm}">
    <Window.Resources>
        <vm:MainWindowViewModel x:Key="vm" />
        <conveters:ConvProvinceID x:Key="ConvProvinceID" />
        <conveters:ConvDistrictID x:Key="ConvDistrictID" />
    </Window.Resources>
    <DockPanel>
        <dxg:GridControl Width="990" ItemsSource="{Binding SelectedStudent.PlacesOfInterest}">
            <dxg:GridControl.View>
                <dxg:TableView/>
            </dxg:GridControl.View>
            <dxg:GridColumn FieldName="ID" IsSmart="True"/>
            <dxg:GridColumn FieldName="StudentID" IsSmart="True"/>
            <dxg:GridColumn FieldName="CountryName" IsSmart="True">
                <dxg:GridColumn.EditSettings>
                    <dxe:ComboBoxEditSettings ItemsSource="{Binding Countries}"
                                              DisplayMember="CountryName" 
                                              ValueMember="CountryName"/>
                </dxg:GridColumn.EditSettings>
            </dxg:GridColumn>
            <dxg:GridColumn FieldName="ProvinceID" IsSmart="True">
                <dxg:GridColumn.EditSettings>
                    <dxe:ComboBoxEditSettings ItemsSource="{Binding Provinces}" 
                                              DisplayMember="ProvinceName" 
                                              ValueMember="ProvinceID"/>
                </dxg:GridColumn.EditSettings>
            </dxg:GridColumn>
            <dxg:GridColumn FieldName="DistrictID" IsSmart="True">
                <dxg:GridColumn.EditSettings>
                    <dxe:ComboBoxEditSettings ItemsSource="{Binding Districts}" 
                                              DisplayMember="DistrictName" 
                                              ValueMember="DistrictID"/>
                </dxg:GridColumn.EditSettings>
            </dxg:GridColumn>
        </dxg:GridControl>
    </DockPanel>
</dx:ThemedWindow>
```**MainWindowViewModel.cs:**

```csharp
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DataGridBindingExampleCore.Models;
using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace DataGridBindingExampleCore
{
    public partial class MainWindowViewModel : ObservableObject
    {
        [ObservableProperty]
        bool isLoading;

        [ObservableProperty]
        ObservableCollection<StudentsModel> students;
        [ObservableProperty]
        ObservableCollection<CountriesModel> countries;
        [ObservableProperty]
        ObservableCollection<ProvincesModel> provinces;
        [ObservableProperty]
        ObservableCollection<DistrictsModel> districts;

        [ObservableProperty]
        StudentsModel selectedStudent;

        int currentIndex = 0;

        [RelayCommand]
        private void NavigateStudents(string ButtonName)
        {
            switch (ButtonName)
            {
                case "NavigateNextButton":
                    currentIndex = Math.Min(currentIndex + 1, Students.Count - 1);
                    break;
                case "NavigateBeforeButton":
                    currentIndex = Math.Max(currentIndex - 1, 0);
                    break;
                default:
                    break;
            }

            this.SelectedStudent = Students[currentIndex];
        }

        public MainWindowViewModel()
        {
            LoadDataAsync();
        }

        private async void LoadDataAsync()
        {
            IsLoading = true;

            this.Students = new ObservableCollection<StudentsModel>(await DAL.LoadStudentsAsync());
            this.Countries = new ObservableCollection<CountriesModel>(await DAL.LoadCountriesAsync());
            this.Provinces = new ObservableCollection<ProvincesModel>(await DAL.LoadProvincesAsync());
            this.Districts = new ObservableCollection<DistrictsModel>(await DAL.LoadDistrictsAsync());

            this.SelectedStudent = Students.FirstOrDefault();
            OnPropertyChanged(nameof(SelectedStudent));  // Notify the UI that SelectedStudent has changed

            IsLoading = false;
        }
    }
}

DAL.cs:

using Dapper;
using DataGridBindingExampleCore.Models;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

namespace DataGridBindingExampleCore
{
    public class DAL
    {
        private static readonly string ConnString = "Data Source=(local);Initial Catalog=CollegeDB;Integrated Security=True";
        public static async Task<List<StudentsModel>> LoadStudentsAsync()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                var students = (await conn.QueryAsync<StudentsModel>("SELECT * FROM Students")).ToList();
                var allPlacesOfInterest = (await conn.QueryAsync<PlacesOfInterest>("SELECT * FROM PlacesOfInterest")).ToList();

                foreach (var student in students)
                {
                    var placesOfInterest = allPlacesOfInterest.Where(p => p.StudentID == student.StudentID).ToList();
                    student.PlacesOfInterest = new ObservableCollection<PlacesOfInterest>(placesOfInterest);
                }

                return students;
            }
        }
        public static async Task<List<CountriesModel>> LoadCountriesAsync()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                var result = await conn.QueryAsync<CountriesModel>("SELECT * FROM Countries");
                return result.ToList();
            }
        }
        public static async Task<List<ProvincesModel>> LoadProvincesAsync()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                var result = await conn.QueryAsync<ProvincesModel>("SELECT * FROM Provinces");
                return result.ToList();
            }
        }
        public static async Task<List<DistrictsModel>> LoadDistrictsAsync()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                var result = await conn.QueryAsync<DistrictsModel>("SELECT * FROM Districts");
                return result.ToList();
            }
        }
    }
}

Models (I have combined them here but they are in different files):

using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;

namespace DataGridBindingExampleCore.Models
{
    public partial class PlacesOfInterest : ObservableObject
    {
        [ObservableProperty]
        int iD;
        [ObservableProperty]
        int studentID;
        [ObservableProperty]
        string countryName;
        partial void OnCountryNameChanged(string oldValue, string newValue)
        {
            this.ProvinceID = 0;
        }
        [ObservableProperty]
        int provinceID;
        partial void OnProvinceIDChanged(int oldValue, int newValue)
        {
            this.DistrictID = 0;
        }
        [ObservableProperty]
        int districtID;
    }

    public partial class StudentsModel : ObservableObject
    {
        [ObservableProperty]
        int studentID;
        [ObservableProperty]
        string studentName;
        [ObservableProperty]
        ObservableCollection<PlacesOfInterest> placesOfInterest;
    }

    public partial class CountriesModel : ObservableObject
    {
        [ObservableProperty]
        string countryName;
    }

    public partial class ProvincesModel : ObservableObject
    {
        [ObservableProperty]
        string countryName;
        [ObservableProperty]
        int provinceID;
        [ObservableProperty]
        string provinceName;
    }

    public partial class DistrictsModel : ObservableObject
    {
        [ObservableProperty]
        int provinceID;
        [ObservableProperty]
        int districtID;
        [ObservableProperty]
        string districtName;
    }
}

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,635 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
9,977 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
744 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Rohit Kumar Tiwari 0 Reputation points
    2024-01-31T06:42:24.8633333+00:00
    1. XAML: Define DataGrid and ComboBox columns
       <DataGrid x:Name="myDataGrid" AutoGenerateColumns="False">
           <DataGrid.Columns>
               <DataGridTextColumn Header="Column1" Binding="{Binding Property1}" />
               <DataGridComboBoxColumn Header="Column2" x:Name="comboBoxColumn" SelectedItemBinding="{Binding Property2}" />
           </DataGrid.Columns>
       </DataGrid>
       
    
    1. Code-behind (C#): Handle ComboBox loading
       using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Controls;
    
    public partial class MainWindow : Window
    {
        public ObservableCollection<MyDataModel> DataList { get; set; }
    
        public MainWindow()
        {
            InitializeComponent();
    
            // Initialize your data source
            DataList = new ObservableCollection<MyDataModel>
            {
                new MyDataModel { Property1 = "Item 1", Property2Options = new ObservableCollection<string> { "Option A", "Option B" } },
                new MyDataModel { Property1 = "Item 2", Property2Options = new ObservableCollection<string> { "Option C", "Option D" } },
                // Add more items as needed
            };
    
            myDataGrid.ItemsSource = DataList;
        }
    
        private void ComboBox_Loaded(object sender, RoutedEventArgs e)
        {
            ComboBox comboBox = sender as ComboBox;
            DataGridRow dataGridRow = FindVisualParent<DataGridRow>(comboBox);
    
            if (dataGridRow != null)
            {
                // Access the previous cell's value
                var previousCellContent = (dataGridRow.Item as MyDataModel).Property1;
    
                // Filter ComboBox options based on the value of the previous cell
                comboBox.ItemsSource = ((MyDataModel)dataGridRow.Item).Property2Options;
            }
        }
    
        // Helper method to find the parent of a specific type in the visual tree
        private T FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
        {
            while (obj != null)
            {
                if (obj is T parent)
                {
                    return parent;
                }
                obj = VisualTreeHelper.GetParent(obj);
            }
            return null;
        }
    }
       
    
    1. This example assumes that you have a data model class MyDataModel with properties Property1 and Property2 representing the columns in the DataGrid. The Property2Options is an ObservableCollection of options for the ComboBox in the second column. Make sure to handle the ComboBox_Loaded event in your XAML file:
       <DataGridComboBoxColumn Header="Column2" x:Name="comboBoxColumn" SelectedItemBinding="{Binding Property2}" Loaded="ComboBox_Loaded" />
       
    
       <DataGridComboBoxColumn Header="Column2" x:Name="comboBoxColumn" SelectedItemBinding="{Binding Property2}" Loaded="ComboBox_Loaded" />
       
    
    1. Adjust the code according to your specific requirements and data model structure.
    0 comments No comments

  2. Mesh Ka 345 Reputation points
    2024-02-04T10:30:36.5533333+00:00

    @Peter Fleischer (former MVP) , @Hui Liu-MSFT :

    This was exactly what i wanted and devexpress gridcontrol solved that in a very simple manner with just 3 lines of code:
    dev3