ListView ItemClick with MVVM

本文关键字:MVVM with ItemClick ListView | 更新日期: 2023-09-27 18:15:23

我是MVVM的新手,有些东西我不明白。到目前为止,我得到的代码是:

在ItemListPage

。xaml:

<ListView ItemsSource="{Binding itemList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          SelectedItem="{Binding itemSelected, Mode=TwoWay}"
          ItemClick="{x:Bind ViewModel.ClickItemList}">

In ItemListViewModel.cs:

    private List<Item> _itemList;
    public List<Item> itemList { get { return _itemList; } set { Set(ref _itemList, value); } }
    private Item _itemSelected;
    public Item itemSelected { get { return _itemSelected; } set { Set(ref _itemSelected, value); } }
    public void ClickItemList()
    {
        System.Diagnostics.Debug.WriteLine("Click on itemlist");
        if (itemSelected != null)
        {
            ((App)App.Current)._itemID = itemSelected._ID;
            NavigationService.Navigate(typeof(Views.ShowItem));
        }
    }

我遇到的问题是我第一次点击一个项目,函数ClickItemList被调用,但属性itemSelected仍然是空的,所以我需要在一个项目上点击第二次来实际做我想做的动作(保存这个项目的ID并导航到下一页)。我怎样才能改变我的代码呢?

除了这个问题,我不明白为什么我应该使用2个属性(例如私有属性_itemSelected和公共"假"属性itemSelected),为什么我不能只使用一个?

ListView ItemClick with MVVM

我遇到的问题是我第一次点击一个项目,函数ClickItemList被调用,但属性itemSelected仍然是空的,所以我需要在一个项目上点击第二次来实际做我想做的动作(保存这个项目的ID并导航到下一页)。我怎样才能改变我的代码呢?

这是因为ListView将在引发ItemClick事件后更新SelectedItem 。如果想知道单击了哪个项,则需要访问事件参数:

public void ClickItemList(object sender, ItemClickEventArgs e)
{
    var clickedItem = (Item)e.ClickedItem;
}

除了这个问题,我不明白为什么我应该使用2个属性(例如私有属性_itemSelected和公共"假"属性itemSelected),为什么我不能只使用一个?

澄清一些术语:

  • _itemList私有字段
  • itemList公共属性*.

属性只不过是一个getter和setter函数。字段实际上将对象存储在类的实例中。出于这个原因,公共属性通常会有一个私有的支持字段,它可以委托访问该字段。

itemList必须是一个公共属性才能绑定到它(通过{Binding})。如果您希望能够在运行时将itemList更改为一个全新的列表,并让ListView自动更新其项目,那么您的视图模型必须实现INotifyPropertyChanged并在itemList更改时引发PropertyChanged事件。这就是在属性的setter中发生的事情(感谢您拥有的辅助Set方法)。在这种情况下,您将需要一个私有字段来存储实际的列表,并使用公共属性来确保在您更改itemList时引发PropertyChanged。

双向绑定ItemsSource是没有意义的(就像你所做的那样),因为ListView永远不会改变ItemsSource。事实上,如果您根本不更改itemList,那么您可以像这样更改视图模型**:

// Generates a hidden private field automatically, and is used as if it were a public field
public List<Item> itemList { get; set; }

和XAML:

<ListView ItemsSource="{x:Bind ViewModel.itemList}" ItemClick="{x:Bind ViewModel.ClickItemList}">

*我建议遵循微软的惯例,以大写字母开头命名公共属性,比如用ItemList代替itemList

** x:Bind也支持绑定到公共字段,而不仅仅是属性,但我建议总是在类的公共接口中使用属性,但这取决于您。

感谢您的回答,这有助于我更好地理解问题和MVVM模式。

我在这里发现的简单解决方案是将触发事件ItemClick更改为SelectionChanged在我的。xaml中,因为ItemClick事件在ListView更新SelectedItem之前引发,但SelectionChanged事件在之后引发。

现在它的工作如预期的:一键点击我的listbox中的一个项目=>更新SelectedItem绑定属性=> SelectionChanged事件被触发=>我的函数ClickItemList被调用,并在保存选定项目的ID后转到下一页

有时私有属性不是必需的,它们的大多数用途是:

  • 当你需要在"返回"或赋值之前做一些处理时

  • 当你需要或想要缓存或延迟加载属性

  • 当属性不供公共访问(冗余)

所以回答你的问题,如果你的公共属性只是封装它,就不需要私有字段。例如:

private string _property;
public string Property {
    get { return _property; }
    set { _property = value; }
}

可以替换为:

public string Property { get; set; }

其他两个答案都是正确的,但具体来说,第一次点击什么也没有发生的原因是没有更改选择。
为了解决这个问题,当您打开页面时(例如在构造函数中),您可以将SelectedItem设置为itemList的第一项,然后您的第一项应该可以工作。