Browse Source

Fixed the Windows.Forms designer not reloading when a referenced assembly has been changed.

TypeResolutionService now additionally keeps track of loaded assemblies on a per-instance basis to support this.
Use Assembly.CodeBase instead of Location for determining the assembly path when possible because Location may refer to a shadow copy.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3595 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Christian Hornung 18 years ago
parent
commit
d2fb81271c
  1. 27
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs
  2. 13
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Gui/CustomComponentsSideTab.cs
  3. 63
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs
  4. 3
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/MyTypeFinder.cs

27
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs

@ -39,6 +39,7 @@ namespace ICSharpCode.FormsDesigner
readonly IDesignerGenerator generator; readonly IDesignerGenerator generator;
readonly ResourceStore resourceStore; readonly ResourceStore resourceStore;
FormsDesignerUndoEngine undoEngine; FormsDesignerUndoEngine undoEngine;
TypeResolutionService typeResolutionService;
Encoding primaryFileEncoding; Encoding primaryFileEncoding;
readonly IDocument primaryFileDocument = new DocumentFactory().CreateDocument(); readonly IDocument primaryFileDocument = new DocumentFactory().CreateDocument();
@ -260,7 +261,8 @@ namespace ICSharpCode.FormsDesigner
serviceContainer.AddService(typeof(System.ComponentModel.Design.IResourceService), new DesignerResourceService(this.resourceStore)); serviceContainer.AddService(typeof(System.ComponentModel.Design.IResourceService), new DesignerResourceService(this.resourceStore));
AmbientProperties ambientProperties = new AmbientProperties(); AmbientProperties ambientProperties = new AmbientProperties();
serviceContainer.AddService(typeof(AmbientProperties), 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(DesignerOptionService), new SharpDevelopDesignerOptionService());
serviceContainer.AddService(typeof(ITypeDiscoveryService), new TypeDiscoveryService()); serviceContainer.AddService(typeof(ITypeDiscoveryService), new TypeDiscoveryService());
serviceContainer.AddService(typeof(MemberRelationshipService), new DefaultMemberRelationshipService()); serviceContainer.AddService(typeof(MemberRelationshipService), new DefaultMemberRelationshipService());
@ -391,6 +393,7 @@ namespace ICSharpCode.FormsDesigner
designSurface = null; designSurface = null;
} }
this.typeResolutionService = null;
this.loader = null; this.loader = null;
foreach (KeyValuePair<Type, TypeDescriptionProvider> entry in this.addedTypeDescriptionProviders) { foreach (KeyValuePair<Type, TypeDescriptionProvider> entry in this.addedTypeDescriptionProviders) {
@ -550,8 +553,30 @@ namespace ICSharpCode.FormsDesigner
void IsActiveViewContentChangedHandler(object sender, EventArgs e) void IsActiveViewContentChangedHandler(object sender, EventArgs e)
{ {
if (this.IsActiveViewContent) { if (this.IsActiveViewContent) {
LoggingService.Debug("FormsDesigner view content activated, setting ActiveDesignSurface to " + ((this.DesignSurface == null) ? "null" : this.DesignSurface.ToString())); LoggingService.Debug("FormsDesigner view content activated, setting ActiveDesignSurface to " + ((this.DesignSurface == null) ? "null" : this.DesignSurface.ToString()));
designSurfaceManager.ActiveDesignSurface = this.DesignSurface; 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 { } else {
LoggingService.Debug("FormsDesigner view content deactivated, setting ActiveDesignSurface to null"); LoggingService.Debug("FormsDesigner view content deactivated, setting ActiveDesignSurface to null");
designSurfaceManager.ActiveDesignSurface = null; designSurfaceManager.ActiveDesignSurface = null;

13
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Gui/CustomComponentsSideTab.cs

@ -137,11 +137,16 @@ namespace ICSharpCode.FormsDesigner.Gui
this.IsTransient = true; this.IsTransient = true;
} }
void Init() void Init(IDesignerHost host)
{ {
LoggingService.Debug("Initializing MyToolBoxItem: " + className); LoggingService.Debug("Initializing MyToolBoxItem: " + className);
if (host == null) throw new ArgumentNullException("host");
if (assemblyLocation != null) { 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) { if (asm != null && usedAssembly != asm) {
Initialize(asm.GetType(className)); Initialize(asm.GetType(className));
usedAssembly = asm; usedAssembly = asm;
@ -151,13 +156,13 @@ namespace ICSharpCode.FormsDesigner.Gui
protected override IComponent[] CreateComponentsCore(IDesignerHost host) protected override IComponent[] CreateComponentsCore(IDesignerHost host)
{ {
Init(); Init(host);
return base.CreateComponentsCore(host); return base.CreateComponentsCore(host);
} }
protected override IComponent[] CreateComponentsCore(IDesignerHost host, System.Collections.IDictionary defaultValues) protected override IComponent[] CreateComponentsCore(IDesignerHost host, System.Collections.IDictionary defaultValues)
{ {
Init(); Init(host);
return base.CreateComponentsCore(host, defaultValues); return base.CreateComponentsCore(host, defaultValues);
} }
} }

63
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/Services/TypeResolutionService.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.ComponentModel.Design; using System.ComponentModel.Design;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using ICSharpCode.Core; using ICSharpCode.Core;
@ -78,6 +79,11 @@ namespace ICSharpCode.FormsDesigner.Services
string formSourceFileName; string formSourceFileName;
IProjectContent callingProject; IProjectContent callingProject;
/// <summary>
/// Dictionary of file name -> hash of loaded assemblies for the currently designed document.
/// Used to detect changes in references assemblies.
/// </summary>
readonly Dictionary<string, string> loadedAssembliesForCurrentDocument = new Dictionary<string, string>(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets the project content of the project that created this TypeResolutionService. /// Gets the project content of the project that created this TypeResolutionService.
@ -112,7 +118,7 @@ namespace ICSharpCode.FormsDesigner.Services
/// <summary> /// <summary>
/// Loads the assembly represented by the project content. Returns null on failure. /// Loads the assembly represented by the project content. Returns null on failure.
/// </summary> /// </summary>
public static Assembly LoadAssembly(IProjectContent pc) public Assembly LoadAssembly(IProjectContent pc)
{ {
// prevent StackOverflow when project contents have cyclic dependencies // prevent StackOverflow when project contents have cyclic dependencies
// Very popular example of cyclic dependency: System <-> System.Xml (yes, really!) // 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(); 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;
}
}
/// <summary> /// <summary>
/// Loads the file in none-locking mode. Returns null on failure. /// Loads the file in none-locking mode. Returns null on failure.
/// </summary> /// </summary>
public static Assembly LoadAssembly(string fileName) public Assembly LoadAssembly(string fileName)
{ {
if (!File.Exists(fileName)) if (!File.Exists(fileName))
return null; return null;
@ -167,7 +187,7 @@ namespace ICSharpCode.FormsDesigner.Services
// FIX for SD2-716, remove when designer gets its own AppDomain // FIX for SD2-716, remove when designer gets its own AppDomain
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
try { try {
if (string.Equals(asm.Location, fileName, StringComparison.InvariantCultureIgnoreCase)) { if (string.Equals(GetOriginalAssemblyFullPath(asm), fileName, StringComparison.OrdinalIgnoreCase)) {
RegisterAssembly(asm); RegisterAssembly(asm);
return asm; return asm;
} }
@ -257,17 +277,24 @@ namespace ICSharpCode.FormsDesigner.Services
if (!designerAssemblies.Contains(asm)) if (!designerAssemblies.Contains(asm))
designerAssemblies.Insert(0, asm); designerAssemblies.Insert(0, asm);
} }
lock (this.loadedAssembliesForCurrentDocument) {
this.loadedAssembliesForCurrentDocument[fileName] = hash;
}
assemblyDict[hash] = asm; assemblyDict[hash] = asm;
return asm; return asm;
} }
} }
public static void RegisterAssembly(Assembly asm) void RegisterAssembly(Assembly asm)
{ {
string file = asm.Location; string file = GetOriginalAssemblyFullPath(asm);
if (file.Length > 0) { if (!String.IsNullOrEmpty(file)) {
string hash = GetHash(file);
lock (assemblyDict) { lock (assemblyDict) {
assemblyDict[GetHash(file)] = asm; assemblyDict[hash] = asm;
}
lock (this.loadedAssembliesForCurrentDocument) {
this.loadedAssembliesForCurrentDocument[file] = hash;
} }
} }
lock (designerAssemblies) { lock (designerAssemblies) {
@ -286,7 +313,7 @@ namespace ICSharpCode.FormsDesigner.Services
return LoadAssembly(name, throwOnError); return LoadAssembly(name, throwOnError);
} }
static Assembly LoadAssembly(AssemblyName name, bool throwOnError) Assembly LoadAssembly(AssemblyName name, bool throwOnError)
{ {
try { try {
Assembly asm = Assembly.Load(name); Assembly asm = Assembly.Load(name);
@ -303,7 +330,7 @@ namespace ICSharpCode.FormsDesigner.Services
{ {
Assembly assembly = GetAssembly(name); Assembly assembly = GetAssembly(name);
if (assembly != null) { if (assembly != null) {
return assembly.Location; return GetOriginalAssemblyFullPath(assembly);
} }
return null; return null;
} }
@ -370,6 +397,12 @@ namespace ICSharpCode.FormsDesigner.Services
LoggingService.Error(e); LoggingService.Error(e);
} }
if (assembly != null) { if (assembly != null) {
string fileName = GetOriginalAssemblyFullPath(assembly);
if (!String.IsNullOrEmpty(fileName)) {
lock (this.loadedAssembliesForCurrentDocument) {
this.loadedAssembliesForCurrentDocument[fileName] = GetHash(fileName);
}
}
lock (designerAssemblies) { lock (designerAssemblies) {
if (!designerAssemblies.Contains(assembly)) if (!designerAssemblies.Contains(assembly))
designerAssemblies.Add(assembly); designerAssemblies.Add(assembly);
@ -407,6 +440,18 @@ namespace ICSharpCode.FormsDesigner.Services
ICSharpCode.Core.LoggingService.Warn("TODO: Add Assembly reference : " + name); ICSharpCode.Core.LoggingService.Warn("TODO: Add Assembly reference : " + name);
} }
/// <summary>
/// Gets whether an assembly referenced by the currently designed document
/// has been changed since it has been loaded.
/// </summary>
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 #region VSDesigner workarounds
/// <summary> /// <summary>
/// HACK - Ignore any requests for types from the Microsoft.VSDesigner /// HACK - Ignore any requests for types from the Microsoft.VSDesigner

3
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/MyTypeFinder.cs

@ -18,6 +18,7 @@ namespace ICSharpCode.WpfDesign.AddIn
public class MyTypeFinder : XamlTypeFinder public class MyTypeFinder : XamlTypeFinder
{ {
OpenedFile file; OpenedFile file;
readonly TypeResolutionService typeResolutionService = new TypeResolutionService();
public static MyTypeFinder Create(OpenedFile file) public static MyTypeFinder Create(OpenedFile file)
{ {
@ -32,7 +33,7 @@ namespace ICSharpCode.WpfDesign.AddIn
if (string.IsNullOrEmpty(name)) { if (string.IsNullOrEmpty(name)) {
IProjectContent pc = GetProjectContent(file); IProjectContent pc = GetProjectContent(file);
if (pc != null) { if (pc != null) {
return TypeResolutionService.LoadAssembly(pc); return this.typeResolutionService.LoadAssembly(pc);
} }
return null; return null;
} else { } else {

Loading…
Cancel
Save