596 lines
17 KiB
C#
596 lines
17 KiB
C#
|
//
|
||
|
// 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
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|