使用MVVM的ListView ItemClick

时间:2016-10-28 22:30:40

标签: c# wpf mvvm uwp

我是MVVM的新手,我不明白。我到目前为止的代码:

在ItemListPage.xaml中:

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

在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和public&#34; fake&#34;属性itemSelected),为什么我不能只使用一个?

4 个答案:

答案 0 :(得分:2)

  

我遇到的问题是我第一次点击某个项目时,调用了函数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方法)。在这种情况下,您需要有一个私有字段来存储实际列表,并使用public属性确保在您更改itemList时引发PropertyChanged。

将ListSource双向绑定是没有意义的(因为你有),因为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}">

*我建议通过命名以大写字母开头的公共属性来坚持Microsoft的惯例,例如ItemList而不是itemList

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

答案 1 :(得分:0)

有时私人财产不是必需的,他们的大部分用途是:

  • 在“返回”或分配值之前需要进行一些处理。

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

  • 当属性不是公共访问权限(冗余)时

因此,要回答您的问题,如果您的公共财产只是封装它,则不需要私有字段。举个例子。

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

可以替换为:

public string Property { get; set; }

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

现在它按预期工作:点击我的列表框中的项目=&gt;更新SelectedItem binded属性=&gt; SelectionChanged事件被触发=&gt;我的函数ClickItemList被调用并在保存所选项目的ID

后转到下一页