无法在 MVVM UWP 中取消选择 ListView 项目

Can't deselect ListView Item in MVVM UWP

我希望能够单击 ListView 项目,然后将我带到相应的页面。但是由于不存在 ClickedItem 之类的东西与 ItemClick 一起使用,我必须使用 SelectedItem (获取用户单击的对象)和 SelectionChanged 在它发生时捕获(因为这是以一种方式设置的,当用户单击时,他会进行选择,从而触发此操作)。

因为在 MVVM 中我不能使用事件,所以我将事件绑定到我的 ViewModel 中的方法。

<GridView x:Name="MyGrid" 
    ItemsSource="{x:Bind ViewModel.myList, Mode=OneWay}" 
    VerticalAlignment="Stretch" 
    HorizontalAlignment="Stretch" 
    IsSwipeEnabled="false"
    SelectedItem="{Binding mySelectedItem, Mode=TwoWay}" // Binding makes it easier to bind the whole object
    SelectionChanged="{x:Bind ViewModel.SelectioMade}"
>

我在 ViewModel 中填写了我的列表。我正在使用 INotifyPropertyChanged 的​​ Template10 实现。

private MyListItemClass _mySelectedItem;
public MyListItemClass mySelectedItem{
    get { return _mySelectedItem; }
    set { Set(ref _mySelectedItem, value); }
}

当用户点击某个项目时,这个简单的方法会将我推送到下一页。

public void SelectioMade() {
    if (_mySelectedItem != null) {
        NavigationService.Navigate(typeof(Views.DetailPage), _mySelectedItem.id);
    }
}

有效。

问题是做出了选择并且它仍然存在。当我点击 DetailPage 上的后退按钮时,我在离开时返回到此列表,但点击的项目仍处于选中状态。因此,再次单击它实际上不会进行选择并触发 SelectionChanged.

明显的选择似乎是在我不再需要该值时将 mySelectedItem 设置为 null,但它不起作用。

public void SelectioMade() {
    if (_mySelectedItem != null) {
        NavigationService.Navigate(typeof(Views.DetailPage), _mySelectedItem.id);
        mySelectedItem = null;
    }
}

我似乎无法将其设置回 null。如果我在 mySelectedItem = null; 上放置一个断点,它就不会执行任何操作。它确实会触发 set { Set(ref _mySelectedItem, value); },但视图不会更新。单击的项目既没有被取消选择,我绑定到 mySelectedItem.id 属性之一的 TextBlock 也没有被更改(或者清空)。

我想知道为什么这不起作用,可能还有解决方法。我的 MVVM 可能并不完美,我还在学习。虽然它可能并不完美,但我并不是真的在寻找如何正确编写 MVVM 的建议。我想知道为什么这不起作用,因为在我看来,它应该可以正常工作。

我的第一直觉是检查您的 Set 方法,以确保它确实向视图发送了正确的通知。我不熟悉 Template10 实现,所以我觉得您不需要为 Set() 提供 属性 名称似乎很奇怪。

除此之外,我建议您重新使用 Click 而不是 SelectionChanged,因为这是您真正感兴趣的行为。您应该阅读一些有关附加属性的内容,这是完成任务的好方法这通常需要代码隐藏而不实际使用代码隐藏。它们使 MVVM 更加实用,而且不那么骇人听闻。

https://msdn.microsoft.com/en-us/library/ms749011(v=vs.110).aspx

附加的 属性 或多或少允许您定义可以附加到 XAML 中任何元素的 DependencyProperty,例如您的 GridView。因为您可以访问 setter 中的元素,所以您可以自由地附加到它的事件。因此,您可以创建一个带有委托类型的附加 属性,它会将点击等事件转发给委托。回到视图中,您将它绑定到 ViewModel 中的处理程序,如下所示:

<GridView something:MyAttachedProperties.ClickHandler="{Binding MyClickHandler}" />

希望对您有所帮助!

SelectedIndex = -1,在您的 SelectedItem 空集之后 属性?所以是的,另一个 属性 将是必需的,或者确保该页面的缓存也被禁用。

GridView 似乎不喜欢在 SelectionChanged 处理程序中更改 SelectedItem 属性(如果不使用守卫,可能会导致无限循环)。您可以改为在该页面的 OnNavigatedTo 处理程序中将 SelectedItem 设置为 null(或者任何等效的模板 10)。

此外,您实际上并不需要订阅 SelectionChanged 事件,因为您可以在 mySelectedItem 属性.

的 setter 中检测到该事件

但是,我认为通过监听选择更改事件来处理项目点击是错误的,因为选择可以通过其他方式更改(例如 up/down 箭头键或 Tab 键)。你想做的就是响应一个项目点击并获取点击的项目,对吧?为此,您可以 x:Bind 将 ItemClick 事件发送到视图模型中的方法:

<GridView ItemClick="{x:Bind ViewModel.ItemClick}" SelectionMode="None" IsItemClickEnabled="True">
public void ItemClick(object sender, ItemClickEventArgs e)
{
    var item = e.ClickedItem;
}

如果您对视图模型中的 ItemClick 方法签名感到不安,那么您可以创建自己的 ItemClick 行为来执行视图模型中公开的命令,并将命令的参数绑定到被单击的项目。

如果您出于某种原因不使用行为,那么您可以制作自己的附件 属性,如下所示:

public class ViewHelpers
{
    #region ItemClickCommand

    public static readonly DependencyProperty ItemClickCommandProperty =
        DependencyProperty.RegisterAttached("ItemClickCommand", typeof(ICommand), typeof(ViewHelpers), new PropertyMetadata(null, onItemClickCommandPropertyChanged));

    public static void SetItemClickCommand(DependencyObject d, ICommand value)
    {
        d.SetValue(ItemClickCommandProperty, value);
    }

    public static ICommand GetItemClickCommand(DependencyObject d)
    {
        return (ICommand)d.GetValue(ItemClickCommandProperty);
    }

    static void onItemClickCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var listView = d as ListViewBase;
        if (listView == null)
            throw new Exception("Dependency object must be a ListViewBase");

        listView.ItemClick -= onItemClick;
        listView.ItemClick += onItemClick;
    }

    static void onItemClick(object sender, ItemClickEventArgs e)
    {
        var listView = sender as ListViewBase;
        var command = GetItemClickCommand(listView);
        if (command != null && command.CanExecute(e.ClickedItem))
            command.Execute(e.ClickedItem);
    }

    #endregion
}

XAML 不需要使用 MVVM 模式,这意味着您需要自己编写很多 "missing" 功能来使 MVVM 对您来说更容易(如上面的 ItemClick 附件属性)。也许模板 10 已经为您提供了一些行为?我不熟悉。