在ViewModel中获取WPF ListView.SelectedItems

时间:2012-06-09 22:02:22

标签: .net wpf listview data-binding mvvm

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

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

5 个答案:

答案 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)

这可以使用如下的交互式触发器来实现

  1. 您需要添加对

    的引用

    Microsoft.Expression.Interactions System.Windows.Interactivity

  2. 将以下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

经过大量的挖掘和谷歌搜索,我终于找到了解决这个常见问题的简单方法。

要使其有效,您必须遵循以下所有规则

  1. 关注Ed Ball's suggestion',在您的XAML命令数据绑定中,在命令属性之前定义 CommandParameter 属性。这是一个非常耗时的错误。

  2. 确保 ICommand CanExecute 执行方法的参数为对象类型。这样,只要数据绑定 CommandParameter 类型与命令方法的参数类型不匹配,就可以防止出现沉默强制转换异常。

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
    {
        // Your goes heres
    }
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems)  
    {
        // Your goes heres
    }
    
  3. 例如,您可以将listview / listbox的 SelectedItems 属性发送给 ICommand 方法或自己的listview / listbox。好的,不是吗?

    希望它可以防止有人花费大量时间来确定如何将 SelectedItems 作为 CanExecute 参数接收。

答案 4 :(得分: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>

这是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;