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