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 @@ -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 @@ -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 @@ -391,6 +393,7 @@ namespace ICSharpCode.FormsDesigner
designSurface = null;
}
this.typeResolutionService = null;
this.loader = null;
foreach (KeyValuePair<Type, TypeDescriptionProvider> entry in this.addedTypeDescriptionProviders) {
@ -550,8 +553,30 @@ namespace ICSharpCode.FormsDesigner @@ -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;

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

@ -137,11 +137,16 @@ namespace ICSharpCode.FormsDesigner.Gui @@ -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 @@ -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);
}
}

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

@ -10,6 +10,7 @@ using System.Collections.Generic; @@ -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 @@ -78,6 +79,11 @@ namespace ICSharpCode.FormsDesigner.Services
string formSourceFileName;
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>
/// Gets the project content of the project that created this TypeResolutionService.
@ -112,7 +118,7 @@ namespace ICSharpCode.FormsDesigner.Services @@ -112,7 +118,7 @@ namespace ICSharpCode.FormsDesigner.Services
/// <summary>
/// Loads the assembly represented by the project content. Returns null on failure.
/// </summary>
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 @@ -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;
}
}
/// <summary>
/// Loads the file in none-locking mode. Returns null on failure.
/// </summary>
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -407,6 +440,18 @@ namespace ICSharpCode.FormsDesigner.Services
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
/// <summary>
/// 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 @@ -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 @@ -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 {

Loading…
Cancel
Save