Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 绑定到MVVM中DataGrid或ListBox中的SelectedItems_C#_Wpf_Vb.net_Xaml_Mvvm - Fatal编程技术网

C# 绑定到MVVM中DataGrid或ListBox中的SelectedItems

C# 绑定到MVVM中DataGrid或ListBox中的SelectedItems,c#,wpf,vb.net,xaml,mvvm,C#,Wpf,Vb.net,Xaml,Mvvm,只是在WPF上做了一些简单的阅读,我需要从数据网格绑定selectedItems,但我无法找到任何具体的东西。我只需要选定的对象 数据网格: <DataGrid Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="4" Name="ui_dtgAgreementDocuments" ItemsSource="{Binding Path=Documents, Mode=TwoWay}" SelectedIt

只是在WPF上做了一些简单的阅读,我需要从数据网格绑定selectedItems,但我无法找到任何具体的东西。我只需要选定的对象

数据网格:

<DataGrid Grid.Row="5" 
    Grid.Column="0" 
    Grid.ColumnSpan="4" 
    Name="ui_dtgAgreementDocuments"
    ItemsSource="{Binding Path=Documents, Mode=TwoWay}"
    SelectedItem="{Binding Path=DocumentSelection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    HorizontalAlignment="Stretch" 
    VerticalAlignment="Stretch" 
    Background="White"
    SelectionMode="Extended" Margin="2,5" 
    IsReadOnly="True" 
    CanUserAddRows="False" 
    CanUserReorderColumns="False" 
    CanUserResizeRows="False"
    GridLinesVisibility="None" 
    HorizontalScrollBarVisibility="Hidden"
    columnHeaderStyle="{StaticResource GreenTea}" 
    HeadersVisibility="Column" 
    BorderThickness="2" 
    BorderBrush="LightGray" 
    CellStyle="{StaticResource NonSelectableDataGridCellStyle}"
    SelectionUnit="FullRow" 
    HorizontalContentAlignment="Stretch" AutoGenerateColumns="False">

您无法绑定到
SelectedItems
,因为它是只读属性。解决这一问题的一个相当MVVM友好的方法是绑定到
DataGridRow
IsSelected
属性

您可以按如下方式设置绑定:

<DataGrid ItemsSource="{Binding DocumentViewModels}"
          SelectionMode="Extended">
    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <Setter Property="IsSelected"
                    Value="{Binding IsSelected}" />
        </Style>
    </DataGrid.Resources>
</DataGrid>
For Each documentViewModel As DocumentViewModel In DocumentViewModels
    documentViewModel.PropertyChanged += DocumentViewModel_PropertyChanged
Next
       <DataGrid.ContextMenu>
            <ContextMenu>
                <MenuItem
                    CommandParameter="{Binding Path=MySelectedItems, Converter={StaticResource selectedItemsConverter}, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
                    Command="{Binding MyDoSomethingWithMySelectedItemsCommand}"
                    Header="Do Something...">
                </MenuItem>
这允许您响应任何
DocumentViewModel
中的更改

最后,在
DocumentViewModel\u PropertyChanged
中,您可以循环浏览列表(或使用Linq查询)以获取每个项目的信息,其中
IsSelected=True

这将起作用:

MultiSelectorBehaviours.vb

Imports System.Collections
Imports System.Windows
Imports System.Windows.Controls.Primitives
Imports System.Windows.Controls
Imports System

