简体   繁体   中英

DataGrid not updating when ItemsSource changes

I have a class AccountManager that is implemented as a Singleton class :

namespace SampleApp.Manager
{
    public class AccountManager : INotifyCollectionChanged, INotifyPropertyChanged
    {
        public static AccountManager Instance { get; } = new AccountManager();

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public event PropertyChangedEventHandler PropertyChanged;

        private List<Account> InternalAccounts { get; } = new List<Account>();

        public IReadOnlyCollection<Account> Accounts => InternalAccounts;

        private AccountManager()
        {

        }

        public void Add(Account account)
        {
            InternalAccounts.Add(account);

            CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, account));
            OnPropertyChanged(nameof(Accounts));
        }

        public void Remove(Account account)
        {
            if (InternalAccounts.Remove(account))
            {
                CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, account));
                OnPropertyChanged(nameof(Accounts));
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Then I have Page where I want to work with these Account instances:

<Page x:Class="SampleApp.View.AccountView"
      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"
      xmlns:mgr="clr-namespace:SampleApp.Manager"
      mc:Ignorable="d" 
      d:DesignWidth="1200" d:DesignHeight="800"
      Title="AccountView">

    <Grid>

        <TextBlock Text="{Binding Source={x:Static mgr:AccountManager.Instance}, Path=Accounts.Count}" />

        <DataGrid ItemsSource="{Binding Source={x:Static mgr:AccountManager.Instance}, Path=Accounts}">

            <DataGrid.Columns>
                <DataGridTextColumn Header="Property1" Binding="{Binding Property1, Mode=TwoWay}" Width="*" />
                <DataGridTextColumn Header="Property2" Binding="{Binding Property2, Mode=TwoWay}" Width="*" />
                <DataGridTextColumn Header="Property3" Binding="{Binding Property3, Mode=TwoWay}" Width="*" />
                <DataGridTextColumn Header="Property4" Binding="{Binding Property4, Mode=TwoWay}" Width="*" />
            </DataGrid.Columns>

        </DataGrid>

    </Grid>
</Page>

I first tried to only trigger AccountManager.CollectionChanged to update the DataGrid in the view but that did nothing. So next I implemented INotifyPropertyChanged and triggered AccountManager.PropertyChanged but that did not update the list but only the TextBlock containing the current number of items in AccountManager.Accounts .

This is just a stripped down version of what the classes actually look like so that's the reason why I don't use ObservableCollection<T> to do this, so please don't blame me for not using ObservableCollection<T> .

I would like to know what's wrong with my code? I don't understand why the DataGrid binding isn't getting updated but the TextBlock gets updated.

InternalAccounts is a List , not an ObservableCollection . You never tell anybody that your list changed. Implementing INotifyCollectionChanged on the viewmodel that owns it doesn't help the List raise any events: When the List changes, you do raise events saying that your viewmodel's own items changed, but it doesn't actually have any items -- its items are in the list, which is what the XAML is bound to.

If you make InternalAccounts an ObservableCollection and make Accounts ReadOnlyObservableCollection , that should fix it.

private OnlyObservableCollection<Account> _accounts = 
    new OnlyObservableCollection<Account>();

private ReadOnlyObservableCollection<Account> _roAccounts;
public ReadOnlyObservableCollection<Account> Accounts
{
    get { 
        if (_roAccounts == null)
            _roAccounts = new ReadOnlyObservableCollection<Account>(_accounts);
        return _roAccounts;
    }
}

Internally, just add and remove stuff from _accounts and don't bother implementing INotifyCollectionChanged on your viewmodel. You just got a full implementation for free, out of the box. This is much easier to do than you thought, which is always nice.

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