diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ClassBrowserSupport.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ClassBrowserSupport.cs index dd9c069aed..e289bf5e16 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ClassBrowserSupport.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ClassBrowserSupport.cs @@ -67,7 +67,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads if (process == null) throw new ArgumentNullException("process"); this.process = process; - this.modules = new SimpleModelCollection(this.process.Modules); + this.modules = new NullSafeSimpleModelCollection(); + this.modules.AddRange(this.process.Modules); this.process.ModuleLoaded += ModuleLoaded; this.process.ModuleUnloaded += ModuleUnloaded; } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs b/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs index f2832c660e..c8df3e0357 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs @@ -40,7 +40,7 @@ namespace ICSharpCode.PackageManagement.Design } } - public readonly SimpleModelCollection> ProjectCollections = new SimpleModelCollection>(); + public readonly IMutableModelCollection> ProjectCollections = new NullSafeSimpleModelCollection>(); IModelCollection allProjects; public IModelCollection AllProjects { diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs index 5fefaf46f7..8ed0ba0fe5 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs @@ -18,7 +18,7 @@ namespace PackageManagement.Tests.Helpers SD.InitializeForUnitTests(); ISolution solution = MockRepository.GenerateStrictMock(); solution.Stub(s => s.MSBuildProjectCollection).Return(new Microsoft.Build.Evaluation.ProjectCollection()); - solution.Stub(s => s.Projects).Return(new SimpleModelCollection()); + solution.Stub(s => s.Projects).Return(new NullSafeSimpleModelCollection()); solution.Stub(s => s.ActiveConfiguration).Return(new ConfigurationAndPlatform("Debug", "Any CPU")); //solution.Stub(s => s.FileName).Return(FileName.Create(@"d:\projects\Test\TestSolution.sln")); return solution; diff --git a/src/Main/Base/Project/Dom/ClassBrowser/IClassBrowser.cs b/src/Main/Base/Project/Dom/ClassBrowser/IClassBrowser.cs index 26a9d84436..ab715b3cb4 100644 --- a/src/Main/Base/Project/Dom/ClassBrowser/IClassBrowser.cs +++ b/src/Main/Base/Project/Dom/ClassBrowser/IClassBrowser.cs @@ -21,7 +21,7 @@ namespace ICSharpCode.SharpDevelop.Dom.ClassBrowser public AssemblyList() { Name = ""; - Assemblies = new SimpleModelCollection(); + Assemblies = new NullSafeSimpleModelCollection(); } } } diff --git a/src/Main/Base/Project/Dom/SimpleModelCollection.cs b/src/Main/Base/Project/Dom/SimpleModelCollection.cs index b91b83193c..90843c1f43 100644 --- a/src/Main/Base/Project/Dom/SimpleModelCollection.cs +++ b/src/Main/Base/Project/Dom/SimpleModelCollection.cs @@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Dom /// public class SimpleModelCollection : IMutableModelCollection { - readonly ModelCollectionChangedEvent collectionChangedEvent; + readonly ModelCollectionChangedEvent collectionChangedEvent = new ModelCollectionChangedEvent(); readonly List list; List addedItems; List removedItems; @@ -24,13 +24,13 @@ namespace ICSharpCode.SharpDevelop.Dom public SimpleModelCollection() { this.list = new List(); - collectionChangedEvent = new ModelCollectionChangedEvent(); } public SimpleModelCollection(IEnumerable items) { this.list = new List(items); - collectionChangedEvent = new ModelCollectionChangedEvent(); + // Note: intentionally not using ValidateItem(), as calling a virtual method + // from a constructor is problematic } protected void CheckReentrancy() @@ -39,6 +39,9 @@ namespace ICSharpCode.SharpDevelop.Dom throw new InvalidOperationException("Cannot modify the collection from within the CollectionChanged event."); } + /// + /// Called before an item + /// protected virtual void ValidateItem(T item) { } @@ -182,15 +185,20 @@ namespace ICSharpCode.SharpDevelop.Dom if (items == null) throw new ArgumentNullException("items"); CheckReentrancy(); - List itemsList = items.ToList(); - for (int i = 0; i < itemsList.Count; i++) { - ValidateItem(itemsList[i]); - } - for (int i = 0; i < itemsList.Count; i++) { - OnAdd(itemsList[i]); + try { + foreach (T item in items) { + // Add each item before validating the next, + // this is necessary because ValidateItem() might be checking + // for duplicates (e.g. KeyedModelCollection<,>) + ValidateItem(item); + OnAdd(item); + list.Add(item); + } + } finally { + // In case validation fails, we still need to raise the event + // for the items that were added successfully. + RaiseEventIfNotInBatch(); } - list.AddRange(itemsList); - RaiseEventIfNotInBatch(); } public bool Remove(T item) diff --git a/src/Main/Base/Project/Dom/SynchronizedModelCollection.cs b/src/Main/Base/Project/Dom/SynchronizedModelCollection.cs index 241cba35de..367796a09e 100644 --- a/src/Main/Base/Project/Dom/SynchronizedModelCollection.cs +++ b/src/Main/Base/Project/Dom/SynchronizedModelCollection.cs @@ -31,10 +31,17 @@ namespace ICSharpCode.SharpDevelop.Dom this.syncRoot = syncRoot; } - // Event registration is thread-safe on the underlying collection public event ModelCollectionChangedEventHandler CollectionChanged { - add { underlyingCollection.CollectionChanged += value; } - remove { underlyingCollection.CollectionChanged -= value; } + add { + lock (syncRoot) { + underlyingCollection.CollectionChanged += value; + } + } + remove { + lock (syncRoot) { + underlyingCollection.CollectionChanged -= value; + } + } } #region IMutableModelCollection implementation diff --git a/src/Main/Base/Project/Src/Project/AbstractProject.cs b/src/Main/Base/Project/Src/Project/AbstractProject.cs index f6862515a9..0c10e144d8 100644 --- a/src/Main/Base/Project/Src/Project/AbstractProject.cs +++ b/src/Main/Base/Project/Src/Project/AbstractProject.cs @@ -196,7 +196,7 @@ namespace ICSharpCode.SharpDevelop.Project #endregion #region ProjectSections - SimpleModelCollection projectSections = new SimpleModelCollection(); + SimpleModelCollection projectSections = new NullSafeSimpleModelCollection(); [Browsable(false)] public IMutableModelCollection ProjectSections { diff --git a/src/Main/SharpDevelop/Dom/ClassBrowser/OpenFromGacDialog.xaml b/src/Main/SharpDevelop/Dom/ClassBrowser/OpenFromGacDialog.xaml index 44117a28f2..dd2e51a4cd 100644 --- a/src/Main/SharpDevelop/Dom/ClassBrowser/OpenFromGacDialog.xaml +++ b/src/Main/SharpDevelop/Dom/ClassBrowser/OpenFromGacDialog.xaml @@ -1,5 +1,6 @@  /// Interaction logic for OpenFromGacDialog.xaml /// - public partial class OpenFromGacDialog : Window + internal partial class OpenFromGacDialog : Window { ObservableCollection gacEntries = new ObservableCollection(); ObservableCollection filteredEntries = new ObservableCollection(); diff --git a/src/Main/SharpDevelop/Project/ProjectService.cs b/src/Main/SharpDevelop/Project/ProjectService.cs index 56f4a5d3d6..f129259394 100644 --- a/src/Main/SharpDevelop/Project/ProjectService.cs +++ b/src/Main/SharpDevelop/Project/ProjectService.cs @@ -17,7 +17,7 @@ namespace ICSharpCode.SharpDevelop.Project { public SDProjectService() { - allSolutions = new SimpleModelCollection(); + allSolutions = new NullSafeSimpleModelCollection(); allProjects = allSolutions.SelectMany(s => s.Projects); SD.GetFutureService().ContinueWith(t => t.Result.ActiveViewContentChanged += ActiveViewContentChanged);