Public NotInheritable Class MultiSelectorBehaviours
    Private Sub New()
    End Sub

    Public Shared ReadOnly SynchronizedSelectedItems As DependencyProperty = _
        DependencyProperty.RegisterAttached("SynchronizedSelectedItems", GetType(IList), GetType(MultiSelectorBehaviours), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf OnSynchronizedSelectedItemsChanged)))

    Private Shared ReadOnly SynchronizationManagerProperty As DependencyProperty = DependencyProperty.RegisterAttached("SynchronizationManager", GetType(SynchronizationManager), GetType(MultiSelectorBehaviours), New PropertyMetadata(Nothing))

    ''' <summary>
    ''' Gets the synchronized selected items.
    ''' </summary>
    ''' <param name="dependencyObject">The dependency object.</param>
    ''' <returns>The list that is acting as the sync list.</returns>
    Public Shared Function GetSynchronizedSelectedItems(ByVal dependencyObject As DependencyObject) As IList
        Return DirectCast(dependencyObject.GetValue(SynchronizedSelectedItems), IList)
    End Function

    ''' <summary>
    ''' Sets the synchronized selected items.
    ''' </summary>
    ''' <param name="dependencyObject">The dependency object.</param>
    ''' <param name="value">The value to be set as synchronized items.</param>
    Public Shared Sub SetSynchronizedSelectedItems(ByVal dependencyObject As DependencyObject, ByVal value As IList)
        dependencyObject.SetValue(SynchronizedSelectedItems, value)
    End Sub

    Private Shared Function GetSynchronizationManager(ByVal dependencyObject As DependencyObject) As SynchronizationManager
        Return DirectCast(dependencyObject.GetValue(SynchronizationManagerProperty), SynchronizationManager)
    End Function

    Private Shared Sub SetSynchronizationManager(ByVal dependencyObject As DependencyObject, ByVal value As SynchronizationManager)
        dependencyObject.SetValue(SynchronizationManagerProperty, value)
    End Sub

    Private Shared Sub OnSynchronizedSelectedItemsChanged(ByVal dependencyObject As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If e.OldValue IsNot Nothing Then
            Dim synchronizer As SynchronizationManager = GetSynchronizationManager(dependencyObject)
            synchronizer.StopSynchronizing()

            SetSynchronizationManager(dependencyObject, Nothing)
        End If

        Dim list As IList = TryCast(e.NewValue, IList)
        Dim selector As Selector = TryCast(dependencyObject, Selector)

        ' check that this property is an IList, and that it is being set on a ListBox
        If list IsNot Nothing AndAlso selector IsNot Nothing Then
            Dim synchronizer As SynchronizationManager = GetSynchronizationManager(dependencyObject)
            If synchronizer Is Nothing Then
                synchronizer = New SynchronizationManager(selector)
                SetSynchronizationManager(dependencyObject, synchronizer)
            End If

            synchronizer.StartSynchronizingList()
        End If
    End Sub

    ''' <summary>
    ''' A synchronization manager.
    ''' </summary>
    Private Class SynchronizationManager
        Private ReadOnly _multiSelector As Selector
        Private _synchronizer As TwoListSynchronizer

        ''' <summary>
        ''' Initializes a new instance of the <see cref="SynchronizationManager"/> class.
        ''' </summary>
        ''' <param name="selector">The selector.</param>
        Friend Sub New(ByVal selector As Selector)
            _multiSelector = selector
        End Sub

        ''' <summary>
        ''' Starts synchronizing the list.
        ''' </summary>
        Public Sub StartSynchronizingList()
            Dim list As IList = GetSynchronizedSelectedItems(_multiSelector)

            If list IsNot Nothing Then
                _synchronizer = New TwoListSynchronizer(GetSelectedItemsCollection(_multiSelector), list)
                _synchronizer.StartSynchronizing()
            End If
        End Sub

        ''' <summary>
        ''' Stops synchronizing the list.
        ''' </summary>
        Public Sub StopSynchronizing()
            _synchronizer.StopSynchronizing()
        End Sub

        Public Shared Function GetSelectedItemsCollection(ByVal selector As Selector) As IList
            If TypeOf selector Is MultiSelector Then
                Return TryCast(selector, MultiSelector).SelectedItems
            ElseIf TypeOf selector Is ListBox Then
                Return TryCast(selector, ListBox).SelectedItems
            Else
                Throw New InvalidOperationException("Target object has no SelectedItems property to bind.")
            End If
        End Function

    End Class
End Class
''' <summary>
''' Converts items in the Master list to Items in the target list, and back again.
''' </summary>
Public Interface IListItemConverter
    ''' <summary>
    ''' Converts the specified master list item.
    ''' </summary>
    ''' <param name="masterListItem">The master list item.</param>
    ''' <returns>The result of the conversion.</returns>
    Function Convert(ByVal masterListItem As Object) As Object

    ''' <summary>
    ''' Converts the specified target list item.
    ''' </summary>
    ''' <param name="targetListItem">The target list item.</param>
    ''' <returns>The result of the conversion.</returns>
    Function ConvertBack(ByVal targetListItem As Object) As Object
End Interface
Imports System.Collections
Imports System.Collections.Specialized
Imports System.Linq
Imports System.Windows

''' <summary>
''' Keeps two lists synchronized. 
''' </summary>
Public Class TwoListSynchronizer
    Implements IWeakEventListener

    Private Shared ReadOnly DefaultConverter As IListItemConverter = New DoNothingListItemConverter()
    Private ReadOnly _masterList As IList
    Private ReadOnly _masterTargetConverter As IListItemConverter
    Private ReadOnly _targetList As IList


    ''' <summary>
    ''' Initializes a new instance of the <see cref="TwoListSynchronizer"/> class.
    ''' </summary>
    ''' <param name="masterList">The master list.</param>
    ''' <param name="targetList">The target list.</param>
    ''' <param name="masterTargetConverter">The master-target converter.</param>
    Public Sub New(ByVal masterList As IList, ByVal targetList As IList, ByVal masterTargetConverter As IListItemConverter)
        _masterList = masterList
        _targetList = targetList
        _masterTargetConverter = masterTargetConverter
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the <see cref="TwoListSynchronizer"/> class.
    ''' </summary>
    ''' <param name="masterList">The master list.</param>
    ''' <param name="targetList">The target list.</param>
    Public Sub New(ByVal masterList As IList, ByVal targetList As IList)
        Me.New(masterList, targetList, DefaultConverter)
    End Sub

    Private Delegate Sub ChangeListAction(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))

    ''' <summary>
    ''' Starts synchronizing the lists.
    ''' </summary>
    Public Sub StartSynchronizing()
        ListenForChangeEvents(_masterList)
        ListenForChangeEvents(_targetList)

        ' Update the Target list from the Master list
        SetListValuesFromSource(_masterList, _targetList, AddressOf ConvertFromMasterToTarget)

        ' In some cases the target list might have its own view on which items should included:
        ' so update the master list from the target list
        ' (This is the case with a ListBox SelectedItems collection: only items from the ItemsSource can be included in SelectedItems)
        If Not TargetAndMasterCollectionsAreEqual() Then
            SetListValuesFromSource(_targetList, _masterList, AddressOf ConvertFromTargetToMaster)
        End If
    End Sub

    ''' <summary>
    ''' Stop synchronizing the lists.
    ''' </summary>
    Public Sub StopSynchronizing()
        StopListeningForChangeEvents(_masterList)
        StopListeningForChangeEvents(_targetList)
    End Sub

    ''' <summary>
    ''' Receives events from the centralized event manager.
    ''' </summary>
    ''' <param name="managerType">The type of the <see cref="T:System.Windows.WeakEventManager"/> calling this method.</param>
    ''' <param name="sender">Object that originated the event.</param>
    ''' <param name="e">Event data.</param>
    ''' <returns>
    ''' true if the listener handled the event. It is considered an error by the <see cref="T:System.Windows.WeakEventManager"/> handling in WPF to register a listener for an event that the listener does not handle. Regardless, the method should return false if it receives an event that it does not recognize or handle.
    ''' </returns>
    Public Function ReceiveWeakEvent(ByVal managerType As Type, ByVal sender As Object, ByVal e As EventArgs) As Boolean Implements System.Windows.IWeakEventListener.ReceiveWeakEvent
        HandleCollectionChanged(TryCast(sender, IList), TryCast(e, NotifyCollectionChangedEventArgs))

        Return True
    End Function

    ''' <summary>
    ''' Listens for change events on a list.
    ''' </summary>
    ''' <param name="list">The list to listen to.</param>
    Protected Sub ListenForChangeEvents(ByVal list As IList)
        If TypeOf list Is INotifyCollectionChanged Then
            CollectionChangedEventManager.AddListener(TryCast(list, INotifyCollectionChanged), Me)
        End If
    End Sub

    ''' <summary>
    ''' Stops listening for change events.
    ''' </summary>
    ''' <param name="list">The list to stop listening to.</param>
    Protected Sub StopListeningForChangeEvents(ByVal list As IList)
        If TypeOf list Is INotifyCollectionChanged Then
            CollectionChangedEventManager.RemoveListener(TryCast(list, INotifyCollectionChanged), Me)
        End If
    End Sub

    Private Sub AddItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
        Dim itemCount As Integer = e.NewItems.Count

        For i As Integer = 0 To itemCount - 1
            Dim insertionPoint As Integer = e.NewStartingIndex + i

            If insertionPoint > list.Count Then
                list.Add(converter(e.NewItems(i)))
            Else
                list.Insert(insertionPoint, converter(e.NewItems(i)))
            End If
        Next
    End Sub

    Private Function ConvertFromMasterToTarget(ByVal masterListItem As Object) As Object
        Return If(_masterTargetConverter Is Nothing, masterListItem, _masterTargetConverter.Convert(masterListItem))
    End Function

    Private Function ConvertFromTargetToMaster(ByVal targetListItem As Object) As Object
        Return If(_masterTargetConverter Is Nothing, targetListItem, _masterTargetConverter.ConvertBack(targetListItem))
    End Function

    Private Sub HandleCollectionChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
        Dim sourceList As IList = TryCast(sender, IList)

        Select Case e.Action
            Case NotifyCollectionChangedAction.Add
                PerformActionOnAllLists(AddressOf AddItems, sourceList, e)
                Exit Select
            Case NotifyCollectionChangedAction.Move
                PerformActionOnAllLists(AddressOf MoveItems, sourceList, e)
                Exit Select
            Case NotifyCollectionChangedAction.Remove
                PerformActionOnAllLists(AddressOf RemoveItems, sourceList, e)
                Exit Select
            Case NotifyCollectionChangedAction.Replace
                PerformActionOnAllLists(AddressOf ReplaceItems, sourceList, e)
                Exit Select
            Case NotifyCollectionChangedAction.Reset
                UpdateListsFromSource(TryCast(sender, IList))
                Exit Select
            Case Else
                Exit Select
        End Select
    End Sub

    Private Sub MoveItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
        RemoveItems(list, e, converter)
        AddItems(list, e, converter)
    End Sub

    Private Sub PerformActionOnAllLists(ByVal action As ChangeListAction, ByVal sourceList As IList, ByVal collectionChangedArgs As NotifyCollectionChangedEventArgs)
        If sourceList Is _masterList Then
            PerformActionOnList(_targetList, action, collectionChangedArgs, AddressOf ConvertFromMasterToTarget)
        Else
            PerformActionOnList(_masterList, action, collectionChangedArgs, AddressOf ConvertFromTargetToMaster)
        End If
    End Sub

    Private Sub PerformActionOnList(ByVal list As IList, ByVal action As ChangeListAction, ByVal collectionChangedArgs As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
        StopListeningForChangeEvents(list)
        action(list, collectionChangedArgs, converter)
        ListenForChangeEvents(list)
    End Sub

    Private Sub RemoveItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
        Dim itemCount As Integer = e.OldItems.Count

        ' for the number of items being removed, remove the item from the Old Starting Index
        ' (this will cause following items to be shifted down to fill the hole).
        For i As Integer = 0 To itemCount - 1
            list.RemoveAt(e.OldStartingIndex)
        Next
    End Sub

    Private Sub ReplaceItems(ByVal list As IList, ByVal e As NotifyCollectionChangedEventArgs, ByVal converter As Converter(Of Object, Object))
        RemoveItems(list, e, converter)
        AddItems(list, e, converter)
    End Sub

    Private Sub SetListValuesFromSource(ByVal sourceList As IList, ByVal targetList As IList, ByVal converter As Converter(Of Object, Object))
        StopListeningForChangeEvents(targetList)

        targetList.Clear()

        For Each o As Object In sourceList
            targetList.Add(converter(o))
        Next

        ListenForChangeEvents(targetList)
    End Sub

    Private Function TargetAndMasterCollectionsAreEqual() As Boolean
        Return _masterList.Cast(Of Object)().SequenceEqual(_targetList.Cast(Of Object)().[Select](Function(item) ConvertFromTargetToMaster(item)))
    End Function

    ''' <summary>
    ''' Makes sure that all synchronized lists have the same values as the source list.
    ''' </summary>
    ''' <param name="sourceList">The source list.</param>
    Private Sub UpdateListsFromSource(ByVal sourceList As IList)
        If sourceList Is _masterList Then
            SetListValuesFromSource(_masterList, _targetList, AddressOf ConvertFromMasterToTarget)
        Else
            SetListValuesFromSource(_targetList, _masterList, AddressOf ConvertFromTargetToMaster)
        End If
    End Sub




    ''' <summary>
    ''' An implementation that does nothing in the conversions.
    ''' </summary>
    Friend Class DoNothingListItemConverter
        Implements IListItemConverter

        ''' <summary>
        ''' Converts the specified master list item.
        ''' </summary>
        ''' <param name="masterListItem">The master list item.</param>
        ''' <returns>The result of the conversion.</returns>
        Public Function Convert(ByVal masterListItem As Object) As Object Implements IListItemConverter.Convert
            Return masterListItem
        End Function

        ''' <summary>
        ''' Converts the specified target list item.
        ''' </summary>
        ''' <param name="targetListItem">The target list item.</param>
        ''' <returns>The result of the conversion.</returns>
        Public Function ConvertBack(ByVal targetListItem As Object) As Object Implements IListItemConverter.ConvertBack
            Return targetListItem
        End Function
    End Class

