有些帖子讨论了为ListView.SelectedItems
添加数据绑定能力的非常重要的代码量。在我的场景中,我不需要从ViewModel
设置它,只是获取所选项目以便对它们执行操作并且它由命令触发,因此也不需要推送更新。
是否有一个简单的解决方案(在代码行方面),可能在代码隐藏中?只要View
和ViewModel
不需要互相引用,我就可以使用代码隐藏。我认为这是一个更通用的问题:“ VM从视图点播中获取数据的最佳实践”,但我似乎找不到任何东西......
答案 0 :(得分:23)
仅在执行命令时获取SelectedItems
,然后使用CommandParameter
并传入ListView.SelectedItems
。
<ListBox x:Name="listbox" ItemsSource="{Binding StringList}" SelectionMode="Multiple"/>
<Button Command="{Binding GetListItemsCommand}" CommandParameter="{Binding SelectedItems, ElementName=listbox}" Content="GetSelectedListBoxItems"/>
答案 1 :(得分:9)
这可以使用如下的交互式触发器来实现
您需要添加对
的引用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初始化Command的构造函数中,如下所示
SelectionChangedCommand = new DelegateCommand<object> (items => {
var itemList = (items as ObservableCollection<object>).Cast<YourDto>().ToList();
}
答案 2 :(得分:4)
我不认为考虑' View和ViewModel不需要彼此了解 '是正确的条件; 在MVVM视图中,始终了解ViewModel。
我也遇到过这种情况,我必须在视图后面的代码中访问ViewModel,然后填充一些数据(比如选定的项目),这在使用像ListView,DataGrid等第三方控件时是必要的。 / p>
如果无法直接绑定VM属性,那么我将侦听ListViw.SelectionChanged事件,然后在该事件中更新我的ViewModels SelectedItems属性。
<强>更新强>
要启用VM从视图中提取数据,您可以在View上公开处理View特定功能的界面,ViewModel将通过该界面引用您的View;使用界面仍然使View和ViewModel在很大程度上解耦,但我一般不喜欢这个。
MVVM, providing the Association of View to ViewModel
我仍然更喜欢在View中处理事件并保持VM更新(使用所选项目)的方法,这样VM在执行任何操作之前不需要担心拉数据,它只需要使用可用的数据(因为它将始终更新一次)。
答案 3 :(得分:3)
我可以向您保证: SelectedItems 确实可绑定为XAML CommandParameter
经过大量的挖掘和谷歌搜索,我终于找到了解决这个常见问题的简单方法。
要使其有效,您必须遵循以下所有规则:
关注Ed Ball's suggestion',在您的XAML命令数据绑定中,在命令属性之前定义 CommandParameter 属性。这是一个非常耗时的错误。
确保 ICommand 的 CanExecute 和执行方法的参数为对象类型。这样,只要数据绑定 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 参数接收。
答案 4 :(得分:0)
由于其他答案都没有帮助我(使用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;