How to do datagrid filtering using combo box selecteditem (mvvm)

don bradman 621 Reputation points
2022-06-02T04:18:56.063+00:00

I'm trying to use mvvm pattern in my c# wpf app. The app window has two tabitems.

First one has 3 controls in it (a datagrid, a combo box and a button). When the app loads the datagrid should get filled with all the rows from the database table (I'm using SQLite). The combo box gets unique items from a specific column (Name) of the database table and when an item is selected from it and the button is clicked then the datagrid should show only those rows which has the combo box selected item in its Name column and whose PaidFeesOn column data is empty.

Second tabitem only has a datagrid, which shows some filtered out data. I'm also planning to add buttons to do CRUD operations on the database so both the datagrid's should update automatically.

Now the problem in my current app is that when I select an item from the combo box and hit the filter button, the datagrid in that same tabitem does not update.

Here is my code:

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();

                this.DataContext = new StudentsViewModel();
            }

            //Model class representing the database table
            public class Students : ViewModelBase
            {
                private int _id;
                public int Id
                {
                    get
                    {
                        return _id;
                    }
                    set
                    {
                        _id = value;
                        OnPropertyChanged("Id");
                    }
                }

                private string _name;
                public string Name
                {
                    get
                    {
                        return _name;
                    }
                    set
                    {
                        _name = value;
                        OnPropertyChanged("Name");
                    }
                }

                //... so on

            }

            //The main viewmodel class
            public class StudentsViewModel : ViewModelBase
            {

                public ICollectionView ComboItems { get; set; }

                //Gets the unique students name to a list to add to the combo box itemsource
                public static List<Students> GetStudents()
                {
                    try
                    {
                        SQLiteConnection m_dbConnection = new SQLiteConnection("Data Source=Students.db");
                        SQLiteCommand sqlCom = new SQLiteCommand("SELECT DISTINCT Name FROM studentdata ORDER BY Name", m_dbConnection);
                        SQLiteDataAdapter sda = new SQLiteDataAdapter(sqlCom);
                        DataTable dt = new DataTable();
                        sda.Fill(dt);
                        var Student = new List<Students>();
                        foreach (DataRow row in dt.Rows)
                        {

                            Student.Add(new Students
                            {
                                Name = (string)row["Name"],
                            });

                            m_dbConnection.Close();
                        }
                        return Student;

                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }


                public List<Students> SData { get; set; }
                public List<Students> SData_Pending { get; set; }

                public RelayCommand ComboFilterButtonClicked { get; set; }

                //Gets all the database table data for showing in the datagrid residing in the first tabitem
                public List<Students> GetAllStudentsFromDatabase()
                {

                    SQLiteConnection m_dbConnection = new SQLiteConnection(@"Data Source=Students.db;");
                    SQLiteCommand sqlCom = new SQLiteCommand("Select * From studentdata", m_dbConnection);
                    SQLiteDataAdapter sda = new SQLiteDataAdapter(sqlCom);
                    DataTable dt = new DataTable();
                    sda.Fill(dt);
                    var xStudent = new List<Students>();
                    foreach (DataRow row in dt.Rows)
                    {
                        var obj = new Students()
                        {
                            Id = Convert.ToInt32(row["Id"]),
                            Name = (string)row["Name"],
                            //... so on
                        };
                        xStudent.Add(obj);
                        m_dbConnection.Close();
                    }
                    return xStudent;

                }

                //Gets partially filtered database table data for showing in the datagrid residing in the second tabitem
                public List<Students> GetStudents_Pen()
                {

                    SQLiteConnection m_dbConnection = new SQLiteConnection(@"Data Source=Students.db;");
                    SQLiteCommand sqlCom = new SQLiteCommand("SELECT * FROM studentdata WHERE PaidFeesOn != ''", m_dbConnection);
                    SQLiteDataAdapter sda = new SQLiteDataAdapter(sqlCom);
                    DataTable dt = new DataTable();
                    sda.Fill(dt);
                    var xStudent = new List<Students>();
                    foreach (DataRow row in dt.Rows)
                    {
                        var obj = new Students()
                        {
                            Id = Convert.ToInt32(row["Id"]),
                            Name = (string)row["Name"],
                            //... so on
                        };
                        xStudent.Add(obj);
                        m_dbConnection.Close();
                    }
                    return xStudent;

                }

                public CollectionView ComboFilterView { get; set; }

                public List<Students> MyItems { get; set; }

                //private Students _selectedStudent;
                //public Students SelectedStudent


                private string _selectedStudent;
                public string SelectedStudent
                {
                    get
                    {
                        return _selectedStudent;
                    }
                    set
                    {
                        _selectedStudent = value;
                        OnPropertyChanged("SelectedStudent");
                    }
                }



                public StudentsViewModel()
                {
                    SData = GetAllStudentsFromDatabase();
                    SData_Pending = GetStudents_Pen();


                    ComboFilterButtonClicked = new RelayCommand(ComboFilterData);
                    MyItems = GetStudents();
                    ComboItems = CollectionViewSource.GetDefaultView(MyItems);
                    ComboFilterView = (CollectionView)CollectionViewSource.GetDefaultView(SData);
                    ComboFilterView.Filter = OnComboFilterTriggered;

                }


                public bool OnComboFilterTriggered(object item)
                {
                    //if (!string.IsNullOrEmpty(_selectedStudent.Name))
                    if (!string.IsNullOrEmpty(_selectedStudent))
                    {
                        var lookup = item as Students;
                        return lookup != null && lookup.Name == _selectedStudent && lookup.PaidFeesOn == "";
                    }
                    return true;
                }

                public void ComboFilterData(object param)
                {
                    CollectionViewSource.GetDefaultView(MyItems).Filter = OnComboFilterTriggered;
                }
            }
        }

