Filtering a ComboBox in a DataGrid based on another combobox in the datagrid in WPF C#?

84 Views Asked by At

In my Example Here i have a 3 ComboBoxes in a Datagrid displayoing Countries, Provinces and Districts respectively.How can i Filter the Provinces by Countries and Districts by Provinces in such a way that when the user selects a country he only gets the provinces that belong to the selected country and when he selects a province he only gets the districts belonging to that province and when he changes the country the province and districts are set to null and when he changes the province the district is set to null:Here is the MainWindwo.xaml:

<Window
    x:Class="DataGridBindingExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:DataGridBindingExample"
    xmlns:materialdesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:DataGridBindingExample"
    Title="MainWindow"
    Width="800"
    Height="450"
    Topmost="True"
    mc:Ignorable="d">
    <Window.Resources>
        <vm:MainWindowViewModel x:Key="MainWindowViewModel" />
    </Window.Resources>

    <Grid>
        <Grid.Background>
            <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                <GradientStop Offset="1" Color="#FF06013F" />
                <GradientStop Color="#FF040F2E" />
            </LinearGradientBrush>
        </Grid.Background>
        <StackPanel>
            <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding PlacesOfInterest}">
                <DataGrid.Columns>
                    <DataGridTemplateColumn Header="Country">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox
                                    Width="120"
                                    DisplayMemberPath="CountryName"
                                    ItemsSource="{Binding DataContext.Countries, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                                    SelectedValue="{Binding CountryName}"
                                    SelectedValuePath="CountryName" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="Province">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox
                                    Width="120"
                                    DisplayMemberPath="ProvinceName"
                                    ItemsSource="{Binding DataContext.Provinces, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                                    SelectedValue="{Binding ProvinceID}"
                                    SelectedValuePath="ProvinceID" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn Header="District">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox
                                    Width="120"
                                    DisplayMemberPath="DistrictName"
                                    ItemsSource="{Binding DataContext.Districts, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                                    SelectedValue="{Binding DistrictID}"
                                    SelectedValuePath="DistrictID" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </

And Here is the ViewModel:

using CommunityToolkit.Mvvm.ComponentModel;
using DataGridBindingExample.Models;
using MaterialDesignFixedHintTextBox;
using System.Collections.ObjectModel;

namespace DataGridBindingExample
{
    public partial class MainWindowViewModel : ObservableObject
    {
        [ObservableProperty]
        ObservableCollection<PlacesOfInterest> placesOfInterest;
        [ObservableProperty]
        ObservableCollection<CountriesModel> countries;
        [ObservableProperty]
        ObservableCollection<ProvincesModel> provinces;
        [ObservableProperty]
        ObservableCollection<DistrictsModel> districts;


        public MainWindowViewModel()
        {
            this.PlacesOfInterest = new ObservableCollection<PlacesOfInterest>(DAL.LoadPlacesOfInterest());
            this.Countries = new ObservableCollection<CountriesModel>(DAL.LoadCountries());
            this.Provinces = new ObservableCollection<ProvincesModel>(DAL.LoadProvinces());
            this.Districts = new ObservableCollection<DistrictsModel>(DAL.LoadDistricts());
        }

    }
}

And Here is the Data Access Layer:

using Dapper;
using DataGridBindingExample.Models;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

namespace MaterialDesignFixedHintTextBox
{
    public class DAL
    {
        private static readonly string ConnString = "Data Source=(local);Initial Catalog=CollegeDB;Integrated Security=True";

        //**************************************************************************************************

        public static List<PlacesOfInterest> LoadPlacesOfInterest()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                if (conn.State == ConnectionState.Closed) conn.Open();
                return conn.Query<PlacesOfInterest>("SELECT * FROM PlacesOfInterest").ToList();
            }
        }

        public static List<CountriesModel> LoadCountries()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                if (conn.State == ConnectionState.Closed) conn.Open();
                return conn.Query<CountriesModel>("SELECT * FROM Countries").ToList();
            }
        }

        public static List<ProvincesModel> LoadProvinces()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                if (conn.State == ConnectionState.Closed) conn.Open();
                return conn.Query<ProvincesModel>("SELECT * FROM Provinces").ToList();
            }
        }

        public static List<DistrictsModel> LoadDistricts()
        {
            using (IDbConnection conn = new SqlConnection(ConnString))
            {
                if (conn.State == ConnectionState.Closed) conn.Open();
                return conn.Query<DistrictsModel>("SELECT * FROM Districts").ToList();
            }
        }
    }
}

