Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2224 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
18 changed files with 653 additions and 21 deletions
@ -0,0 +1,23 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using ICSharpCode.WpfDesign.Extensions; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign.Designer.Extensions |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Makes the TabItem clickable.
|
||||||
|
/// </summary>
|
||||||
|
[ExtensionFor(typeof(TabItem))] |
||||||
|
public sealed class TabItemClickableExtension : BehaviorExtension |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign.Extensions |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Base class for extensions that provide a behavior interface for the designed item.
|
||||||
|
/// These extensions are always loaded. They must have an parameter-less constructor.
|
||||||
|
/// </summary>
|
||||||
|
[ExtensionServer(typeof(BehaviorExtension.BehaviorExtensionServer))] |
||||||
|
public abstract class BehaviorExtension : Extension |
||||||
|
{ |
||||||
|
DesignItem _extendedItem; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item that is being extended by the BehaviorExtension.
|
||||||
|
/// </summary>
|
||||||
|
public DesignItem ExtendedItem { |
||||||
|
get { |
||||||
|
if (_extendedItem == null) |
||||||
|
throw new InvalidOperationException("Cannot access BehaviorExtension.ExtendedItem: " + |
||||||
|
"The property is not initialized yet. Please move initialization logic " + |
||||||
|
"that depends on ExtendedItem into the OnInitialized method."); |
||||||
|
return _extendedItem; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the design context of the extended item. "Context" is equivalent to "ExtendedItem.Context".
|
||||||
|
/// </summary>
|
||||||
|
public DesignContext Context { |
||||||
|
get { |
||||||
|
return this.ExtendedItem.Context; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the service container of the extended item. "Services" is equivalent to "ExtendedItem.Services".
|
||||||
|
/// </summary>
|
||||||
|
public ServiceContainer Services { |
||||||
|
get { |
||||||
|
return this.ExtendedItem.Services; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is called after the ExtendedItem was set.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnInitialized() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
sealed class BehaviorExtensionServer : ExtensionServer |
||||||
|
{ |
||||||
|
public override bool ShouldApplyExtensions(DesignItem extendedItem) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override Extension CreateExtension(Type extensionType, DesignItem extendedItem) |
||||||
|
{ |
||||||
|
BehaviorExtension ext = (BehaviorExtension)Activator.CreateInstance(extensionType); |
||||||
|
ext._extendedItem = extendedItem; |
||||||
|
ext.OnInitialized(); |
||||||
|
return ext; |
||||||
|
} |
||||||
|
|
||||||
|
public override void RemoveExtension(Extension extension) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign.Extensions |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Attribute to specify that the decorated class is a WPF extension for the specified item type.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true, Inherited=false)] |
||||||
|
public sealed class ExtensionForAttribute : Attribute |
||||||
|
{ |
||||||
|
Type _designedItemType; |
||||||
|
Type _overrideExtension; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the item that is designed using this extension.
|
||||||
|
/// </summary>
|
||||||
|
public Type DesignedItemType { |
||||||
|
get { return _designedItemType; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/Sets the type of another extension that this extension is overriding.
|
||||||
|
/// </summary>
|
||||||
|
public Type OverrideExtension { |
||||||
|
get { return _overrideExtension; } |
||||||
|
set { |
||||||
|
_overrideExtension = value; |
||||||
|
if (value != null) { |
||||||
|
if (!typeof(Extension).IsAssignableFrom(value)) { |
||||||
|
throw new ArgumentException("OverrideExtension must specify the type of an Extension."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new ExtensionForAttribute that specifies that the decorated class
|
||||||
|
/// is a WPF extension for the specified item type.
|
||||||
|
/// </summary>
|
||||||
|
public ExtensionForAttribute(Type designedItemType) |
||||||
|
{ |
||||||
|
if (designedItemType == null) |
||||||
|
throw new ArgumentNullException("designedItemType"); |
||||||
|
if (!designedItemType.IsClass) |
||||||
|
throw new ArgumentException("designedItemType must be a class"); |
||||||
|
_designedItemType = designedItemType; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,190 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reflection; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign.Extensions |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Manages extension creation for a design context.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ExtensionManager |
||||||
|
{ |
||||||
|
readonly DesignContext _context; |
||||||
|
|
||||||
|
internal ExtensionManager(DesignContext context) |
||||||
|
{ |
||||||
|
Debug.Assert(context != null); |
||||||
|
this._context = context; |
||||||
|
|
||||||
|
context.Services.Subscribe<IComponentService>( |
||||||
|
delegate(IComponentService componentService) { |
||||||
|
componentService.ComponentRegistered += OnComponentRegistered; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
void OnComponentRegistered(object sender, DesignItemEventArgs e) |
||||||
|
{ |
||||||
|
e.Item.SetExtensionServers(GetExtensionServersForItem(e.Item)); |
||||||
|
e.Item.ApplyExtensions(this); |
||||||
|
} |
||||||
|
|
||||||
|
#region Manage ExtensionEntries
|
||||||
|
sealed class ExtensionEntry |
||||||
|
{ |
||||||
|
internal readonly Type ExtensionType; |
||||||
|
internal readonly ExtensionServer Server; |
||||||
|
internal readonly Type OverriddenExtensionType; |
||||||
|
|
||||||
|
public ExtensionEntry(Type extensionType, ExtensionServer server, Type overriddenExtensionType) |
||||||
|
{ |
||||||
|
this.ExtensionType = extensionType; |
||||||
|
this.Server = server; |
||||||
|
this.OverriddenExtensionType = overriddenExtensionType; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Dictionary<Type, List<ExtensionEntry>> _extensions = new Dictionary<Type, List<ExtensionEntry>>(); |
||||||
|
|
||||||
|
void AddExtensionEntry(Type extendedItemType, ExtensionEntry entry) |
||||||
|
{ |
||||||
|
List<ExtensionEntry> list; |
||||||
|
if (!_extensions.TryGetValue(extendedItemType, out list)) { |
||||||
|
list = _extensions[extendedItemType] = new List<ExtensionEntry>(); |
||||||
|
} |
||||||
|
list.Add(entry); |
||||||
|
} |
||||||
|
|
||||||
|
List<ExtensionEntry> GetExtensionEntries(Type extendedItemType) |
||||||
|
{ |
||||||
|
List<ExtensionEntry> result; |
||||||
|
if (extendedItemType.BaseType != null) |
||||||
|
result = GetExtensionEntries(extendedItemType.BaseType); |
||||||
|
else |
||||||
|
result = new List<ExtensionEntry>(); |
||||||
|
|
||||||
|
List<ExtensionEntry> list; |
||||||
|
if (_extensions.TryGetValue(extendedItemType, out list)) { |
||||||
|
foreach (ExtensionEntry entry in list) { |
||||||
|
if (entry.OverriddenExtensionType != null) { |
||||||
|
result.RemoveAll(delegate(ExtensionEntry oldEntry) { |
||||||
|
return oldEntry.ExtensionType == entry.OverriddenExtensionType; |
||||||
|
}); |
||||||
|
} |
||||||
|
result.Add(entry); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Create Extensions
|
||||||
|
static readonly ExtensionEntry[] emptyExtensionEntryArray = new ExtensionEntry[0]; |
||||||
|
|
||||||
|
IEnumerable<ExtensionEntry> GetExtensionEntries(DesignItem extendedItem) |
||||||
|
{ |
||||||
|
if (extendedItem.Component == null) |
||||||
|
return emptyExtensionEntryArray; |
||||||
|
else |
||||||
|
return GetExtensionEntries(extendedItem.Component.GetType()); |
||||||
|
} |
||||||
|
|
||||||
|
ExtensionServer[] GetExtensionServersForItem(DesignItem item) |
||||||
|
{ |
||||||
|
Debug.Assert(item != null); |
||||||
|
|
||||||
|
HashSet<ExtensionServer> servers = new HashSet<ExtensionServer>(); |
||||||
|
foreach (ExtensionEntry entry in GetExtensionEntries(item)) { |
||||||
|
servers.Add(entry.Server); |
||||||
|
} |
||||||
|
return Linq.ToArray(servers); |
||||||
|
} |
||||||
|
|
||||||
|
internal IEnumerable<Extension> CreateExtensions(ExtensionServer server, DesignItem item) |
||||||
|
{ |
||||||
|
Debug.Assert(server != null); |
||||||
|
Debug.Assert(item != null); |
||||||
|
|
||||||
|
foreach (ExtensionEntry entry in GetExtensionEntries(item)) { |
||||||
|
if (entry.Server == server) { |
||||||
|
yield return server.CreateExtension(entry.ExtensionType, item); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RegisterAssembly
|
||||||
|
HashSet<Assembly> _registeredAssemblies = new HashSet<Assembly>(); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers extensions from the specified assembly.
|
||||||
|
/// </summary>
|
||||||
|
public void RegisterAssembly(Assembly assembly) |
||||||
|
{ |
||||||
|
if (assembly == null) |
||||||
|
throw new ArgumentNullException("assembly"); |
||||||
|
|
||||||
|
// object[] assemblyAttributes = assembly.GetCustomAttributes(typeof(IsWpfDesignerAssemblyAttribute), false);
|
||||||
|
// if (assemblyAttributes.Length == 0)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
if (!_registeredAssemblies.Add(assembly)) { |
||||||
|
// the assembly already is registered, don't try to register it again.
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// IsWpfDesignerAssemblyAttribute isWpfDesignerAssembly = (IsWpfDesignerAssemblyAttribute)assemblyAttributes[0];
|
||||||
|
// foreach (Type type in isWpfDesignerAssembly.UsePrivateReflection ? assembly.GetTypes() : assembly.GetExportedTypes()) {
|
||||||
|
foreach (Type type in assembly.GetTypes()) { |
||||||
|
object[] extensionForAttributes = type.GetCustomAttributes(typeof(ExtensionForAttribute), false); |
||||||
|
if (extensionForAttributes.Length == 0) |
||||||
|
continue; |
||||||
|
|
||||||
|
foreach (ExtensionForAttribute designerFor in extensionForAttributes) { |
||||||
|
ExtensionServer server = GetServerForExtension(type); |
||||||
|
AddExtensionEntry(designerFor.DesignedItemType, new ExtensionEntry(type, server, designerFor.OverrideExtension)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Extension Server Creation
|
||||||
|
// extension server type => extension server instance
|
||||||
|
Dictionary<Type, ExtensionServer> _extensionServers = new Dictionary<Type, ExtensionServer>(); |
||||||
|
|
||||||
|
ExtensionServer GetServerForExtension(Type extensionType) |
||||||
|
{ |
||||||
|
Debug.Assert(extensionType != null); |
||||||
|
|
||||||
|
foreach (ExtensionServerAttribute esa in extensionType.GetCustomAttributes(typeof(ExtensionServerAttribute), true)) { |
||||||
|
return GetExtensionServer(esa); |
||||||
|
} |
||||||
|
throw new DesignerException("Extension types must have a [ExtensionServer] attribute."); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the extension server for the specified extension server attribute.
|
||||||
|
/// </summary>
|
||||||
|
public ExtensionServer GetExtensionServer(ExtensionServerAttribute attribute) |
||||||
|
{ |
||||||
|
Type extensionServerType = attribute.ExtensionServerType; |
||||||
|
|
||||||
|
ExtensionServer server; |
||||||
|
if (_extensionServers.TryGetValue(extensionServerType, out server)) |
||||||
|
return server; |
||||||
|
|
||||||
|
server = (ExtensionServer)Activator.CreateInstance(extensionServerType); |
||||||
|
server.InitializeExtensionServer(_context); |
||||||
|
_extensionServers[extensionServerType] = server; |
||||||
|
return server; |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Diagnostics; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign.Extensions |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// An ExtensionServer manages a creating and removing extensions of the specific extension type.
|
||||||
|
/// For a given DesignContext, a ExtensionServer is created only once.
|
||||||
|
/// The ExtensionServer can handle events raised by services without having to unregister its events
|
||||||
|
/// handlers because the ExtensionServer runs for the lifetime of the DesignContext.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ExtensionServer |
||||||
|
{ |
||||||
|
DesignContext _context; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the context this extension server was created for.
|
||||||
|
/// </summary>
|
||||||
|
public DesignContext Context { |
||||||
|
[DebuggerStepThrough] |
||||||
|
get { |
||||||
|
if (_context == null) |
||||||
|
throw new InvalidOperationException("Cannot access ExtensionServer.Context: " + |
||||||
|
"The property is not initialized yet. Please move initialization logic " + |
||||||
|
"that depends on Context into the OnInitialized method."); |
||||||
|
return _context; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the service container of the current context. "x.Services" is equivalent to "x.Context.Services".
|
||||||
|
/// </summary>
|
||||||
|
public ServiceContainer Services { |
||||||
|
[DebuggerStepThrough] |
||||||
|
get { return this.Context.Services; } |
||||||
|
} |
||||||
|
|
||||||
|
internal void InitializeExtensionServer(DesignContext context) |
||||||
|
{ |
||||||
|
Debug.Assert(this._context == null); |
||||||
|
Debug.Assert(context != null); |
||||||
|
this._context = context; |
||||||
|
OnInitialized(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is called after the extension server is initialized and the <see cref="Context"/> property has been set.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnInitialized() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets if the extension manager should apply the extensions from this server to the specified item.
|
||||||
|
/// Is called by the ExtensionManager.
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool ShouldApplyExtensions(DesignItem extendedItem); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an extension of the specified type.
|
||||||
|
/// Is called by the ExtensionManager.
|
||||||
|
/// </summary>
|
||||||
|
public abstract Extension CreateExtension(Type extensionType, DesignItem extendedItem); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method is called before an extension is removed from its DesignItem because it should not be applied anymore.
|
||||||
|
/// Is called by the ExtensionManager.
|
||||||
|
/// </summary>
|
||||||
|
public abstract void RemoveExtension(Extension extension); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign.Extensions |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Attribute to specify that the decorated class is an extension using the specified extension server.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)] |
||||||
|
public sealed class ExtensionServerAttribute : Attribute |
||||||
|
{ |
||||||
|
Type _extensionServerType; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the item that is designed using this extension.
|
||||||
|
/// </summary>
|
||||||
|
public Type ExtensionServerType { |
||||||
|
get { return _extensionServerType; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new ExtensionServerAttribute that specifies that the decorated extension
|
||||||
|
/// uses the specified extension server.
|
||||||
|
/// </summary>
|
||||||
|
public ExtensionServerAttribute(Type extensionServerType) |
||||||
|
{ |
||||||
|
if (extensionServerType == null) |
||||||
|
throw new ArgumentNullException("extensionServerType"); |
||||||
|
if (!typeof(ExtensionServer).IsAssignableFrom(extensionServerType)) |
||||||
|
throw new ArgumentException("extensionServerType must derive from ExtensionServer"); |
||||||
|
if (extensionServerType.GetConstructor(new Type[0]) == null) |
||||||
|
throw new ArgumentException("extensionServerType must have a parameter-less constructor"); |
||||||
|
_extensionServerType = extensionServerType; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace ICSharpCode.WpfDesign |
||||||
|
{ |
||||||
|
// Static helpers that should become extension methods in the future
|
||||||
|
static class Linq |
||||||
|
{ |
||||||
|
public static T[] ToArray<T>(ICollection<T> collection) |
||||||
|
{ |
||||||
|
T[] arr = new T[collection.Count]; |
||||||
|
collection.CopyTo(arr, 0); |
||||||
|
return arr; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue