当 ItemsSource 更改时,数据网格不会更新

本文关键字:网格 数据网 更新 数据 ItemsSource | 更新日期: 2023-09-27 17:59:11

我有一个类AccountManager作为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));
        }
    }
}

然后,我Page我想在何处使用这些Account实例:

<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>

我首先尝试仅触发AccountManager.CollectionChanged来更新视图中的DataGrid,但这没有任何作用。所以接下来我实现了INotifyPropertyChanged并触发了AccountManager.PropertyChanged但那没有更新列表,而只更新了包含当前AccountManager.Accounts项数的TextBlock

这只是类实际外观的精简版本,所以这就是我不使用 ObservableCollection<T> 来执行此操作的原因,所以请不要责怪我没有使用 ObservableCollection<T> .

我想知道我的代码出了什么问题?我不明白为什么DataGrid绑定没有更新,但TextBlock更新了。

当 ItemsSource 更改时,数据网格不会更新

InternalAccounts是一个List,而不是一个ObservableCollection。你从不告诉任何人你的名单改变了。在拥有它的视图模型上实现INotifyCollectionChanged无助于List引发任何事件:当列表更改时,你确实会引发事件,指出视图模型自己的项已更改,但它实际上没有任何项 - 其项位于列表中,这是 XAML 绑定到的内容。

如果你把InternalAccounts做一个ObservableCollection并做Accounts ReadOnlyObservableCollection,那应该可以解决它。

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;
    }
}

内部,只需在_accounts中添加和删除内容,而不必费心在视图模型上实现INotifyCollectionChanged。您刚刚免费获得了开箱即用的完整实现。这比你想象的要容易得多,这总是很好的。