From 6074590cd8e152733a6529c9ca975528163e2932 Mon Sep 17 00:00:00 2001 From: 9swampy Date: Sun, 16 Nov 2014 11:28:29 +0000 Subject: [PATCH] Add a little starting point thread safety and test coverage for MultiSelectorBehaviours and TwoListSynchronizer. --- .../MultiSelectorBehavioursTests.cs | 136 ++++++++++++++++++ ...S.SelectedItemsSynchronizer.CiTests.csproj | 110 ++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++ .../TwoListSynchronizerAdditionBehaviour.cs | 65 +++++++++ .../TwoListSynchronizerDeletionBehaviour.cs | 118 +++++++++++++++ .../TwoListSynchronizerInitialization.cs | 37 +++++ .../TwoListSynchronizerInsertionBehaviour.cs | 70 +++++++++ .../TwoListSynchronizerMoveBehaviour.cs | 72 ++++++++++ ...TwoListSynchronizerReplacementBehaviour.cs | 70 +++++++++ .../packages.config | 4 + SelectedItemsBindingDemo/Window1.xaml | 10 +- SelectedItemsSynchronizer.sln | 6 + .../MultiSelectorBehaviours.cs | 4 +- .../TwoListSynchronizer.cs | 73 +++++----- 14 files changed, 769 insertions(+), 42 deletions(-) create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/MultiSelectorBehavioursTests.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/PrimS.SelectedItemsSynchronizer.CiTests.csproj create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/Properties/AssemblyInfo.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerAdditionBehaviour.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerDeletionBehaviour.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInitialization.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInsertionBehaviour.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerMoveBehaviour.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerReplacementBehaviour.cs create mode 100644 PrimS.SelectedItemsSynchronizer.CiTests/packages.config diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/MultiSelectorBehavioursTests.cs b/PrimS.SelectedItemsSynchronizer.CiTests/MultiSelectorBehavioursTests.cs new file mode 100644 index 0000000..c97090f --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/MultiSelectorBehavioursTests.cs @@ -0,0 +1,136 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + using System.Windows.Controls; + + [TestClass] + public class MultiSelectorBehavioursTests + { + List names; + ObservableCollection selectedNames; + ListView listView; + + [TestInitialize] + public void TestInitialize() + { + names = new List() { "Abraham", "Lincoln", "James", "Buchanan" }; + selectedNames = new ObservableCollection(); + listView = new ListView(); + listView.ItemsSource = names; + listView.SelectionMode = SelectionMode.Extended; + MultiSelectorBehaviours.SetSynchronizedSelectedItems(listView, (IList)selectedNames); + } + + [TestMethod] + public void InitialiseToNoSelection() + { + this.selectedNames.Count().Should().Be(0); + } + + [TestMethod] + public void ShouldSynchroniseListViewSelectAll() + { + // Act + this.listView.SelectAll(); + + // Assert + this.selectedNames.Count().Should().Be(this.names.Count()); + } + + [TestMethod] + public void ShouldSynchroniseListViewSetSelectedIndex() + { + // Act + this.listView.SelectedIndex = 0; + + // Assert + this.selectedNames.Count().Should().Be(1); + } + + [TestMethod] + public void ShouldSynchroniseListViewSetSelectedItem() + { + //Arrange + Random random = new Random(DateTime.Now.Millisecond); + object itemToSelect = this.listView.Items.GetItemAt(random.Next(this.names.Count)); + + // Act + this.listView.SelectedItem = itemToSelect; + + // Assert + this.selectedNames.Count().Should().Be(1); + } + + [TestMethod] + public void ShouldSynchroniseListViewAddSingleSelectedItem() + { + //Arrange + Random random = new Random(DateTime.Now.Millisecond); + object itemToSelect = this.listView.Items.GetItemAt(random.Next(this.names.Count)); + + // Act + this.listView.SelectedItems.Add(itemToSelect); + + // Assert + this.selectedNames.Count().Should().Be(1); + } + + [TestMethod] + public void ShouldSynchroniseListViewAddMultipleSelectedItems() + { + // Act + this.listView.SelectedItems.Add(this.listView.Items.GetItemAt(0)); + this.listView.SelectedItems.Add(this.listView.Items.GetItemAt(1)); + + // Assert + this.selectedNames.ShouldBeEquivalentTo(this.listView.SelectedItems, opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldSynchroniseListAddMultipleSelectedItems() + { + // Act + this.selectedNames.Add(this.names.First()); + this.selectedNames.Add(this.names.Last()); + + // Assert + this.listView.SelectedItems.ShouldBeEquivalentTo(this.selectedNames, opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldNotSynchroniseListAddMultipleSelectedItems() + { + //Arrange + ObservableCollection secondSelectedNames = new ObservableCollection(); + + // Act + MultiSelectorBehaviours.SetSynchronizedSelectedItems(listView, (IList)secondSelectedNames); + this.selectedNames.Add(this.names.First()); + this.selectedNames.Add(this.names.Last()); + + // Assert + this.listView.SelectedItems.Should().NotBeEquivalentTo(this.selectedNames); + } + + [TestMethod] + public void ShouldSynchroniseChangedListAddMultipleSelectedItems() + { + //Arrange + ObservableCollection secondSelectedNames = new ObservableCollection(); + + // Act + MultiSelectorBehaviours.SetSynchronizedSelectedItems(listView, (IList)secondSelectedNames); + secondSelectedNames.Add(this.names.First()); + secondSelectedNames.Add(this.names.Last()); + + // Assert + this.listView.SelectedItems.ShouldBeEquivalentTo(secondSelectedNames); + } + } +} diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/PrimS.SelectedItemsSynchronizer.CiTests.csproj b/PrimS.SelectedItemsSynchronizer.CiTests/PrimS.SelectedItemsSynchronizer.CiTests.csproj new file mode 100644 index 0000000..ec2a724 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/PrimS.SelectedItemsSynchronizer.CiTests.csproj @@ -0,0 +1,110 @@ + + + + Debug + AnyCPU + {918FE624-1E12-48BE-A3F0-FC1B5CF1412F} + Library + Properties + PrimS.SelectedItemsSynchronizer.CiTests + PrimS.SelectedItemsSynchronizer.CiTests + v4.5.1 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\FluentAssertions.3.3.0.20\lib\net45\FluentAssertions.dll + + + ..\packages\FluentAssertions.3.3.0.20\lib\net45\FluentAssertions.Core.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {6fd3314f-9a43-4fcf-a073-6c97ca2d1e81} + PrimS.SelectedItemsSynchronizer + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/Properties/AssemblyInfo.cs b/PrimS.SelectedItemsSynchronizer.CiTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e9aabdd --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PrimS.SelectedItemsSynchronizer.CiTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PrimS.SelectedItemsSynchronizer.CiTests")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5726167d-d6a5-4e8b-930f-e5d3009afd66")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerAdditionBehaviour.cs b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerAdditionBehaviour.cs new file mode 100644 index 0000000..769d152 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerAdditionBehaviour.cs @@ -0,0 +1,65 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using System; + using System.Collections.ObjectModel; + using System.Linq; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TwoListSynchronizerAdditionBehaviour + { + private ObservableCollection masterList; + private ObservableCollection targetList; + private TwoListSynchronizer sut; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + } + + [TestInitialize] + public void TestInitialize() + { + this.masterList = new ObservableCollection(); + this.targetList = new ObservableCollection(); + this.sut = new TwoListSynchronizer(this.masterList, this.targetList); + } + + [TestMethod] + public void ShouldNotSynchroniseAddition() + { + // Act + this.masterList.Add("testString"); + + // Assert + this.targetList.Count().Should().Be(0); + } + + [TestMethod] + public void ShouldSynchroniseAddition() + { + // Arrange + this.sut.StartSynchronizing(); + + // Act + this.masterList.Add("testString"); + + // Assert + this.targetList.Count().Should().Be(1); + } + + [TestMethod] + public void ShouldSynchroniseAdditionOnTarget() + { + // Arrange + this.sut.StartSynchronizing(); + + // Act + this.targetList.Add("testString"); + + // Assert + this.masterList.Count().Should().Be(1); + } + } +} \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerDeletionBehaviour.cs b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerDeletionBehaviour.cs new file mode 100644 index 0000000..8e68ab2 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerDeletionBehaviour.cs @@ -0,0 +1,118 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using System.Collections.ObjectModel; + using System.Linq; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TwoListSynchronizerDeletionBehaviour + { + private ObservableCollection masterList; + private ObservableCollection targetList; + private TwoListSynchronizer sut; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + } + + [TestInitialize] + public void TestInitialize() + { + this.masterList = new ObservableCollection(); + this.masterList.Add("InitialisedFirst"); + this.masterList.Add("InitialisedSecond"); + this.targetList = new ObservableCollection(); + this.sut = new TwoListSynchronizer(this.masterList, this.targetList); + this.sut.StartSynchronizing(); + } + + [TestMethod] + public void ShouldBeSynchronised() + { + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList, opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldNotSynchroniseDeletion() + { + // Arrange + this.sut.StopSynchronizing(); + + // Act + this.masterList.Remove("InitialisedFirst"); + + // Assert + this.targetList.Count().Should().Be(this.masterList.Count() + 1); + } + + [TestMethod] + public void ShouldNotSynchroniseDeletionByIndex() + { + // Arrange + this.sut.StopSynchronizing(); + + // Act + this.masterList.RemoveAt(0); + + // Assert + this.targetList.Count().Should().Be(this.masterList.Count() + 1); + } + + [TestMethod] + public void ShouldSynchroniseDeletion() + { + // Act + this.masterList.Remove("InitialisedFirst"); + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldSynchroniseDeletionByIndex() + { + // Act + this.masterList.RemoveAt(0); + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldNotSynchroniseClearing() + { + // Arrange + this.sut.StopSynchronizing(); + int priorCount = this.masterList.Count(); + + // Act + this.masterList.Clear(); + + // Assert + this.targetList.Count().Should().Be(priorCount); + } + + [TestMethod] + public void ShouldSynchroniseClearing() + { + // Act + this.masterList.Clear(); + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldSynchroniseClearingOfTarget() + { + // Act + this.targetList.Clear(); + + // Assert + this.masterList.ShouldBeEquivalentTo(this.targetList); + } + } +} \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInitialization.cs b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInitialization.cs new file mode 100644 index 0000000..3b3682b --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInitialization.cs @@ -0,0 +1,37 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using System.Collections.ObjectModel; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TwoListSynchronizerInitialization + { + [TestMethod] + public void ShouldCopyInitialMembersToTarget() + { + ObservableCollection masterList = new ObservableCollection(); + masterList.Add("Initial"); + ObservableCollection targetList = new ObservableCollection(); + + TwoListSynchronizer sut = new TwoListSynchronizer(masterList, targetList); + sut.StartSynchronizing(); + + targetList.ShouldBeEquivalentTo(masterList, "ctor should initialise targetList."); + } + + [TestMethod] + public void ShouldCopyInitialMembersFromTarget() + { + ObservableCollection masterList = new ObservableCollection(); + ObservableCollection targetList = new ObservableCollection(); + targetList.Add("Initial"); + + TwoListSynchronizer sut = new TwoListSynchronizer(masterList, targetList); + sut.StartSynchronizing(); + + targetList.ShouldBeEquivalentTo(masterList, "ctor should initialise targetList."); + targetList.Should().BeEmpty("ctor should overwrite targetList with masterList content."); + } + } +} \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInsertionBehaviour.cs b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInsertionBehaviour.cs new file mode 100644 index 0000000..bed3d20 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerInsertionBehaviour.cs @@ -0,0 +1,70 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using System.Collections.ObjectModel; + using System.Linq; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TwoListSynchronizerInsertionBehaviour + { + private ObservableCollection masterList; + private ObservableCollection targetList; + private TwoListSynchronizer sut; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + } + + [TestInitialize] + public void TestInitialize() + { + this.masterList = new ObservableCollection(); + this.masterList.Add("Initialised"); + this.targetList = new ObservableCollection(); + this.sut = new TwoListSynchronizer(this.masterList, this.targetList); + this.sut.StartSynchronizing(); + } + + [TestMethod] + public void ShouldBeSynchronised() + { + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldNotSynchroniseInsertion() + { + // Arrange + this.sut.StopSynchronizing(); + + // Act + this.masterList.Insert(0, "Insertion"); + + // Assert + this.targetList.Count().Should().Be(this.masterList.Count() - 1); + } + + [TestMethod] + public void ShouldSynchroniseInsertion() + { + // Act + this.masterList.Insert(0, "Insertion"); + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList, opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldSynchroniseInsertionOnTarget() + { + // Act + this.targetList.Insert(0, "Insertion"); + + // Assert + this.masterList.ShouldBeEquivalentTo(this.targetList, opt => opt.WithStrictOrdering()); + } + } +} \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerMoveBehaviour.cs b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerMoveBehaviour.cs new file mode 100644 index 0000000..24ab3f1 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerMoveBehaviour.cs @@ -0,0 +1,72 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using System.Collections.ObjectModel; + using System.Linq; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TwoListSynchronizerMoveBehaviour + { + private ObservableCollection masterList; + private ObservableCollection targetList; + private TwoListSynchronizer sut; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + } + + [TestInitialize] + public void TestInitialize() + { + this.masterList = new ObservableCollection(); + this.masterList.Add("InitialisedFirst"); + this.masterList.Add("InitialisedSecond"); + this.targetList = new ObservableCollection(); + this.sut = new TwoListSynchronizer(this.masterList, this.targetList); + this.sut.StartSynchronizing(); + } + + [TestMethod] + public void ShouldBeSynchronised() + { + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldNotSynchroniseMove() + { + // Arrange + this.sut.StopSynchronizing(); + + // Act + this.masterList.Move(0, 1); + + // Assert + this.targetList.First().Should().Be(this.masterList.Last()); + this.targetList.ShouldBeEquivalentTo(this.masterList.Reverse(), opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldSynchroniseMove() + { + // Act + this.masterList.Move(0, 1); + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList, opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldSynchroniseMoveOnTarget() + { + // Act + this.targetList.Move(0, 1); + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList, opt => opt.WithStrictOrdering()); + } + } +} \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerReplacementBehaviour.cs b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerReplacementBehaviour.cs new file mode 100644 index 0000000..ece4917 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/TwoListSynchronizerReplacementBehaviour.cs @@ -0,0 +1,70 @@ +namespace PrimS.SelectedItemsSynchronizer.CiTests +{ + using System.Collections.ObjectModel; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TwoListSynchronizerReplacementBehaviour + { + private ObservableCollection masterList; + private ObservableCollection targetList; + private TwoListSynchronizer sut; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + } + + [TestInitialize] + public void TestInitialize() + { + this.masterList = new ObservableCollection(); + this.masterList.Add("InitialisedFirst"); + this.masterList.Add("InitialisedSecond"); + this.targetList = new ObservableCollection(); + this.sut = new TwoListSynchronizer(this.masterList, this.targetList); + this.sut.StartSynchronizing(); + } + + [TestMethod] + public void ShouldBeSynchronised() + { + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldNotSynchroniseReplacement() + { + // Arrange + this.sut.StopSynchronizing(); + + // Act + this.masterList[0] = "Replaced"; + + // Assert + this.targetList.Should().NotBeEquivalentTo(this.masterList); + } + + [TestMethod] + public void ShouldSynchroniseReplacement() + { + // Act + this.masterList[0] = "Replaced"; + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList, opt => opt.WithStrictOrdering()); + } + + [TestMethod] + public void ShouldSynchroniseReplacementOnTarget() + { + // Act + this.targetList[0] = "Replaced"; + + // Assert + this.targetList.ShouldBeEquivalentTo(this.masterList, opt => opt.WithStrictOrdering()); + } + } +} \ No newline at end of file diff --git a/PrimS.SelectedItemsSynchronizer.CiTests/packages.config b/PrimS.SelectedItemsSynchronizer.CiTests/packages.config new file mode 100644 index 0000000..c9d1a22 --- /dev/null +++ b/PrimS.SelectedItemsSynchronizer.CiTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SelectedItemsBindingDemo/Window1.xaml b/SelectedItemsBindingDemo/Window1.xaml index 03dc2ab..08db7e0 100644 --- a/SelectedItemsBindingDemo/Window1.xaml +++ b/SelectedItemsBindingDemo/Window1.xaml @@ -30,17 +30,11 @@ - - - - - - - + diff --git a/SelectedItemsSynchronizer.sln b/SelectedItemsSynchronizer.sln index b04b4a5..0b244a2 100644 --- a/SelectedItemsSynchronizer.sln +++ b/SelectedItemsSynchronizer.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SelectedItemsBindingDemo", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrimS.SelectedItemsSynchronizer", "SelectedItemsSynchronizer\PrimS.SelectedItemsSynchronizer.csproj", "{6FD3314F-9A43-4FCF-A073-6C97CA2D1E81}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrimS.SelectedItemsSynchronizer.CiTests", "PrimS.SelectedItemsSynchronizer.CiTests\PrimS.SelectedItemsSynchronizer.CiTests.csproj", "{918FE624-1E12-48BE-A3F0-FC1B5CF1412F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {6FD3314F-9A43-4FCF-A073-6C97CA2D1E81}.Debug|Any CPU.Build.0 = Debug|Any CPU {6FD3314F-9A43-4FCF-A073-6C97CA2D1E81}.Release|Any CPU.ActiveCfg = Release|Any CPU {6FD3314F-9A43-4FCF-A073-6C97CA2D1E81}.Release|Any CPU.Build.0 = Release|Any CPU + {918FE624-1E12-48BE-A3F0-FC1B5CF1412F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {918FE624-1E12-48BE-A3F0-FC1B5CF1412F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {918FE624-1E12-48BE-A3F0-FC1B5CF1412F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {918FE624-1E12-48BE-A3F0-FC1B5CF1412F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SelectedItemsSynchronizer/MultiSelectorBehaviours.cs b/SelectedItemsSynchronizer/MultiSelectorBehaviours.cs index e540bf8..47f92a6 100644 --- a/SelectedItemsSynchronizer/MultiSelectorBehaviours.cs +++ b/SelectedItemsSynchronizer/MultiSelectorBehaviours.cs @@ -73,7 +73,7 @@ private static void OnSynchronizedSelectedItemsChanged(DependencyObject dependen SetSynchronizationManager(dependencyObject, synchronizer); } - synchronizer.StartSynchronizingList(); + synchronizer.StartSynchronizing(); } } @@ -113,7 +113,7 @@ public static IList GetSelectedItemsCollection(Selector selector) /// /// Starts synchronizing the list. /// - public void StartSynchronizingList() + public void StartSynchronizing() { IList list = GetSynchronizedSelectedItems(this.multiSelector); diff --git a/SelectedItemsSynchronizer/TwoListSynchronizer.cs b/SelectedItemsSynchronizer/TwoListSynchronizer.cs index 060356c..0a68304 100644 --- a/SelectedItemsSynchronizer/TwoListSynchronizer.cs +++ b/SelectedItemsSynchronizer/TwoListSynchronizer.cs @@ -46,18 +46,21 @@ public TwoListSynchronizer(IList masterList, IList targetList) /// public void StartSynchronizing() { - this.ListenForChangeEvents(this.masterList); - this.ListenForChangeEvents(this.targetList); + lock (this) + { + this.ListenForChangeEvents(this.masterList); + this.ListenForChangeEvents(this.targetList); - // Update the Target list from the Master list - this.SetListValuesFromSource(this.masterList, this.targetList, this.ConvertFromMasterToTarget); + // Update the Target list from the Master list + this.SetListValuesFromSource(this.masterList, this.targetList, this.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 (!this.TargetAndMasterCollectionsAreEqual()) - { - this.SetListValuesFromSource(this.targetList, this.masterList, this.ConvertFromTargetToMaster); + // 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 (!this.TargetAndMasterCollectionsAreEqual()) + { + this.SetListValuesFromSource(this.targetList, this.masterList, this.ConvertFromTargetToMaster); + } } } @@ -66,8 +69,11 @@ public void StartSynchronizing() /// public void StopSynchronizing() { - this.StopListeningForChangeEvents(this.masterList); - this.StopListeningForChangeEvents(this.targetList); + lock (this) + { + this.StopListeningForChangeEvents(this.masterList); + this.StopListeningForChangeEvents(this.targetList); + } } /// @@ -141,27 +147,30 @@ private object ConvertFromTargetToMaster(object targetListItem) private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - IList sourceList = sender as IList; - - switch (e.Action) + lock (this) { - case NotifyCollectionChangedAction.Add: - this.PerformActionOnAllLists(this.AddItems, sourceList, e); - break; - case NotifyCollectionChangedAction.Move: - this.PerformActionOnAllLists(this.MoveItems, sourceList, e); - break; - case NotifyCollectionChangedAction.Remove: - this.PerformActionOnAllLists(this.RemoveItems, sourceList, e); - break; - case NotifyCollectionChangedAction.Replace: - this.PerformActionOnAllLists(this.ReplaceItems, sourceList, e); - break; - case NotifyCollectionChangedAction.Reset: - this.UpdateListsFromSource(sender as IList); - break; - default: - break; + IList sourceList = sender as IList; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + this.PerformActionOnAllLists(this.AddItems, sourceList, e); + break; + case NotifyCollectionChangedAction.Move: + this.PerformActionOnAllLists(this.MoveItems, sourceList, e); + break; + case NotifyCollectionChangedAction.Remove: + this.PerformActionOnAllLists(this.RemoveItems, sourceList, e); + break; + case NotifyCollectionChangedAction.Replace: + this.PerformActionOnAllLists(this.ReplaceItems, sourceList, e); + break; + case NotifyCollectionChangedAction.Reset: + this.UpdateListsFromSource(sender as IList); + break; + default: + throw new NotImplementedException(string.Format("Unhandled enum member {0}", e.Action.ToString("f"))); + } } }