// // System.ComponentModel.Design.DesignerHost // // Authors: // Ivan N. Zlatev (contact i-nZ.net) // // (C) 2006-2007 Ivan N. Zlatev // // 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.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Windows.Forms.Design; using System.Reflection; namespace System.ComponentModel.Design { // A container for components and their designers // internal sealed class DesignerHost : Container, IDesignerLoaderHost, IDesignerHost, IServiceProvider, IComponentChangeService { #region DesignerHostTransaction : DesignerTransaction private enum TransactionAction { Commit, Cancel } private sealed class DesignerHostTransaction : DesignerTransaction { DesignerHost _designerHost; public DesignerHostTransaction (DesignerHost host, string description) : base (description) { _designerHost = host; } protected override void OnCancel () { _designerHost.OnTransactionClosing (this, TransactionAction.Cancel); _designerHost.OnTransactionClosed (this, TransactionAction.Cancel); } protected override void OnCommit () { _designerHost.OnTransactionClosing (this, TransactionAction.Commit); _designerHost.OnTransactionClosed (this, TransactionAction.Commit); } } // DesignerHostTransaction #endregion private IServiceProvider _serviceProvider; private Hashtable _designers; private Stack _transactions; private IServiceContainer _serviceContainer; private bool _loading; private bool _unloading; public DesignerHost (IServiceProvider serviceProvider) { if (serviceProvider == null) throw new ArgumentNullException ("serviceProvider"); _serviceProvider = serviceProvider; _serviceContainer = serviceProvider.GetService (typeof (IServiceContainer)) as IServiceContainer; _designers = new Hashtable (); _transactions = new Stack (); _loading = true; } #region IContainer // XXX: More validation here? // (e.g: Make use of a potentially existing INameCreationService) // public override void Add (IComponent component, string name) { AddPreProcess (component, name); base.Add (component, name); AddPostProcess (component, name); } internal void AddPreProcess (IComponent component, string name) { if (ComponentAdding != null) ComponentAdding (this, new ComponentEventArgs (component)); } internal void AddPostProcess (IComponent component, string name) { IDesigner designer; if (_rootComponent == null) { _rootComponent = component; designer = this.CreateDesigner (component, true); } else { designer = this.CreateDesigner (component, false); } if (designer != null) { _designers[component] = designer; designer.Initialize (component); } else { IUIService uiService = GetService (typeof (IUIService)) as IUIService; if (uiService != null) { uiService.ShowError ("Unable to load a designer for component type '" + component.GetType ().Name + "'"); } this.DestroyComponent (component); } // Activate the host and design surface once the root component is added to // the container and its designer is loaded and added to the designers collection if (component == _rootComponent) this.Activate (); if (component is IExtenderProvider) { IExtenderProviderService service = this.GetService (typeof (IExtenderProviderService)) as IExtenderProviderService; if (service != null) service.AddExtenderProvider ((IExtenderProvider) component); } if (ComponentAdded != null) ComponentAdded (this, new ComponentEventArgs (component)); } public override void Remove (IComponent component) { DesignerTransaction transaction = this.CreateTransaction ("Remove " + component.Site.Name); RemovePreProcess (component); base.Remove (component); RemovePostProcess (component); transaction.Commit (); } internal void RemovePreProcess (IComponent component) { if (!_unloading && ComponentRemoving != null) ComponentRemoving (this, new ComponentEventArgs (component)); IDesigner designer = _designers[component] as IDesigner; if (designer != null) designer.Dispose (); _designers.Remove (component); if (component == _rootComponent) _rootComponent = null; if (component is IExtenderProvider) { IExtenderProviderService service = GetService (typeof (IExtenderProviderService)) as IExtenderProviderService; if (service != null) service.RemoveExtenderProvider ((IExtenderProvider) component); } } internal void RemovePostProcess (IComponent component) { if (!_unloading && ComponentRemoved != null) ComponentRemoved (this, new ComponentEventArgs (component)); } protected override ISite CreateSite (IComponent component, string name) { if (name == null) { INameCreationService nameService = this.GetService (typeof (INameCreationService)) as INameCreationService; if (nameService != null) name = nameService.CreateName (this, component.GetType ()); } return new DesignModeSite (component, name, this, this); } #endregion #region IDesignerHost private IComponent _rootComponent; public IContainer Container { get { return this; } } public bool InTransaction { get { if (_transactions != null && _transactions.Count > 0) return true; return false; } } public bool Loading { get { return _loading; } } public IComponent RootComponent { get { return _rootComponent; } } public string RootComponentClassName { get { if (_rootComponent != null) return ((object)_rootComponent).GetType ().AssemblyQualifiedName; return null; } } public string TransactionDescription { get { if (_transactions != null && _transactions.Count > 0) return ((DesignerHostTransaction) _transactions.Peek()).Description; return null; } } // GUI loading in the designer should be done after the Activated event is raised. // public void Activate () { ISelectionService selectionService = GetService (typeof (ISelectionService)) as ISelectionService; // Set the Primary Selection to be the root component // if (selectionService != null) selectionService.SetSelectedComponents (new IComponent[] { _rootComponent }); if (Activated != null) Activated (this, EventArgs.Empty); } public IComponent CreateComponent (Type componentClass) { return CreateComponent (componentClass, null); } public IComponent CreateComponent (Type componentClass, string name) { if (componentClass == null) throw new ArgumentNullException ("componentClass"); else if (!typeof(IComponent).IsAssignableFrom(componentClass)) throw new ArgumentException ("componentClass"); IComponent component = this.CreateInstance (componentClass) as IComponent; this.Add (component, name); return component; } internal object CreateInstance (Type type) { if (type == null) throw new System.ArgumentNullException ("type"); // FIXME: Should I use TypeDescriptor.CreateInstance() for 2.0 ? // return Activator.CreateInstance (type, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance, null, null, null); } internal IDesigner CreateDesigner (IComponent component, bool rootDesigner) { if (component == null) throw new System.ArgumentNullException ("component"); if (rootDesigner) { //return TypeDescriptor.CreateDesigner (component, typeof (IRootDesigner)); return this.CreateDesigner (component, typeof (IRootDesigner)); } else { //return TypeDescriptor.CreateDesigner (component, typeof (IDesigner)); return this.CreateDesigner (component, typeof (IDesigner)); } } // Since most of the specific designers are missing this temporary method // will fallback to the first available designer type in the type's base types // private IDesigner CreateDesigner (IComponent component, Type designerBaseType) { IDesigner instance = null; AttributeCollection attributes = TypeDescriptor.GetAttributes (component); foreach (Attribute attribute in attributes) { DesignerAttribute designerAttr = attribute as DesignerAttribute; if (designerAttr != null && (designerBaseType.FullName == designerAttr.DesignerBaseTypeName || designerBaseType.AssemblyQualifiedName == designerAttr.DesignerBaseTypeName)) { Type type = Type.GetType (designerAttr.DesignerTypeName); if (type == null && designerBaseType == typeof (IRootDesigner)) type = typeof (System.Windows.Forms.Design.DocumentDesigner); if (type != null) instance = (IDesigner) Activator.CreateInstance (type); break; } } if (instance == null) { Type baseType = component.GetType ().BaseType; do { attributes = TypeDescriptor.GetAttributes (baseType); foreach (Attribute attribute in attributes) { DesignerAttribute designerAttr = attribute as DesignerAttribute; if (designerAttr != null && (designerBaseType.FullName == designerAttr.DesignerBaseTypeName || designerBaseType.AssemblyQualifiedName == designerAttr.DesignerBaseTypeName)) { Type type = Type.GetType (designerAttr.DesignerTypeName); if (type != null) instance = (IDesigner) Activator.CreateInstance (type); break; } } baseType = baseType.BaseType; } while (instance == null && baseType != null); } return instance; } public void DestroyComponent (IComponent component) { if (component.Site != null && component.Site.Container == this) { this.Remove (component); // takes care for the designer as well component.Dispose (); } } public IDesigner GetDesigner (IComponent component) { if (component == null) throw new ArgumentNullException ("component"); return _designers[component] as IDesigner; } public DesignerTransaction CreateTransaction () { return CreateTransaction (null); } public DesignerTransaction CreateTransaction (string description) { if (TransactionOpening != null) TransactionOpening (this, EventArgs.Empty); DesignerHostTransaction transaction = new DesignerHostTransaction (this, description); _transactions.Push (transaction); if (TransactionOpened != null) TransactionOpened (this, EventArgs.Empty); return transaction; } public Type GetType (string typeName) { Type result; ITypeResolutionService s = GetService (typeof (ITypeResolutionService)) as ITypeResolutionService; if (s != null) result = s.GetType (typeName); else result = Type.GetType (typeName); return result; } // Take care of disposing the designer the base.Dispose will cleanup // the components. // protected override void Dispose (bool disposing) { Unload (); base.Dispose (disposing); } public event EventHandler Activated; public event EventHandler Deactivated; public event EventHandler LoadComplete; public event DesignerTransactionCloseEventHandler TransactionClosed; public event DesignerTransactionCloseEventHandler TransactionClosing; public event EventHandler TransactionOpened; public event EventHandler TransactionOpening; private void OnTransactionClosing (DesignerHostTransaction raiser, TransactionAction action) { bool commit = false; bool lastTransaction = false; if (_transactions.Peek () != raiser) throw new InvalidOperationException ("Current transaction differs from the one a commit was requested for."); if (_transactions.Count == 1) lastTransaction = true; if (action == TransactionAction.Commit) commit = true; if (TransactionClosing != null) TransactionClosing (this, new DesignerTransactionCloseEventArgs (commit, lastTransaction)); } private void OnTransactionClosed (DesignerHostTransaction raiser, TransactionAction action) { bool commit = false; bool lastTransaction = false; if (_transactions.Peek () != raiser) throw new InvalidOperationException ("Current transaction differs from the one a commit was requested for."); if (_transactions.Count == 1) lastTransaction = true; if (action == TransactionAction.Commit) commit = true; _transactions.Pop (); if (TransactionClosed != null) TransactionClosed (this, new DesignerTransactionCloseEventArgs (commit, lastTransaction)); } #endregion #region IDesignerLoaderHost internal event LoadedEventHandler DesignerLoaderHostLoaded; internal event EventHandler DesignerLoaderHostLoading; internal event EventHandler DesignerLoaderHostUnloading; internal event EventHandler DesignerLoaderHostUnloaded; public void EndLoad (string rootClassName, bool successful, ICollection errorCollection) { if (DesignerLoaderHostLoaded != null) DesignerLoaderHostLoaded (this, new LoadedEventArgs (successful, errorCollection)); if (LoadComplete != null) LoadComplete (this, EventArgs.Empty); _loading = false; // _loading = true is set by the ctor } // BasicDesignerLoader invokes this.Reload, then invokes BeginLoad on itself, // then when loading it the loader is done it ends up in this.EndLoad. // At the end of the day Reload is more like Unload. // public void Reload () { _loading = true; Unload (); if (DesignerLoaderHostLoading != null) DesignerLoaderHostLoading (this, EventArgs.Empty); } private void Unload () { _unloading = true; if (DesignerLoaderHostUnloading != null) DesignerLoaderHostUnloading (this, EventArgs.Empty); IComponent[] components = new IComponent[this.Components.Count]; this.Components.CopyTo (components, 0); foreach (IComponent component in components) this.Remove (component); _transactions.Clear (); if (DesignerLoaderHostUnloaded != null) DesignerLoaderHostUnloaded (this, EventArgs.Empty); _unloading = false; } #endregion #region IComponentChangeService public event ComponentEventHandler ComponentAdded; public event ComponentEventHandler ComponentAdding; public event ComponentChangedEventHandler ComponentChanged; public event ComponentChangingEventHandler ComponentChanging; public event ComponentEventHandler ComponentRemoved; public event ComponentEventHandler ComponentRemoving; public event ComponentRenameEventHandler ComponentRename; public void OnComponentChanged (object component, MemberDescriptor member, object oldValue, object newValue) { if (ComponentChanged != null) ComponentChanged (this, new ComponentChangedEventArgs (component, member, oldValue, newValue)); } public void OnComponentChanging (object component, MemberDescriptor member) { if (ComponentChanging != null) ComponentChanging (this, new ComponentChangingEventArgs (component, member)); } internal void OnComponentRename (object component, string oldName, string newName) { if (ComponentRename != null) ComponentRename (this, new ComponentRenameEventArgs (component, oldName, newName)); } #endregion #region IServiceContainer // Wrapper around the DesignSurface service container // public void AddService (Type serviceType, object serviceInstance) { _serviceContainer.AddService (serviceType, serviceInstance); } public void AddService (Type serviceType, object serviceInstance, bool promote) { _serviceContainer.AddService (serviceType, serviceInstance, promote); } public void AddService (Type serviceType, ServiceCreatorCallback callback) { _serviceContainer.AddService (serviceType, callback); } public void AddService (Type serviceType, ServiceCreatorCallback callback, bool promote) { _serviceContainer.AddService (serviceType, callback, promote); } public void RemoveService (Type serviceType) { _serviceContainer.RemoveService (serviceType); } public void RemoveService (Type serviceType, bool promote) { _serviceContainer.RemoveService (serviceType, promote); } #endregion #region IServiceProvider public new object GetService (Type serviceType) { if (_serviceProvider != null) return _serviceProvider.GetService (serviceType); return null; } #endregion } }