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();
}
}
}