End Class

功劳归于:

我知道这篇帖子有点老了,已经被回复了。但我想出了一个非MVVM的答案。这很简单,对我来说很有效。添加另一个DataGrid,假设所选集合为SelectedResults:

    <DataGrid x:Name="SelectedGridRows" 
        ItemsSource="{Binding SelectedResults,Mode=OneWayToSource}" 
        Visibility="Collapsed" >

通过一些技巧,您可以扩展DataGrid以创建可绑定版本的
SelectedItems
属性。我的解决方案要求绑定具有
Mode=OneWayToSource
,因为我只想读取属性,但也可以扩展我的解决方案以允许属性读写

我假设ListBox也可以使用类似的技术,但我还没有尝试过

public class BindableMultiSelectDataGrid : DataGrid
{
    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.Register("SelectedItems", typeof(IList), typeof(BindableMultiSelectDataGrid), new PropertyMetadata(default(IList)));

    public new IList SelectedItems
    {
        get { return (IList)GetValue(SelectedItemsProperty); }
        set { throw new Exception("This property is read-only. To bind to it you must use 'Mode=OneWayToSource'."); }
    }

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);
        SetValue(SelectedItemsProperty, base.SelectedItems);
    }
}

我有一个解决方案,使用适合我需要的变通方法

MouseUp
上的
ListItemTemplate
上创建一个
EventToCommand
,并作为
CommandParameter
发送
SelectedItems
集合

 <i:Interaction.Triggers>
     <i:EventTrigger EventName="MouseUp">
         <helpers:EventToCommand Command="{Binding DataContext.SelectionChangedUpdate,
                                 RelativeSource={RelativeSource AncestorType=UserControl}}"
                                 CommandParameter="{Binding ElementName=personsList, Path=SelectedItems}" />
    </i:EventTrigger>                         
