using System.ComponentModel; using System.Drawing.Design; using System.Linq; using System.Windows.Forms; using System.Windows.Forms.Design; namespace Vanara.Windows.Forms.Design; /// A for editing flag enums. /// The type of the enum. /// public class FlagEnumUIEditor : UITypeEditor where TE : struct, Enum { private readonly FlagCheckedListBox listBox; /// Initializes a new instance of the class. public FlagEnumUIEditor() => listBox = new FlagCheckedListBox { BorderStyle = BorderStyle.None }; /// /// Edits the specified object's value using the editor style indicated by the method. /// /// /// An that can be used to gain additional context information. /// /// An that this editor can use to obtain services. /// The object to edit. /// /// The new value of the object. If the value of the object has not changed, this should return the same object it was passed. /// public override object? EditValue(ITypeDescriptorContext? context, IServiceProvider provider, object? value) { if (context?.Instance != null && provider != null && value != null) { var edSvc = provider.GetService(); if (edSvc is null) return null; var e = (TE)Convert.ChangeType(value, context.PropertyDescriptor?.PropertyType ?? throw new ArgumentNullException(nameof(context))); listBox.Value = e; edSvc.DropDownControl(listBox); return listBox.Value; } return null; } /// /// Gets the editor style used by the method. /// /// /// An that can be used to gain additional context information. /// /// /// A value that indicates the style of editor used by the method. If the does not support this method, then will return . /// public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext? context) => UITypeEditorEditStyle.DropDown; /// A checked list box to use as the editor. /// public class FlagCheckedListBox : CheckedListBox { private readonly Container? components = null; private TE enumValue; private bool isUpdatingCheckStates; /// Initializes a new instance of the class. public FlagCheckedListBox() => CheckOnClick = true; /// Gets or sets the value. /// The value. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public TE Value { get { long sum = 0; for (var i = 0; i < Items.Count; i++) { if (Items[i] is FlagCheckedListBoxItem item && GetItemChecked(i)) sum |= item.LongVal; } return FromLong(sum); } set { Items.Clear(); enumValue = value; foreach (TE val in Enum.GetValues(typeof(TE)).Cast()) Add(val); UpdateCheckedItems(enumValue); } } /// Adds the specified v. /// The v. public void Add(TE v) => Items.Add(new FlagCheckedListBoxItem(v)); /// /// Releases the unmanaged resources used by the and its child controls 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) components?.Dispose(); base.Dispose(disposing); } /// Raises the event. /// The instance containing the event data. protected override void OnItemCheck(ItemCheckEventArgs e) { base.OnItemCheck(e); if (isUpdatingCheckStates) return; if (Items[e.Index] is FlagCheckedListBoxItem item) UpdateCheckedItems(item, e.NewValue); } /// Updates the checked items. /// The value. protected void UpdateCheckedItems(TE value) { isUpdatingCheckStates = true; var lval = Convert.ToInt64(value); // Iterate over all items for (var i = 0; i < Items.Count; i++) { if (Items[i] is not FlagCheckedListBoxItem item) continue; SetItemChecked(i, item.LongVal == 0 && lval == 0 || value.IsFlagSet(item.Value)); //SetItemChecked(i, item.Value == 0 ? value == 0 : (item.value & value) == item.value && item.value != 0); } isUpdatingCheckStates = false; } /// Updates items in the CheckListBox. /// The item that was checked/unchecked. /// The check state of that item. protected void UpdateCheckedItems(FlagCheckedListBoxItem composite, CheckState cs) { long sum = 0; if (composite.LongVal != 0) { sum = Convert.ToInt64(Value); // If the item has been unchecked, remove its bits from the sum if (cs == CheckState.Unchecked) sum &= ~composite.LongVal; // If the item has been checked, combine its bits with the sum else sum |= composite.LongVal; } UpdateCheckedItems(FromLong(sum)); } private static TE FromLong(long val) => (TE)Enum.ToObject(typeof(TE), val); /// Represents an item in the CheckListBox /// public class FlagCheckedListBoxItem { /// Initializes a new instance of the class. /// The value. public FlagCheckedListBoxItem(TE value) => Value = value; /// Gets the long value. /// The long value. public long LongVal => Convert.ToInt64(Value); /// Gets the value. /// The value. public TE Value { get; } /// Converts to string. /// A that represents this instance. public override string ToString() => Value.ToString(); } } }