【问题标题】:ListView is not updated when ObservableCollection object changes当 ObservableCollection 对象改变时 ListView 不更新
【发布时间】:2016-08-24 18:42:00
【问题描述】:

为了自动更新 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 人员(私有字段),但 UI 中不会更新 ListView。

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

【问题讨论】:

    标签: wpf xaml listview xamarin.forms xamarin-studio


    【解决方案1】:

    打电话

    _people = getPeople(SetOption.Set1);
    

    在按钮单击处理程序中不修改现有集合实例,而是创建一个新集合实例,因此不更改listViewPeople.ItemsSource 中的项目。 p>

    您必须在每次创建新集合时设置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 一次,然后在 现有 集合中添加和删除项目,例如_people.Add(new Person { ... });


    除此之外,listView.ItemsSource = list; 不是一个 Binding,而只是一个常规的赋值。

    【讨论】:

    • 感谢您的回复。在示例 1 中,它之所以有效,是因为 ObservableCollection 列表对象不是一个新对象,而是一个现有对象(已连接到 listView.ItemsSource),并且它在 Device.StarTimer 的回调中使用新的 DateTime 条目进行了扩展(),这是真的吗?
    • 对于示例 2 的另一件事,如果我不想在后面的代码中将对象集合显式分配给 listView.ItemsSource,那么我可以使用 ViewModel(其一些公共属性字段作为集合对象)在后端,然后如何在 Xaml 文件中将 ViewModel 绑定到 listView 的 ItemsSource?
    • 对于您的第一条评论:是的,这是真的(并且已经在答案中解释过)。对于第二个:您将拥有一个具有 Items 属性(ObservableCollection 类型)的视图模型类,该类实现 INotifyPropertyChanged 接口并在 Items 属性更改时触发 PropertyChanged,即当它设置为新集合实例时。然后将 ListView 的 ItemsSource 属性绑定到此视图模型属性。
    猜你喜欢
    • 1970-01-01
    • 2016-04-22
    • 2021-11-26
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 2019-10-25
    相关资源
    最近更新 更多