Wpf mvvm ObservableCollection doesn't update listView

Anja 426 Reputation points
2021-04-19T14:22:34.443+00:00

I want to update a listview in View when adding/removing an entry.

When running to curser I can see, that my Categories_GetActive is updated with row 4(and so on) but it doesn't show up in my ListView.

When starting the page, the 3 rows from db is in the listview.

Can anyone please help me to see, what I'm doing wrong?

Best regards
Simsen :-)

What I have done

My Listview here I want to bind to Categories_GetActive

    <ListView x:Name="LivCategories" ItemsSource="{Binding Categories_GetActive}" 
                                  SelectedItem="{Binding SelectedCategory}" Grid.Row="0" Grid.Column="0">
                            <ListView.View>
                                <GridView>
                                    <GridViewColumn Header="Id" Width="50" DisplayMemberBinding="{Binding CategoryId}"/>
                                    <GridViewColumn Header="Navn" Width="250"  DisplayMemberBinding="{Binding CategoryName}"/>
                                    <GridViewColumn Header="Global" Width="50"  DisplayMemberBinding="{Binding CategoryIsGlobal}"/>
                                    <GridViewColumn Header="Project" Width="150"  DisplayMemberBinding="{Binding ProjectName}"/>
                                    <GridViewColumn Header="ProjectId" Width="0"  DisplayMemberBinding="{Binding ProjectId}" />
                                </GridView>
                            </ListView.View>
                        </ListView>

In my ViewModel

public CategoryViewModel()
            {
                LoadCategories();
                LoadProjects();
                CmdSave = new MyICommand(SaveChanges);
                CmdSaveNew = new MyICommand(SaveNew);
                //CmdUpdateLists = new MyICommand(UpdateLists);
            }

and

public ObservableCollection<Category> Categories_GetAll { get; set; }
            public ObservableCollection<Category> Categories_GetActive { get; set; }
            public ObservableCollection<Category> Categories_GetInactive { get; set; }
            public void LoadCategories()
            {
                DalCategory dalCategory = new DalCategory();
                var categories = dalCategory.GetCategories();

                if (categories != null)
                {
                    //All
                    List<Category> Categories_GetAllList = categories;
                    Categories_GetAll = new ObservableCollection<Category>(Categories_GetAllList);

                    //Active
                    List<Category> Categories_GetActiveList = categories.Where(x => x.CategoryIsObsolete == false).ToList();
                    Categories_GetActive = new ObservableCollection<Category>(Categories_GetActiveList);

                    //Inactive
                    List<Category> Categories_GetInactiveList = categories.Where(x => x.CategoryIsObsolete == true).ToList();
                    Categories_GetInactive = new ObservableCollection<Category>(Categories_GetInactiveList);
                }
            }

And here I add a row to the Categories_GetActive

public MyICommand CmdSaveNew { get; set; }

        public void SaveNew()
        {
            ResetMessages();

            DalCategory dal = new DalCategory();

            if (!string.IsNullOrEmpty(CategoryName))
            {
                string name = CategoryName;
                bool global = CategoryIsGlobal;
                bool obsolete = CategoryIsObsolete;
                int projectId = ProjectId;

                Category cat = new Category();
                cat.CategoryName = CategoryName;
                cat.CategoryIsGlobal = CategoryIsGlobal;
                cat.CategoryIsObsolete = CategoryIsObsolete;
                cat.ProjectId = ProjectId;

                int categoryId = dal.NewCategory(cat);

                if (cat.CategoryIsObsolete == false)
                {
                    Categories_GetActive.Add(cat);
                }
                else
                {
                    Categories_GetInactive.Add(cat);
                }

                MessageOk = "Kategorien er gemt";
            }
            else
            {
                MessageError = "Kategorinavn skal udfyldes";
            }
        }
