.net 在 ViewModel 中获取 WPF ListView.SelectedItems

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10964910/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 16:28:42  来源:igfitidea点击:

Getting WPF ListView.SelectedItems in ViewModel

.netwpflistviewdata-bindingmvvm

提问by NS.X.

There are some posts discussing adding data-binding ability for ListView.SelectedItemswith non-trivial amount of code. In my scenario I don't need to set it from the ViewModel, just getting selected items in order to perform action on them and it is triggered by command so push update is also not necessary.

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

Is there a simple solution (in terms of lines of code), maybe in code-behind? I am fine with code-behind as long as Viewand ViewModeldon't need to reference each other. I think this is a more generic question: "best practice for VM to get data from the View on-demand", but I can't seem to find anything...

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

回答by evanb

To get the SelectedItemsonly when a command is executed then use CommandParameterand pass in the ListView.SelectedItems.

要获得SelectedItems执行命令的唯一时间,请使用CommandParameter并传入ListView.SelectedItems.

<ListBox x:Name="listbox" ItemsSource="{Binding StringList}" SelectionMode="Multiple"/>
<Button Command="{Binding GetListItemsCommand}" CommandParameter="{Binding SelectedItems, ElementName=listbox}" Content="GetSelectedListBoxItems"/>

回答by Rajnikant

This can be achieved using Interaction triggers as below

这可以使用交互触发器来实现,如下所示

  1. You will need to add reference to

    Microsoft.Expression.Interactions System.Windows.Interactivity

  1. 您将需要添加对

    Microsoft.Expression.Interactions System.Windows.Interactivity

Add below xmlns to your xaml

将以下 xmlns 添加到您的 xaml

xmlns:i="http://schemas.microsoft.com/expression//2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

Add code below just inside your GridView tag

在您的 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>

Code Inside ViewModel declare property below

ViewModel 中的代码在下面声明属性

public DelegateCommand<object> SelectionChangedCommand {get;set;}

within constructor of Viewmodel initialize Command as below

在 Viewmodel 初始化命令的构造函数中,如下所示

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

回答by akjoshi

I don't think it's correct condition to consider that 'View and ViewModel don't need to know each other'; In MVVM view always know about ViewModel.

我认为认为“ View 和 ViewModel 不需要相互了解是不正确的;在 MVVM 视图中总是知道 ViewModel。

I have also come across this kind of situation where I had to access ViewModel in view's code behind and then populate some data(like selected items), this becomes necessary while using 3'rd party controls like ListView, DataGrid etc.

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

If directly binding the VM property is not possible then I would listen to ListViw.SelectionChanged event and then update my ViewModels SelectedItems property in that event.

如果无法直接绑定 VM 属性,那么我将侦听 ListViw.SelectionChanged 事件,然后在该事件中更新我的 ViewModels SelectedItems 属性。

Update:

更新:

To enable VM pull data from view, You can expose an interface on the View that handles View-specific functionality and ViewModel will have reference of your View through that interface; Using an interface still keeps the View and ViewModel largely decoupled but I genrally don't prefer this.

要启用 VM 从视图中提取数据,您可以在视图上公开一个接口来处理特定于视图的功能,并且 ViewModel 将通过该接口引用您的视图;使用接口仍然可以使 View 和 ViewModel 在很大程度上解耦,但我通常不喜欢这样做。

MVVM, providing the Association of View to ViewModel

MVVM,提供View到ViewModel的关联

I would still prefer the approch of handling the event in View and keep the VM updated(with the selected items), this way VM don't need to worry about pulling the data before performing any operation, it just needs to use the data available(as that will always be updated one).

我仍然更喜欢在 View 中处理事件并保持 VM 更新(使用所选项目)的方法,这样 VM 在执行任何操作之前不需要担心提取数据,它只需要使用可用的数据(因为这将始终更新一个)。

回答by Julio Nobre

I can assure you: SelectedItemsis indeed bindable as a XAML CommandParameter

我可以向你保证:SelectedItems确实可以作为 XAML CommandParameter绑定

After a lot of digging and googling, I have finally found a simple solution to this common issue.

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

To make it work you must follow ALL the following rules:

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

  1. Following Ed Ball's suggestion', on you XAML command databinding, define CommandParameterproperty BEFORE Commandproperty. This a very time-consuming bug.

  2. Make sure your ICommand's CanExecuteand Executemethods have a parameter of objecttype. This way you can prevent silencedcast exceptions that occurs whenever databinding CommandParametertype does not match your command method's parameter type.

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
    {
        // Your goes heres
    }
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems)  
    {
        // Your goes heres
    }
    
  1. 遵循Ed Ball 的建议',在您的 XAML 命令数据绑定上,定义CommandParameter属性 BEFORE Command属性。这是一个非常耗时的错误。

  2. 确保您的ICommandCanExecuteExecute方法具有对象类型的参数。这样就可以防止沉默,每当发生数据绑定转换异常CommandParameter类型不匹配您的命令方法的参数类型。

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

For example, you can either send a listview/listbox's SelectedItemsproperty to you ICommandmethods or the listview/listbox it self. Great, isn't it?

例如,您可以将列表视图/列表框的SelectedItems属性发送给您的ICommand方法或列表视图/列表框本身。很棒,不是吗?

Hope it prevents someone spending the huge amount of time I did to figure out how to receive SelectedItemsas CanExecuteparameter.

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

回答by Honza Kalfus

Since none of the other answers helped me (using SelectedItemsas CommandParameterwas always null), here is a solution for Universal Windows Platform (UWP) apps. It works using Microsoft.Xaml.Interactivityand Microsoft.Xaml.Interactions.Core.

由于没有其他的答案帮助我(使用SelectedItems作为CommandParameter总是null),这里是通用Windows平台(UWP)应用的解决方案。它使用Microsoft.Xaml.Interactivity和工作Microsoft.Xaml.Interactions.Core

Here's the View:

这是视图:

<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>

Here's the ViewModel (RelayCommandis a class from MVVM Light):

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

Beware that if you are going to remove items from the original collection after the selection is completed (user pushes a button etc.), it will also remove the items from your _selectedItemslist! If you do this in a foreach loop, you'll get an InvalidOperationException. To avoid this, simply add a guard in the marked place like:

请注意,如果您要在选择完成后从原始集合中删除项目(用户按下按钮等),它也会从您的_selectedItems列表中删除项目!如果您在 foreach 循环中执行此操作,您将获得一个InvalidOperationException. 为避免这种情况,只需在标记的地方添加一个守卫,例如:

if (_deletingItems)
    return;

and then in the method where you for example remove the items, do this:

然后在您例如删除项目的方法中,执行以下操作:

_deletingItems = true;
foreach (var item in _selectedItems)
    YourOriginalCollection.Remove(item);
_deletingItems = false;