【问题标题】:Update display of one item in a ListView's ObservableCollection更新 ListView 的 ObservableCollection 中一项的显示
【发布时间】:2019-12-24 22:46:55
【问题描述】:

我有一个绑定到 ObservableCollection 的 ListView。

有没有办法在 SomeModel 项的属性更改时更新单个单元格,而无需通过更改 ObservableCollection 重新加载 ListView?

(问题是从https://forums.xamarin.com/discussion/40084/update-item-properties-in-a-listviews-observablecollection复制的,我的回答也是。)

【问题讨论】:

    标签: xamarin.forms


    【解决方案1】:

    正如我所见,您正在尝试使用 MVVM 作为 Xamarin.Forms 应用程序的模式。您已经在使用ObservableCollection 来显示数据列表。当从集合中添加或删除新项目时,UI 将相应刷新,这是因为 ObserverbleCollection 正在实现 INotifyCollectionChanged

    您想要通过这个问题实现的是下一个行为,当您想要更改集合中项目的特定值并更新 UI 时,实现这一目标的最佳和最简单的方法是为模型实现 INotifyPropertyChanged您收藏中的项目。

    下面,我有一个简单的演示示例说明如何实现这一点,您的答案正如我所见,但我相信这个示例更适合您使用。

    我有简单的Button 和命令,ListView 保存我的收集数据。

    这是我的页面,SimpleMvvmExamplePage.xaml:

    <StackLayout>
    
        <Button Text="Set status" 
                Command="{Binding SetStatusCommand}" 
                Margin="6"/>
    
        <ListView ItemsSource="{Binding Cars}"
                  HasUnevenRows="True">
    
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
    
                        <StackLayout Orientation="Vertical" 
                                     Margin="8">
    
                            <Label Text="{Binding Name}" 
                                   FontAttributes="Bold" />
    
                            <StackLayout Orientation="Horizontal">
    
                                <Label Text="Seen?" 
                                       VerticalOptions="Center"/>
    
                                <CheckBox IsChecked="{Binding Seen}"
                                          Margin="8,0,0,0" 
                                          VerticalOptions="Center" 
                                          IsEnabled="False" />
                            </StackLayout>
    
                         </StackLayout>
    
                    </ViewCell>
                </DataTemplate>
              </ListView.ItemTemplate>
         </ListView>
    
     </StackLayout>
    

    此演示的基本思想是更改属性Seen 的值,并在用户单击ListView 上方的按钮时为CheckBox 设置值。

    这是我的Car.cs 课程。

    public class Car : INotifyPropertyChanged
    {
        private string name;
        public string Name
        {
            get { return name; }
            set 
            { 
                name = value;
                OnPropertyChanged();
            }
        }
    
        private bool seen;
        public bool Seen
        {
            get { return seen; }
            set 
            { 
                seen = value;
                OnPropertyChanged();
            }
        }
    
        // Make base class for this logic, something like BindableBase
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    在我的 Github 上的完整演示示例中,我正在使用我的 BindableBase 类,当在 props 的 setter 中使用此 SetProperty 方法更改某些属性值时,我会处理提升 INotifyPropertyChanged

    你可以找到实现here:https://github.com/almirvuk/Theatrum/tree/master/Theatrum.Mobile/Theatrum.Mobile

    最后要显示的是这个页面的ViewModel,在ViewModel 内部,当用户点击Button 高于 ListView。这是我的SimpleMvvmExamplePageViewModel.cs

    public class SimpleMvvmExamplePageViewModel
    {
        public ObservableCollection<Car> Cars { get; set; }
    
        public ICommand SetStatusCommand { get; private set; }
    
        public SimpleMvvmExamplePageViewModel()
        {
            // Set simple dummy data for our ObservableCollection of Cars
            Cars = new ObservableCollection<Car>()
            {
                new Car()
                {
                    Name = "Audi R8",
                    Seen = false
                },
    
                new Car()
                {
                    Name = "BMW M5",
                    Seen = false
                },
    
                new Car()
                {
                    Name = "Ferrari 430 Scuderia",
                    Seen = false
                },
    
                new Car()
                {
                    Name = "Lamborghini Veneno",
                    Seen = false
                },
    
                new Car()
                {
                    Name = "Mercedes-AMG GT R",
                    Seen = false
                }
            };
    
            SetStatusCommand = new Command(SetStatus);
        }
    
        private void SetStatus()
        {
            Car selectedCar = Cars.Where(c => c.Seen == false)
                .FirstOrDefault();
    
            if (selectedCar != null)
            {
                // Change the value and update UI automatically
                selectedCar.Seen = true;
            }
        }
    }
    

    这段代码将帮助我们实现这种行为:当用户点击Button时,我们将从集合中更改项目属性的值,并且UI将被刷新,复选框值将被选中。

    这个演示的最终结果可以在下面这个 gif 中看到。

    附注我可以将它与来自ListViewItemTapped 事件结合起来,但我想让它变得非常简单,所以这个例子是这样的。

    希望这对您有所帮助,祝您编码顺利!

    【讨论】:

      【解决方案2】:

      如果在 Observable Collection 中用自身替换项目,则与模型项目关联的任何 UI 都将被刷新。

      详情:

      在 ViewModel 中,给定属性:

      public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>();
      

      Item 是您的模型类。
      添加一些项目(未显示)后,假设您要使项目“项目”自行刷新:

      public void RefreshMe(Item item)
      {
          // Replace the item with itself.
          Items[Items.IndexOf(item)] = item;
      }
      

      注意:上面的代码假定“item”已知在“Items”中。如果不知道,请在执行替换之前测试 IndexOf 是否返回 &gt;= 0

      在我的例子中,我在集合中有一个DataTemplateSelector,并且该项目以需要不同模板的方式进行了更改。 (具体来说,通过 TemplateSelector 从模型项目中读取 IsExpanded 属性,单击项目可在折叠视图和展开/详细视图之间切换。)

      注意:使用 CollectionView 进行测试,但 AFAIK 也适用于较旧的 ListView 类。
      在 iOS 和 Android 上测试。

      技术说明:
      项目的这种替换可能会触发Replace NotifyCollectionChangedEventnewItemsoldItems 都只包含item

      【讨论】:

      • 几天后添加了更好的答案;我接受了这个答案。
      • 对于这个问题,它不是最佳答案,但如果需要,这种方法也会更新项目模板。
      猜你喜欢
      • 2011-07-02
      • 1970-01-01
      • 2019-05-02
      • 1970-01-01
      • 1970-01-01
      • 2014-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多