Developer technologies | Windows Presentation Foundation
0 comments No comments
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2021-04-22T04:37:22.47+00:00

    Hi Anja,
    instead of using different instances use singleton pattern:

    namespace ViewModel.Account
    {
      public class CategoryViewModel : INotifyPropertyChanged
      {
    
        private static CategoryModel _instance = null;
    
        public static CategoryModel GetInstance()
        {
          if (_instance == null) _instance = new CategoryModel();
          return _instance;
        }
    
    ...
    
    namespace Views.Account
    {
      /// <summary>
      /// Interaction logic for AccountObsoleteView.xaml
      /// </summary>
      public partial class AccountObsoleteView : UserControl
      {
        public AccountObsoleteView()
        {
          InitializeComponent();
          //DataContext = new ViewModel.Account.CategoryViewModel(); replace with following assignment
          DataContext = ViewModel.Account.CategoryViewModel.GetInstance();
        }
    
    ...
    
    namespace Views.Account
    {
      /// <summary>
      /// Interaction logic for AccountObsoleteViewCategoryNew.xaml
      /// </summary>
      public partial class AccountObsoleteViewCategoryNew : UserControl
      {
        public AccountObsoleteViewCategoryNew()
        {
          InitializeComponent();
          //DataContext = new ViewModel.Account.CategoryViewModel(); replace with following assignment
          DataContext = ViewModel.Account.CategoryViewModel.GetInstance();
        }
    
    ...
    

3 additional answers

Sort by: Most helpful
  1. Anja 426 Reputation points
    2021-04-19T15:11:49.36+00:00

    I have made a few changes to see if that help but no.

    I have added it to an OnPropertyChanged (for all 3)

    private ObservableCollection<Category> _categories_GetAll;
            public ObservableCollection<Category> Categories_GetAll 
            { 
                get
                {
                    return _categories_GetAll;
                }
                set
                {
                    _categories_GetAll = value;
                    OnPropertyChanged("Categories_GetAll");
                }
            }
    

    And then tried this in the View of the ListView

    <ListView x:Name="LivCategories" ItemsSource="{Binding Categories_GetActive, UpdateSourceTrigger=PropertyChanged}" 
                                          SelectedItem="{Binding SelectedCategory}" Grid.Row="0" Grid.Column="0">
                                    <ListView.View>
                                        <GridView>
                                            <GridViewColumn Header="Id" Width="50" DisplayMemberBinding="{Binding CategoryId}"/>
                                            <GridViewColumn Header="Navn" Width="250"  DisplayMemberBinding="{Binding CategoryName}"/>
                                            <GridViewColumn Header="Global" Width="50"  DisplayMemberBinding="{Binding CategoryIsGlobal}"/>
                                            <GridViewColumn Header="Project" Width="150"  DisplayMemberBinding="{Binding ProjectName}"/>
                                            <GridViewColumn Header="ProjectId" Width="0"  DisplayMemberBinding="{Binding ProjectId}" />
                                        </GridView>
                                    </ListView.View>
                                </ListView>
    

  2. Peter Fleischer (former MVP) 19,341 Reputation points
    2021-04-20T05:19:54.07+00:00

    Hi Anja,
    I change your code a little bit and it works fine. Please, try my demo.

    XAML:

    <Window x:Class="WpfApp1.Window035"  
            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:WpfApp035"  
            mc:Ignorable="d"  
            Title="MainWindow" Height="450" Width="800">  
      <Window.DataContext>  
        <local:CategoryViewModel/>  
      </Window.DataContext>  
      <Grid>  
        <Grid.RowDefinitions>  
          <RowDefinition/>  
          <RowDefinition Height="auto"/>  
          <RowDefinition Height="auto"/>  
          <RowDefinition Height="auto"/>  
          <RowDefinition Height="auto"/>  
          <RowDefinition Height="auto"/>  
        </Grid.RowDefinitions>  
        <ListView x:Name="LivCategories"   
                  ItemsSource="{Binding Categories_GetActive}"   
                  SelectedItem="{Binding SelectedCategory}" Grid.Row="0" Grid.Column="0">  
          <ListView.View>  
            <GridView>  
              <GridViewColumn Header="Id" Width="50" DisplayMemberBinding="{Binding CategoryId}"/>  
              <GridViewColumn Header="Navn" Width="250"  DisplayMemberBinding="{Binding CategoryName}"/>  
              <GridViewColumn Header="Global" Width="50"  DisplayMemberBinding="{Binding CategoryIsGlobal}"/>  
              <GridViewColumn Header="Project" Width="150"  DisplayMemberBinding="{Binding ProjectName}"/>  
              <GridViewColumn Header="ProjectId" Width="0"  DisplayMemberBinding="{Binding ProjectId}" />  
            </GridView>  
          </ListView.View>  
        </ListView>  
          
        <!-- Button to add Catergories-->  
        <Button Grid.Row="1" Content="Add Category" Command="{Binding CmdSaveNew}" Width="150" Margin="5"/>  
      
        <!--And here I show you a Checkbox witch works perfectly-->  
        <CheckBox Content="Inaktiv:" Grid.Row="2"  
                  IsChecked="{Binding ElementName=LivCategories, Path=SelectedValue.CategoryIsObsolete, Mode=TwoWay}" />  
      
        <!--One of the ways I have tried for the ComboBox you can see here-->  
        <ComboBox Grid.Row="3"  
                  ItemsSource="{Binding Path=Projects_GetActive}" DisplayMemberPath="ProjectName"  
                  Text="{Binding ElementName=LivCategories, Path= SelectedValue.ProjectName}"/>  
          
      </Grid>  
    </Window>  
    

    And classes:

    using System;  
    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Windows;  
    using System.Windows.Data;  
    using System.Windows.Input;  
      
    namespace WpfApp035  
    {  
      public class CategoryViewModel  
      {  
        public CategoryViewModel()  
        {  
          LoadCategories();  
          LoadProjects();  
          //CmdSave = new MyICommand(SaveChanges);  
          CmdSaveNew = new MyICommand(SaveNew);  
          //CmdUpdateLists = new MyICommand(UpdateLists);  
        }  
      
        private void LoadCategories()  
        {  
          //DalCategory dalCategory = new DalCategory();  
          //var categories = dalCategory.GetCategories();  
      
          //if (categories != null)  
          //{  
          //  //All  
          //  List<Category> Categories_GetAllList = categories;  
          //  Categories_GetAll = new ObservableCollection<Category>(Categories_GetAllList);  
      
          //  //Active  
          //  List<Category> Categories_GetActiveList = categories.Where(x => x.CategoryIsObsolete == false).ToList();  
          //  Categories_GetActive = new ObservableCollection<Category>(Categories_GetActiveList);  
      
          //  //Inactive  
          //List<Category> Categories_GetInactiveList = categories.Where(x => x.CategoryIsObsolete == true).ToList();  
      
          // for demo only  
          colCA.Add(new Category() { CategoryId = 1, CategoryIsGlobal = true, CategoryIsObsolete = false, CategoryName = "Cat 1", ProjectId = 11, ProjectName = "Project 11" });  
          colCA.Add(new Category() { CategoryId = 2, CategoryIsGlobal = false, CategoryIsObsolete = false, CategoryName = "Cat 2", ProjectId = 12, ProjectName = "Project 12" });  
          colCA.Add(new Category() { CategoryId = 3, CategoryIsGlobal = true, CategoryIsObsolete = false, CategoryName = "Cat 3", ProjectId = 13, ProjectName = "Project 13" });  
          colCA.Add(new Category() { CategoryId = 4, CategoryIsGlobal = false, CategoryIsObsolete = true, CategoryName = "Cat 4", ProjectId = 14, ProjectName = "Project 14" });  
          colCA.Add(new Category() { CategoryId = 5, CategoryIsGlobal = true, CategoryIsObsolete = false, CategoryName = "Cat 5", ProjectId = 15, ProjectName = "Project 15" });  
          cvsCA.Source = colCA;  
        }  
      
        private void LoadProjects()  
        {  
          ObservableCollection<Project> colPA = new ObservableCollection<Project>();  
          colPA.Add(new Project() { ProjectName = "Project 11" });  
          colPA.Add(new Project() { ProjectName = "Project 12" });  
          colPA.Add(new Project() { ProjectName = "Project 13" });  
          colPA.Add(new Project() { ProjectName = "Project 14" });  
          colPA.Add(new Project() { ProjectName = "Project 15" });  
          cvsPA.Source = colPA;  
        }  
      
        ObservableCollection<Category> colCA = new ObservableCollection<Category>();  
         public ICollectionView Categories_GetActive { get => cvsCA.View; }  
       //public ObservableCollection<Category> Categories_GetActive { get; set; }  
        public ObservableCollection<Category> Categories_GetAll { get; set; }  
        public ObservableCollection<Category> Categories_GetInactive { get; set; }  
      
        private CollectionViewSource cvsCA = new CollectionViewSource();  
      
        public Category SelectedCategory { get; set; }  
      
        private CollectionViewSource cvsPA = new CollectionViewSource();  
        public ICollectionView Projects_GetActive { get => cvsPA.View; }  
      
      
        public ICommand CmdSaveNew { get; set; }  
      
        public void SaveNew(object parameter)  
        {  
          //ResetMessages();  
      
          //DalCategory dal = new DalCategory();  
      
          if (!string.IsNullOrEmpty("x")) // CategoryName))  
          {  
            //string name = CategoryName;  
            //bool global = CategoryIsGlobal;  
            //bool obsolete = CategoryIsObsolete;  
            //int projectId = ProjectId;  
      
            Category cat = new Category();   
            cat.CategoryName = "Cat new"; // CategoryName;  
            cat.CategoryIsGlobal = true; // CategoryIsGlobal;  
            cat.CategoryIsObsolete = false; // CategoryIsObsolete;  
            cat.ProjectId = 11; // ProjectId;  
      
            //int categoryId = dal.NewCategory(cat);  
      
      
            if (cat.CategoryIsObsolete == false)  
            {  
              //Categories_GetActive.Add(cat);  
              colCA.Add(cat);  
            }  
            else  
            {  
              Categories_GetInactive.Add(cat);  
            }  
      
            //MessageOk = "Kategorien er gemt";  
            MessageBox.Show( "Kategorien er gemt");  
          }  
          else  
          {  
            //MessageError = "Kategorinavn skal udfyldes";  
            MessageBox.Show("Kategorinavn skal udfyldes");  
          }  
        }  
      }  
      
      public class Category  
      {  
        public int CategoryId { get; set; }  
        public string CategoryName { get; set; }  
        public bool CategoryIsGlobal { get; set; }  
        public string ProjectName { get; set; }  
        public int ProjectId { get; set; }  
        public bool CategoryIsObsolete { get; set; }  
      }  
      
      public class Project  
      {  
        public string ProjectName { get; set; }  
      }  
      
      public class MyICommand : ICommand  
      {  
        private readonly Predicate<object> _canExecute;  
        private readonly Action<object> _action;  
        public MyICommand(Action<object> action) { _action = action; _canExecute = null; }  
        public MyICommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }  
        public void Execute(object o) => _action(o);  
        public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);  
        public event EventHandler CanExecuteChanged  
        {  
          add { CommandManager.RequerySuggested += value; }  
          remove { CommandManager.RequerySuggested -= value; }  
        }  
      }  
    }  
    

    Result:

    89364-x.gif


  3. Anja 426 Reputation points
    2021-04-20T18:23:14.943+00:00

    I have uploaded the solution and a backup of the database.

    I need to guide you into the problem:

    Run the solution.

    Click on Administration menu button in the top
    Click on "Ret konto" (Edit account)
    Now you are on the window with the ListView
    Now click on "Ny" (New) button

    Write something in the textbox.
    Click on "Gem" (Save)
    It will now go into the CategoryViewModel SaveNew() method.
    And I can see the Category is in the Categories_GetActive but not listed in the ListView.

    I don't have the problem when saving the exact same Method in a button on the window where the Listview are.

    I have saved it to github: AnsiBug


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.