using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Forms; namespace Vanara.Windows.Forms { /// /// A combo box that displays the items of an type. If the Enum type has a , then the /// drop-down will be checked list of the values. /// /// public class EnumComboBox : CustomComboBox { private readonly List items = new List(); private readonly Timer timer = new Timer { Interval = 150 }; private CheckedListBox checkListBox; private Type type = null; /// Initializes a new instance of the class. public EnumComboBox() : base() { base.DropDownStyle = ComboBoxStyle.DropDownList; timer.Tick += Timer_Tick; } /// Gets or sets the data source for this . [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new object DataSource { get => base.DataSource; set => base.DataSource = value; } /// Gets or sets the property to display for this . [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new string DisplayMember { get => base.DisplayMember; set => base.DisplayMember = value; } /// Gets or sets a value specifying the style of the combo box. /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ComboBoxStyle DropDownStyle { get => base.DropDownStyle; set => base.DropDownStyle = value; } /// Gets or sets the Enum type that is used to populate the values of the combo box. /// The enum type string. /// EnumTypeString must be an enumerated type. [DefaultValue(""), Category("Behavior"), Description("The Enum type that is used to populate the values of the combo box.")] public string EnumTypeString { get => type?.FullName ?? ""; set { type = FindType(value, true); if (type is null || !type.IsEnum) throw new ArgumentException("EnumTypeString must be an enumerated type."); items.Clear(); foreach (var item in Enum.GetValues(type)) items.Add(new ECBItem(item)); if (type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) { if (checkListBox == null) { checkListBox = new CheckedListBox() { BorderStyle = BorderStyle.None, CheckOnClick = true, FormattingEnabled = true, Location = new System.Drawing.Point(17, 35), MultiColumn = false, Name = "checkedListBox1", Size = new System.Drawing.Size(187, 105), TabIndex = 0 }; checkListBox.ItemCheck += CheckListBox_ItemCheck; } checkListBox.Items.Clear(); checkListBox.Items.AddRange(items.ToArray()); DropDownControl = checkListBox; } else { DropDownControl = null; DisplayMember = "Text"; ValueMember = "Value"; DataSource = items; } } } /// /// Gets or sets the value of the member property specified by the property. /// public new object SelectedValue { get { var ret = 0L; if (!HasFlags) { ret = Convert.ToInt64(items[SelectedIndex].Value); } else { for (var i = 0; i < checkListBox.CheckedItems.Count; i++) { var o = checkListBox.CheckedItems[i] as ECBItem; if (o != null && o.Value is IConvertible) try { ret |= Convert.ToInt64(o.Value); } catch { } } } return Convert.ChangeType(ret, GetEnumUnderlyingType(type)); } set { if (!HasFlags) SelectedIndex = items.FindIndex(i => i.Value == value); else { var lval = Convert.ToInt64(value); checkListBox.BeginUpdate(); for (var i = 0; i < checkListBox.Items.Count; i++) { long? val = null; var o = checkListBox.Items[i] as ECBItem; if (o != null && o.Value is IConvertible) try { val = Convert.ToInt64(o.Value); } catch { } checkListBox.SetItemCheckState(i, (val.HasValue && (val.Value & lval) == val.Value) ? CheckState.Checked : CheckState.Unchecked); } checkListBox.EndUpdate(); } } } /// Gets or sets the property to use as the actual value for the items in the . [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new string ValueMember { get => base.ValueMember; set => base.ValueMember = value; } /// Gets an object representing the collection of the items contained in this . [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ObjectCollection Items => base.Items; private bool HasFlags => DropDownControl != null; /// Gets the selected value. /// The type of the value to retrieve. /// The selected value cast to . public T GetSelectedValue() => SelectedValue == null ? default : (T)SelectedValue; private static Type GetEnumUnderlyingType(Type eType) { if (!eType.IsEnum) throw new ArgumentException("Must be an Enum type.", nameof(eType)); var fields = eType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); if (fields == null || fields.Length != 1) throw new ArgumentException("Invalid Enum type.", nameof(eType)); return fields[0].FieldType; } private void CheckListBox_ItemCheck(object sender, ItemCheckEventArgs e) => timer.Enabled = true; private Type FindType(string name, bool ignoreCase) => AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType(name, false, ignoreCase)).Where(t => t != null).FirstOrDefault(); private string GetFlagText() { var items = new string[checkListBox.CheckedItems.Count]; for (var i = 0; i < checkListBox.CheckedItems.Count; i++) items[i] = checkListBox.CheckedItems[i].ToString(); return string.Join(", ", items); } private void Timer_Tick(object sender, EventArgs e) { timer.Enabled = false; if (HasFlags) Text = GetFlagText(); } [Serializable] private class ECBItem { public ECBItem(object value) { Value = value; Text = value.ToString(); // TODO: Alternatively get text from resource or translation service. } public string Text { get; set; } public object Value { get; set; } public override string ToString() => Text; } } }