using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Windows.Forms.Design; using System.Windows.Forms.Design.Behavior; namespace System.ComponentModel.Design { internal static class ComponentDesignerExtension { public const System.Reflection.BindingFlags AllInstBind = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance; public static object EditValue(this ComponentDesigner designer, object objectToChange, string propName) { PropertyDescriptor prop = TypeDescriptor.GetProperties(objectToChange)[propName]; EditorServiceContext context = new EditorServiceContext(designer, prop); var editor = prop.GetEditor(typeof(System.Drawing.Design.UITypeEditor)) as System.Drawing.Design.UITypeEditor; object curVal = prop.GetValue(objectToChange); object newVal = editor.EditValue(context, context, curVal); if (newVal != curVal) try { prop.SetValue(objectToChange, newVal); } catch (CheckoutException) { } return newVal; } public static List GetAllAttributedActionItems(this DesignerActionList actionList) { var fullAIList = new List(); foreach (var mbr in actionList.GetType().GetMethods(AllInstBind)) { foreach (IActionGetItem attr in mbr.GetCustomAttributes(typeof(DesignerActionMethodAttribute), false)) { 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)) { foreach (IActionGetItem attr in mbr.GetCustomAttributes(typeof(DesignerActionPropertyAttribute), false)) fullAIList.Add(attr.GetItem(actionList, mbr)); } fullAIList.Sort(CompareItems); return fullAIList; } 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; } public static DesignerActionItemCollection GetFilteredActionItems(this DesignerActionList actionList, List fullAIList) { DesignerActionItemCollection col = new DesignerActionItemCollection(); fullAIList.ForEach(ai => { if (CheckCondition(actionList, ai)) { col.Add(ai); } }); // Add header items for displayed items int i = 0; string cat = null; while (i < col.Count) { string curCat = col[i].Category; if (string.Compare(curCat, cat, true, Globalization.CultureInfo.CurrentCulture) != 0) { col.Insert(i++, new DesignerActionHeaderItem(curCat)); cat = curCat; } i++; } return col; } public static IDictionary> GetRedirectedProperties(this ComponentDesigner d) { var ret = new Dictionary>(); foreach (var prop in d.GetType().GetProperties(AllInstBind)) { foreach (RedirectedDesignerPropertyAttribute attr in prop.GetCustomAttributes(typeof(RedirectedDesignerPropertyAttribute), false)) { List attributes; if (attr.ApplyOtherAttributes) { attributes = new List(Array.ConvertAll(prop.GetCustomAttributes(false), o => o as System.Attribute)); attributes.RemoveAll(a => a is RedirectedDesignerPropertyAttribute); } else attributes = new List(); ret.Add(prop.Name, attributes); } } return ret; } public static void RedirectRegisteredProperties(this ComponentDesigner d, System.Collections.IDictionary properties, IDictionary> redirectedProps) { foreach (var propName in redirectedProps.Keys) { PropertyDescriptor oldPropertyDescriptor = (PropertyDescriptor)properties[propName]; if (oldPropertyDescriptor != null) { List attributes = redirectedProps[propName]; properties[propName] = TypeDescriptor.CreateProperty(d.GetType(), oldPropertyDescriptor, attributes.ToArray()); } } } [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "d")] public static void RemoveProperties(this ComponentDesigner d, System.Collections.IDictionary properties, IEnumerable propertiesToRemove) { foreach (string p in propertiesToRemove) if (properties.Contains(p)) properties.Remove(p); } public static void SetComponentProperty(this ComponentDesigner d, string propName, T value) { PropertyDescriptor propDesc = TypeDescriptor.GetProperties(d.Component)[propName]; if (propDesc != null && propDesc.PropertyType == typeof(T) && !propDesc.IsReadOnly && propDesc.IsBrowsable) propDesc.SetValue(d.Component, value); } public static System.Windows.Forms.DialogResult ShowDialog(this ComponentDesigner designer, System.Windows.Forms.Form dialog) { EditorServiceContext context = new EditorServiceContext(designer); return context.ShowDialog(dialog); } private static bool CheckCondition(DesignerActionList actionList, 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; } private static int CompareItems(DesignerActionItem a, DesignerActionItem b) { int c = string.Compare(a.Category ?? string.Empty, b.Category ?? string.Empty, true, Globalization.CultureInfo.CurrentCulture); if (c != 0) return c; c = (int)a.Properties["Order"] - (int)b.Properties["Order"]; if (c != 0) return c; return string.Compare(a.DisplayName, b.DisplayName, true, Globalization.CultureInfo.CurrentCulture); } } [System.AttributeUsage(System.AttributeTargets.Property, Inherited = true, AllowMultiple = false)] internal sealed class RedirectedDesignerPropertyAttribute : System.Attribute { public RedirectedDesignerPropertyAttribute() { ApplyOtherAttributes = true; } public bool ApplyOtherAttributes { get; set; } } } namespace System.Windows.Forms.Design { internal interface IActionGetItem { string Category { get; } DesignerActionItem GetItem(DesignerActionList actions, Reflection.MemberInfo mbr); } [System.AttributeUsage(System.AttributeTargets.Method, Inherited = true, AllowMultiple = false)] internal sealed class DesignerActionMethodAttribute : System.Attribute, IActionGetItem { public DesignerActionMethodAttribute(string displayName, int displayOrder = 0) { DisplayName = displayName; DisplayOrder = displayOrder; } public bool AllowAssociate { get; set; } public string Category { get; set; } public string Condition { get; set; } public string Description { get; set; } public string DisplayName { get; } public int DisplayOrder { get; } public bool IncludeAsDesignerVerb { get; set; } DesignerActionItem IActionGetItem.GetItem(DesignerActionList actions, 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; } } [System.AttributeUsage(System.AttributeTargets.Property, Inherited = true, AllowMultiple = false)] internal sealed class DesignerActionPropertyAttribute : System.Attribute, IActionGetItem { public DesignerActionPropertyAttribute(string displayName, int displayOrder = 0) { DisplayName = displayName; DisplayOrder = displayOrder; } public bool AllowAssociate { get; set; } public string Category { get; set; } public string Condition { get; set; } public string Description { get; set; } public string DisplayName { get; } public int DisplayOrder { get; } DesignerActionItem IActionGetItem.GetItem(DesignerActionList actions, 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; } } [System.AttributeUsage(System.AttributeTargets.Method, Inherited = true, AllowMultiple = false)] internal sealed class DesignerVerbAttribute : System.Attribute { private CommandID cmdId; private string menuText; public DesignerVerbAttribute(string menuText) { this.menuText = menuText; } public DesignerVerbAttribute(string menuText, Guid commandMenuGroup, int commandId) { this.menuText = menuText; cmdId = new CommandID(commandMenuGroup, commandId); } internal DesignerVerb GetDesignerVerb(object obj, Reflection.MethodInfo mi) { EventHandler handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), obj, mi); if (cmdId != null) return new DesignerVerb(menuText, handler, cmdId); return new DesignerVerb(menuText, handler); } } internal class EditorServiceContext : IWindowsFormsEditorService, ITypeDescriptorContext, IServiceProvider { private IComponentChangeService _componentChangeSvc; private ComponentDesigner _designer; private PropertyDescriptor _targetProperty; internal EditorServiceContext(ComponentDesigner designer) { _designer = designer; } internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop) { _designer = designer; _targetProperty = prop; if (prop == null) { prop = TypeDescriptor.GetDefaultProperty(designer.Component); if ((prop != null) && typeof(System.Collections.ICollection).IsAssignableFrom(prop.PropertyType)) _targetProperty = prop; } } internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop, string newVerbText) : this(designer, prop) { _designer.Verbs.Add(new DesignerVerb(newVerbText, new EventHandler(OnEditItems))); } private T GetService() => (T)((IServiceProvider)this).GetService(typeof(T)); private void OnEditItems(object sender, EventArgs e) { object component = _targetProperty.GetValue(_designer.Component); if (component != null) { CollectionEditor editor = TypeDescriptor.GetEditor(component, typeof(System.Drawing.Design.UITypeEditor)) as CollectionEditor; if (editor != null) editor.EditValue(this, this, component); } } 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; } object IServiceProvider.GetService(Type serviceType) { if ((serviceType == typeof(ITypeDescriptorContext)) || (serviceType == typeof(IWindowsFormsEditorService))) return this; if ((_designer.Component != null) && (_designer.Component.Site != null)) return _designer.Component.Site.GetService(serviceType); return null; } void IWindowsFormsEditorService.CloseDropDown() { } void IWindowsFormsEditorService.DropDownControl(Control control) { } public DialogResult ShowDialog(Form dialog) { if (dialog == null) throw new ArgumentNullException(nameof(dialog)); IUIService service = GetService(); if (service != null) return service.ShowDialog(dialog); return dialog.ShowDialog(_designer.Component as IWin32Window); } private IComponentChangeService ChangeService { get { if (_componentChangeSvc == null) _componentChangeSvc = GetService(); return _componentChangeSvc; } } IContainer ITypeDescriptorContext.Container { get { if (_designer.Component.Site != null) return _designer.Component.Site.Container; return null; } } object ITypeDescriptorContext.Instance => _designer.Component; PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor => _targetProperty; } internal abstract class RichBehavior : Behavior.Behavior where D : ControlDesigner { public RichBehavior(D designer) { Designer = designer; } public D Designer { get; } } internal class RichComponentDesigner : ComponentDesigner where C : Component where A : _BaseDesignerActionList { private A actions; private Adorner adorner; private IDictionary> redirectedProps; private DesignerVerbCollection verbs; public override DesignerActionListCollection ActionLists { get { if (actions == null) actions = Activator.CreateInstance(typeof(A), this, Component) as A; return new DesignerActionListCollection(new DesignerActionList[] { actions }); } } public BehaviorService BehaviorService { get; private set; } public IComponentChangeService ComponentChangeService { get; private set; } public new C Component => (C)base.Component; public virtual GlyphCollection Glyphs => Adorner.Glyphs; public ISelectionService SelectionService { get; private set; } public override DesignerVerbCollection Verbs { get { if (verbs == null) verbs = this.GetAttributedVerbs(); return verbs; } } internal Adorner Adorner { get { if (adorner == null) { adorner = new Adorner(); BehaviorService.Adorners.Add(adorner); } return adorner; } } protected virtual IEnumerable PropertiesToRemove => new string[0]; 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; } protected override void Dispose(bool disposing) { if (disposing) { if (BehaviorService != null & adorner != null) BehaviorService.Adorners.Remove(adorner); ISelectionService ss = SelectionService; if (ss != null) ss.SelectionChanged -= OnSelectionChanged; IComponentChangeService cs = ComponentChangeService; if (cs != null) cs.ComponentChanged -= OnComponentChanged; } base.Dispose(disposing); } protected virtual S GetService() where S : class => (S)GetService(typeof(S)); protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) { } protected virtual void OnSelectionChanged(object sender, EventArgs e) { } protected override void PreFilterProperties(System.Collections.IDictionary properties) { base.PreFilterProperties(properties); // RedirectRegisteredProperties if (redirectedProps == null) redirectedProps = this.GetRedirectedProperties(); this.RedirectRegisteredProperties(properties, redirectedProps); // Remove properties this.RemoveProperties(properties, PropertiesToRemove); } } internal class RichControlDesigner : ControlDesigner where C : Control where A : _BaseDesignerActionList { private A actions; private Adorner adorner; private IDictionary> redirectedProps; private DesignerVerbCollection verbs; public override DesignerActionListCollection ActionLists { get { if (actions == null) actions = Activator.CreateInstance(typeof(A), this, Component) as A; return new DesignerActionListCollection(new DesignerActionList[] { actions }); } } public new BehaviorService BehaviorService => base.BehaviorService; public IComponentChangeService ComponentChangeService { get; private set; } public new C Control => (C)base.Control; public virtual GlyphCollection Glyphs => Adorner.Glyphs; public ISelectionService SelectionService { get; private set; } public override DesignerVerbCollection Verbs { get { if (verbs == null) verbs = this.GetAttributedVerbs(); return verbs; } } internal Adorner Adorner { get { if (adorner == null) { adorner = new Adorner(); BehaviorService.Adorners.Add(adorner); } return adorner; } } protected virtual IEnumerable PropertiesToRemove => new string[0]; 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; } protected override void Dispose(bool disposing) { if (disposing) { if (BehaviorService != null) BehaviorService.Adorners.Remove(adorner); ISelectionService ss = SelectionService; if (ss != null) ss.SelectionChanged -= OnSelectionChanged; IComponentChangeService cs = ComponentChangeService; if (cs != null) cs.ComponentChanged -= OnComponentChanged; } base.Dispose(disposing); } protected virtual S GetService() where S : class => (S)GetService(typeof(S)); protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) { } protected virtual void OnSelectionChanged(object sender, EventArgs e) { } protected override void PreFilterProperties(System.Collections.IDictionary properties) { base.PreFilterProperties(properties); // RedirectRegisteredProperties if (redirectedProps == null) redirectedProps = this.GetRedirectedProperties(); this.RedirectRegisteredProperties(properties, redirectedProps); // Remove properties this.RemoveProperties(properties, PropertiesToRemove); } } internal abstract class _BaseDesignerActionList : DesignerActionList { private List fullAIList; public _BaseDesignerActionList(ComponentDesigner designer, IComponent component) : base(component) { base.AutoShow = true; ParentDesigner = designer; } public ComponentDesigner ParentDesigner { get; } 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); } protected T GetComponentProperty(string propName) { var p = ComponentProp(propName, typeof(T)); if (p != null) return (T)p.GetValue(Component, null); return default(T); } protected void SetComponentProperty(string propName, T value) { var p = ComponentProp(propName, typeof(T)); if (p != null) p.SetValue(Component, value, null); } private Reflection.PropertyInfo ComponentProp(string propName, Type retType) => Component.GetType().GetProperty(propName, ComponentDesignerExtension.AllInstBind, null, retType, Type.EmptyTypes, null); } internal abstract class RichDesignerActionList : _BaseDesignerActionList where D : ComponentDesigner where C : Component { public RichDesignerActionList(D designer, C component) : base(designer, component) { ParentDesigner = designer; } public new C Component => (C)base.Component; public new D ParentDesigner { get; } } internal abstract class RichGlyph : Glyph, IDisposable where D : ControlDesigner { public RichGlyph(D designer, Behavior.Behavior behavior) : base(behavior) { Designer = designer; } public D Designer { get; } public virtual void Dispose() { } public void SetBehavior(RichBehavior b) { base.SetBehavior(b); } } internal class RichParentControlDesigner : ParentControlDesigner where C : Control where A : _BaseDesignerActionList { private A actions; private Adorner adorner; private IDictionary> redirectedProps; private DesignerVerbCollection verbs; public override DesignerActionListCollection ActionLists { get { if (actions == null) actions = Activator.CreateInstance(typeof(A), this, Component) as A; return new DesignerActionListCollection(new DesignerActionList[] { actions }); } } public new BehaviorService BehaviorService => base.BehaviorService; public IComponentChangeService ComponentChangeService { get; private set; } public new C Control => (C)base.Control; public virtual GlyphCollection Glyphs => Adorner.Glyphs; public ISelectionService SelectionService { get; private set; } public override DesignerVerbCollection Verbs { get { if (verbs == null) verbs = this.GetAttributedVerbs(); return verbs; } } internal Adorner Adorner { get { if (adorner == null) { adorner = new Adorner(); BehaviorService.Adorners.Add(adorner); } return adorner; } } protected virtual IEnumerable PropertiesToRemove => new string[0]; 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; } protected override void Dispose(bool disposing) { if (disposing) { if (BehaviorService != null & adorner != null) BehaviorService.Adorners.Remove(adorner); ISelectionService ss = SelectionService; if (ss != null) ss.SelectionChanged -= OnSelectionChanged; IComponentChangeService cs = ComponentChangeService; if (cs != null) cs.ComponentChanged -= OnComponentChanged; } base.Dispose(disposing); } protected virtual S GetService() where S : class => (S)GetService(typeof(S)); protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) { } protected virtual void OnSelectionChanged(object sender, EventArgs e) { } protected override void PreFilterProperties(System.Collections.IDictionary properties) { base.PreFilterProperties(properties); // RedirectRegisteredProperties if (redirectedProps == null) redirectedProps = this.GetRedirectedProperties(); this.RedirectRegisteredProperties(properties, redirectedProps); // Remove properties this.RemoveProperties(properties, PropertiesToRemove); } } }