更新 ListView 的 ObservableCollection 中一项的显示

Too*_*eve 1 xamarin.forms

我有一个绑定到 ObservableCollection 的 ListView。

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

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

Alm*_*Vuk 8

正如我所看到的,您正在尝试使用 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>
Run Code Online (Sandbox Code Playgroud)

此演示的基本思想是更改属性的值Seen并设置CheckBox当用户单击ListView.

这是我的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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

到节目的最后一件事是我ViewModel这个页面,内ViewModel,我会的值更改Seen属性True集合中的物品时,在用户点击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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

这个演示的最终结果可以在这个 gif 波纹管上看到。

在此处输入图片说明

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

希望这对您有所帮助,祝您在编码方面好运!


Too*_*eve 7

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

细节:

在 ViewModel 中,给定属性:

public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>();
Run Code Online (Sandbox Code Playgroud)

Item你的模型类在哪里。
添加一些项目(未显示)后,假设您想让项目“item”自行刷新:

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

注意:上面的代码假设“item”已知位于“Items”中。如果未知,请在执行替换之前测试IndexOf返回值。>= 0

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

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

技术说明:
此项目替换可能会触发Replace NotifyCollectionChangedEvent,newItemsoldItems两者仅包含item