using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing.Design; using System.Linq; using System.Reflection; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Windows.Forms.Design.Behavior; using Vanara.Extensions; namespace Vanara.Windows.Forms.Design { /// Interface for an action that has items and a category. public interface IActionGetItem { /// Gets the category. /// The category. string Category { get; } /// Gets the item. /// The actions. /// The MBR. /// DesignerActionItem GetItem(DesignerActionList actions, global::System.Reflection.MemberInfo mbr); } /// Methods to assist when using designer code. public static class ComponentDesignerExtension { /// Launches the design-time editor for the property of the component behind a designer. /// The designer for a component. /// The name of the property to edit. If this value is null, the default property for the object is used. /// /// The object on which to edit the property. If this value is null, the Component property of the designer is used. /// /// The new value returned by the editor. public static object EditValue(this ComponentDesigner designer, string propName, object objectToChange = null) { if (objectToChange == null) objectToChange = designer.Component; var prop = (propName == null) ? TypeDescriptor.GetDefaultProperty(objectToChange) : TypeDescriptor.GetProperties(objectToChange)[propName]; if (prop == null) throw new ArgumentException("Unable to retrieve specified property."); var context = new EditorServiceContext(designer, prop); var editor = prop.GetEditor(typeof(UITypeEditor)) as UITypeEditor; var curVal = prop.GetValue(objectToChange); var newVal = editor?.EditValue(context, context, curVal); if (newVal != curVal) try { prop.SetValue(objectToChange, newVal); } catch (CheckoutException) { } return newVal; } /// Sets a property on the component behind a designer. /// The type of the property value. /// The designer for a component. /// /// The name of the property to set. If this value is null, the default property for the object is used. This method will not set /// the property if the property type does not match , if the property is read-only, or if the property is /// not browsable. /// /// The value to assign to the property. public static void SetComponentProperty(this ComponentDesigner d, string propName, T value) { var propDesc = (propName == null) ? TypeDescriptor.GetDefaultProperty(d.Component) : TypeDescriptor.GetProperties(d.Component)[propName]; if (propDesc != null && propDesc.PropertyType == typeof(T) && !propDesc.IsReadOnly && propDesc.IsBrowsable) propDesc.SetValue(d.Component, value); } /// Shows a form tied to a designer. /// The designer for a component. /// A form instance. /// The result of calling ShowDialog on the form. public static DialogResult ShowDialog(this ComponentDesigner designer, Form dialog) { var context = new EditorServiceContext(designer); return context.ShowDialog(dialog); } } /// Extension methods for IServiceProvider. public static partial class ServiceProviderExtension { /// Gets the service. /// /// The sp. /// public static T GetService(this IServiceProvider sp) where T : class => (T)sp.GetService(typeof(T)); } /// A designer for components that support attributes. /// The type of the component. /// public abstract class AttributedComponentDesigner : ComponentDesigner where TComponent : Component { private IDictionary> redirectedProps; private DesignerVerbCollection verbs; /// Initializes a new instance of the class. public AttributedComponentDesigner() { redirectedProps = this.GetRedirectedProperties(); verbs = this.GetAttributedVerbs(); } /// Gets the design-time verbs supported by the component that is associated with the designer. public override DesignerVerbCollection Verbs => verbs; /// Gets the design-time action lists supported by the component associated with the designer. public override DesignerActionListCollection ActionLists => this.GetActionLists(Component, Actions, base.ActionLists, Verbs); /// Gets the component this designer is designing. public new TComponent Component => (TComponent)base.Component; /// Gets the actions. /// The actions. protected virtual AttributedDesignerActionList Actions => null; /// Gets the properties to remove. /// The properties to remove. protected virtual IEnumerable PropertiesToRemove => null; /// Allows a designer to add to the set of properties that it exposes through a . /// The properties for the class of the component. protected override void PreFilterProperties(IDictionary properties) { base.PreFilterProperties(properties); if (redirectedProps != null) this.RedirectRegisteredProperties(properties, redirectedProps); if (PropertiesToRemove != null) this.RemoveItems(properties, PropertiesToRemove); } } /// An extended designer for components that support attributes. /// The type of the component. /// public abstract class AttributedComponentDesignerEx : AttributedComponentDesigner where TComponent : Component { private Adorner adorner; /// Gets the behavior service. /// The behavior service. public BehaviorService BehaviorService { get; private set; } /// Gets the component change service. /// The component change service. public IComponentChangeService ComponentChangeService { get; private set; } /// Gets the selection service. /// The selection service. public ISelectionService SelectionService { get; private set; } /// Gets the glyphs. /// The glyphs. public virtual GlyphCollection Glyphs => Adorner.Glyphs; internal Adorner Adorner { get { if (adorner == null) BehaviorService.Adorners.Add(adorner = new Adorner()); return adorner; } } /// Prepares the designer to view, edit, and design the specified component. /// The component for this designer. public override void Initialize(IComponent component) { base.Initialize(component); BehaviorService = GetService(); SelectionService = GetService(); if (SelectionService != null) SelectionService.SelectionChanged += OnSelectionChanged; ComponentChangeService = GetService(); if (ComponentChangeService != null) ComponentChangeService.ComponentChanged += OnComponentChanged; } /// /// Releases the unmanaged resources used by the and optionally /// releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { if (BehaviorService != null & adorner != null) BehaviorService.Adorners.Remove(adorner); var ss = SelectionService; if (ss != null) ss.SelectionChanged -= OnSelectionChanged; var cs = ComponentChangeService; if (cs != null) cs.ComponentChanged -= OnComponentChanged; } base.Dispose(disposing); } /// Gets the service. /// The type of the s. /// protected virtual TS GetService() where TS : class => (TS)GetService(typeof(TS)); /// Called when [component changed]. /// The sender. /// The instance containing the event data. protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) { } /// Called when [selection changed]. /// The sender. /// The instance containing the event data. protected virtual void OnSelectionChanged(object sender, EventArgs e) { } } /// A designer for controls that support attributes. /// The type of the control. /// public abstract class AttributedControlDesigner : ControlDesigner where TControl : Control { private IDictionary> redirectedEvents; private IDictionary> redirectedProps; private DesignerVerbCollection verbs; /// Initializes a new instance of the class. public AttributedControlDesigner() { redirectedEvents = this.GetRedirectedEvents(); redirectedProps = this.GetRedirectedProperties(); verbs = this.GetAttributedVerbs(); } /// Gets the design-time verbs supported by the component that is associated with the designer. public override DesignerVerbCollection Verbs => verbs; /// Gets the design-time action lists supported by the component associated with the designer. public override DesignerActionListCollection ActionLists => this.GetActionLists(Control, Actions, base.ActionLists, Verbs); /// Gets the control that the designer is designing. public new TControl Control => (TControl)base.Control; /// Gets the actions. /// The actions. protected virtual AttributedDesignerActionList Actions => null; /// Gets the events to remove. /// The events to remove. protected virtual IEnumerable EventsToRemove => null; /// Gets the properties to remove. /// The properties to remove. protected virtual IEnumerable PropertiesToRemove => null; /// Allows a designer to add to the set of events that it exposes through a . /// The events for the class of the component. protected override void PreFilterEvents(IDictionary events) { base.PreFilterEvents(events); if (redirectedEvents != null) this.RedirectRegisteredEvents(events, redirectedEvents); if (EventsToRemove != null) this.RemoveItems(events, EventsToRemove); } /// Adjusts the set of properties the component exposes through a . /// An containing the properties for the class of the component. protected override void PreFilterProperties(IDictionary properties) { base.PreFilterProperties(properties); if (redirectedProps != null) this.RedirectRegisteredProperties(properties, redirectedProps); if (PropertiesToRemove != null) this.RemoveItems(properties, PropertiesToRemove); } } /// An extended designer for controls that support attributes. /// The type of the control. /// public abstract class AttributedControlDesignerEx : AttributedControlDesigner where TControl : Control { private Adorner adorner; /// Gets the component change service. /// The component change service. public IComponentChangeService ComponentChangeService { get; private set; } /// Gets the selection service. /// The selection service. public ISelectionService SelectionService { get; private set; } /// Gets the from the design environment. public new BehaviorService BehaviorService => base.BehaviorService; /// Gets the glyphs. /// The glyphs. public virtual GlyphCollection Glyphs => Adorner.Glyphs; internal Adorner Adorner { get { if (adorner == null) BehaviorService.Adorners.Add(adorner = new Adorner()); return adorner; } } /// Initializes the designer with the specified component. /// /// The to associate the designer with. This component must always be an instance /// of, or derive from, . /// public override void Initialize(IComponent component) { base.Initialize(component); SelectionService = GetService(); if (SelectionService != null) SelectionService.SelectionChanged += OnSelectionChanged; ComponentChangeService = GetService(); if (ComponentChangeService != null) ComponentChangeService.ComponentChanged += OnComponentChanged; } /// /// Releases the unmanaged resources used by the and optionally releases /// the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { BehaviorService?.Adorners.Remove(adorner); var ss = SelectionService; if (ss != null) ss.SelectionChanged -= OnSelectionChanged; var cs = ComponentChangeService; if (cs != null) cs.ComponentChanged -= OnComponentChanged; } base.Dispose(disposing); } /// Gets the service. /// The type of the s. /// protected virtual TS GetService() where TS : class => (TS)GetService(typeof(TS)); /// Called when a component has changed. /// The sender. /// The instance containing the event data. protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) { } /// Called when the selection on the designer has changed. /// The sender. /// The instance containing the event data. protected virtual void OnSelectionChanged(object sender, EventArgs e) { } } /// A designer action list pulled from attributes. /// public abstract class AttributedDesignerActionList : DesignerActionList { private IEnumerable fullAIList; /// Initializes a new instance of the class. /// The designer. /// The component. protected AttributedDesignerActionList(ComponentDesigner designer, IComponent component) : base(component) { ParentDesigner = designer; AutoShow = true; } /// Gets the parent designer. /// The parent designer. public ComponentDesigner ParentDesigner { get; } /// /// Returns the collection of objects contained in the list. /// /// A array that contains the items in this list. public override DesignerActionItemCollection GetSortedActionItems() { // Retrieve all attributed methods and properties if (fullAIList == null) fullAIList = this.GetAllAttributedActionItems(); // Filter for conditions and load return this.GetFilteredActionItems(fullAIList); } /// Gets the component property. /// /// Name of the property. /// protected T GetComponentProperty(string propName) { var p = ComponentProp(propName, typeof(T)); if (p != null) return (T)p.GetValue(Component, null); return default; } /// Sets the component property. /// /// Name of the property. /// The value. protected void SetComponentProperty(string propName, T value) { ComponentProp(propName, typeof(T))?.SetValue(Component, value, null); } private global::System.Reflection.PropertyInfo ComponentProp(string propName, Type retType) => Component.GetType().GetProperty(propName, InternalComponentDesignerExtension.allInstBind, null, retType, Type.EmptyTypes, null); } /// A designer for parent controls supported by attributes. /// The type of the control. /// public abstract class AttributedParentControlDesigner : ParentControlDesigner where TControl : Control { private IDictionary> redirectedProps; private DesignerVerbCollection verbs; /// Initializes a new instance of the class. public AttributedParentControlDesigner() { redirectedProps = this.GetRedirectedProperties(); verbs = this.GetAttributedVerbs(); } /// Gets the design-time verbs supported by the component that is associated with the designer. public override DesignerVerbCollection Verbs => verbs; /// Gets the design-time action lists supported by the component associated with the designer. public override DesignerActionListCollection ActionLists => this.GetActionLists(Control, Actions, base.ActionLists, Verbs); /// Gets the control that the designer is designing. public new TControl Control => (TControl)base.Control; /// Gets the actions. /// The actions. protected virtual AttributedDesignerActionList Actions => null; /// Gets the properties to remove. /// The properties to remove. protected virtual IEnumerable PropertiesToRemove => null; /// Adjusts the set of properties the component will expose through a . /// /// An that contains the properties for the class of the component. /// protected override void PreFilterProperties(IDictionary properties) { base.PreFilterProperties(properties); if (redirectedProps != null) this.RedirectRegisteredProperties(properties, redirectedProps); if (PropertiesToRemove != null) this.RemoveItems(properties, PropertiesToRemove); } } /// An extended designer for parent controls supported by attributes. /// The type of the control. /// public abstract class AttributedParentControlDesignerEx : AttributedParentControlDesigner where TControl : Control { private Adorner adorner; /// Gets the component change service. /// The component change service. public IComponentChangeService ComponentChangeService { get; private set; } /// Gets the selection service. /// The selection service. public ISelectionService SelectionService { get; private set; } /// Gets the from the design environment. public new BehaviorService BehaviorService => base.BehaviorService; /// Gets the glyphs. /// The glyphs. public virtual GlyphCollection Glyphs => Adorner.Glyphs; internal Adorner Adorner { get { if (adorner == null) BehaviorService.Adorners.Add(adorner = new Adorner()); return adorner; } } /// Initializes the designer with the specified component. /// The to associate with the designer. public override void Initialize(IComponent component) { base.Initialize(component); SelectionService = GetService(); if (SelectionService != null) SelectionService.SelectionChanged += OnSelectionChanged; ComponentChangeService = GetService(); if (ComponentChangeService != null) ComponentChangeService.ComponentChanged += OnComponentChanged; } /// /// Releases the unmanaged resources used by the , and optionally /// releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { if (BehaviorService != null & adorner != null) BehaviorService.Adorners.Remove(adorner); var ss = SelectionService; if (ss != null) ss.SelectionChanged -= OnSelectionChanged; var cs = ComponentChangeService; if (cs != null) cs.ComponentChanged -= OnComponentChanged; } base.Dispose(disposing); } /// Gets the service. /// The type of the s. /// protected virtual TS GetService() where TS : class => (TS)GetService(typeof(TS)); /// Called when [component changed]. /// The sender. /// The instance containing the event data. protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) { } /// Called when [selection changed]. /// The sender. /// The instance containing the event data. protected virtual void OnSelectionChanged(object sender, EventArgs e) { } } /// Attribute placed on methods that indicate they support a designer action. /// /// [AttributeUsage(AttributeTargets.Method)] public sealed class DesignerActionMethodAttribute : Attribute, IActionGetItem { /// Initializes a new instance of the class. /// The display name. /// The display order. public DesignerActionMethodAttribute(string displayName, int displayOrder = 0) { DisplayName = displayName; DisplayOrder = displayOrder; } /// Gets or sets a value indicating whether [allow associate]. /// if [allow associate]; otherwise, . public bool AllowAssociate { get; set; } /// Gets or sets the category. /// The category. public string Category { get; set; } /// Gets or sets the condition. /// The condition. public string Condition { get; set; } /// Gets or sets the description. /// The description. public string Description { get; set; } /// Gets the display name. /// The display name. public string DisplayName { get; } /// Gets the display order. /// The display order. public int DisplayOrder { get; } /// Gets or sets a value indicating whether [include as designer verb]. /// if [include as designer verb]; otherwise, . public bool IncludeAsDesignerVerb { get; set; } DesignerActionItem IActionGetItem.GetItem(DesignerActionList actions, global::System.Reflection.MemberInfo mbr) { var ret = new DesignerActionMethodItem(actions, mbr.Name, DisplayName, Category, Description, IncludeAsDesignerVerb) { AllowAssociate = AllowAssociate }; if (!string.IsNullOrEmpty(Condition)) ret.Properties.Add("Condition", Condition); ret.Properties.Add("Order", DisplayOrder); return ret; } } /// Attribute placed on properties that indicate they support a designer action. /// /// [AttributeUsage(AttributeTargets.Property)] public sealed class DesignerActionPropertyAttribute : Attribute, IActionGetItem { /// Initializes a new instance of the class. /// The display name. /// The display order. public DesignerActionPropertyAttribute(string displayName, int displayOrder = 0) { DisplayName = displayName; DisplayOrder = displayOrder; } /// Gets or sets a value indicating whether [allow associate]. /// if [allow associate]; otherwise, . public bool AllowAssociate { get; set; } /// Gets or sets the category. /// The category. public string Category { get; set; } /// Gets or sets the condition. /// The condition. public string Condition { get; set; } /// Gets or sets the description. /// The description. public string Description { get; set; } /// Gets the display name. /// The display name. public string DisplayName { get; } /// Gets the display order. /// The display order. public int DisplayOrder { get; } DesignerActionItem IActionGetItem.GetItem(DesignerActionList actions, global::System.Reflection.MemberInfo mbr) { var ret = new DesignerActionPropertyItem(mbr.Name, DisplayName, Category, Description) { AllowAssociate = AllowAssociate }; if (!string.IsNullOrEmpty(Condition)) ret.Properties.Add("Condition", Condition); ret.Properties.Add("Order", DisplayOrder); return ret; } } /// Attribute placed on methods that indicate they support a designer attribute. /// [AttributeUsage(AttributeTargets.Method)] public sealed class DesignerVerbAttribute : Attribute { private readonly CommandID cmdId; private readonly string menuText; /// Initializes a new instance of the class. /// The menu text. public DesignerVerbAttribute(string menuText) { this.menuText = menuText; } /// Initializes a new instance of the class. /// The menu text. /// The command menu group. /// The command identifier. public DesignerVerbAttribute(string menuText, Guid commandMenuGroup, int commandId) { this.menuText = menuText; cmdId = new CommandID(commandMenuGroup, commandId); } internal DesignerVerb GetDesignerVerb(object obj, global::System.Reflection.MethodInfo mi) { var handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), obj, mi); if (cmdId != null) return new DesignerVerb(menuText, handler, cmdId); return new DesignerVerb(menuText, handler); } } /// A service context implementation for an editor. /// /// public class EditorServiceContext : IWindowsFormsEditorService, ITypeDescriptorContext { private readonly ComponentDesigner designer; private readonly PropertyDescriptor targetProperty; private IComponentChangeService componentChangeSvc; internal EditorServiceContext(ComponentDesigner designer) { this.designer = designer; } internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop) { this.designer = designer; targetProperty = prop; if (prop == null) { prop = TypeDescriptor.GetDefaultProperty(designer.Component); if ((prop != null) && typeof(ICollection).IsAssignableFrom(prop.PropertyType)) targetProperty = prop; } } internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop, string newVerbText) : this(designer, prop) { this.designer.Verbs.Add(new DesignerVerb(newVerbText, OnEditItems)); } IContainer ITypeDescriptorContext.Container => designer.Component.Site?.Container; object ITypeDescriptorContext.Instance => designer.Component; PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor => targetProperty; private IComponentChangeService ChangeService => componentChangeSvc ?? (componentChangeSvc = GetService()); /// Shows the specified . /// The to display. /// A indicating the result code returned by the . public DialogResult ShowDialog(Form dialog) { var service = GetService(); if (service != null) return service.ShowDialog(dialog); return dialog.ShowDialog(designer.Component as IWin32Window); } void IWindowsFormsEditorService.CloseDropDown() { } void IWindowsFormsEditorService.DropDownControl(Control control) { } object IServiceProvider.GetService(Type serviceType) { if ((serviceType == typeof(ITypeDescriptorContext)) || (serviceType == typeof(IWindowsFormsEditorService))) return this; return designer.Component?.Site?.GetService(serviceType); } void ITypeDescriptorContext.OnComponentChanged() { ChangeService.OnComponentChanged(designer.Component, targetProperty, null, null); } bool ITypeDescriptorContext.OnComponentChanging() { try { ChangeService.OnComponentChanging(designer.Component, targetProperty); } catch (CheckoutException exception) { if (exception != CheckoutException.Canceled) throw; return false; } return true; } private T GetService() where T : class => ((IServiceProvider)this).GetService(); private void OnEditItems(object sender, EventArgs e) { var component = targetProperty.GetValue(designer.Component); if (component != null) { var editor = TypeDescriptor.GetEditor(component, typeof(UITypeEditor)) as CollectionEditor; editor?.EditValue(this, this, component); } } } /// Attribute placed on class items that indicate they support a designer redirected item. /// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Method)] public sealed class RedirectedDesignerItemAttribute : Attribute { /// Initializes a new instance of the class. public RedirectedDesignerItemAttribute() { ApplyOtherAttributes = true; } /// Gets or sets a value indicating whether to apply other attributes. /// if this should apply other attributes; otherwise, . public bool ApplyOtherAttributes { get; set; } } /// A behavior derivative for a supplied type. /// The type of the control designer. /// public abstract class TypedBehavior : Behavior where TControlDesigner : ControlDesigner { /// Initializes a new instance of the class. /// The designer. protected TypedBehavior(TControlDesigner designer) { Designer = designer; } /// Gets the designer. /// The designer. public TControlDesigner Designer { get; } } /// An action list for a generic designer. /// The type of the component designer. /// The type of the component. /// public abstract class TypedDesignerActionList : AttributedDesignerActionList where TComponentDesigner : ComponentDesigner where TComponent : Component { /// Initializes a new instance of the class. /// The designer. /// The component. protected TypedDesignerActionList(TComponentDesigner designer, TComponent component) : base(designer, component) { ParentDesigner = designer; } /// Gets the parent designer. /// The parent designer. public new TComponentDesigner ParentDesigner { get; } /// Gets the component related to . public new TComponent Component => (TComponent)base.Component; } /// A glyph associated with a designer. /// The type of the control designer. /// /// public abstract class TypedGlyph : Glyph, IDisposable where TControlDesigner : ControlDesigner { /// Initializes a new instance of the class. /// The designer. /// The behavior. protected TypedGlyph(TControlDesigner designer, Behavior behavior) : base(behavior) { Designer = designer; } /// Gets the designer. /// The designer. public TControlDesigner Designer { get; } /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public virtual void Dispose() { } /// Sets the behavior. /// The b. public void SetBehavior(TypedBehavior b) { base.SetBehavior(b); } } internal static class InternalComponentDesignerExtension { public const BindingFlags allInstBind = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; public static DesignerActionListCollection GetActionLists(this ComponentDesigner designer, Component component, AttributedDesignerActionList actions, DesignerActionListCollection baseActions, DesignerVerbCollection verbs) { var lists = new DesignerActionListCollection(); if (baseActions != null && baseActions.Count > 0) lists.AddRange(baseActions); if (actions != null) lists.Add(actions); if (verbs != null && verbs.Count > 0) lists.Add(new DesignerActionVerbList(verbs)); return lists; } public static IEnumerable GetAllAttributedActionItems(this DesignerActionList actionList) { var fullAIList = new List(); foreach (var mbr in actionList.GetType().GetMethods(allInstBind)) { foreach (IActionGetItem attr in mbr.GetCustomAttributes(typeof(DesignerActionMethodAttribute), true)) { if (mbr.ReturnType == typeof(void) && mbr.GetParameters().Length == 0) fullAIList.Add(attr.GetItem(actionList, mbr)); else throw new FormatException("DesignerActionMethodAttribute must be applied to a method returning void and having no parameters."); } } foreach (var mbr in actionList.GetType().GetProperties(allInstBind)) { if (mbr.GetIndexParameters().Length > 0) throw new FormatException("DesignerActionPropertyAttribute must be applied to non-indexed properties."); foreach (IActionGetItem attr in mbr.GetCustomAttributes(typeof(DesignerActionPropertyAttribute), true)) fullAIList.Add(attr.GetItem(actionList, mbr)); } fullAIList.Sort(CompareItems); return fullAIList; int CompareItems(DesignerActionItem a, DesignerActionItem b) { var c = string.Compare(a?.Category ?? string.Empty, b?.Category ?? string.Empty, true); if (c != 0) return c; c = (int)(a?.Properties["Order"] ?? 0) - (int)(b?.Properties["Order"] ?? 0); if (c != 0) return c; return string.Compare(a?.DisplayName, b?.DisplayName, true); } } public static DesignerVerbCollection GetAttributedVerbs(this ComponentDesigner designer) { var verbs = new DesignerVerbCollection(); foreach (var m in designer.GetType().GetMethods(allInstBind)) { foreach (DesignerVerbAttribute attr in m.GetCustomAttributes(typeof(DesignerVerbAttribute), true)) { verbs.Add(attr.GetDesignerVerb(designer, m)); } } return verbs.Count > 0 ? verbs : null; } public static DesignerActionItemCollection GetFilteredActionItems(this DesignerActionList actionList, IEnumerable fullAIList) { var col = new DesignerActionItemCollection(); foreach (var ai in fullAIList) if (CheckCondition(ai)) col.Add(ai); // Add header items for displayed items string cat = null; for (var i = 0; i < col.Count; i++) { var curCat = col[i].Category; if (string.Compare(curCat, cat, true) != 0) { col.Insert(i++, new DesignerActionHeaderItem(curCat)); cat = curCat; } } return col; bool CheckCondition(DesignerActionItem ai) { if (ai.Properties["Condition"] != null) { var p = actionList.GetType().GetProperty((string)ai.Properties["Condition"], allInstBind, null, typeof(bool), Type.EmptyTypes, null); if (p != null) return (bool)p.GetValue(actionList, null); } return true; } } public static IDictionary> GetRedirectedEvents(this ComponentDesigner d) { var ret = new Dictionary>(); foreach (var evt in d.GetType().GetEvents(allInstBind)) { foreach (var attr in evt.GetCustomAttributes(false)) { var attributes = attr.ApplyOtherAttributes ? evt.GetCustomAttributes().Where(a => !(a is RedirectedDesignerItemAttribute)).ToList() : new List(); ret.Add(evt.Name, attributes); } } return ret.Count > 0 ? ret : null; } public static void RedirectRegisteredEvents(this ComponentDesigner d, IDictionary properties, IDictionary> redirectedProps) { foreach (var propName in redirectedProps.Keys) { var oldPropertyDescriptor = (PropertyDescriptor)properties[propName]; if (oldPropertyDescriptor != null) { var attributes = redirectedProps[propName]; properties[propName] = TypeDescriptor.CreateProperty(d.GetType(), oldPropertyDescriptor, attributes.ToArray()); } } } public static IDictionary> GetRedirectedProperties(this ComponentDesigner d) { var ret = new Dictionary>(); foreach (var prop in d.GetType().GetProperties(allInstBind)) { foreach (var attr in prop.GetCustomAttributes(false)) { var attributes = attr.ApplyOtherAttributes ? prop.GetCustomAttributes().Where(a => !(a is RedirectedDesignerItemAttribute)).ToList() : new List(); ret.Add(prop.Name, attributes); } } return ret.Count > 0 ? ret : null; } public static void RedirectRegisteredProperties(this ComponentDesigner d, IDictionary properties, IDictionary> redirectedProps) { foreach (var propName in redirectedProps.Keys) { var oldPropertyDescriptor = (PropertyDescriptor)properties[propName]; if (oldPropertyDescriptor != null) { var attributes = redirectedProps[propName]; properties[propName] = TypeDescriptor.CreateProperty(d.GetType(), oldPropertyDescriptor, attributes.ToArray()); } } } public static void RemoveItems(this ComponentDesigner d, IDictionary values, IEnumerable keysToRemove) { foreach (var p in keysToRemove) if (values.Contains(p)) values.Remove(p); } } internal class DesignerActionVerbList : DesignerActionList { private DesignerVerbCollection _verbs; public DesignerActionVerbList(DesignerVerbCollection verbs) : base(null) { _verbs = verbs; } public override bool AutoShow => false; public override DesignerActionItemCollection GetSortedActionItems() { DesignerActionItemCollection items = new DesignerActionItemCollection(); foreach (DesignerVerb v in _verbs) if ((v.Visible && v.Enabled) && v.Supported) items.Add(new DesignerActionVerbItem(v)); return items; } public class DesignerActionVerbItem : DesignerActionMethodItem { private DesignerVerb targetVerb; public DesignerActionVerbItem(DesignerVerb verb) : base(null, null, verb.Text, "Verbs", verb.Description, false) { targetVerb = verb; } public override void Invoke() { targetVerb.Invoke(); } } } }