简体   繁体   中英

ObservableCollection not updated to UI when property changed

Question : ObservableCollection not updated to UI when property changed

What I have tried :

XAML view:

<UserControl x:Class="FlexilineDotNetGui.Flexiline.UserControls.UCRealestate"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:translations="clr-namespace:FlexilineDotNetGui.Flexiline.Translations"
             xmlns:viewModels="clr-namespace:FlexilineDotNetGui.Flexiline.ViewModels"
             x:Name="Realestate"
             DataContext="{StaticResource vmRealestate}">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" >
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <DataGrid Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" Height="200" Margin="5"
                          ItemsSource="{Binding Panden, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" 
                          CanUserAddRows="False" IsReadOnly="True" SelectedItem="{Binding Pand}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="{x:Static translations:UCRealestate.RegistrationType}" Binding="{Binding RegistrationType, Mode=TwoWay}" Width="Auto"/>
                    </DataGrid.Columns>
                </DataGrid>
                <Button Grid.Column="1"  Grid.Row="0" Background="Transparent"
                        Width="20" Height="20" Padding="0" Margin="5" Command="{Binding AddPandCMD}"
                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type viewModels:RealestateViewModel}}}">
                    <Image Source="/Flexiline;component/Resources/New_16x16.ico"/>
                </Button>
                <Button Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" 
                        Width="20" Height="20" Padding="0" Background="Transparent" Margin="5" Command="{Binding DeletePandCMD}"
                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type viewModels:RealestateViewModel}}}">
                    <Image Source="/Flexiline;component/Resources/Delete_16x16.ico"/>
                </Button>
                <Grid Grid.Row="3" Margin="5">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid Grid.Row="1" Grid.Column="1" Margin="0,5">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" MaxWidth="100"/>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                        </Grid>         
                        <Label Grid.Row="2" Grid.Column="2" Content="{x:Static translations:UCRealestate.RegistrationType}" HorizontalContentAlignment="Right" VerticalAlignment="Center"/>
                        <ComboBox Grid.Row="2" Grid.Column="3" Margin="5" ItemsSource="{Binding AardInschrijving, UpdateSourceTrigger=PropertyChanged}" SelectedValue="{Binding SelectedAardInschrijving, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Description" SelectedValuePath="FlexKey"/>
                    </Grid>
                </Grid>
            </Grid>
        </Grid>
    </Grid>
</UserControl>

ViewModel :

using FlexilineDotNet.SharedDomainLogic.Models.CommunicationModels;
using FlexilineDotNetGui.Domain.Controls;
using FlexilineDotNetGui.Domain.Models;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;

namespace FlexilineDotNetGui.Flexiline.ViewModels
{
    public class RealestateViewModel : ANavigationPaneSubViewModel, INotifyPropertyChanged
    {
        BorgRealestate _borgRealestate = new BorgRealestate();
        ObservableCollection<BorgRealestate> ocPanden = new ObservableCollection<BorgRealestate>();
        private DomainController _controller = DomainController.GetInstance();

        public RelayCommand<object> AddPandCMD { get; private set; }
        public RelayCommand<object> DeletePandCMD { get; private set; }

        #region "properties"

        public ObservableCollection<BorgRealestate> Panden
        {
            get
            {           
                return ocPanden ?? (ocPanden = new ObservableCollection<BorgRealestate>());
            }
            set
            {
                if (ocPanden == value)
                    return;

                ocPanden = value;
                OnPropertyChanged("Panden");
            }
        }

        //public ObservableCollection<BorgRealestate> Panden { get; }


        public BorgRealestate Pand
        {
            get
            {
                return _borgRealestate ?? (_borgRealestate = new BorgRealestate());
            }
            set
            {
                if (value == _borgRealestate) return;
                _borgRealestate = value;
                OnPropertyChanged();
                OnPropertyChanged("Panden");
            }
        }


        public List<DropDownItem> AardInschrijving { get { return _controller.GetDropDownItemsByType("AardInschrijving"); } }

        private DropDownItem SelectedAardInschrijvingDI
        {
            get
            {
                DropDownItem test = _controller.GetDropDownItemsByType("AardInschrijving").FirstOrDefault(x => x.FlexKey == _borgRealestate?.RegistrationType);
                if (test == null)
                {
                    test = _controller.GetDropDownItemsByType("AardInschrijving").FirstOrDefault();
                }
                return test;
            }
            set
            {
                OnPropertyChanged();
                OnPropertyChanged("Panden");
            }
        }

