当 ObservableCollection 对象更改时,ListView 不会更新

ListView is not updated when ObservableCollection object changes

为了自动更新 Xamarin.Forms ListView 的 ItemsSource 属性,我尝试在代码隐藏文件中使用 ObservableCollection 对象。然后我将该 ObservableCollection 对象分配给 ListView.ItemsSource 属性 。但是我只是在代码隐藏文件的构造函数中只做了一个赋值。

带有计时器的示例 1,更新 ObservableCollection 对象会自动更改 Xaml 文件中的视图。 示例 1 工作 正如我对 ObservableCollection 的预期。

示例 2 在按下名为 "Set 2" 的按钮时不起作用。 xaml 文件中的视图不会随着后面代码中该按钮更新的新数据事件处理程序而改变。 请解释为什么即使我使用与示例 1 中相同的 ObservableCollection 概念,示例 2 也不起作用。

以下是示例 1 的代码,符合我的预期

using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;

namespace ObservableLogger
{
    public partial class ObservableLoggerPage : ContentPage
    {
        public ObservableLoggerPage()
        {
            InitializeComponent();

            ObservableCollection<DateTime> list = new ObservableCollection<DateTime>();  // ObservableCollection<DateTime> object is used
            listView.ItemsSource = list; // ********** bind

            Device.StartTimer(TimeSpan.FromSeconds(1), () =>
            {
                list.Add(DateTime.Now); // *** changing the ObservableCollection<DateTime> object automatically makes ListView's ItemsSource is rebound and ListView's UI changed.
                return true;
            });
        }
    }
}

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ObservableLogger.ObservableLoggerPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="10, 20, 10, 0"
                    Android="10, 0"
                    WinPhone="10, 0" />
    </ContentPage.Padding>

    <ListView x:Name="listView" />
</ContentPage>

以下是示例 2 的代码,无法正常工作,如我所料。当名为 "Set 2" 的按钮被点击时,它的事件处理程序会更改 ObservableCollection 人(一个私有字段),但 ListView 不会在 UI.

中更新
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace Mvvm1
{
    public partial class Mvvm1Page : ContentPage
    {
        private ObservableCollection<Person> _people; // private field

        public Mvvm1Page()
        {
            InitializeComponent();

            _people = getPeople(SetOption.Set1);

            listViewPeople.ItemsSource = _people; // Only one time of assigning
        }

        public void ButtonSet1OnClicked(object sender, EventArgs e)
        {
            _people = getPeople(SetOption.Set1); // *** do change the ObservableCollection<Person> but ListView is not updated in UI.
        }

        public void ButtonSet2OnClicked(object sender, EventArgs e)
        {
            _people = getPeople(SetOption.Set2); // *** do change the ObservableCollection<Person> but ListView is not updated in UI.
        }

        private ObservableCollection<Person> getPeople(SetOption op)
        {
            var list = new ObservableCollection<Person>();

            var p1 = new Person { Id = 1, Age = 19, FirstName = "Anna", LastName = "Larson" };
            var p2 = new Person { Id = 2, Age = 23, FirstName = "Beri", LastName = "Slovik" };
            var p3 = new Person { Id = 3, Age = 65, FirstName = "Ron", LastName = "Prelosi" };
            var p4 = new Person { Id = 4, Age = 32, FirstName = "William", LastName = "Maxel" };
            var p5 = new Person { Id = 5, Age = 71, FirstName = "Fred", LastName = "Lipez" };
            var p6 = new Person { Id = 6, Age = 44, FirstName = "Dave", LastName = "Vanoviz" };

            switch (op)
            {
                case SetOption.Set1:
                    list.Add(p1);
                    list.Add(p2);
                    list.Add(p3);
                    list.Add(p4);
                    break;
                case SetOption.Set2:
                    list.Add(p5);
                    list.Add(p6);
                    break;
            }

            return list;
        }
    }
}

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Mvvm1.Mvvm1Page">
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness" iOS="0,20,0,0" />
  </ContentPage.Padding>


  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"></ColumnDefinition>
      <ColumnDefinition Width="0"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"></RowDefinition>
      <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>

    <StackLayout VerticalOptions="StartAndExpand" Orientation="Horizontal" Grid.Row="0" Grid.Column="0">
      <Button Text="Set 1" Clicked="ButtonSet1OnClicked"></Button>
      <Button Text="Set 2" Clicked="ButtonSet2OnClicked"></Button>
    </StackLayout>

    <ListView x:Name="listViewPeople" HasUnevenRows="True" Grid.Column="0" Grid.Row="1" Header="People">

      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ContentView Padding="5">
              <Frame OutlineColor="Accent" Padding="10">
                <StackLayout Orientation="Horizontal">
                  <StackLayout>
                    <Label Text="{Binding Id}"></Label>
                    <Label Text="{Binding FirstName}"></Label>
                    <Label Text="{Binding LastName}"></Label>
                    <Label Text="{Binding Age}"></Label>
                  </StackLayout>
                </StackLayout>
              </Frame>
            </ContentView>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </Grid>
</ContentPage>

using System.Text;
using System.Threading.Tasks;

namespace Mvvm1
{
    public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public int Age { get; set; }
    }

    public enum SetOption
    {
        Set1 = 1,
        Set2 = 2
    }
}

通话中

_people = getPeople(SetOption.Set1);

在 Button Click 处理程序中 不会 修改现有集合实例,而是创建一个新集合实例,因此 不会 更改listViewPeople.ItemsSource.

中的项目

每次创建新集合时,您都必须设置 ItemsSource 属性。那么你根本不需要本地 _people 变量:

public Mvvm1Page()
{
    InitializeComponent();
    listViewPeople.ItemsSource = getPeople(SetOption.Set1);
}

public void ButtonSet1OnClicked(object sender, EventArgs e)
{
    listViewPeople.ItemsSource = getPeople(SetOption.Set1);
}

public void ButtonSet2OnClicked(object sender, EventArgs e)
{
    listViewPeople.ItemsSource = getPeople(SetOption.Set2);
}

或者,您可以分配 ItemsSource 一次,然后在 existing 集合中添加和删除项目 to/from,例如_people.Add(new Person { ... });


除此之外,listView.ItemsSource = list;不是绑定,只是一个普通的赋值。