You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1021 lines
35 KiB
1021 lines
35 KiB
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.ComponentModel; |
|
using System.ComponentModel.Design; |
|
using System.ComponentModel.Design.Serialization; |
|
using System.Drawing; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Reflection; |
|
using System.Text; |
|
using System.Windows.Forms; |
|
using ICSharpCode.Core; |
|
using ICSharpCode.SharpDevelop.Designer; |
|
using ICSharpCode.SharpDevelop.Widgets; |
|
using ICSharpCode.SharpDevelop.WinForms; |
|
using ICSharpCode.SharpDevelop.Workbench; |
|
using ICSharpCode.FormsDesigner.Services; |
|
using ICSharpCode.FormsDesigner.UndoRedo; |
|
using ICSharpCode.NRefactory.Editor; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using ICSharpCode.SharpDevelop; |
|
using ICSharpCode.SharpDevelop.Editor; |
|
using ICSharpCode.SharpDevelop.Gui; |
|
using ICSharpCode.SharpDevelop.Project; |
|
using ICSharpCode.SharpDevelop.Refactoring; |
|
|
|
namespace ICSharpCode.FormsDesigner |
|
{ |
|
public class FormsDesignerViewContent : AbstractViewContentHandlingLoadErrors, IClipboardHandler, IUndoHandler, IHasPropertyContainer, IContextHelpProvider, IToolsHost, IFileDocumentProvider |
|
{ |
|
readonly Control pleaseWaitLabel = new Label() { Text = StringParser.Parse("${res:Global.PleaseWait}"), TextAlign=ContentAlignment.MiddleCenter }; |
|
DesignSurface designSurface; |
|
bool disposing; |
|
Timer timer = new Timer { Interval = 200 }; |
|
|
|
readonly IViewContent primaryViewContent; |
|
readonly IDesignerLoaderProvider loaderProvider; |
|
DesignerLoader loader; |
|
readonly ResourceStore resourceStore; |
|
FormsDesignerUndoEngine undoEngine; |
|
TypeResolutionService typeResolutionService; |
|
|
|
readonly DesignerSourceCodeStorage sourceCodeStorage; |
|
|
|
readonly Dictionary<Type, TypeDescriptionProvider> addedTypeDescriptionProviders = new Dictionary<Type, TypeDescriptionProvider>(); |
|
|
|
protected DesignSurface DesignSurface { |
|
get { |
|
return designSurface; |
|
} |
|
} |
|
|
|
public IDesignerHost Host { |
|
get { |
|
if (designSurface == null) |
|
return null; |
|
return (IDesignerHost)designSurface.GetService(typeof(IDesignerHost)); |
|
} |
|
} |
|
|
|
public OpenedFile DesignerCodeFile { |
|
get { return this.sourceCodeStorage.DesignerCodeFile; } |
|
} |
|
|
|
public IDocument PrimaryFileDocument { |
|
get { return this.sourceCodeStorage[this.PrimaryFile]; } |
|
} |
|
|
|
public ITextSource PrimaryFileContent { |
|
get { return this.PrimaryFileDocument.CreateSnapshot(); } |
|
} |
|
|
|
public IDocument DesignerCodeFileDocument { |
|
get { |
|
if (this.sourceCodeStorage.DesignerCodeFile == null) { |
|
return null; |
|
} else { |
|
return this.sourceCodeStorage[this.sourceCodeStorage.DesignerCodeFile]; |
|
} |
|
} |
|
} |
|
|
|
public string DesignerCodeFileContent { |
|
get { return this.DesignerCodeFileDocument.Text; } |
|
set { this.DesignerCodeFileDocument.Text = value; } |
|
} |
|
|
|
public IDocument GetDocumentForFile(OpenedFile file) |
|
{ |
|
return this.sourceCodeStorage[file]; |
|
} |
|
|
|
public IEnumerable<KeyValuePair<OpenedFile, IDocument>> SourceFiles { |
|
get { return this.sourceCodeStorage; } |
|
} |
|
|
|
protected DesignerSourceCodeStorage SourceCodeStorage { |
|
get { return this.sourceCodeStorage; } |
|
} |
|
|
|
public IViewContent PrimaryViewContent { |
|
get { return this.primaryViewContent; } |
|
} |
|
|
|
protected override string LoadErrorHeaderText { |
|
get { return StringParser.Parse("${res:ICSharpCode.SharpDevelop.FormDesigner.LoadErrorCheckSourceCodeForErrors}") + Environment.NewLine + Environment.NewLine; } |
|
} |
|
|
|
FormsDesignerViewContent(IViewContent primaryViewContent) |
|
: base() |
|
{ |
|
this.TabPageText = "${res:FormsDesigner.DesignTabPages.DesignTabPage}"; |
|
|
|
if (!FormKeyHandler.inserted) { |
|
FormKeyHandler.Insert(); |
|
} |
|
|
|
this.primaryViewContent = primaryViewContent; |
|
|
|
this.UserContent = this.pleaseWaitLabel; |
|
|
|
this.sourceCodeStorage = new DesignerSourceCodeStorage(); |
|
this.resourceStore = new ResourceStore(this); |
|
|
|
this.IsActiveViewContentChanged += this.IsActiveViewContentChangedHandler; |
|
|
|
timer.Tick += Timer_Tick; |
|
FileService.FileRemoving += this.FileServiceFileRemoving; |
|
SD.Debugger.DebugStarting += this.DebugStarting; |
|
} |
|
|
|
public FormsDesignerViewContent(IViewContent primaryViewContent, IDesignerLoaderProvider loaderProvider) |
|
: this(primaryViewContent) |
|
{ |
|
if (loaderProvider == null) |
|
throw new ArgumentNullException("loaderProvider"); |
|
|
|
this.loaderProvider = loaderProvider; |
|
|
|
this.Files.Add(this.primaryViewContent.PrimaryFile); |
|
} |
|
|
|
/// <summary> |
|
/// This constructor allows running in unit test mode with a mock file. |
|
/// </summary> |
|
public FormsDesignerViewContent(IViewContent primaryViewContent, OpenedFile mockFile) |
|
: this(primaryViewContent) |
|
{ |
|
this.sourceCodeStorage.AddFile(mockFile, Encoding.UTF8); |
|
this.sourceCodeStorage.DesignerCodeFile = mockFile; |
|
this.Files.Add(primaryViewContent.PrimaryFile); |
|
} |
|
|
|
void Timer_Tick(object sender, System.EventArgs e) |
|
{ |
|
// The WinForms designer internally relies on Application.Idle for some actions, e.g. 'Show Code' |
|
// This event does not get raised in a WPF application. |
|
// While we do forward WPF's equivalent idle event to WinForms (see WorkbenchStartup.cs), |
|
// it doesn't happen often enough -- in particular, it doesn't get raised while the mouse |
|
// is over the WinForms design surface. |
|
// This caused the bug: https://github.com/icsharpcode/SharpDevelop/issues/525 |
|
// As a workaround, we use a timer to raise the event while the designer is open. |
|
// Note: this timer is implemented in the WinForms designer and not globally in SharpDevelop |
|
// so that we don't wake up the CPU unnecessarily when the designer is not in use. |
|
Application.RaiseIdle(e); |
|
} |
|
|
|
bool inMasterLoadOperation; |
|
|
|
protected override void LoadInternal(OpenedFile file, System.IO.Stream stream) |
|
{ |
|
LoggingService.Debug("Forms designer: Load " + file.FileName + "; inMasterLoadOperation=" + this.inMasterLoadOperation); |
|
|
|
if (this.typeResolutionService != null) |
|
this.typeResolutionService.ClearCaches(); |
|
|
|
if (inMasterLoadOperation) { |
|
|
|
if (this.sourceCodeStorage.ContainsFile(file)) { |
|
LoggingService.Debug("Forms designer: Loading " + file.FileName + " in source code storage"); |
|
this.sourceCodeStorage.LoadFile(file, stream); |
|
} else { |
|
LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store"); |
|
this.resourceStore.Load(file, stream); |
|
} |
|
|
|
} else if (file == this.PrimaryFile || this.sourceCodeStorage.ContainsFile(file)) { |
|
|
|
if (this.loader != null && this.loader.Loading) { |
|
throw new InvalidOperationException("Designer loading a source code file while DesignerLoader is loading and the view is not in a master load operation. This must not happen."); |
|
} |
|
|
|
if (this.designSurface != null) { |
|
this.UnloadDesigner(); |
|
} |
|
|
|
this.inMasterLoadOperation = true; |
|
|
|
try { |
|
|
|
this.sourceCodeStorage.LoadFile(file, stream); |
|
|
|
LoggingService.Debug("Forms designer: Determining designer source files for " + file.FileName); |
|
OpenedFile newDesignerCodeFile; |
|
IReadOnlyList<OpenedFile> sourceFiles = loaderProvider.GetSourceFiles(this, out newDesignerCodeFile); |
|
if (sourceFiles == null || newDesignerCodeFile == null) { |
|
throw new FormsDesignerLoadException("The designer source files could not be determined."); |
|
} |
|
|
|
// Unload all source files from the view which are no longer in the returned collection |
|
foreach (OpenedFile f in this.Files.Except(sourceFiles).ToArray()) { |
|
// Ensure that we only unload source files, but not resource files. |
|
if (this.sourceCodeStorage.ContainsFile(f)) { |
|
LoggingService.Debug("Forms designer: Unloading file '" + f.FileName + "' because it no longer belongs to the designed form"); |
|
this.Files.Remove(f); |
|
this.sourceCodeStorage.RemoveFile(f); |
|
} |
|
} |
|
|
|
// Load all files which are new in the returned collection |
|
foreach (OpenedFile f in sourceFiles.Except(this.Files).ToArray()) { |
|
this.sourceCodeStorage.AddFile(f); |
|
this.Files.Add(f); |
|
} |
|
|
|
this.sourceCodeStorage.DesignerCodeFile = newDesignerCodeFile; |
|
|
|
this.LoadAndDisplayDesigner(); |
|
|
|
} finally { |
|
this.inMasterLoadOperation = false; |
|
} |
|
|
|
} else { |
|
|
|
// Loading a resource file |
|
|
|
bool mustReload; |
|
if (this.loader != null && !this.loader.Loading) { |
|
LoggingService.Debug("Forms designer: Reloading designer because of LoadInternal on resource file"); |
|
this.UnloadDesigner(); |
|
mustReload = true; |
|
this.inMasterLoadOperation = true; |
|
} else { |
|
mustReload = false; |
|
} |
|
|
|
try { |
|
LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store"); |
|
this.resourceStore.Load(file, stream); |
|
if (mustReload) { |
|
this.LoadAndDisplayDesigner(); |
|
} |
|
} finally { |
|
this.inMasterLoadOperation = false; |
|
} |
|
|
|
} |
|
} |
|
|
|
protected override void SaveInternal(OpenedFile file, System.IO.Stream stream) |
|
{ |
|
LoggingService.Debug("Forms designer: Save " + file.FileName); |
|
if (hasUnmergedChanges) { |
|
this.MergeFormChanges(); |
|
} |
|
if (this.sourceCodeStorage.ContainsFile(file)) { |
|
this.sourceCodeStorage.SaveFile(file, stream); |
|
} else { |
|
this.resourceStore.Save(file, stream); |
|
} |
|
} |
|
|
|
internal void AddResourceFile(OpenedFile file) |
|
{ |
|
this.Files.Add(file); |
|
} |
|
|
|
void LoadDesigner() |
|
{ |
|
LoggingService.Info("Form Designer: BEGIN INITIALIZE"); |
|
|
|
DefaultServiceContainer serviceContainer = new DefaultServiceContainer(); |
|
serviceContainer.AddService(typeof(System.Windows.Forms.Design.IUIService), new UIService()); |
|
serviceContainer.AddService(typeof(System.Drawing.Design.IToolboxService), ToolboxProvider.ToolboxService); |
|
|
|
serviceContainer.AddService(typeof(IHelpService), new HelpService()); |
|
serviceContainer.AddService(typeof(System.Drawing.Design.IPropertyValueUIService), new PropertyValueUIService()); |
|
|
|
serviceContainer.AddService(typeof(System.ComponentModel.Design.IResourceService), new DesignerResourceService(this.resourceStore)); |
|
AmbientProperties ambientProperties = new AmbientProperties(); |
|
serviceContainer.AddService(typeof(AmbientProperties), ambientProperties); |
|
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()); |
|
serviceContainer.AddService(typeof(ProjectResourceService), CreateProjectResourceService()); |
|
|
|
// Provide the ImageResourceEditor for all Image and Icon properties |
|
this.addedTypeDescriptionProviders.Add(typeof(Image), TypeDescriptor.AddAttributes(typeof(Image), new EditorAttribute(typeof(ImageResourceEditor), typeof(System.Drawing.Design.UITypeEditor)))); |
|
this.addedTypeDescriptionProviders.Add(typeof(Icon), TypeDescriptor.AddAttributes(typeof(Icon), new EditorAttribute(typeof(ImageResourceEditor), typeof(System.Drawing.Design.UITypeEditor)))); |
|
|
|
// if (generator.CodeDomProvider != null) { |
|
// serviceContainer.AddService(typeof(System.CodeDom.Compiler.CodeDomProvider), generator.CodeDomProvider); |
|
// } |
|
|
|
designSurface = CreateDesignSurface(serviceContainer); |
|
designSurface.Loading += this.DesignerLoading; |
|
designSurface.Loaded += this.DesignerLoaded; |
|
designSurface.Flushed += this.DesignerFlushed; |
|
designSurface.Unloading += this.DesignerUnloading; |
|
|
|
serviceContainer.AddService(typeof(System.ComponentModel.Design.IMenuCommandService), new ICSharpCode.FormsDesigner.Services.MenuCommandService(this, designSurface)); |
|
|
|
this.loader = loaderProvider.CreateLoader(this); |
|
designSurface.BeginLoad(this.loader); |
|
|
|
if (!designSurface.IsLoaded) { |
|
throw new FormsDesignerLoadException(FormatLoadErrors(designSurface)); |
|
} |
|
|
|
undoEngine = new FormsDesignerUndoEngine(Host); |
|
serviceContainer.AddService(typeof(UndoEngine), undoEngine); |
|
|
|
IComponentChangeService componentChangeService = (IComponentChangeService)designSurface.GetService(typeof(IComponentChangeService)); |
|
componentChangeService.ComponentChanged += ComponentChanged; |
|
componentChangeService.ComponentAdded += ComponentListChanged; |
|
componentChangeService.ComponentRemoved += ComponentListChanged; |
|
componentChangeService.ComponentRename += ComponentListChanged; |
|
this.Host.TransactionClosed += TransactionClose; |
|
|
|
ISelectionService selectionService = (ISelectionService)designSurface.GetService(typeof(ISelectionService)); |
|
selectionService.SelectionChanged += SelectionChangedHandler; |
|
|
|
if (IsTabOrderMode) { // fixes SD2-1015 |
|
tabOrderMode = false; // let ShowTabOrder call the designer command again |
|
ShowTabOrder(); |
|
} |
|
|
|
UpdatePropertyPad(); |
|
|
|
hasUnmergedChanges = false; |
|
timer.Start(); |
|
|
|
LoggingService.Info("Form Designer: END INITIALIZE"); |
|
} |
|
|
|
ProjectResourceService CreateProjectResourceService() |
|
{ |
|
var project = GetProjectForFile(); |
|
return new ProjectResourceService(project); |
|
} |
|
|
|
IProject GetProjectForFile() |
|
{ |
|
return SD.ProjectService.FindProjectContainingFile(this.DesignerCodeFile.FileName); |
|
} |
|
|
|
bool hasUnmergedChanges; |
|
|
|
void MakeDirty() |
|
{ |
|
hasUnmergedChanges = true; |
|
this.DesignerCodeFile.MakeDirty(); |
|
this.resourceStore.MarkResourceFilesAsDirty(); |
|
System.Windows.Input.CommandManager.InvalidateRequerySuggested(); |
|
} |
|
|
|
bool shouldUpdateSelectableObjects = false; |
|
|
|
void TransactionClose(object sender, DesignerTransactionCloseEventArgs e) |
|
{ |
|
if (shouldUpdateSelectableObjects) { |
|
// update the property pad after the transaction is *really* finished |
|
// (including updating the selection) |
|
SD.MainThread.InvokeAsyncAndForget(UpdatePropertyPad); |
|
shouldUpdateSelectableObjects = false; |
|
} |
|
} |
|
|
|
void ComponentChanged(object sender, ComponentChangedEventArgs e) |
|
{ |
|
bool loading = loader != null && loader.Loading; |
|
LoggingService.Debug("Forms designer: ComponentChanged: " + (e.Component == null ? "<null>" : e.Component.ToString()) + ", Member=" + (e.Member == null ? "<null>" : e.Member.Name) + ", OldValue=" + (e.OldValue == null ? "<null>" : e.OldValue.ToString()) + ", NewValue=" + (e.NewValue == null ? "<null>" : e.NewValue.ToString()) + "; Loading=" + loading + "; Unloading=" + unloading); |
|
if (!loading && !unloading) { |
|
MakeDirty(); |
|
} |
|
} |
|
|
|
void ComponentListChanged(object sender, EventArgs e) |
|
{ |
|
bool loading = this.loader != null && this.loader.Loading; |
|
LoggingService.Debug("Forms designer: Component added/removed/renamed, Loading=" + loading + ", Unloading=" + this.unloading); |
|
if (!loading && !unloading) { |
|
shouldUpdateSelectableObjects = true; |
|
this.MakeDirty(); |
|
} |
|
} |
|
|
|
void UnloadDesigner() |
|
{ |
|
LoggingService.Debug("FormsDesigner unloading, setting ActiveDesignSurface to null"); |
|
designSurfaceManager.ActiveDesignSurface = null; |
|
timer.Stop(); |
|
|
|
bool savedIsDirty = (this.DesignerCodeFile == null) ? false : this.DesignerCodeFile.IsDirty; |
|
this.UserContent = this.pleaseWaitLabel; |
|
if (this.DesignerCodeFile != null) { |
|
this.DesignerCodeFile.IsDirty = savedIsDirty; |
|
} |
|
|
|
if (designSurface != null) { |
|
designSurface.Loading -= this.DesignerLoading; |
|
designSurface.Loaded -= this.DesignerLoaded; |
|
designSurface.Flushed -= this.DesignerFlushed; |
|
designSurface.Unloading -= this.DesignerUnloading; |
|
|
|
IComponentChangeService componentChangeService = designSurface.GetService(typeof(IComponentChangeService)) as IComponentChangeService; |
|
if (componentChangeService != null) { |
|
componentChangeService.ComponentChanged -= ComponentChanged; |
|
componentChangeService.ComponentAdded -= ComponentListChanged; |
|
componentChangeService.ComponentRemoved -= ComponentListChanged; |
|
componentChangeService.ComponentRename -= ComponentListChanged; |
|
} |
|
if (this.Host != null) { |
|
this.Host.TransactionClosed -= TransactionClose; |
|
} |
|
|
|
ISelectionService selectionService = designSurface.GetService(typeof(ISelectionService)) as ISelectionService; |
|
if (selectionService != null) { |
|
selectionService.SelectionChanged -= SelectionChangedHandler; |
|
} |
|
|
|
designSurface.Unloaded += delegate { |
|
ServiceContainer serviceContainer = designSurface.GetService(typeof(ServiceContainer)) as ServiceContainer; |
|
if (serviceContainer != null) { |
|
// Workaround for .NET bug: .NET unregisters the designer host only if no component throws an exception, |
|
// but then in a finally block assumes that the designer host is already unloaded. |
|
// Thus we would get the confusing "InvalidOperationException: The container cannot be disposed at design time" |
|
// when any component throws an exception. |
|
|
|
// See http://community.sharpdevelop.net/forums/p/10928/35288.aspx |
|
// Reproducible with a custom control that has a designer that crashes on unloading |
|
// e.g. http://www.codeproject.com/KB/toolbars/WinFormsRibbon.aspx |
|
|
|
// We work around this problem by unregistering the designer host manually. |
|
try { |
|
var services = (Dictionary<Type, object>)typeof(ServiceContainer).InvokeMember( |
|
"Services", |
|
BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic, |
|
null, serviceContainer, null); |
|
foreach (var pair in services.ToArray()) { |
|
if (pair.Value is IDesignerHost) { |
|
serviceContainer.GetType().InvokeMember( |
|
"RemoveFixedService", |
|
BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, |
|
null, serviceContainer, new object[] { pair.Key }); |
|
} |
|
} |
|
} catch (Exception ex) { |
|
LoggingService.Error(ex); |
|
} |
|
} |
|
}; |
|
try { |
|
designSurface.Dispose(); |
|
} catch (ExceptionCollection exceptions) { |
|
foreach (Exception ex in exceptions.Exceptions) { |
|
LoggingService.Error(ex); |
|
} |
|
} finally { |
|
designSurface = null; |
|
} |
|
} |
|
|
|
this.typeResolutionService = null; |
|
this.loader = null; |
|
UpdatePropertyPad(); |
|
|
|
foreach (KeyValuePair<Type, TypeDescriptionProvider> entry in this.addedTypeDescriptionProviders) { |
|
TypeDescriptor.RemoveProvider(entry.Value, entry.Key); |
|
} |
|
this.addedTypeDescriptionProviders.Clear(); |
|
} |
|
|
|
readonly PropertyContainer propertyContainer = new PropertyContainer(); |
|
|
|
public PropertyContainer PropertyContainer { |
|
get { |
|
return propertyContainer; |
|
} |
|
} |
|
|
|
public void ShowHelp() |
|
{ |
|
if (Host == null) { |
|
return; |
|
} |
|
|
|
ISelectionService selectionService = (ISelectionService)Host.GetService(typeof(ISelectionService)); |
|
if (selectionService != null) { |
|
Control ctl = selectionService.PrimarySelection as Control; |
|
if (ctl != null) { |
|
ICSharpCode.SharpDevelop.HelpProvider.ShowHelp(ctl.GetType().FullName); |
|
} |
|
} |
|
} |
|
|
|
void LoadAndDisplayDesigner() |
|
{ |
|
SD.AnalyticsMonitor.TrackFeature(typeof(FormsDesignerViewContent), "Load"); |
|
try { |
|
|
|
LoadDesigner(); |
|
|
|
} catch (Exception e) { |
|
|
|
if (e.InnerException is FormsDesignerLoadException) { |
|
throw new FormsDesignerLoadException(e.InnerException.Message, e); |
|
} else if (e is FormsDesignerLoadException) { |
|
throw; |
|
} else if (designSurface != null && !designSurface.IsLoaded && designSurface.LoadErrors != null) { |
|
throw new FormsDesignerLoadException(FormatLoadErrors(designSurface), e); |
|
} else { |
|
throw; |
|
} |
|
|
|
} |
|
} |
|
|
|
internal new Control UserContent { |
|
get { |
|
CustomWindowsFormsHost host = base.UserContent as CustomWindowsFormsHost; |
|
return host != null ? host.Child : null; |
|
} |
|
set { |
|
CustomWindowsFormsHost host = base.UserContent as CustomWindowsFormsHost; |
|
if (value == null) { |
|
base.UserContent = null; |
|
if (host != null) |
|
host.Dispose(); |
|
return; |
|
} |
|
if (host != null && host.Child == value) { |
|
return; |
|
} |
|
if (host == null) { |
|
host = SD.WinForms.CreateWindowsFormsHost(this, true); |
|
} |
|
host.Child = value; |
|
base.UserContent = host; |
|
} |
|
} |
|
|
|
void DesignerLoading(object sender, EventArgs e) |
|
{ |
|
LoggingService.Debug("Forms designer: DesignerLoader loading..."); |
|
this.reloadPending = false; |
|
this.unloading = false; |
|
this.UserContent = this.pleaseWaitLabel; |
|
} |
|
|
|
void DesignerUnloading(object sender, EventArgs e) |
|
{ |
|
LoggingService.Debug("Forms designer: DesignerLoader unloading..."); |
|
this.unloading = true; |
|
if (!this.disposing) { |
|
this.UserContent = this.pleaseWaitLabel; |
|
} |
|
} |
|
|
|
bool reloadPending; |
|
bool unloading; |
|
|
|
void DesignerLoaded(object sender, LoadedEventArgs e) |
|
{ |
|
// This method is called when the designer has loaded. |
|
LoggingService.Debug("Forms designer: DesignerLoader loaded, HasSucceeded=" + e.HasSucceeded.ToString()); |
|
this.reloadPending = false; |
|
this.unloading = false; |
|
|
|
if (e.HasSucceeded) { |
|
// Display the designer on the view content |
|
bool savedIsDirty = this.DesignerCodeFile.IsDirty; |
|
Control designView = (Control)this.designSurface.View; |
|
|
|
designView.BackColor = Color.White; |
|
designView.RightToLeft = RightToLeft.No; |
|
// Make sure auto-scaling is based on the correct font. |
|
// This is required on Vista, I don't know why it works correctly in XP |
|
designView.Font = System.Windows.Forms.Control.DefaultFont; |
|
|
|
this.UserContent = designView; |
|
LoggingService.Debug("FormsDesigner loaded, setting ActiveDesignSurface to " + this.designSurface.ToString()); |
|
designSurfaceManager.ActiveDesignSurface = this.designSurface; |
|
this.DesignerCodeFile.IsDirty = savedIsDirty; |
|
this.UpdatePropertyPad(); |
|
} else { |
|
// This method can not only be called during initialization, |
|
// but also when the designer reloads itself because of |
|
// a language change. |
|
// When a load error occurs there, we are not somewhere |
|
// below the Load method which handles load errors. |
|
// That is why we create an error text box here anyway. |
|
TextBox errorTextBox = new TextBox() { Multiline=true, ScrollBars=ScrollBars.Both, ReadOnly=true, BackColor=SystemColors.Window, Dock=DockStyle.Fill }; |
|
errorTextBox.Text = String.Concat(this.LoadErrorHeaderText, FormatLoadErrors(designSurface)); |
|
this.UserContent = errorTextBox; |
|
} |
|
} |
|
|
|
void DesignerFlushed(object sender, EventArgs e) |
|
{ |
|
this.resourceStore.CommitAllResourceChanges(); |
|
this.hasUnmergedChanges = false; |
|
} |
|
|
|
static string FormatLoadErrors(DesignSurface designSurface) |
|
{ |
|
StringBuilder sb = new StringBuilder(); |
|
foreach(Exception le in designSurface.LoadErrors) { |
|
sb.AppendLine(le.ToString()); |
|
sb.AppendLine(); |
|
} |
|
return sb.ToString(); |
|
} |
|
|
|
public virtual void MergeFormChanges() |
|
{ |
|
SD.AnalyticsMonitor.TrackFeature(typeof(FormsDesignerViewContent), "Save"); |
|
if (this.HasLoadError || this.designSurface == null) { |
|
LoggingService.Debug("Forms designer: Cannot merge form changes because the designer is not loaded successfully or not loaded at all"); |
|
return; |
|
} else if (this.DesignerCodeFile == null) { |
|
throw new InvalidOperationException("Cannot merge form changes without a designer code file."); |
|
} |
|
bool isDirty = this.DesignerCodeFile.IsDirty; |
|
LoggingService.Info("Merging form changes..."); |
|
designSurface.Flush(); |
|
this.resourceStore.CommitAllResourceChanges(); |
|
LoggingService.Info("Finished merging form changes"); |
|
hasUnmergedChanges = false; |
|
this.DesignerCodeFile.IsDirty = isDirty; |
|
} |
|
|
|
public void ShowSourceCode(int lineNumber = 0) |
|
{ |
|
this.WorkbenchWindow.ActiveViewContent = this.PrimaryViewContent; |
|
ITextEditor editor = this.primaryViewContent.GetService<ITextEditor>(); |
|
if (editor != null) { |
|
editor.JumpTo(lineNumber, 1); |
|
} |
|
} |
|
|
|
/* |
|
public void ShowSourceCode(IComponent component, EventDescriptor edesc, string eventMethodName) |
|
{ |
|
int position; |
|
string file; |
|
bool eventCreated = generator.InsertComponentEvent(component, edesc, eventMethodName, "", out file, out position); |
|
if (eventCreated) { |
|
if (FileUtility.IsEqualFileName(file, this.primaryViewContent.PrimaryFileName)) { |
|
ShowSourceCode(position); |
|
} else { |
|
FileService.JumpToFilePosition(file, position, 0); |
|
} |
|
} |
|
} |
|
|
|
public ICollection GetCompatibleMethods(EventDescriptor edesc) |
|
{ |
|
return generator.GetCompatibleMethods(edesc); |
|
} |
|
*/ |
|
|
|
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"); |
|
this.reloadPending = true; |
|
if (!loaderService.Reload()) { |
|
this.reloadPending = false; |
|
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; |
|
} |
|
} |
|
|
|
public override void Dispose() |
|
{ |
|
disposing = true; |
|
try { |
|
// base.Dispose() is called first because it may trigger a call |
|
// to SaveInternal which requires the designer to be loaded. |
|
base.Dispose(); |
|
} finally { |
|
SD.Debugger.DebugStarting -= this.DebugStarting; |
|
FileService.FileRemoving -= this.FileServiceFileRemoving; |
|
|
|
this.UnloadDesigner(); |
|
|
|
this.IsActiveViewContentChanged -= this.IsActiveViewContentChangedHandler; |
|
|
|
this.resourceStore.Dispose(); |
|
|
|
this.UserContent = null; |
|
this.pleaseWaitLabel.Dispose(); |
|
} |
|
} |
|
|
|
void SelectionChangedHandler(object sender, EventArgs args) |
|
{ |
|
UpdatePropertyPadSelection((ISelectionService)sender); |
|
} |
|
|
|
void UpdatePropertyPadSelection(ISelectionService selectionService) |
|
{ |
|
ICollection selection = selectionService.GetSelectedComponents(); |
|
object[] selArray = new object[selection.Count]; |
|
selection.CopyTo(selArray, 0); |
|
propertyContainer.SelectedObjects = selArray; |
|
System.Windows.Input.CommandManager.InvalidateRequerySuggested(); |
|
} |
|
|
|
protected void UpdatePropertyPad() |
|
{ |
|
if (Host != null) { |
|
propertyContainer.Host = Host; |
|
propertyContainer.SelectableObjects = Host.Container.Components; |
|
ISelectionService selectionService = (ISelectionService)Host.GetService(typeof(ISelectionService)); |
|
if (selectionService != null) { |
|
UpdatePropertyPadSelection(selectionService); |
|
} |
|
} else { |
|
propertyContainer.Host = null; |
|
propertyContainer.SelectableObjects = null; |
|
} |
|
} |
|
|
|
#region IUndoHandler implementation |
|
public bool EnableUndo { |
|
get { |
|
if (undoEngine != null) { |
|
return undoEngine.EnableUndo; |
|
} |
|
return false; |
|
} |
|
} |
|
public bool EnableRedo { |
|
get { |
|
if (undoEngine != null) { |
|
return undoEngine.EnableRedo; |
|
} |
|
return false; |
|
} |
|
} |
|
public virtual void Undo() |
|
{ |
|
if (undoEngine != null) { |
|
undoEngine.Undo(); |
|
} |
|
} |
|
|
|
public virtual void Redo() |
|
{ |
|
if (undoEngine != null) { |
|
undoEngine.Redo(); |
|
} |
|
} |
|
#endregion |
|
|
|
#region IClipboardHandler implementation |
|
bool IsMenuCommandEnabled(CommandID commandID) |
|
{ |
|
if (designSurface == null) { |
|
return false; |
|
} |
|
|
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
if (menuCommandService == null) { |
|
return false; |
|
} |
|
|
|
System.ComponentModel.Design.MenuCommand menuCommand = menuCommandService.FindCommand(commandID); |
|
if (menuCommand == null) { |
|
return false; |
|
} |
|
|
|
//int status = menuCommand.OleStatus; |
|
return menuCommand.Enabled; |
|
} |
|
|
|
public bool EnableCut { |
|
get { |
|
return IsMenuCommandEnabled(StandardCommands.Cut); |
|
} |
|
} |
|
|
|
public bool EnableCopy { |
|
get { |
|
return IsMenuCommandEnabled(StandardCommands.Copy); |
|
} |
|
} |
|
|
|
const string ComponentClipboardFormat = "CF_DESIGNERCOMPONENTS"; |
|
public bool EnablePaste { |
|
get { |
|
return IsMenuCommandEnabled(StandardCommands.Paste); |
|
} |
|
} |
|
|
|
public bool EnableDelete { |
|
get { |
|
return IsMenuCommandEnabled(StandardCommands.Delete); |
|
} |
|
} |
|
|
|
public bool EnableSelectAll { |
|
get { |
|
return designSurface != null; |
|
} |
|
} |
|
|
|
public void Cut() |
|
{ |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.Cut); |
|
} |
|
|
|
public void Copy() |
|
{ |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.Copy); |
|
} |
|
|
|
public void Paste() |
|
{ |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.Paste); |
|
} |
|
|
|
public void Delete() |
|
{ |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.Delete); |
|
} |
|
|
|
public void SelectAll() |
|
{ |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.SelectAll); |
|
} |
|
#endregion |
|
|
|
#region Tab Order Handling |
|
bool tabOrderMode = false; |
|
public virtual bool IsTabOrderMode { |
|
get { |
|
return tabOrderMode; |
|
} |
|
} |
|
|
|
public virtual void ShowTabOrder() |
|
{ |
|
if (!IsTabOrderMode) { |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.TabOrder); |
|
tabOrderMode = true; |
|
} |
|
} |
|
|
|
public virtual void HideTabOrder() |
|
{ |
|
if (IsTabOrderMode) { |
|
IMenuCommandService menuCommandService = (IMenuCommandService)designSurface.GetService(typeof(IMenuCommandService)); |
|
menuCommandService.GlobalInvoke(StandardCommands.TabOrder); |
|
tabOrderMode = false; |
|
} |
|
} |
|
#endregion |
|
|
|
protected void MergeAndUnloadDesigner() |
|
{ |
|
propertyContainer.Clear(); |
|
if (!this.HasLoadError) { |
|
MergeFormChanges(); |
|
} |
|
UnloadDesigner(); |
|
} |
|
|
|
protected void ReloadDesignerFromMemory() |
|
{ |
|
using(MemoryStream ms = new MemoryStream(this.sourceCodeStorage.GetFileEncoding(this.DesignerCodeFile).GetBytes(this.DesignerCodeFileContent), false)) { |
|
this.Load(this.DesignerCodeFile, ms); |
|
} |
|
|
|
UpdatePropertyPad(); |
|
} |
|
|
|
public virtual object ToolsContent { |
|
get { return ToolboxProvider.FormsDesignerSideBar; } |
|
} |
|
|
|
void FileServiceFileRemoving(object sender, FileCancelEventArgs e) |
|
{ |
|
if (!e.Cancel) { |
|
this.CheckForDesignerCodeFileDeletion(e); |
|
} |
|
} |
|
|
|
void CheckForDesignerCodeFileDeletion(FileCancelEventArgs e) |
|
{ |
|
OpenedFile file; |
|
|
|
if (e.IsDirectory) { |
|
file = this.Files.SingleOrDefault( |
|
f => FileUtility.IsBaseDirectory(e.FileName, f.FileName) |
|
); |
|
} else { |
|
file = this.Files.SingleOrDefault( |
|
f => FileUtility.IsEqualFileName(f.FileName, e.FileName) |
|
); |
|
} |
|
|
|
if (file == null || file == this.PrimaryFile) |
|
return; |
|
|
|
LoggingService.Info("Forms designer: Handling deletion of open designer code file '" + file.FileName + "'"); |
|
|
|
if (file == this.sourceCodeStorage.DesignerCodeFile) { |
|
this.UnloadDesigner(); |
|
this.sourceCodeStorage.DesignerCodeFile = null; |
|
} |
|
|
|
// When any of our designer code files is deleted, |
|
// remove the file from the file list so that |
|
// the primary view is not closed because of this event. |
|
this.Files.Remove(file); |
|
this.sourceCodeStorage.RemoveFile(file); |
|
} |
|
|
|
#region Design surface manager (static) |
|
|
|
static readonly DesignSurfaceManager designSurfaceManager = new DesignSurfaceManager(); |
|
|
|
public static DesignSurface CreateDesignSurface(IServiceProvider serviceProvider) |
|
{ |
|
return designSurfaceManager.CreateDesignSurface(serviceProvider); |
|
} |
|
|
|
#endregion |
|
|
|
#region Debugger event handling (to prevent designer reload while debugger is starting) |
|
|
|
void DebugStarting(object sender, EventArgs e) |
|
{ |
|
if (designSurfaceManager.ActiveDesignSurface != this.DesignSurface || |
|
!this.reloadPending) |
|
return; |
|
|
|
// The designer loader does not reload immediately, |
|
// but only when the Application.Idle event is raised. |
|
// When the IsActiveViewContentChangedHandler has been called because of the |
|
// layout change prior to starting the debugger, and it has |
|
// initiated a reload because of a changed referenced assembly, |
|
// the reload can interrupt the starting of the debugger. |
|
// To prevent this, we explicitly raise the Idle event here. |
|
LoggingService.Debug("Forms designer: DebugStarting raises the Idle event to force pending reload now"); |
|
Cursor oldCursor = Cursor.Current; |
|
Cursor.Current = Cursors.WaitCursor; |
|
try { |
|
Application.RaiseIdle(EventArgs.Empty); |
|
} finally { |
|
Cursor.Current = oldCursor; |
|
} |
|
} |
|
|
|
#endregion |
|
} |
|
}
|
|
|