我希望能够点击ListView项目,然后将我带到适当的页面。但由于ClickedItem
不存在与ItemClick
一起存在的任何内容,我必须使用SelectedItem
(以获取用户点击的对象)和{ {1}}捕获它何时发生(因为这是以用户点击时的方式设置,他进行选择,触发此操作)。
因为在MVVM中我不能使用事件,所以我将事件绑定到我的ViewModel中的方法。
SelectionChanged
我在ViewModel中填写了我的列表。我正在使用INOifyPropertyChanged的Template10实现。
<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}"
>
当用户点击某个项目时,这个简单的方法会将我推到下一页。
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); }
属性的TextBlock被更改(或者更确切地说是清空)。
我想知道为什么没有这项工作,可能还有如何修复它。我的MVVM可能不完美,我还在学习。虽然它可能不完美,但我并不是在寻找如何正确编写MVVM的建议。我想知道为什么这不起作用,因为在我看来,它应该可以正常工作。
答案 0 :(得分:2)
似乎GridView不喜欢在SelectionChanged处理程序中更改SelectedItem属性(如果不使用防护,则可能导致无限循环)。您可以在该页面的OnNavigatedTo处理程序中将SelectedItem设置为null(或者与模板10相当的任何东西)。
您也不需要订阅SelectionChanged事件,因为您可以在mySelectedItem属性的setter中检测到它。
但是,我认为通过侦听选择更改事件来处理项目点击是错误的,因为可以通过其他方式(例如上/下箭头键或Tab键)更改选择。您要做的就是回复项目点击并获取点击的项目,对吧?为此,您可以x:将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模式,这意味着您需要自己编写许多“缺失”功能以使MVVM更容易(如上面的ItemClick附加属性)。也许模板10已经为你提供了一些行为?我不熟悉它。
答案 1 :(得分:0)
我的第一直觉是检查您的Set方法,以确保它确实向视图发送了正确的通知。我不熟悉Template10的实现,所以我觉得你不需要提供一个带有Set()的属性名称。
除此之外,我建议您回到使用Click而不是SelectionChanged,因为这是您实际感兴趣的行为。您应该阅读一些关于附加属性的内容,这是完成任务的好方法。通常需要代码隐藏而不实际使用代码隐藏。它们使MVVM变得更加实用,而且不那么苛刻。
https://msdn.microsoft.com/en-us/library/ms749011(v=vs.110).aspx
附加属性或多或少允许您定义可以附加到XAML中任何元素的DependencyProperty,如GridView。因为您可以访问setter中的Element,所以您可以自由附加到其事件。因此,您可以使用委托类型创建附加属性,该属性会将单击事件转发给委托。回到视图中,将它绑定到ViewModel中的处理程序,如下所示:
<GridView something:MyAttachedProperties.ClickHandler="{Binding MyClickHandler}" />
希望这有帮助!
答案 2 :(得分:0)
SelectedIndex = -1,在您的SelectedItem属性的空集后面?所以是的,需要另一个属性,或者确保也禁用该页面的缓存。