diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Configuration/AssemblyInfo.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Configuration/AssemblyInfo.cs index dd3bf27b2b..d49c6131a6 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Configuration/AssemblyInfo.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Configuration/AssemblyInfo.cs @@ -7,6 +7,7 @@ using System.Resources; using System.Globalization; using System.Windows; using System.Runtime.InteropServices; +using ICSharpCode.WpfDesign.Extensions; #endregion diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TabItemClickableExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TabItemClickableExtension.cs new file mode 100644 index 0000000000..c8bfc7dbe3 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Extensions/TabItemClickableExtension.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows; +using System.Windows.Controls; +using ICSharpCode.WpfDesign.Extensions; + +namespace ICSharpCode.WpfDesign.Designer.Extensions +{ + /// + /// Makes the TabItem clickable. + /// + [ExtensionFor(typeof(TabItem))] + public sealed class TabItemClickableExtension : BehaviorExtension + { + + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Linq.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Linq.cs index defcfbe596..63a4231a11 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Linq.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Linq.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; namespace ICSharpCode.WpfDesign.Designer { + // Static helpers that should become extension methods in the future static class Linq { public static T[] ToArray(ICollection collection) diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs index 49bc669d7b..93ad29feb7 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Services/ToolService.cs @@ -50,11 +50,11 @@ namespace ICSharpCode.WpfDesign.Designer.Services public void OnMouseDown(IDesignPanel designPanel, MouseButtonEventArgs e) { e.Handled = true; - new SelectionTask().Start(designPanel, e); + new SelectionGesture().Start(designPanel, e); } } - abstract class TaskBase + abstract class MouseGestureBase { protected IDesignPanel designPanel; bool isStarted; @@ -120,7 +120,7 @@ namespace ICSharpCode.WpfDesign.Designer.Services protected virtual void OnStopped() {} } - sealed class SelectionTask : TaskBase + sealed class SelectionGesture : MouseGestureBase { protected override void OnStarted(MouseButtonEventArgs e) { diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj index 454eba962f..840b6ae382 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj @@ -58,7 +58,7 @@ - + @@ -72,6 +72,7 @@ + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs index acb4843f58..596ceb23cf 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs @@ -30,6 +30,9 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml _componentService = new XamlComponentService(this); this.Services.AddService(typeof(IComponentService), _componentService); + // register extensions from this assembly: + this.Services.ExtensionManager.RegisterAssembly(typeof(XamlDesignContext).Assembly); + _rootItem = _componentService.RegisterXamlComponentRecursive(doc.RootElement); } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignContext.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignContext.cs index 7d5a495982..c9a76b8edb 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignContext.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignContext.cs @@ -18,6 +18,14 @@ namespace ICSharpCode.WpfDesign { readonly ServiceContainer _services = new ServiceContainer(); + /// + /// Creates a new DesignContext instance. + /// + protected DesignContext() + { + _services.AddService(typeof(Extensions.ExtensionManager), new Extensions.ExtensionManager(this)); + } + /// /// Gets the . /// diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs index 326f2c5f3d..2c4b74edac 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItem.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; +using ICSharpCode.WpfDesign.Extensions; namespace ICSharpCode.WpfDesign { @@ -16,11 +17,6 @@ namespace ICSharpCode.WpfDesign /// The DesignItem connects a component with the service system and the designers. /// Equivalent to Cider's ModelItem. /// - /// - /// About the Cider extension system: - /// http://blogs.msdn.com/jnak/archive/2006/04/24/580393.aspx - /// http://blogs.msdn.com/jnak/archive/2006/08/04/687166.aspx - /// public abstract class DesignItem { /// @@ -45,5 +41,72 @@ namespace ICSharpCode.WpfDesign [DebuggerStepThrough] get { return this.Context.Services; } } + + #region Extensions support + private struct ExtensionEntry + { + internal readonly Extension Extension; + internal readonly ExtensionServer Server; + + public ExtensionEntry(Extension extension, ExtensionServer server) + { + this.Extension = extension; + this.Server = server; + } + } + + ExtensionServer[] _extensionServers; + bool[] _extensionServerIsApplied; + + List _extensions = new List(); + + /// + /// Gets the extensions registered for this DesignItem. + /// + public IEnumerable Extensions { + get { + foreach (ExtensionEntry entry in _extensions) { + yield return entry.Extension; + } + } + } + + internal void SetExtensionServers(ExtensionServer[] extensionServers) + { + Debug.Assert(_extensionServers == null); + Debug.Assert(extensionServers != null); + + _extensionServers = extensionServers; + _extensionServerIsApplied = new bool[extensionServers.Length]; + } + + internal void ApplyExtensions(ExtensionManager extensionManager) + { + Debug.Assert(_extensionServers != null); + for (int i = 0; i < _extensionServers.Length; i++) { + bool shouldApply = _extensionServers[i].ShouldApplyExtensions(this); + if (shouldApply != _extensionServerIsApplied[i]) { + ExtensionServer server = _extensionServers[i]; + if (shouldApply) { + // add extensions + foreach (Extension ext in extensionManager.CreateExtensions(server, this)) { + _extensions.Add(new ExtensionEntry(ext, server)); + } + } else { + // remove extensions + _extensions.RemoveAll( + delegate (ExtensionEntry entry) { + if (entry.Server == server) { + server.RemoveExtension(entry.Extension); + return true; + } else { + return false; + } + }); + } + } + } + } + #endregion } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/BehaviorExtension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/BehaviorExtension.cs new file mode 100644 index 0000000000..c447c36d6a --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/BehaviorExtension.cs @@ -0,0 +1,79 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign.Extensions +{ + /// + /// 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. + /// + [ExtensionServer(typeof(BehaviorExtension.BehaviorExtensionServer))] + public abstract class BehaviorExtension : Extension + { + DesignItem _extendedItem; + + /// + /// Gets the item that is being extended by the BehaviorExtension. + /// + 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; + } + } + + /// + /// Gets the design context of the extended item. "Context" is equivalent to "ExtendedItem.Context". + /// + public DesignContext Context { + get { + return this.ExtendedItem.Context; + } + } + + /// + /// Gets the service container of the extended item. "Services" is equivalent to "ExtendedItem.Services". + /// + public ServiceContainer Services { + get { + return this.ExtendedItem.Services; + } + } + + /// + /// Is called after the ExtendedItem was set. + /// + 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) + { + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/Extension.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/Extension.cs index 45ab7340c1..75fc9ad030 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/Extension.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/Extension.cs @@ -6,12 +6,19 @@ // using System; +using System.Diagnostics; namespace ICSharpCode.WpfDesign.Extensions { /// /// Base class for all Extensions. /// + /// + /// The class design in the ICSharpCode.WpfDesign.Extensions namespace was made to match that of Cider + /// as described in the blog posts: + /// http://blogs.msdn.com/jnak/archive/2006/04/24/580393.aspx + /// http://blogs.msdn.com/jnak/archive/2006/08/04/687166.aspx + /// public abstract class Extension { } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionForAttribute.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionForAttribute.cs new file mode 100644 index 0000000000..01a0ad813e --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionForAttribute.cs @@ -0,0 +1,56 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign.Extensions +{ + /// + /// Attribute to specify that the decorated class is a WPF extension for the specified item type. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple=true, Inherited=false)] + public sealed class ExtensionForAttribute : Attribute + { + Type _designedItemType; + Type _overrideExtension; + + /// + /// Gets the type of the item that is designed using this extension. + /// + public Type DesignedItemType { + get { return _designedItemType; } + } + + /// + /// Gets/Sets the type of another extension that this extension is overriding. + /// + 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."); + } + } + } + } + + /// + /// Create a new ExtensionForAttribute that specifies that the decorated class + /// is a WPF extension for the specified item type. + /// + public ExtensionForAttribute(Type designedItemType) + { + if (designedItemType == null) + throw new ArgumentNullException("designedItemType"); + if (!designedItemType.IsClass) + throw new ArgumentException("designedItemType must be a class"); + _designedItemType = designedItemType; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs new file mode 100644 index 0000000000..877aec2c2d --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionManager.cs @@ -0,0 +1,190 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection; + +namespace ICSharpCode.WpfDesign.Extensions +{ + /// + /// Manages extension creation for a design context. + /// + public sealed class ExtensionManager + { + readonly DesignContext _context; + + internal ExtensionManager(DesignContext context) + { + Debug.Assert(context != null); + this._context = context; + + context.Services.Subscribe( + 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> _extensions = new Dictionary>(); + + void AddExtensionEntry(Type extendedItemType, ExtensionEntry entry) + { + List list; + if (!_extensions.TryGetValue(extendedItemType, out list)) { + list = _extensions[extendedItemType] = new List(); + } + list.Add(entry); + } + + List GetExtensionEntries(Type extendedItemType) + { + List result; + if (extendedItemType.BaseType != null) + result = GetExtensionEntries(extendedItemType.BaseType); + else + result = new List(); + + List 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 GetExtensionEntries(DesignItem extendedItem) + { + if (extendedItem.Component == null) + return emptyExtensionEntryArray; + else + return GetExtensionEntries(extendedItem.Component.GetType()); + } + + ExtensionServer[] GetExtensionServersForItem(DesignItem item) + { + Debug.Assert(item != null); + + HashSet servers = new HashSet(); + foreach (ExtensionEntry entry in GetExtensionEntries(item)) { + servers.Add(entry.Server); + } + return Linq.ToArray(servers); + } + + internal IEnumerable 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 _registeredAssemblies = new HashSet(); + + /// + /// Registers extensions from the specified assembly. + /// + 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 _extensionServers = new Dictionary(); + + 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."); + } + + /// + /// Gets the extension server for the specified extension server attribute. + /// + 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 + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs new file mode 100644 index 0000000000..220644f910 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServer.cs @@ -0,0 +1,78 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; + +namespace ICSharpCode.WpfDesign.Extensions +{ + /// + /// 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. + /// + public abstract class ExtensionServer + { + DesignContext _context; + + /// + /// Gets the context this extension server was created for. + /// + 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; + } + } + + /// + /// Gets the service container of the current context. "x.Services" is equivalent to "x.Context.Services". + /// + 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(); + } + + /// + /// Is called after the extension server is initialized and the property has been set. + /// + protected virtual void OnInitialized() + { + } + + /// + /// Gets if the extension manager should apply the extensions from this server to the specified item. + /// Is called by the ExtensionManager. + /// + public abstract bool ShouldApplyExtensions(DesignItem extendedItem); + + /// + /// Create an extension of the specified type. + /// Is called by the ExtensionManager. + /// + public abstract Extension CreateExtension(Type extensionType, DesignItem extendedItem); + + /// + /// This method is called before an extension is removed from its DesignItem because it should not be applied anymore. + /// Is called by the ExtensionManager. + /// + public abstract void RemoveExtension(Extension extension); + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServerAttribute.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServerAttribute.cs new file mode 100644 index 0000000000..7734f9c6de --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Extensions/ExtensionServerAttribute.cs @@ -0,0 +1,42 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign.Extensions +{ + /// + /// Attribute to specify that the decorated class is an extension using the specified extension server. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)] + public sealed class ExtensionServerAttribute : Attribute + { + Type _extensionServerType; + + /// + /// Gets the type of the item that is designed using this extension. + /// + public Type ExtensionServerType { + get { return _extensionServerType; } + } + + /// + /// Create a new ExtensionServerAttribute that specifies that the decorated extension + /// uses the specified extension server. + /// + 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; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/HashSet.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/HashSet.cs similarity index 87% rename from src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/HashSet.cs rename to src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/HashSet.cs index cafcd32627..a963c79abe 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/HashSet.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/HashSet.cs @@ -11,7 +11,7 @@ using System.Text; using System.Collections; using System.Collections.Specialized; -namespace ICSharpCode.WpfDesign.Designer +namespace ICSharpCode.WpfDesign { /// /// Represents a set of items. The set does not preserve the order of items and does not allow items to @@ -19,12 +19,16 @@ namespace ICSharpCode.WpfDesign.Designer /// It supports collection change notifications and is cloned by sharing the underlying /// data structure and delaying the actual copy until the next change. /// - sealed class HashSet : ICollection, ICollection, ICloneable, INotifyCollectionChanged + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + public sealed class HashSet : ICollection, ICollection, ICloneable, INotifyCollectionChanged where T : class { Dictionary _dict; bool _copyOnWrite; + /// + /// This event is raised whenever the collection changes. + /// public event NotifyCollectionChangedEventHandler CollectionChanged; /// @@ -65,7 +69,10 @@ namespace ICSharpCode.WpfDesign.Designer return true; } } - + + /// + /// Adds a list of items to the set. This is equivalent to calling for each item in . + /// public void AddRange(IEnumerable items) { foreach (T item in items) { @@ -147,6 +154,9 @@ namespace ICSharpCode.WpfDesign.Designer } #region IEnumerable Members + /// + /// Gets an enumerator to enumerate the items in the set. + /// public IEnumerator GetEnumerator() { return _dict.Keys.GetEnumerator(); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Linq.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Linq.cs new file mode 100644 index 0000000000..69a7cfdcd5 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/Linq.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision$ +// + +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(ICollection collection) + { + T[] arr = new T[collection.Count]; + collection.CopyTo(arr, 0); + return arr; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ServiceContainer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ServiceContainer.cs index 49288c4cd9..082b3bf4f5 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ServiceContainer.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ServiceContainer.cs @@ -18,7 +18,8 @@ namespace ICSharpCode.WpfDesign /// public sealed class ServiceContainer : IServiceProvider { - Dictionary _services = new Dictionary(); + readonly Dictionary _services = new Dictionary(); + readonly Dictionary _waitingSubscribers = new Dictionary(); /// /// Adds a new service to the container. @@ -37,6 +38,12 @@ namespace ICSharpCode.WpfDesign throw new ArgumentNullException("serviceInstance"); _services.Add(serviceInterface, serviceInstance); + + Delegate subscriber; + if (_waitingSubscribers.TryGetValue(serviceInterface, out subscriber)) { + _waitingSubscribers.Remove(serviceInterface); + subscriber.DynamicInvoke(serviceInstance); + } } /// @@ -50,7 +57,7 @@ namespace ICSharpCode.WpfDesign } /// - /// Gets the service object of the specified type. + /// Gets the service object of the type T. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] public T GetService() where T : class @@ -58,7 +65,28 @@ namespace ICSharpCode.WpfDesign return (T)GetService(typeof(T)); } - T GetServiceChecked() where T : class + /// + /// Subscribes to the service of type T. + /// serviceAvailableAction will be called after the service gets available. If the service is already available, + /// the action will be called immediately. + /// + public void Subscribe(Action serviceAvailableAction) where T : class + { + T service = GetService(); + if (service != null) { + serviceAvailableAction(service); + } else { + Type serviceInterface = typeof(T); + Delegate existingSubscriber; + if (_waitingSubscribers.TryGetValue(serviceInterface, out existingSubscriber)) { + _waitingSubscribers[serviceInterface] = Delegate.Combine(existingSubscriber, serviceAvailableAction); + } else { + _waitingSubscribers[serviceInterface] = serviceAvailableAction; + } + } + } + + T GetServiceOrThrowException() where T : class { T service = (T)GetService(typeof(T)); if (service == null) { @@ -69,31 +97,41 @@ namespace ICSharpCode.WpfDesign /// /// Gets the . - /// This service is guaranteed to always exist -> this property will never return null. + /// Throws an exception if the service is not found. /// public ISelectionService Selection { get { - return GetServiceChecked(); + return GetServiceOrThrowException(); } } /// /// Gets the . - /// This service is guaranteed to always exist -> this property will never return null. + /// Throws an exception if the service is not found. /// public IToolService Tool { get { - return GetServiceChecked(); + return GetServiceOrThrowException(); } } /// /// Gets the . - /// This service is guaranteed to always exist -> this property will never return null. + /// Throws an exception if the service is not found. /// public IComponentService Component { get { - return GetServiceChecked(); + return GetServiceOrThrowException(); + } + } + + /// + /// Gets the . + /// Throws an exception if the service is not found. + /// + public Extensions.ExtensionManager ExtensionManager { + get { + return GetServiceOrThrowException(); } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj index 8bacd284b6..478d9a8cf3 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj @@ -16,6 +16,8 @@ False File ..\..\..\..\..\..\AddIns\AddIns\DisplayBindings\WpfDesign\ + False + -Microsoft.Globalization#CA1303 true @@ -59,7 +61,14 @@ + + + + + + +