And are the Models:

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

   //ProvincesModel
   public partial class ProvincesModel : ObservableObject
   {
       [ObservableProperty]
       string countryName;

       [ObservableProperty]
       int provinceID;

       [ObservableProperty]
       string provinceName;
   }

   //DistrictsModel
   public partial class DistrictsModel : ObservableObject
   {
       [ObservableProperty]
       int provinceID;

       [ObservableProperty]
       int districtID;

       [ObservableProperty]
       string districtName;
   }

   //PlacesOfInterest
       public partial class PlacesOfInterest : ObservableObject
   {
       [ObservableProperty]
       int iD;

       [ObservableProperty]
       string countryName;

       [ObservableProperty]
       int provinceID;

       [ObservableProperty]
       int districtID;
   }

DATABASE:

USE [CollegeDB]
GO
/****** Object:  Table [dbo].[Countries]    Script Date: 1/7/2024 7:29:40 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Countries](
    [CountryName] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Countries] PRIMARY KEY CLUSTERED 
(
    [CountryName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[Districts]    Script Date: 1/7/2024 7:29:40 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Districts](
    [ProvinceID] [int] NULL,
    [DistrictID] [int] IDENTITY(1,1) NOT NULL,
    [DistrictName] [nvarchar](50) NULL,
 CONSTRAINT [PK_Districts] PRIMARY KEY CLUSTERED 
(
    [DistrictID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[PlacesOfInterest]    Script Date: 1/7/2024 7:29:40 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[PlacesOfInterest](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [CountryName] [nvarchar](50) NULL,
    [ProvinceID] [int] NULL,
    [DistrictID] [int] NULL,
 CONSTRAINT [PK_PlacesOfInterest] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[Provinces]    Script Date: 1/7/2024 7:29:40 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Provinces](
    [CountryName] [nvarchar](50) NULL,
    [ProvinceID] [int] IDENTITY(1,1) NOT NULL,
    [ProvinceName] [nvarchar](50) NULL,
 CONSTRAINT [PK_Provinces] PRIMARY KEY CLUSTERED 
(
    [ProvinceID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[Countries] ([CountryName]) VALUES (N'Country1')
GO
INSERT [dbo].[Countries] ([CountryName]) VALUES (N'Country2')
GO
INSERT [dbo].[Countries] ([CountryName]) VALUES (N'Country3')
GO
INSERT [dbo].[Countries] ([CountryName]) VALUES (N'Country4')
GO
INSERT [dbo].[Countries] ([CountryName]) VALUES (N'Country5')
GO
SET IDENTITY_INSERT [dbo].[Districts] ON 
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (1, 1, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (1, 2, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (2, 3, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (2, 4, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (3, 5, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (3, 6, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (4, 7, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (4, 8, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (5, 9, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (5, 10, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (6, 11, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (6, 12, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (7, 13, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (7, 14, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (8, 15, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (8, 16, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (9, 17, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (9, 18, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (10, 19, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (10, 20, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (11, 21, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (11, 22, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (12, 23, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (12, 24, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (13, 25, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (13, 26, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (14, 27, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (14, 28, N'District2')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (15, 29, N'District1')
GO
INSERT [dbo].[Districts] ([ProvinceID], [DistrictID], [DistrictName]) VALUES (15, 30, N'District2')
GO
SET IDENTITY_INSERT [dbo].[Districts] OFF
GO
SET IDENTITY_INSERT [dbo].[PlacesOfInterest] ON 
GO
INSERT [dbo].[PlacesOfInterest] ([ID], [CountryName], [ProvinceID], [DistrictID]) VALUES (1, N'Country1', 1, 1)
GO
SET IDENTITY_INSERT [dbo].[PlacesOfInterest] OFF
GO
SET IDENTITY_INSERT [dbo].[Provinces] ON 
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country1', 1, N'Province1')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country1', 2, N'Province2')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country1', 3, N'Province3')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country2', 4, N'Province1')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country2', 5, N'Province2')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country2', 6, N'Province3')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country3', 7, N'Province1')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country3', 8, N'Province2')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country3', 9, N'Province3')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country4', 10, N'Province1')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country4', 11, N'Province2')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country4', 12, N'Province3')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country5', 13, N'Province1')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country5', 14, N'Province2')
GO
INSERT [dbo].[Provinces] ([CountryName], [ProvinceID], [ProvinceName]) VALUES (N'Country5', 15, N'Province3')
GO
SET IDENTITY_INSERT [dbo].[Provinces] OFF
GO
ALTER TABLE [dbo].[Districts]  WITH CHECK ADD  CONSTRAINT [FK_Districts_Provinces] FOREIGN KEY([ProvinceID])
REFERENCES [dbo].[Provinces] ([ProvinceID])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Districts] CHECK CONSTRAINT [FK_Districts_Provinces]
GO
ALTER TABLE [dbo].[PlacesOfInterest]  WITH CHECK ADD  CONSTRAINT [FK_PlacesOfInterest_Countries] FOREIGN KEY([CountryName])
REFERENCES [dbo].[Countries] ([CountryName])
GO
ALTER TABLE [dbo].[PlacesOfInterest] CHECK CONSTRAINT [FK_PlacesOfInterest_Countries]
GO
ALTER TABLE [dbo].[PlacesOfInterest]  WITH CHECK ADD  CONSTRAINT [FK_PlacesOfInterest_Districts] FOREIGN KEY([DistrictID])
REFERENCES [dbo].[Districts] ([DistrictID])
GO
ALTER TABLE [dbo].[PlacesOfInterest] CHECK CONSTRAINT [FK_PlacesOfInterest_Districts]
GO
ALTER TABLE [dbo].[PlacesOfInterest]  WITH CHECK ADD  CONSTRAINT [FK_PlacesOfInterest_Provinces] FOREIGN KEY([ProvinceID])
REFERENCES [dbo].[Provinces] ([ProvinceID])
GO
ALTER TABLE [dbo].[PlacesOfInterest] CHECK CONSTRAINT [FK_PlacesOfInterest_Provinces]
GO
ALTER TABLE [dbo].[Provinces]  WITH CHECK ADD  CONSTRAINT [FK_Provinces_Countries] FOREIGN KEY([CountryName])
REFERENCES [dbo].[Countries] ([CountryName])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Provinces] CHECK CONSTRAINT [FK_Provinces_Countries]
GO
2

There are 2 best solutions below

0
Moha On BEST ANSWER

I solved the issue by Using Converters and CellTemplating, Here is what i added and Changed in the Code and Xaml:

  1. Added Converter two Converters: ConvProvinceID:
    public class ConvProvinceID : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            int i = 0;

            if (values[0] != DependencyProperty.UnsetValue)
            {
                i = (int)values[0];
            }

            IList<ProvincesModel> l = (IList<ProvincesModel>)values[1];
            return i > 0 && (l.Count > 0) ? l[i - 1].ProvinceName : string.Empty;
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

ConvDistrictID:

    public class ConvDistrictID : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            int i = 0;

            if (values[0] != DependencyProperty.UnsetValue)
            {
                i = (int)values[0];
            }

            IList<DistrictsModel> l = (IList<DistrictsModel>)values[1];
            return (i > 0 && l.Count > 0) ? l[i - 1].DistrictName : string.Empty;
        }
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
  1. Changed the Grid in XAML like this:
//Here I Added the Converters reference to the Windows Resources:
    <Window.Resources>
        <vm:MainWindowViewModel x:Key="vm" />
        <conveters:ConvProvinceID x:Key="ConvProvinceID" />
        <conveters:ConvDistrictID x:Key="ConvDistrictID" />
    </Window.Resources>


//... Other parts of the Code as i provided before in the question


//I Added the Datacontext for the Grid and connected it to the viewmodel
<Grid DataContext="{StaticResource vm}">


//... Other parts of the Code as i provided before in the question
<DataGrid
    AutoGenerateColumns="False"
    ItemsSource="{Binding PlacesOfInterest}"
    SelectedItem="{Binding SelectedPlace}"
    ColumnHeaderStyle="{StaticResource MaterialDesignFlatButton}">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Country">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox
                        Width="120"
                        DisplayMemberPath="CountryName"
                        ItemsSource="{Binding Countries, Source={StaticResource vm}}"
                        SelectedValue="{Binding CountryName, UpdateSourceTrigger=PropertyChanged}"
                        SelectedValuePath="CountryName" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Province">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock>
                        <TextBlock.Text>
                            <MultiBinding Converter="{StaticResource ConvProvinceID}">
                                <Binding Path="ProvinceID" />
                                <Binding Path="Provinces" Source="{StaticResource vm}" />
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate DataType="ComboBox">
                    <ComboBox
                        Width="120"
                        DisplayMemberPath="ProvinceName"
                        ItemsSource="{Binding CurrentProvinces, Source={StaticResource vm}}"
                        SelectedValue="{Binding ProvinceID, UpdateSourceTrigger=PropertyChanged}"
                        SelectedValuePath="ProvinceID" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="District">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock>
                        <TextBlock.Text>
                            <MultiBinding Converter="{StaticResource ConvDistrictID}">
                                <Binding Path="DistrictID" />
                                <Binding Path="Districts" Source="{StaticResource vm}" />
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ComboBox
                        Width="120"
                        DisplayMemberPath="DistrictName"
                        ItemsSource="{Binding CurrentDistricts, Source={StaticResource vm}}"
                        SelectedValue="{Binding DistrictID, UpdateSourceTrigger=PropertyChanged}"
                        SelectedValuePath="DistrictID" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

//... Other parts of the Code as i provided before in the question
  1. In the MainWindowViewModel i added:

        public object CurrentProvinces
        { get => new ObservableCollection<ProvincesModel>(Provinces.Where((p) => p.CountryName == SelectedPlace.CountryName)); }
        public object CurrentDistricts
        { get => new ObservableCollection<DistrictsModel>(Districts.Where((p) => p.ProvinceID == SelectedPlace.ProvinceID)); }

        private PlacesOfInterest _selectedPlace;
        public PlacesOfInterest SelectedPlace
        {
            get => this._selectedPlace;
            set { this._selectedPlace = value; }
        }

  1. And Changed the PlacesOfInterestModel to:
    public partial class PlacesOfInterest : ObservableObject
    {
        public int ID { get; set; }

        string _countryName;
        public string CountryName
        {
            get => this._countryName;
            set { this._countryName = value; ProvinceID = 0; DistrictID = 0; OnPropertyChanged(); }
        }

        int _provinceID;
        public int ProvinceID
        {
            get => this._provinceID;
            set { this._provinceID = value; DistrictID = 0; OnPropertyChanged(); }
        }

        int _districtID;
        public int DistrictID
        {
            get => this._districtID;
            set { this._districtID = value; OnPropertyChanged(); }
        }
    }

Solved

Now with that Solved, another issue on the part of the UI rose;

  1. The province and the District Column don't look like a ComboBox untill the user double clicks in it unlike the Country Column which is how i wanted the ComboBoxes to look like.
  2. The province and the District Column's values Becomes empty when clicked in except the first row. (Look at the gif when i click in the second row)

I went through the XAML and found that the issue was the CellTemplate has two Controls; One a TextBox to Display the value and the Other one was ComboBox in CellEditingTemplate.

Now How can i make that ComboBox be on top or display them like the Country Column's ComboBoxes so that my Users can't confuse them with a textbox.

0
Gerry Schmitz On

You should use ListViews; and don't need the DataGrid; use a Grid instead for layout. (The ListView "panel template" can stack and / or wrap items).

You filter and sync each (List) "view" via .SelectionChanged and "cascading".

When a "parent" (view item) is selected, it retrieves (and sorts) it's children, loads the .ItemsSource of its child ListView, sets that .SelectedIndex to 0; which cascades to "its" child view(s).

(All this is very fast if you preload the "query sources" into memory)

If a ToString() override returns a name, you're all set. Else you need a DataTemplate for your ListViews. I tend to include a (flag) image or (font) icon (via a "functional" property in the item's data object) when appropriate. No impact on the pattern itself.