</i:Interaction.Triggers>

这样,您可以在viewmodel中使用一个命令来处理此问题或保存选定的项目以供以后使用。
快乐编码

SelectedItems可作为XAML命令参数绑定

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

要使其正常工作,您必须遵守以下所有规则:

  • 在XAML命令数据绑定之后,在命令属性之前定义命令参数属性。这是一个非常耗时的bug

  • 确保您的ICommandCanExecuteExecute方法具有object类型的参数。通过这种方式,可以防止在数据绑定CommandParameter类型与命令方法的参数类型不匹配时发生沉默强制转换异常

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
    {
        // Your code goes here
    }
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems)  
    {
        // Your code goes here
    }
    
  • 例如,您可以将listview/listbox的SelectedItems属性发送给您的ICommand方法或listview/listbox本身。太好了,不是吗


    希望它能防止有人花费大量时间来找出如何将SelectedItems作为CanExecute参数来接收。

    我来这里是为了得到答案,我得到了很多很棒的答案。我将它们合并成一个附加属性,与上面奥马尔提供的属性非常相似,但属于一个类。处理INotifyCollectionChanged和切换列表。不会泄露事件。我写了它,所以它将是非常简单的代码遵循。用C#编写,处理listbox selectedItems和dataGrid selectedItems

    这适用于DataGrid和ListBox

    (我刚学会如何使用GitHub) GitHub

    使用:

    <ListBox ItemsSource="{Binding MyList}" a:Ex.SelectedItems="{Binding ObservableList}" 
             SelectionMode="Extended"/>
    <DataGrid ItemsSource="{Binding MyList}" a:Ex.SelectedItems="{Binding OtherObservableList}" />
    
    
    
    这是代码。Git上有一个小示例

    public class Ex : DependencyObject
    {
        public static readonly DependencyProperty IsSubscribedToSelectionChangedProperty = DependencyProperty.RegisterAttached(
            "IsSubscribedToSelectionChanged", typeof(bool), typeof(Ex), new PropertyMetadata(default(bool)));
        public static void SetIsSubscribedToSelectionChanged(DependencyObject element, bool value) { element.SetValue(IsSubscribedToSelectionChangedProperty, value); }
        public static bool GetIsSubscribedToSelectionChanged(DependencyObject element) { return (bool)element.GetValue(IsSubscribedToSelectionChangedProperty); }
    
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
            "SelectedItems", typeof(IList), typeof(Ex), new PropertyMetadata(default(IList), OnSelectedItemsChanged));
        public static void SetSelectedItems(DependencyObject element, IList value) { element.SetValue(SelectedItemsProperty, value); }
        public static IList GetSelectedItems(DependencyObject element) { return (IList)element.GetValue(SelectedItemsProperty); }
    
        /// <summary>
        /// Attaches a list or observable collection to the grid or listbox, syncing both lists (one way sync for simple lists).
        /// </summary>
        /// <param name="d">The DataGrid or ListBox</param>
        /// <param name="e">The list to sync to.</param>
        private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ListBox || d is MultiSelector))
                throw new ArgumentException("Somehow this got attached to an object I don't support. ListBoxes and Multiselectors (DataGrid), people. Geesh =P!");
    
            var selector = (Selector)d;
            var oldList = e.OldValue as IList;
            if (oldList != null)
            {
                var obs = oldList as INotifyCollectionChanged;
                if (obs != null)
                {
                    obs.CollectionChanged -= OnCollectionChanged;
                }
                // If we're orphaned, disconnect lb/dg events.
                if (e.NewValue == null)
                {
                    selector.SelectionChanged -= OnSelectorSelectionChanged;
                    SetIsSubscribedToSelectionChanged(selector, false);
                }
            }
            var newList = (IList)e.NewValue;
            if (newList != null)
            {
                var obs = newList as INotifyCollectionChanged;
                if (obs != null)
                {
                    obs.CollectionChanged += OnCollectionChanged;
                }
                PushCollectionDataToSelectedItems(newList, selector);
                var isSubscribed = GetIsSubscribedToSelectionChanged(selector);
                if (!isSubscribed)
                {
                    selector.SelectionChanged += OnSelectorSelectionChanged;
                    SetIsSubscribedToSelectionChanged(selector, true);
                }
            }
        }
    
        /// <summary>
        /// Initially set the selected items to the items in the newly connected collection,
        /// unless the new collection has no selected items and the listbox/grid does, in which case
        /// the flow is reversed. The data holder sets the state. If both sides hold data, then the
        /// bound IList wins and dominates the helpless wpf control.
        /// </summary>
        /// <param name="obs">The list to sync to</param>
        /// <param name="selector">The grid or listbox</param>
        private static void PushCollectionDataToSelectedItems(IList obs, DependencyObject selector)
        {
            var listBox = selector as ListBox;
            if (listBox != null)
            {
                if (obs.Count > 0)
                {
                    listBox.SelectedItems.Clear();
                    foreach (var ob in obs) { listBox.SelectedItems.Add(ob); }
                }
                else
                {
                    foreach (var ob in listBox.SelectedItems) { obs.Add(ob); }
                }
                return;
            }
            // Maybe other things will use the multiselector base... who knows =P
            var grid = selector as MultiSelector;
            if (grid != null)
            {
                if (obs.Count > 0)
                {
                    grid.SelectedItems.Clear();
                    foreach (var ob in obs) { grid.SelectedItems.Add(ob); }
                }
                else
                {
                    foreach (var ob in grid.SelectedItems) { obs.Add(ob); }
                }
                return;
            }
            throw new ArgumentException("Somehow this got attached to an object I don't support. ListBoxes and Multiselectors (DataGrid), people. Geesh =P!");
        }
        /// <summary>
        /// When the listbox or grid fires a selectionChanged even, we update the attached list to
        /// match it.
        /// </summary>
        /// <param name="sender">The listbox or grid</param>
        /// <param name="e">Items added and removed.</param>
        private static void OnSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var dep = (DependencyObject)sender;
            var items = GetSelectedItems(dep);
            var col = items as INotifyCollectionChanged;
    
            // Remove the events so we don't fire back and forth, then re-add them.
            if (col != null) col.CollectionChanged -= OnCollectionChanged;
            foreach (var oldItem in e.RemovedItems) items.Remove(oldItem);
            foreach (var newItem in e.AddedItems) items.Add(newItem);
            if (col != null) col.CollectionChanged += OnCollectionChanged;
        }
    
        /// <summary>
        /// When the attached object implements INotifyCollectionChanged, the attached listbox
        /// or grid will have its selectedItems adjusted by this handler.
        /// </summary>
        /// <param name="sender">The listbox or grid</param>
        /// <param name="e">The added and removed items</param>
        private static void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Push the changes to the selected item.
            var listbox = sender as ListBox;
            if (listbox != null)
            {
                listbox.SelectionChanged -= OnSelectorSelectionChanged;
                if (e.Action == NotifyCollectionChangedAction.Reset) listbox.SelectedItems.Clear();
                else
                {
                    foreach (var oldItem in e.OldItems) listbox.SelectedItems.Remove(oldItem);
                    foreach (var newItem in e.NewItems) listbox.SelectedItems.Add(newItem);
                }
                listbox.SelectionChanged += OnSelectorSelectionChanged;
            }
            var grid = sender as MultiSelector;
            if (grid != null)
            {
                grid.SelectionChanged -= OnSelectorSelectionChanged;
                if (e.Action == NotifyCollectionChangedAction.Reset) grid.SelectedItems.Clear();
                else
                {
                    foreach (var oldItem in e.OldItems) grid.SelectedItems.Remove(oldItem);
                    foreach (var newItem in e.NewItems) grid.SelectedItems.Add(newItem);
                }
                grid.SelectionChanged += OnSelectorSelectionChanged;
            }
        }
     }
    
    公共类Ex:DependencyObject
    {
    公共静态只读DependencyProperty IsubscribedToSelectionChangedProperty=DependencyProperty.RegisterAttached(
    “IssuSubscribedToSelectionChanged”、typeof(bool)、typeof(Ex)、new PropertyMetadata(default(bool));
    公共静态无效SetIsSubscribedToSelectionChanged(DependencyObject元素,bool值){element.SetValue(IsSubscribedToSelectionChangedProperty,value);}
    公共静态bool GetIsSubscribedToSelectionChanged(DependencyObject元素){return(bool)元素.GetValue(IsSubscribedToSelectionChangedProperty);}
    公共静态只读DependencyProperty SelectedItemsProperty=DependencyProperty.RegisterAttached(
    “SelectedItems”、typeof(IList)、typeof(Ex)、新属性元数据(默认值(IList)、OnSelectedItemsChanged);
    公共静态void SetSelectedItems(DependencyObject元素,IList值){element.SetValue(SelectedItemsProperty,value);}
    公共静态IList GetSelectedItems(DependencyObject元素){return(IList)元素。GetValue(SelectedItemsProperty);}
    /// 
    ///将列表或可观察集合附加到网格或列表框,同步两个列表(简单列表的单向同步)。
    /// 
    ///数据网格或列表框
    ///要同步到的列表。
    SelectedItemsChanged上的私有静态无效(DependencyObject d、DependencyPropertyChangedEventArgs e)
    {
    如果(!(d是列表框| | d是多选择器))
    抛出新的ArgumentException(“不知何故,它连接到了一个我不支持的对象。ListBox和MultiSelector(DataGrid),people.Geesh=P!”);
    变量选择器=(选择器)d;
    var oldList=e.OldValue作为IList;
    if(oldList!=null)
    {
    var obs=INotifyCollectionChanged的旧列表;
    如果(obs!=null)
    {
    obs.CollectionChanged-=OnCollectionChanged;
    }
    //如果我们是孤立的,断开lb/dg事件。
    如果(e.NewValue==null)
    {
    selector.SelectionChanged-=OnSelectorSelectionChanged;
    SetIssuSubscribedToSelectionChanged(选择器,false);
    }
    }
    var newList=(IList)e.NewValue;
    if(newList!=null)
    
    <ListBox ItemsSource="{Binding MyList}" a:Ex.SelectedItems="{Binding ObservableList}" 
             SelectionMode="Extended"/>
    <DataGrid ItemsSource="{Binding MyList}" a:Ex.SelectedItems="{Binding OtherObservableList}" />
    
    public class Ex : DependencyObject
    {
        public static readonly DependencyProperty IsSubscribedToSelectionChangedProperty = DependencyProperty.RegisterAttached(
            "IsSubscribedToSelectionChanged", typeof(bool), typeof(Ex), new PropertyMetadata(default(bool)));
        public static void SetIsSubscribedToSelectionChanged(DependencyObject element, bool value) { element.SetValue(IsSubscribedToSelectionChangedProperty, value); }
        public static bool GetIsSubscribedToSelectionChanged(DependencyObject element) { return (bool)element.GetValue(IsSubscribedToSelectionChangedProperty); }
    
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
            "SelectedItems", typeof(IList), typeof(Ex), new PropertyMetadata(default(IList), OnSelectedItemsChanged));
        public static void SetSelectedItems(DependencyObject element, IList value) { element.SetValue(SelectedItemsProperty, value); }
        public static IList GetSelectedItems(DependencyObject element) { return (IList)element.GetValue(SelectedItemsProperty); }
    
        /// <summary>
        /// Attaches a list or observable collection to the grid or listbox, syncing both lists (one way sync for simple lists).
        /// </summary>
        /// <param name="d">The DataGrid or ListBox</param>
        /// <param name="e">The list to sync to.</param>
        private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ListBox || d is MultiSelector))
                throw new ArgumentException("Somehow this got attached to an object I don't support. ListBoxes and Multiselectors (DataGrid), people. Geesh =P!");
    
            var selector = (Selector)d;
            var oldList = e.OldValue as IList;
            if (oldList != null)
            {
                var obs = oldList as INotifyCollectionChanged;
                if (obs != null)
                {
                    obs.CollectionChanged -= OnCollectionChanged;
                }
                // If we're orphaned, disconnect lb/dg events.
                if (e.NewValue == null)
                {
                    selector.SelectionChanged -= OnSelectorSelectionChanged;
                    SetIsSubscribedToSelectionChanged(selector, false);
                }
            }
            var newList = (IList)e.NewValue;
            if (newList != null)
            {
                var obs = newList as INotifyCollectionChanged;
                if (obs != null)
                {
                    obs.CollectionChanged += OnCollectionChanged;
                }
                PushCollectionDataToSelectedItems(newList, selector);
                var isSubscribed = GetIsSubscribedToSelectionChanged(selector);
                if (!isSubscribed)
                {
                    selector.SelectionChanged += OnSelectorSelectionChanged;
                    SetIsSubscribedToSelectionChanged(selector, true);
                }
            }
        }
    
        /// <summary>
        /// Initially set the selected items to the items in the newly connected collection,
        /// unless the new collection has no selected items and the listbox/grid does, in which case
        /// the flow is reversed. The data holder sets the state. If both sides hold data, then the
        /// bound IList wins and dominates the helpless wpf control.
        /// </summary>
        /// <param name="obs">The list to sync to</param>
        /// <param name="selector">The grid or listbox</param>
        private static void PushCollectionDataToSelectedItems(IList obs, DependencyObject selector)
        {
            var listBox = selector as ListBox;
            if (listBox != null)
            {
                if (obs.Count > 0)
                {
                    listBox.SelectedItems.Clear();
                    foreach (var ob in obs) { listBox.SelectedItems.Add(ob); }
                }
                else
                {
                    foreach (var ob in listBox.SelectedItems) { obs.Add(ob); }
                }
                return;
            }
            // Maybe other things will use the multiselector base... who knows =P
            var grid = selector as MultiSelector;
            if (grid != null)
            {
                if (obs.Count > 0)
                {
                    grid.SelectedItems.Clear();
                    foreach (var ob in obs) { grid.SelectedItems.Add(ob); }
                }
                else
                {
                    foreach (var ob in grid.SelectedItems) { obs.Add(ob); }
                }
                return;
            }
            throw new ArgumentException("Somehow this got attached to an object I don't support. ListBoxes and Multiselectors (DataGrid), people. Geesh =P!");
        }
        /// <summary>
        /// When the listbox or grid fires a selectionChanged even, we update the attached list to
        /// match it.
        /// </summary>
        /// <param name="sender">The listbox or grid</param>
        /// <param name="e">Items added and removed.</param>
        private static void OnSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var dep = (DependencyObject)sender;
            var items = GetSelectedItems(dep);
            var col = items as INotifyCollectionChanged;
    
            // Remove the events so we don't fire back and forth, then re-add them.
            if (col != null) col.CollectionChanged -= OnCollectionChanged;
            foreach (var oldItem in e.RemovedItems) items.Remove(oldItem);
            foreach (var newItem in e.AddedItems) items.Add(newItem);
            if (col != null) col.CollectionChanged += OnCollectionChanged;
        }
    
        /// <summary>
        /// When the attached object implements INotifyCollectionChanged, the attached listbox
        /// or grid will have its selectedItems adjusted by this handler.
        /// </summary>
        /// <param name="sender">The listbox or grid</param>
        /// <param name="e">The added and removed items</param>
        private static void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Push the changes to the selected item.
            var listbox = sender as ListBox;
            if (listbox != null)
            {
                listbox.SelectionChanged -= OnSelectorSelectionChanged;
                if (e.Action == NotifyCollectionChangedAction.Reset) listbox.SelectedItems.Clear();
                else
                {
                    foreach (var oldItem in e.OldItems) listbox.SelectedItems.Remove(oldItem);
                    foreach (var newItem in e.NewItems) listbox.SelectedItems.Add(newItem);
                }
                listbox.SelectionChanged += OnSelectorSelectionChanged;
            }
            var grid = sender as MultiSelector;
            if (grid != null)
            {
                grid.SelectionChanged -= OnSelectorSelectionChanged;
                if (e.Action == NotifyCollectionChangedAction.Reset) grid.SelectedItems.Clear();
                else
                {
                    foreach (var oldItem in e.OldItems) grid.SelectedItems.Remove(oldItem);
                    foreach (var newItem in e.NewItems) grid.SelectedItems.Add(newItem);
                }
                grid.SelectionChanged += OnSelectorSelectionChanged;
            }
        }
     }
    
    <DataGrid.Resources>
            <Style TargetType="DataGridRow">
                <Setter Property="IsSelected"
                        Value="{Binding IsSelected}" />
            </Style>
        </DataGrid.Resources>
    
    private void MyDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        (DataContext as MyViewModel).SelectedItems.Clear();
        foreach (var i in MyDataGrid.SelectedItems) (DataContext as MyViewModel).SelectedItems.Add(i as ItemType);
    }
    
    public class SelectedItemsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var dg = value as DataGrid;
            return dg?.SelectedItems;
        }
    ...
    
           <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem
                        CommandParameter="{Binding Path=MySelectedItems, Converter={StaticResource selectedItemsConverter}, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
                        Command="{Binding MyDoSomethingWithMySelectedItemsCommand}"
                        Header="Do Something...">
                    </MenuItem>
    
    public class GetSelectedItemsCommand : ICommand
    {
        public GetSelectedItemsCommand(Action<object> action)
        {
            _action = action;
        }
    
        private readonly Action<object> _action;
    
        public bool CanExecute(object parameter)
        {
            return true;
        }
    
        public event EventHandler CanExecuteChanged;
    
        public void Execute(object parameter)
        {
            _action(parameter);
        }
    }
    
    <DataGrid x:Name="DataGridOfDesperatePeople" SelectionMode="Extended">
        <i:Interaction.Triggers>
             <i:EventTrigger EventName="SelectionChanged">
                  <i:InvokeCommandAction CommandParameter="{Binding ElementName=DataGridOfDesperatePeople, Path=SelectedItems}" Command="{Binding SelectedItemsCommand }" />
              </i:EventTrigger>
        </i:Interaction.Triggers>
    </DataGrid>
    
    public List<YourClassOfItemInTheGrid> SelectedItems { get; set; }  = new List<YourClassOfItemInTheGrid>();
    
    public ICommand SelectedItemsCommand
    {
        get
        {
            return new GetSelectedItemsCommand(list =>
            {
    
                SelectedItems.Clear();
                IList items = (IList)list;
                IEnumerable<YourClassOfItemInTheGrid> collection = items.Cast<YourClassOfItemInTheGrid>();
                SelectedItems = collection.ToList();
            });
        }
    }
    
    <DataGrid Grid.Row="1" Name="dgvMain" SelectionChanged="DataGrid_SelectionChanged" />
    
    ViewModel mModel = null;
    public Designer()
    {
        InitializeComponent();
    
        mModel = new ViewModel();
        this.DataContext = mModel;
    }
    
    private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        mModel.SelectedItems = dgvMain.SelectedItems;
    }
    
    public class ViewModel
    {
        public IList SelectedItems { get; set; }
    }