I'm not used to Git or Github so sharing my projects onedrive link for testing.

Please help!

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,701 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.
10,527 questions
0 comments No comments
{count} votes

Accepted answer
  1. Hui Liu-MSFT 47,101 Reputation points Microsoft Vendor
    2022-06-02T07:26:43.777+00:00

    Code in TabItem:

    <Border>  
            <TabControl>  
                <TabItem Header="First">  
                    <Grid>  
                        <Grid.RowDefinitions>  
                            <RowDefinition/>  
                            <RowDefinition Height="Auto"/>  
                        </Grid.RowDefinitions>  
                        <DataGrid Grid.Row="0" Margin="8" Name="dg1" AutoGenerateColumns="False" CanUserAddRows="False"  
               ItemsSource="{Binding ComboFilterView}" >  
                            <DataGrid.Columns>  
                               ...  
                            </DataGrid.Columns>  
                        </DataGrid>  
                        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="8, 0, 8, 8" HorizontalAlignment="Center" VerticalAlignment="Center">  
      
                            <ComboBox x:Name="cb" IsEditable="True" Width="300"  
               ItemsSource="{Binding ComboItems}" DisplayMemberPath="Name"  
               Text="{Binding SelectedStudent,Mode=TwoWay}">  
                            </ComboBox>  
                            <Button x:Name="FilterFromCombo" Content="Filter" Width="100" Height="28" Margin="8"  
             Command="{Binding ComboFilterButtonClicked}"/>  
                        </StackPanel>  
                    </Grid>  
                </TabItem>  
                <TabItem Header="Second">  
                </TabItem>  
            </TabControl>  
        </Border>  
    

    I updated the code below in ViewModel:

    public StudentsViewModel()  
     {  
     SData = GetAllStudentsFromDatabase();  
     SData_Pending = GetStudents_Pen();  
      
      
     ComboFilterButtonClicked = new RelayCommand(ComboFilterData);  
     MyItems = GetStudents();  
     ComboItems = CollectionViewSource.GetDefaultView(MyItems);  
     ComboFilterView = (CollectionView)CollectionViewSource.GetDefaultView(SData);  
     ComboFilterView.Filter = OnComboFilterTriggered;  
      
     }  
     public bool OnComboFilterTriggered(object item)  
     {  
       if (String.IsNullOrEmpty(SelectedStudent))  
       return true;  
       else  
       {  
      var lookup = item as Students;  
      return lookup != null && lookup.Name == _selectedStudent && lookup.PaidFeesOn =="";  
       }  
     }  
     public void ComboFilterData(object param)  
     {  
     CollectionViewSource.GetDefaultView(SData).Filter = OnComboFilterTriggered;  
               ComboFilterView.Refresh();  
     }  
    

    The result:
    207740-9.gif

    Update:
    Xaml:

      <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="8, 0, 8, 8" HorizontalAlignment="Center" VerticalAlignment="Center">  
      
                              
                            <ComboBox x:Name="cb" IsEditable="True" Width="300"    
    						          ItemsSource="{Binding ComboItems}" DisplayMemberPath="Name"   
    						          Text="{Binding SelectedStudent,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >  
                            </ComboBox>  
      
                        </StackPanel>  
    

    I updated the code below in ViewModel:

     private string _selectedStudent;  
    			public string SelectedStudent  
    			{  
    				get  
    				{  
    					return _selectedStudent;  
    				}  
    				set  
    				{  
    					_selectedStudent = value;  
    					OnPropertyChanged("SelectedStudent");  
    					CollectionViewSource.GetDefaultView(SData).Filter = OnComboFilterTriggered;  
    					ComboFilterView.Refresh();  
    				}  
    			}  
    			public StudentsViewModel()  
    			{  
    				SData = GetAllStudentsFromDatabase();  
    				SData_Pending = GetStudents_Pen();  
    				MyItems = GetStudents();  
    				ComboItems = CollectionViewSource.GetDefaultView(MyItems);  
    				ComboFilterView = (CollectionView)CollectionViewSource.GetDefaultView(SData);  
    				ComboFilterView.Filter = OnComboFilterTriggered;  
      
    			}  
    			public bool OnComboFilterTriggered(object item)  
    			{  
    			  if (String.IsNullOrEmpty(SelectedStudent))  
    				  return true;  
    			  else  
    			  {  
    				 var lookup = item as Students;  
    				 return lookup != null && lookup.Name == _selectedStudent && lookup.PaidFeesOn =="";  
    			  }  
    			}  
    

    The result:
    208604-2.gif


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,306 Reputation points
    2022-06-02T06:45:28.687+00:00

    Hi,
    I simplified your code. try following demo:

    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel;  
    using System.Linq;  
    using System.Runtime.CompilerServices;  
    using System.Windows;  
    using System.Windows.Data;  
      
    namespace WpfApp1  
    {  
      /// <summary>  
      /// Interaction logic for Window023.xaml  
      /// </summary>  
      public partial class Window023 : Window  
      {  
        public Window023()  
        {  
          InitializeComponent();  
        }  
      }  
    }  
      
    namespace WpfApp023  
    {  
      //The main viewmodel class  
      public class StudentsViewModel : ViewModelBase  
      {  
      
        /// <summary>  
        /// ctor - assign all students  
        /// </summary>  
        public StudentsViewModel()  
        {  
          cvsAll.Source = m.AllStudents;  
        }  
      
        Model m = new Model();  
      
        CollectionViewSource cvsAll = new CollectionViewSource();  
        public ICollectionView AllStudents { get => cvsAll.View; }  
      
        public List<string> ComboItems { get => m.UniqueValues(); }  
      
        CollectionViewSource cvsFiltered = new CollectionViewSource();  
        public ICollectionView FilteredStudents { get => cvsFiltered.View; }  
      
        private string _selectedCBItem;  
        public string SelectedCBItem  
        {  
          get => _selectedCBItem;  
          set  
          {  
            _selectedCBItem = value;  
            cvsFiltered.Source = m.GetStudents_Pen(value);  
            OnPropertyChanged(nameof(FilteredStudents));  
          }  
        }  
      }  
      
      public class Model  
      {  
        public List<Student> AllStudents  
        {  
          get  
          {  
            if (_allStudents == null) LoadAllStudentsFromDatabase(); return _allStudents;  
          }  
        }  
      
        private List<Student> _allStudents;  
        public void LoadAllStudentsFromDatabase()  
        {  
          // generate demo data  
          _allStudents = new List<Student>();  
          Random rnd = new Random();  
          for (int i = 1; i < 100; i++) _allStudents.Add(new Student()  
          {  
            Id = i,  
            Name = $"Student {i}",  
            PaidFeesOn = (rnd.NextDouble() > .3) ? rnd.Next(1, 5).ToString() : ""  
          });  
        }  
      
        public List<string> UniqueValues() =>   
          _allStudents.Select((s) => s.PaidFeesOn).Distinct().OrderBy((s) => s).ToList();  
      
        //Gets partially filtered database table data for showing in the datagrid residing in the second tabitem  
        public List<Student> GetStudents_Pen(string filterValue) =>   
          _allStudents.FindAll(s => s.PaidFeesOn == filterValue).ToList();  
      }  
      
      //Model class representing the database table  
      public class Student : ViewModelBase  
      {  
        private int _id;  
        public int Id  
        {  
          get => _id;  
          set { _id = value; OnPropertyChanged(); }  
        }  
      
        private string _name;  
        public string Name  
        {  
          get => _name;  
          set { _name = value; OnPropertyChanged(); }  
        }  
      
        private string _paidFeesOn;  
        public string PaidFeesOn  
        {  
          get => _paidFeesOn;  
          set { _paidFeesOn = value; OnPropertyChanged(); }  
        }  
      
        //... so on  
      
      }  
      
      public class ViewModelBase : INotifyPropertyChanged  
      {  
        public event PropertyChangedEventHandler PropertyChanged;  
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
      }  
    }  
    

    Result:

    207739-x.gif

    And code for old versions of framework and C# compiler:

    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel;  
    using System.Linq;  
    using System.Runtime.CompilerServices;  
    using System.Windows;  
    using System.Windows.Data;  
      
    namespace WpfApp023  
    {  
        //The main viewmodel class  
        public class StudentsViewModel : ViewModelBase  
        {  
      
            /// <summary>  
            /// ctor - assign all students  
            /// </summary>  
            public StudentsViewModel()  
            {  
                cvsAll.Source = m.AllStudents;  
            }  
      
            Model m = new Model();  
      
            CollectionViewSource cvsAll = new CollectionViewSource();  
            public ICollectionView AllStudents  
            {  
                get { return cvsAll.View; }  
            }  
      
      
            public List<string> ComboItems  
            {  
                get { return m.UniqueValues(); }  
            }  
      
            CollectionViewSource cvsFiltered = new CollectionViewSource();  
            public ICollectionView FilteredStudents  
            {  
                get { return cvsFiltered.View; }  
            }  
      
      
            private string _selectedCBItem;  
            public string SelectedCBItem  
            {  
                get { return _selectedCBItem; }  
                set  
                {  
                    _selectedCBItem = value;  
                    cvsFiltered.Source = m.GetStudents_Pen(value);  
                    OnPropertyChanged(nameof(FilteredStudents));  
                }  
            }  
        }  
      
        public class Model  
        {  
            public List<Student> AllStudents  
            {  
                get  
                {  
                    if (_allStudents == null) LoadAllStudentsFromDatabase(); return _allStudents;  
                }  
            }  
      
            private List<Student> _allStudents;  
            public void LoadAllStudentsFromDatabase()  
            {  
                // generate demo data  
                _allStudents = new List<Student>();  
                Random rnd = new Random();  
                for (int i = 1; i < 100; i++) _allStudents.Add(new Student()  
                {  
                    Id = i,  
                    Name = $"Student {i}",  
                    PaidFeesOn = (rnd.NextDouble() > .3) ? rnd.Next(1, 5).ToString() : ""  
                });  
            }  
      
            public List<string> UniqueValues() =>  
              _allStudents.Select((s) => s.PaidFeesOn).Distinct().OrderBy((s) => s).ToList();  
      
            //Gets partially filtered database table data for showing in the datagrid residing in the second tabitem  
            public List<Student> GetStudents_Pen(string filterValue) =>  
              _allStudents.FindAll(s => s.PaidFeesOn == filterValue).ToList();  
        }  
      
        //Model class representing the database table  
        public class Student : ViewModelBase  
        {  
            private int _id;  
            public int Id  
            {  
                get { return _id; }  
                set { _id = value; OnPropertyChanged(); }  
            }  
      
      
            private string _name;  
            public string Name  
            {  
                get { return _name; }  
                set { _name = value; OnPropertyChanged(); }  
            }  
      
      
            private string _paidFeesOn;  
            public string PaidFeesOn  
            {  
                get { return _paidFeesOn; }  
                set { _paidFeesOn = value; OnPropertyChanged(); }  
            }  
      
            //... so on  
      
        }  
      
        public class ViewModelBase : INotifyPropertyChanged  
        {  
            public event PropertyChangedEventHandler PropertyChanged;  
            protected void OnPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
        }  
    }  
      
    <Window x:Class="WpfApplication1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
            xmlns:local="clr-namespace:WpfApp023"  
            mc:Ignorable="d"  
            Title="MainWindow" Height="350" Width="525">  
        <Window.DataContext>  
            <local:StudentsViewModel/>  
        </Window.DataContext>  
        <TabControl>  
            <TabItem Header="all students">  
                <DataGrid ItemsSource="{Binding AllStudents}"/>  
            </TabItem>  
            <TabItem Header="afiltered students">  
                <StackPanel>  
                    <ComboBox ItemsSource="{Binding ComboItems}" SelectedItem="{Binding SelectedCBItem}"/>  
                    <DataGrid ItemsSource="{Binding FilteredStudents}"/>  
               </StackPanel>  
            </TabItem>  
        </TabControl>  
    </Window>