        public int SelectedAardInschrijving
        {
            get
            {
                return SelectedAardInschrijvingDI.FlexKey;
            }
            set
            {
                if (value == Pand?.RegistrationType) return;
                Pand.RegistrationType = value;
                OnPropertyChanged();
                OnPropertyChanged("Panden");
            }
        }

        public string SelectedAardInschrijvingText
        {
            get
            {
                return SelectedAardInschrijvingDI.Description;
            }
        }


        #endregion

        #region "ctor"

        public RealestateViewModel()
        {
            //Panden = new ObservableCollection<BorgRealestate>();
            AddPandCMD = new RelayCommand<object>(o => AddPand());
            DeletePandCMD = new RelayCommand<object>(o => DeletePand());
        }

        #endregion

        #region "methods"

        private void AddPand()
        {
            BorgRealestate newBorgRealestate = new BorgRealestate { RegistrationType = SelectedAardInschrijving };
            Panden.Add(newBorgRealestate);      
        }

        private void DeletePand()
        {
            Panden.Remove(Pand);
        }

        #endregion
    }
}  

Problem description:

When I Update the combobox value, it's updated in the "Panden" property when I check with a breakpoint but the datagrid in the view is not updated. The problem exists with Oneway and also with TwoWay as I defined in datagrid columns. I have both modes tried.

The views are inside a dxnavbar control, when I switch between navbar items and back then the view is updated OK.

EDIT:

When the application starts, the Panden list is indeed null, I had forgotten to mention something....

In my view there is a datagrid with a button where I add items to the observable collection where also some of the properies as example nature inscription combobox value is shown in datagrid. This button is then connected to a relay command. That is the collection that i have binded to the datagridview.

the getter of the property property is not empty and is therefore filled with the correct values, only the changes do not change in the view.

it is indeed normal that the collection is null if no "Pand" items are added to the "Panden" collection via the provided button on the UI (relaycommand). But the "Pand" item in de datagrid will not update after i change a value from the combobox after adding an item with the button to the collection.

EDIT 21/11 08:54

This is the logic for adding the pand:

private void AddPand()
        {
            BorgRealestate newBorgRealestate = new BorgRealestate { RegistrationType = SelectedAardInschrijving };
            Panden.Add(newBorgRealestate);      
        }

But after i add i see the items gets added to the collection and get also updated in the collection after the combobox value changed only not in ui. (when the row in the datagrid have the focus)

In this code here:

public ObservableCollection<BorgRealestate> Panden
{
    get
    {
        return ocPanden ?? (ocPanden = new ObservableCollection<BorgRealestate>());
    }

You create a new collection but you don't at that point call OnPropertyChanged; and you shouldn't because it would be wrong to do this here. Therefore, the UI doesn't know you've created the new collection. It looks like the first time the above getter is called is in your AddPand function. Therefore, the UI never receives an OnPropertyChanged for Panden and so does not update.

Instead of this create the collection in your constructor and you should find it will update. Also you may not need a setter at all for Panden if it is never recreated.

in constructor:

Panden = new ObservableCollection<BorgRealestate>();

Then Panden property becomes:

public ObservableCollection<BorgRealestate> Panden { get; }

Which can be simplified to:

public ObservableCollection<BorgRealestate> Panden { get; } = new ObservableCollection<BorgRealestate>();

In the Viewmodel:

That short piece of code did the trick:

CollectionViewSource.GetDefaultView(ocPanden).Refresh();

Replace this:

 public ObservableCollection<BorgRealestate> Panden
        {
            get
            {           
                return ocPanden ?? (ocPanden = new ObservableCollection<BorgRealestate>());
            }
            set
            {
                if (ocPanden == value)
                    return;

                ocPanden = value;
                OnPropertyChanged("Panden");
            }
        }

With this:

public ObservableCollection<BorgRealestate> Panden
        {
            get
            {
                if(ocPanden != null)
                {
                    CollectionViewSource.GetDefaultView(ocPanden).Refresh(); //This will do the trick
                }
                return ocPanden ?? (ocPanden = new ObservableCollection<BorgRealestate>());
            }
            set
            {
                if (ocPanden == value)
                    return;

                ocPanden = value;
                OnPropertyChanged("Panden");
            }
        }

Unfortunately I have not yet found out where the cause really comes from?

If someone can know that then leave a message?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM