2012-06-09 71 views
10

有一些帖子讨论为ListView.SelectedItems添加数据绑定功能以及不重要​​的代码量。在我的场景中,我不需要从ViewModel中设置它,只需获取选定的项目以便对它们执行操作,并且它由命令触发,因此推送更新也不是必需的。在ViewModel中获取WPF ListView.SelectedItems

是否有一个简单的解决方案(就代码行而言),也许在代码隐藏?只要ViewViewModel不需要互相引用,我就可以使用代码隐藏。我认为这是一个更通用的问题:“虚拟机从视图点播获取数据的最佳做法”,但我似乎无法找到任何东西...

回答

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"/> 
+4

'SelectedItems'(复数)不支持数据绑定。请参阅[此链接](http://stackoverflow.com/questions/803216/managing-multiple-selections-with-mvvm)和[此链接](http://social.msdn.microsoft.com/forums/en-美国/ WPF /线程/ edd335ea-e5e1-48e1-91a2-793d613f5cc3 /)。它不能作为'CommandParameter'工作,我总是得到'null',而使用'SelectedItem'(单数)是好的。 –

+0

@ user986080我没有意识到'SelectedItems'不支持绑定。我从答案中删除了这个。然而'CommandParameter'确实可行,我已经测试过了,并且能够列出所选项目。 – evanb

+0

我的XAML示例显示了一个'ListBox',但我也测试了一个'ListView',并且能够从命令参数中获取所选项目。 – evanb

2

我不认为这是正确的条件考虑'View和ViewModel不需要互相了解'; 在MVVM视图中总是知道ViewModel。

我也遇到过这种情况,我必须在视图的代码中访问ViewModel,然后填充一些数据(如选定的项目),这在使用第三方控件如ListView,DataGrid等时变得必要

如果直接绑定VM属性是不可能的,那么我会监听ListViw.SelectionChanged事件,然后在该事件中更新ViewModels SelectedItems属性。

更新:

要启用从视图VM提取数据,您可以公开对处理视图特有的功能和视图模型将有你的浏览通过该接口的参考视图的接口;使用界面仍然保持视图和ViewModel在很大程度上分离,但我通常不喜欢这个。

MVVM, providing the Association of View to ViewModel

我还是喜欢处理中查看事件的计算策略,并保持更新VM(与所选择的项目),这样VM不必担心执行任何操作之前拉低数据,它只需要使用可用的数据(因为它将始终被更新)。

+0

对不起,我不清楚。由于不了解对方,我的意思是一个不参考另一个。 注册到'SelectionChanged'事件并不是完全必要的,因为ViewModel只需要在命令执行时获取选定的项目。这更像是“虚拟机如何根据需求从视图中提取数据”。 –

8

这可以通过互动达到触发如下

  1. 您将需要添加引用

    Microsoft.Expression.Interactions System.Windows。互动

添加下面的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;} 

内构造或视图模型的初始化命令,如下

SelectionChangedCommand = new DelegateCommand<object> (items => { 
    var itemList = (items as ObservableCollection<object>).Cast<YourDto>().ToList(); 
} 
+0

我有相同的解决方法, – Mihai

2

我可以向你保证:SelectedItems是一个XAML CommandParameter

确实可绑定了大量挖掘和谷歌搜索后,我终于找到了一个简单的解决方案到这个共同的问题。

为了使它工作,你必须遵守所有的规则如下

  1. Ed Ball's suggestion”,你XAML命令绑定,定义CommandParameter属性之前命令财产。这是一个非常耗时的错误。

  2. 确保您的ICommandCanExecute执行方法有对象类型的参数。通过这种方式,您可以防止沉默每当数据绑定时发生转换异常CommandParameter类型与您的命令方法的参数类型不匹配。

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems) 
    { 
        // Your goes heres 
    } 
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems) 
    { 
        // Your goes heres 
    } 
    

例如,您可以发送一个ListView/ListBox的SelectedItems属性为您ICommand的方法或列表视图/列表框它的自我。太好了,不是吗?

希望它可以防止有人花费的时间我没有弄清楚巨额如何接收SelectedItemsCanExecute参数。

0

由于没有其他答案帮助我(使用SelectedItems作为CommandParameter总是null),这里是通用Windows平台(UWP)应用程序的解决方案。它使用Microsoft.Xaml.InteractivityMicrosoft.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> 

这里的视图模型(RelayCommand是MVVM光类):

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;