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),为什么我不能只使用一个?
我遇到的问题是我第一次点击一个项目,函数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
的第一项,然后您的第一项应该可以工作。