【问题标题】:Getting WPF ListView.SelectedItems in ViewModel在 ViewModel 中获取 WPF ListView.SelectedItems
【发布时间】:2012-06-13 11:04:09
【问题描述】:

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

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

【问题讨论】:

    标签: .net wpf listview data-binding mvvm


    【解决方案1】:

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

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

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

    更新:

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

    MVVM, providing the Association of View to ViewModel

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

    【讨论】:

    • 对不起,我不清楚。不认识对方是指一个人不参考另一个人。同样注册到SelectionChanged 事件也不是完全必要的,因为 ViewModel 只需要在执行命令时获取选定的项目。它更像是“VM 如何按需从视图中提取数据”。
    【解决方案2】:

    要仅在执行命令时获取SelectedItems,请使用CommandParameter 并传入ListView.SelectedItems

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

    【讨论】:

    • SelectedItems(复数)不支持数据绑定。请参阅 this linkthis link。它也不能作为CommandParameter 工作,我总是得到null,而使用SelectedItem(单数)很好。
    • @user986080 我没有意识到SelectedItems 不支持绑定。我从答案中删除了它。但是CommandParameter 确实有效,我已经对其进行了测试,并且能够显示所选项目的列表。
    • 我的 XAML 示例显示了 ListBox,但我也测试了 ListView,并且能够从命令参数中获取所选项目。
    • 我想我正在点击this CommandParameter bug,这很麻烦。但理想情况下,您的答案是正确的方法。
    • 非常感谢提供这个 CommandParamter 解决方案。对我很有效
    【解决方案3】:

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

    1. 您需要添加对

      的引用

      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();
    }
    

    【讨论】:

      【解决方案4】:

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

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

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

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

      2. 确保您的 ICommandCanExecuteExecute 方法具有 object 类型的参数。这样,您可以防止在数据绑定 CommandParameter 类型与命令方法的参数类型不匹配时发生的 silenced 强制转换异常。

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

      例如,您可以将 listview/listbox 的 SelectedItems 属性发送给您的 ICommand 方法或它自己的 listview/listbox。很好,不是吗?

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

      【讨论】:

        【解决方案5】:

        由于没有其他答案对我有帮助(使用 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;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-06
          • 1970-01-01
          • 2013-07-04
          • 2017-09-07
          • 1970-01-01
          • 2011-01-20
          相关资源
          最近更新 更多