有一些帖子讨论了为ListView.SelectedItems
大量代码添加数据绑定能力。在我的场景中,我不需要从 中设置它ViewModel
,只需获取选定的项目以便对它们执行操作,它是由命令触发的,因此也不需要推送更新。
是否有一个简单的解决方案(就代码行而言),也许在代码隐藏中?只要不需要相互引用View
,我就可以使用代码隐藏。ViewModel
我认为这是一个更通用的问题:“ VM 从按需查看数据的最佳实践”,但我似乎找不到任何东西......
有一些帖子讨论了为ListView.SelectedItems
大量代码添加数据绑定能力。在我的场景中,我不需要从 中设置它ViewModel
,只需获取选定的项目以便对它们执行操作,它是由命令触发的,因此也不需要推送更新。
是否有一个简单的解决方案(就代码行而言),也许在代码隐藏中?只要不需要相互引用View
,我就可以使用代码隐藏。ViewModel
我认为这是一个更通用的问题:“ VM 从按需查看数据的最佳实践”,但我似乎找不到任何东西......
要获得SelectedItems
仅在执行命令时,请使用CommandParameter
并传入ListView.SelectedItems
.
<ListBox x:Name="listbox" ItemsSource="{Binding StringList}" SelectionMode="Multiple"/>
<Button Command="{Binding GetListItemsCommand}" CommandParameter="{Binding SelectedItems, ElementName=listbox}" Content="GetSelectedListBoxItems"/>
这可以使用交互触发器来实现,如下所示
您将需要添加参考
Microsoft.Expression.Interactions System.Windows.Interactivity
将以下 xmlns 添加到您的 xaml
xmlns:i="http://schemas.microsoft.com/expression//2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
在您的 GridView 标记内添加下面的代码
<GridView x:Name="GridName">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Datacontext.SelectionChangedCommand, ElementName=YourUserControlName}" CommandParameter="{Binding SelectedItems, ElementName=GridName}" />
</i:EventTrigger>
</i:Interaction.Triggers>
ViewModel 中的代码在下面声明属性
public DelegateCommand<object> SelectionChangedCommand {get;set;}
在 Viewmodel 的构造函数中初始化命令如下
SelectionChangedCommand = new DelegateCommand<object> (items => {
var itemList = (items as ObservableCollection<object>).Cast<YourDto>().ToList();
}
我认为考虑“视图和视图模型不需要相互了解”是不正确的条件;在 MVVM 视图中总是知道 ViewModel。
我也遇到过这种情况,我必须在后面的视图代码中访问 ViewModel,然后填充一些数据(如所选项目),这在使用 ListView、DataGrid 等第三方控件时变得很有必要。
如果无法直接绑定 VM 属性,那么我将监听 ListViw.SelectionChanged 事件,然后在该事件中更新我的 ViewModels SelectedItems 属性。
更新:
要启用 VM 从视图中提取数据,您可以在视图上公开一个处理视图特定功能的接口,并且 ViewModel 将通过该接口引用您的视图;使用接口仍然使 View 和 ViewModel 在很大程度上解耦,但我一般不喜欢这样。
我仍然更喜欢在 View 中处理事件并保持 VM 更新(使用所选项目)的方法,这样 VM 在执行任何操作之前不必担心提取数据,它只需要使用可用的数据(因为那将永远更新一个)。
我可以向您保证:SelectedItems确实可以绑定为 XAML CommandParameter
经过大量的挖掘和谷歌搜索,我终于找到了解决这个常见问题的简单方法。
要使其正常工作,您必须遵循以下所有规则:
按照Ed Ball 的建议,在您的 XAML 命令数据绑定上,在Command属性之前定义CommandParameter属性。这是一个非常耗时的错误。
确保您的ICommand的CanExecute和Execute方法具有对象类型的参数。这样,您可以防止数据绑定CommandParameter类型与命令方法的参数类型不匹配时发生的静默强制转换异常。
private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)
{
// Your goes heres
}
private bool OnDeleteSelectedItemsExecute(object SelectedItems)
{
// Your goes heres
}
例如,您可以将 listview/listbox 的SelectedItems属性发送给您的ICommand方法或它自己的 listview/listbox。太好了,不是吗?
希望它可以防止有人花费大量时间来弄清楚如何接收SelectedItems作为CanExecute参数。
由于没有其他答案对我有帮助(一如既往地使用SelectedItems
)CommandParameter
,null
因此这是通用 Windows 平台 (UWP) 应用程序的解决方案。它使用Microsoft.Xaml.Interactivity
和工作Microsoft.Xaml.Interactions.Core
。
这是视图:
<ListView x:Name="ItemsList">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="SelectionChanged">
<Core:InvokeCommandAction Command="{x:Bind ViewModel.SelectedItemsChanged}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<!-- content etc. -->
</ListView>
这是 ViewModel(RelayCommand
是 MVVM Light 的一个类):
private List<YourType> _selectedItems = new List<YourType>();
private RelayCommand<SelectionChangedEventArgs> _selectedItemsChanged;
public RelayCommand<SelectionChangedEventArgs> SelectedItemsChanged
{
get
{
if (_selectedItemsChanged == null)
_selectedItemsChanged = new RelayCommand<SelectionChangedEventArgs>((selectionChangedArgs) =>
{
// add a guard here to immediatelly return if you are modifying the original collection from code
foreach (var item in selectionChangedArgs.AddedItems)
_selectedItems.Add((YourType)item);
foreach (var item in selectionChangedArgs.RemovedItems)
_selectedItems.Remove((YourType)item);
});
return _selectedItemsChanged;
}
}
请注意,如果您要在选择完成后从原始集合中删除项目(用户按下按钮等),它也会从您的_selectedItems
列表中删除项目!如果您在 foreach 循环中执行此操作,您将获得一个InvalidOperationException
. 为避免这种情况,只需在标记的位置添加一个守卫,例如:
if (_deletingItems)
return;
然后在例如删除项目的方法中,执行以下操作:
_deletingItems = true;
foreach (var item in _selectedItems)
YourOriginalCollection.Remove(item);
_deletingItems = false;