diff --git a/DataGridMultiselectManaging/IListItemConverter.vb b/DataGridMultiselectManaging/IListItemConverter.vb new file mode 100644 index 0000000..6b19886 --- /dev/null +++ b/DataGridMultiselectManaging/IListItemConverter.vb @@ -0,0 +1,4 @@ +Public Interface IListItemConverter + Function Convert(ByVal masterListItem As Object) As Object + Function ConvertBack(ByVal targetListItem As Object) As Object +End Interface diff --git a/DataGridMultiselectManaging/MultiSelectorBehaviours.vb b/DataGridMultiselectManaging/MultiSelectorBehaviours.vb new file mode 100644 index 0000000..1b43aee --- /dev/null +++ b/DataGridMultiselectManaging/MultiSelectorBehaviours.vb @@ -0,0 +1,80 @@ +Imports System.Windows.Controls.Primitives +Imports System.ComponentModel + + +Public Class MultiSelectorBehaviours + + Public Shared ReadOnly SynchronizedSelectedItemsProperty As DependencyProperty = DependencyProperty.RegisterAttached("SynchronizedSelectedItems", GetType(IList), GetType(MultiSelectorBehaviours), New PropertyMetadata(Nothing, AddressOf OnSynchronizedSelectedItemsChanged)) + Public Shared ReadOnly SynchronizationManagerProperty As DependencyProperty = DependencyProperty.RegisterAttached("SynchronizationManager", GetType(SynchronizationManager), GetType(MultiSelectorBehaviours), New PropertyMetadata(Nothing)) + + + Public Shared Function GetSynchronizedSelectedItems(ByVal dependencyObject As DependencyObject) As IList + Return CType(dependencyObject.GetValue(SynchronizedSelectedItemsProperty), IList) + End Function + + Public Shared Sub SetSynchronizedSelectedItems(ByVal dependencyObject As DependencyObject, ByVal value As IList) + dependencyObject.SetValue(SynchronizedSelectedItemsProperty, value) + End Sub + + Private Shared Function GetSynchronizationManager(ByVal dependencyObject As DependencyObject) As SynchronizationManager + Return CType(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) + + 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 + + Private Class SynchronizationManager + Private ReadOnly _multiSelector As Selector + Private _synchronizer As TwoListSynchronizer + + Friend Sub New(ByVal selector As Selector) + _multiSelector = selector + End Sub + + 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 + + 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 diff --git a/DataGridMultiselectManaging/TwoListSynchronizer.vb b/DataGridMultiselectManaging/TwoListSynchronizer.vb new file mode 100644 index 0000000..f443d53 --- /dev/null +++ b/DataGridMultiselectManaging/TwoListSynchronizer.vb @@ -0,0 +1,163 @@ +Imports System.Collections.Specialized + +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 + + Public Sub New(ByVal masterList As IList, ByVal targetList As IList, ByVal masterTargetConverter As IListItemConverter) + _masterList = masterList + _targetList = targetList + _masterTargetConverter = masterTargetConverter + End Sub + + 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)) + + Public Sub StartSynchronizing() + ListenForChangeEvents(_masterList) + ListenForChangeEvents(_targetList) + SetListValuesFromSource(_masterList, _targetList, New Converter(Of Object, Object)(AddressOf ConvertFromMasterToTarget)) + + If Not TargetAndMasterCollectionsAreEqual() Then + SetListValuesFromSource(_targetList, _masterList, New Converter(Of Object, Object)(AddressOf ConvertFromTargetToMaster)) + End If + End Sub + + Public Sub StopSynchronizing() + StopListeningForChangeEvents(_masterList) + StopListeningForChangeEvents(_targetList) + End Sub + + Public Function ReceiveWeakEvent(ByVal managerType As Type, ByVal sender As Object, ByVal e As EventArgs) As Boolean Implements IWeakEventListener.ReceiveWeakEvent + HandleCollectionChanged(TryCast(sender, IList), TryCast(e, NotifyCollectionChangedEventArgs)) + Return True + End Function + + Protected Sub ListenForChangeEvents(ByVal list As IList) + If TypeOf list Is INotifyCollectionChanged Then + CollectionChangedEventManager.AddListener(TryCast(list, INotifyCollectionChanged), Me) + End If + End Sub + + 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) + Case NotifyCollectionChangedAction.Move + PerformActionOnAllLists(AddressOf MoveItems, sourceList, e) + Case NotifyCollectionChangedAction.Remove + PerformActionOnAllLists(AddressOf RemoveItems, sourceList, e) + Case NotifyCollectionChangedAction.Replace + PerformActionOnAllLists(AddressOf ReplaceItems, sourceList, e) + Case NotifyCollectionChangedAction.Reset + UpdateListsFromSource(TryCast(sender, IList)) + Case Else + 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, New Converter(Of Object, Object)(AddressOf ConvertFromMasterToTarget)) + Else + PerformActionOnList(_masterList, action, collectionChangedArgs, New Converter(Of Object, Object)(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)) + If e.OldItems.Count = 1 AndAlso e.OldStartingIndex <= list.Count - 1 Then + list.RemoveAt(e.OldStartingIndex) + Else + For Each Item In e.OldItems + list.Remove(Item) + Next + End If + 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 + + Private Sub UpdateListsFromSource(ByVal sourceList As IList) + If sourceList Is _masterList Then + SetListValuesFromSource(_masterList, _targetList, New Converter(Of Object, Object)(AddressOf ConvertFromMasterToTarget)) + Else + SetListValuesFromSource(_targetList, _masterList, New Converter(Of Object, Object)(AddressOf ConvertFromTargetToMaster)) + End If + End Sub + + Friend Class DoNothingListItemConverter + Implements IListItemConverter + + Public Function Convert(ByVal masterListItem As Object) As Object Implements IListItemConverter.Convert + Return masterListItem + End Function + + Public Function ConvertBack(ByVal targetListItem As Object) As Object Implements IListItemConverter.ConvertBack + Return targetListItem + End Function + End Class +End Class diff --git a/Special-Doors/MTableDbV.xaml b/Special-Doors/MTableDbV.xaml index ae8099c..afd788f 100644 --- a/Special-Doors/MTableDbV.xaml +++ b/Special-Doors/MTableDbV.xaml @@ -142,21 +142,24 @@ + EgtCAM5:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedItem.SelectedAssociations, + ElementName=TablesListBox}" + IsSynchronizedWithCurrentItem="False" + AutoGenerateColumns="False" + CanUserSortColumns="False" + CanUserResizeColumns="False" + CanUserResizeRows="False" + CanUserReorderColumns="False" + SelectionMode="Extended" + PreviewMouseDown="DataGrid_PreviewMouseDown" + PreviewMouseUp="DataGrid_PreviewMouseUp" + PreviewMouseMove="DataGrid_PreviewMouseMove" + ScrollViewer.CanContentScroll="True" + ScrollViewer.VerticalScrollBarVisibility="Auto" + ScrollViewer.HorizontalScrollBarVisibility="Hidden" + VirtualizingStackPanel.IsVirtualizing="False"> @@ -389,6 +392,7 @@ @@ -555,6 +559,18 @@ + + + + + @@ -601,24 +617,25 @@ CommandParameter="{Binding Path=SelectedItem, ElementName=TablesListBox}" IsEnabled="{Binding MoveRow_IsEnabled}" Grid.Column="4"/> +