diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs index e21b7487f4..241a8f2691 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs @@ -39,6 +39,7 @@ namespace ICSharpCode.FormsDesigner readonly IDesignerGenerator generator; readonly ResourceStore resourceStore; FormsDesignerUndoEngine undoEngine; + TypeResolutionService typeResolutionService; Encoding primaryFileEncoding; readonly IDocument primaryFileDocument = new DocumentFactory().CreateDocument(); @@ -260,7 +261,8 @@ namespace ICSharpCode.FormsDesigner serviceContainer.AddService(typeof(System.ComponentModel.Design.IResourceService), new DesignerResourceService(this.resourceStore)); AmbientProperties ambientProperties = new AmbientProperties(); serviceContainer.AddService(typeof(AmbientProperties), ambientProperties); - serviceContainer.AddService(typeof(ITypeResolutionService), new TypeResolutionService(this.PrimaryFileName)); + this.typeResolutionService = new TypeResolutionService(this.PrimaryFileName); + serviceContainer.AddService(typeof(ITypeResolutionService), this.typeResolutionService); serviceContainer.AddService(typeof(DesignerOptionService), new SharpDevelopDesignerOptionService()); serviceContainer.AddService(typeof(ITypeDiscoveryService), new TypeDiscoveryService()); serviceContainer.AddService(typeof(MemberRelationshipService), new DefaultMemberRelationshipService()); @@ -391,6 +393,7 @@ namespace ICSharpCode.FormsDesigner designSurface = null; } + this.typeResolutionService = null; this.loader = null; foreach (KeyValuePair entry in this.addedTypeDescriptionProviders) { @@ -550,8 +553,30 @@ namespace ICSharpCode.FormsDesigner void IsActiveViewContentChangedHandler(object sender, EventArgs e) { if (this.IsActiveViewContent) { + LoggingService.Debug("FormsDesigner view content activated, setting ActiveDesignSurface to " + ((this.DesignSurface == null) ? "null" : this.DesignSurface.ToString())); designSurfaceManager.ActiveDesignSurface = this.DesignSurface; + + if (this.DesignSurface != null && this.Host != null) { + // Reload designer when a referenced assembly has changed + // (the default Load/Save logic using OpenedFile cannot catch this case) + if (this.typeResolutionService.ReferencedAssemblyChanged) { + IDesignerLoaderService loaderService = this.DesignSurface.GetService(typeof(IDesignerLoaderService)) as IDesignerLoaderService; + if (loaderService != null) { + if (!this.Host.Loading) { + LoggingService.Info("Forms designer reloading due to change in referenced assembly"); + if (!loaderService.Reload()) { + MessageService.ShowMessage("The designer has detected that a referenced assembly has been changed, but the designer loader did not accept the reload command. Please reload the designer manually by closing and reopening this file."); + } + } else { + LoggingService.Debug("Forms designer detected change in referenced assembly, but is in load operation"); + } + } else { + MessageService.ShowMessage("The designer has detected that a referenced assembly has been changed, but it cannot reload itself because IDesignerLoaderService is unavailable. Please reload the designer manually by closing and reopening this file."); + } + } + } + } else { LoggingService.Debug("FormsDesigner view content deactivated, setting ActiveDesignSurface to null"); designSurfaceManager.ActiveDesignSurface = null; diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Gui/CustomComponentsSideTab.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Gui/CustomComponentsSideTab.cs index 8f27cc7216..be01e4e5c4 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Gui/CustomComponentsSideTab.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Gui/CustomComponentsSideTab.cs @@ -137,11 +137,16 @@ namespace ICSharpCode.FormsDesigner.Gui this.IsTransient = true; } - void Init() + void Init(IDesignerHost host) { LoggingService.Debug("Initializing MyToolBoxItem: " + className); + if (host == null) throw new ArgumentNullException("host"); if (assemblyLocation != null) { - Assembly asm = TypeResolutionService.LoadAssembly(assemblyLocation); + TypeResolutionService typeResolutionService = host.GetService(typeof(ITypeResolutionService)) as TypeResolutionService; + if (typeResolutionService == null) { + throw new InvalidOperationException("Cannot initialize CustomComponentToolBoxItem because the designer host does not provide a SharpDevelop TypeResolutionService."); + } + Assembly asm = typeResolutionService.LoadAssembly(assemblyLocation); if (asm != null && usedAssembly != asm) { Initialize(asm.GetType(className)); usedAssembly = asm; @@ -151,13 +156,13 @@ namespace ICSharpCode.FormsDesigner.Gui protected override IComponent[] CreateComponentsCore(IDesignerHost host) { - Init(); + Init(host); return base.CreateComponentsCore(host); } protected override IComponent[] CreateComponentsCore(IDesignerHost host, System.Collections.IDictionary defaultValues) { - Init(); + Init(host); return base.CreateComponentsCore(host, defaultValues); } } diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs index e23a56fea5..7888d489b5 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.ComponentModel.Design; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using ICSharpCode.Core; @@ -78,6 +79,11 @@ namespace ICSharpCode.FormsDesigner.Services string formSourceFileName; IProjectContent callingProject; + /// + /// Dictionary of file name -> hash of loaded assemblies for the currently designed document. + /// Used to detect changes in references assemblies. + /// + readonly Dictionary loadedAssembliesForCurrentDocument = new Dictionary(StringComparer.Ordinal); /// /// Gets the project content of the project that created this TypeResolutionService. @@ -112,7 +118,7 @@ namespace ICSharpCode.FormsDesigner.Services /// /// Loads the assembly represented by the project content. Returns null on failure. /// - public static Assembly LoadAssembly(IProjectContent pc) + public Assembly LoadAssembly(IProjectContent pc) { // prevent StackOverflow when project contents have cyclic dependencies // Very popular example of cyclic dependency: System <-> System.Xml (yes, really!) @@ -156,10 +162,24 @@ namespace ICSharpCode.FormsDesigner.Services return Path.GetFileName(fileName).ToLowerInvariant() + File.GetLastWriteTimeUtc(fileName).Ticks.ToString(); } + static string GetOriginalAssemblyFullPath(Assembly asm) + { + if (asm == null) throw new ArgumentNullException("asm"); + try { + return new Uri(asm.CodeBase, UriKind.Absolute).LocalPath; + } catch (UriFormatException ex) { + LoggingService.Warn("Could not determine path for assembly '" + asm.ToString() + "', CodeBase='" + asm.CodeBase + "': " + ex.Message); + return asm.Location; + } catch (InvalidOperationException ex) { + LoggingService.Warn("Could not determine path for assembly '" + asm.ToString() + "', CodeBase='" + asm.CodeBase + "': " + ex.Message); + return asm.Location; + } + } + /// /// Loads the file in none-locking mode. Returns null on failure. /// - public static Assembly LoadAssembly(string fileName) + public Assembly LoadAssembly(string fileName) { if (!File.Exists(fileName)) return null; @@ -167,7 +187,7 @@ namespace ICSharpCode.FormsDesigner.Services // FIX for SD2-716, remove when designer gets its own AppDomain foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { try { - if (string.Equals(asm.Location, fileName, StringComparison.InvariantCultureIgnoreCase)) { + if (string.Equals(GetOriginalAssemblyFullPath(asm), fileName, StringComparison.OrdinalIgnoreCase)) { RegisterAssembly(asm); return asm; } @@ -257,17 +277,24 @@ namespace ICSharpCode.FormsDesigner.Services if (!designerAssemblies.Contains(asm)) designerAssemblies.Insert(0, asm); } + lock (this.loadedAssembliesForCurrentDocument) { + this.loadedAssembliesForCurrentDocument[fileName] = hash; + } assemblyDict[hash] = asm; return asm; } } - public static void RegisterAssembly(Assembly asm) + void RegisterAssembly(Assembly asm) { - string file = asm.Location; - if (file.Length > 0) { + string file = GetOriginalAssemblyFullPath(asm); + if (!String.IsNullOrEmpty(file)) { + string hash = GetHash(file); lock (assemblyDict) { - assemblyDict[GetHash(file)] = asm; + assemblyDict[hash] = asm; + } + lock (this.loadedAssembliesForCurrentDocument) { + this.loadedAssembliesForCurrentDocument[file] = hash; } } lock (designerAssemblies) { @@ -286,7 +313,7 @@ namespace ICSharpCode.FormsDesigner.Services return LoadAssembly(name, throwOnError); } - static Assembly LoadAssembly(AssemblyName name, bool throwOnError) + Assembly LoadAssembly(AssemblyName name, bool throwOnError) { try { Assembly asm = Assembly.Load(name); @@ -303,7 +330,7 @@ namespace ICSharpCode.FormsDesigner.Services { Assembly assembly = GetAssembly(name); if (assembly != null) { - return assembly.Location; + return GetOriginalAssemblyFullPath(assembly); } return null; } @@ -370,6 +397,12 @@ namespace ICSharpCode.FormsDesigner.Services LoggingService.Error(e); } if (assembly != null) { + string fileName = GetOriginalAssemblyFullPath(assembly); + if (!String.IsNullOrEmpty(fileName)) { + lock (this.loadedAssembliesForCurrentDocument) { + this.loadedAssembliesForCurrentDocument[fileName] = GetHash(fileName); + } + } lock (designerAssemblies) { if (!designerAssemblies.Contains(assembly)) designerAssemblies.Add(assembly); @@ -407,6 +440,18 @@ namespace ICSharpCode.FormsDesigner.Services ICSharpCode.Core.LoggingService.Warn("TODO: Add Assembly reference : " + name); } + /// + /// Gets whether an assembly referenced by the currently designed document + /// has been changed since it has been loaded. + /// + public bool ReferencedAssemblyChanged { + get { + return this.loadedAssembliesForCurrentDocument.Any( + pair => !File.Exists(pair.Key) || !String.Equals(pair.Value, GetHash(pair.Key), StringComparison.Ordinal) + ); + } + } + #region VSDesigner workarounds /// /// HACK - Ignore any requests for types from the Microsoft.VSDesigner diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/MyTypeFinder.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/MyTypeFinder.cs index 44a7855c86..69ea707f72 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/MyTypeFinder.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/MyTypeFinder.cs @@ -18,6 +18,7 @@ namespace ICSharpCode.WpfDesign.AddIn public class MyTypeFinder : XamlTypeFinder { OpenedFile file; + readonly TypeResolutionService typeResolutionService = new TypeResolutionService(); public static MyTypeFinder Create(OpenedFile file) { @@ -32,7 +33,7 @@ namespace ICSharpCode.WpfDesign.AddIn if (string.IsNullOrEmpty(name)) { IProjectContent pc = GetProjectContent(file); if (pc != null) { - return TypeResolutionService.LoadAssembly(pc); + return this.typeResolutionService.LoadAssembly(pc); } return null; } else {