更新 ObservableCollection 无法正确更新 Xamarin 表单中的 ListView

Posted

技术标签:

【中文标题】更新 ObservableCollection 无法正确更新 Xamarin 表单中的 ListView【英文标题】:Updating ObservableCollection does not properly update ListView in Xamarin Forms 【发布时间】:2018-08-14 16:21:29 【问题描述】:

我在 XAML 中有一个 ListView,它绑定到 ViewModel 中的 ObservableCollection。在初始化或 OnAppearing() 时,ListView 项目会完美显示。

但是,当我尝试从页面内(通过 ViewModel)更新 ListView 项目时,项目已更新,但旧项目仍然存在。

基本上,新项目被添加到 ListView 但低于之前 ObservableCollection 中的项目。我已经实现了 INotifyPropertyChanged,并且我认为我所做的一切都是正确的(尽管显然不是)。

请告诉我我做错了什么。我在 Collection 上尝试过 Clear() 但无济于事(结果相同)。

BaseViewModel:

public class BaseViewModel : INotifyPropertyChanged
    
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        

        protected void SetValue<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null)
        
            if (EqualityComparer<T>.Default.Equals(backingField, value))
                return;

            backingField = value;

            OnPropertyChanged(propertyName);
        

XAML:

<ListView       IsEnabled="Binding IsLoadingTable, Converter=Helpers:InverseBoolConverter"
                        IsVisible="Binding IsLoadingTable, Converter=Helpers:InverseBoolConverter"
                        ItemsSource="Binding LeagueStandings"
                        SelectedItem="Binding SelectedTeam, Mode=TwoWay"
                        ItemSelected="ListView_ItemSelected"
                        RowHeight="60"
                        SeparatorVisibility="Default"
                        SeparatorColor="DynamicResource accentColor">

Page.cs:

protected override void OnAppearing()
        
            base.OnAppearing();

            ViewModel.LoadLeagueStandingsCommand.Execute(_divisionId);
            ViewModel.LoadPickerItemsCommand.Execute(null);
        

ViewModel 初始化:

private ObservableCollection<Standing> _leagueStandings;
        public ObservableCollection<Standing> LeagueStandings
        
            get  return _leagueStandings ?? (_leagueStandings = new ObservableCollection<Standing>()); 
            set  SetValue(ref _leagueStandings, value); 
        

ViewModel 方法:

private async Task LoadLeagueStandings(string divId)
        

            if (_hasLoadedStandings)
                return;

            if (IsLoadingTable)
                return;

            _hasLoadedStandings = true;

            _divisionId = divId;

            try
            
                IsLoadingTable = true;
                await _pageService.DisplayAlert("loading Selected", _divisionId, "ok");
                var v = await GetLeagueTableAsync(_htmlParser, _divisionId);

                LeagueStandings = new ObservableCollection<Standing>(v);

            
            catch (Exception)
            
                System.Diagnostics.Debug.WriteLine("Exception caught in DivisionsViewModel.cs.(LoadLeagueStandings).");
            
            finally
            
                IsLoadingTable = false;
            

        

Picker 项目更改时调用的 ViewModel 方法:

private async Task SelectItem(string item)
        
            if (item == null)
                return;

            SelectedItem = null;

            var id = await _divisionFinder.GetDivisionIdAsync(item);

            var v = await GetLeagueTableAsync(_htmlParser, id);

            LeagueStandings = new ObservableCollection<Standing>(v);
        

编辑* - 结果图片。第一个集合在数字 5 处结束,新集合再次从 1 开始追加到列表视图的末尾。

ListView Image

编辑 2:

public async Task<IEnumerable<Standing>> GetLeagueTableAsync(string divisionId)
    
        // todo: get division Id from picker

        string address = "";
        if (IsOnline)
        
            if (divisionId != "")
                address = $"BaseUrls.LeagueStandingsdivisionId";

            try
            
                var config = Configuration.Default.WithDefaultLoader();
                var document = await BrowsingContext.New(config).OpenAsync(address);
                var cSelector = "table[class='table table-striped table-hover table-bordered'] tr";

                var table = document.QuerySelectorAll(cSelector).Skip(1);

                int count = 0;
                foreach (var c in table)
                
                    var cells = c.QuerySelectorAll("td").ToArray();

                    _leagueStandings.Add(new Standing(++count, cells[0].TextContent.Trim(), cells[1].TextContent.Trim(),
                                                               cells[2].TextContent.Trim(), cells[3].TextContent.Trim(),
                                                               cells[4].TextContent.Trim(), cells[5].TextContent.Trim(),
                                                               cells[6].TextContent.Trim(), cells[7].TextContent.Trim()));
                
            
            catch(Exception e)
            
                System.Diagnostics.Debug.WriteLine($"\n\n Exception caught LoadingLeagueTable - e \n\n");
            
        
        return _leagueStandings;

【问题讨论】:

【参考方案1】:

由于您既没有添加也没有删除项目,并且您正在替换引用,您需要引发事件来告知视图已更改。而不是你的代码,用这个替换它

    private ObservableCollection<Standing> _leagueStandings;
    public ObservableCollection<Standing> LeagueStandings
    
        get  return _leagueStandings; 
        set  
              _leagueStandings = value;
              RaisePropertyChanged("LeagueStandings"); 
            
    

为了将来的参考,ObservableCollection 已经实现了INotifyPropertyChanged,所以你不需要SetValue(x)..

【讨论】:

我已经完成了上述操作,但仍然得到相同的结果。 private ObservableCollection&lt;Standing&gt; _leagueStandings; public ObservableCollection&lt;Standing&gt; LeagueStandings get return _leagueStandings; set _leagueStandings = value; OnPropertyChanged("LeagueStandings"); 我将编辑帖子以显示结果 你能不能尝试在ObservableCollection&lt;Standing&gt; temp = new ObservableCollection&lt;Standing&gt;(v); LeagueStandings = temp;中破解它 好的,我刚刚完成了这个。在断点处,LeagueStandings 的计数是 8(正确),临时计数是 13(应该是 5)所以看起来问题可能不在于 observablecollection 而是我的异步调用? Temp 的计数应该是 5,但似乎是 8(先前计数)+ 5(新计数)。有什么想法吗?? GetLeagueTableAsync 可能和这条线有关.. 不知道你在里面做什么 我已将该方法添加到我的帖子中。如果你有第二个,请帮帮我!感谢您的努力!

以上是关于更新 ObservableCollection 无法正确更新 Xamarin 表单中的 ListView的主要内容,如果未能解决你的问题,请参考以下文章

如何通过工作线程更新 ObservableCollection?

从另一个线程更新 ObservableCollection

如何根据 WPF DataGrid 中的更改更新 ObservableCollection 项属性?

ObservableCollection 不更新 ListView (WPF) [重复]

当 ObservableCollection 更改时 ItemsControl 不更新

Listview 不会根据 ObservableCollection 属性进行更新