属性网格复选框列表下拉列表 简介背景FlagEnumUIEditor 类FlagCheckedListBox 类使用代码

问题描述

我正在尝试将属性网格上的下拉列表转换为复选框列表下拉列表,该下拉列表允许用户选择多个结果。目前,我已经设法建立了一个用户可以选择的下拉选项列表,但它只允许用户选择一个。我希望用户选择 1 个以上的选项,并且 Game_Type 字段将跟踪使用逗号分隔选项(如“射手、体育”)所选择的内容。我该怎么做?

enter image description here

预期控件的名称旁边应该有一个复选框,如下所示:

enter image description here

我下面显示代码包括一个游戏类、一个下拉列表的类型转换器以及创建对象并将其添加属性网格的表单。

public class Game
{
    public string name { set; get; }

    [browsable(false)]
    public List<string> listofGameTypes { set; get; }

    [displayName("Game Type")]
    [TypeConverter(typeof(Game_Type_Converter))]
    public string Game_Type { get; set; }

    public Game()
    {
        listofGameTypes = new List<string>();
    }
}

.

public class Game_Type_Converter : TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        var obj = context.Instance as Game;

        if (obj != null)
        {
            List<string> list = new List<string>();
            foreach (String s in obj.listofGameTypes)
            {
                list.Add(s);
            }
            return new StandardValuesCollection(list);
        }


        return base.GetStandardValues(context);
    }


}

.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender,EventArgs e)
    {
        Game g = new Game();
        g.name = "FIFA 22";
        g.listofGameTypes.Add("Shooter");
        g.listofGameTypes.Add("Sports");
        g.listofGameTypes.Add("Action");

        propertyGrid1.Selectedobject = g;
    }
}

解决方法

以下内容转载自https://www.codeproject.com/Articles/13793/A-UITypeEditor-for-easy-editing-of-flag-enum-prope

我不是那篇文章的作者

由于文章很旧,毫无疑问可以进行一些小的改进,例如使用 Enum.HasFlags


enter image description here

简介

Visual Studio .NET 中的设计时支持非常好,可以非常轻松地快速开发表单和用户控件。即便如此,它也有一些小缺点——有些刺激,有些造成不便。一个这样的缺点是缺乏一种在设计时属性浏览器中编辑“标志枚举属性”的好方法。标志枚举属性是类型为枚举的属性,该枚举应用了 FlagsAttribute。该属性表示可以将枚举视为位字段;也就是说,一组标志。这种枚举的一个例子是 FontStyle 枚举。它包含粗体、斜体和下划线样式的位值,如果需要,可以将它们组合在一起。但是,属性网格没有内置对此类标志枚举的支持 - 如果您想指定字体应为粗体和斜体,唯一的方法是编写等效代码。

本文介绍了一个简单的 UITypeEditor,它可以应用于此类标志枚举,并使使用设计时属性浏览器编辑此类属性变得非常容易。

背景

在讨论代码之前,有必要先简单介绍一下什么是UI Type Editor。在属性浏览器提供的简单的字符串到值和值到字符串的转换不充分或不方便的情况下,可以使用 UI 类型编辑器提供高级 UI 来编辑属性。例如,所有控件的 Dock 和 Anchor 属性都使用 UI 类型编辑器,以便轻松编辑属性。要指定属性应使用 UI 类型编辑器,EditorAttribute 应用于属性的类型或属性本身。在第一种情况下,只要属性具有应用该属性的类型,就会使用 UI 类型编辑器。在第二种情况下,UI 类型编辑器仅用于该属性。

FlagEnumUIEditor 类

用于标志枚举的 UI 类型编辑器称为 FlagEnumUIEditor,它派生自 UITypeEditor。它是一个下拉样式编辑器,这意味着选择属性时,属性浏览器中会显示一个小的下拉按钮。单击此按钮时,将显示可用于编辑属性的控件。 FlagEnumUIEditor 使用派生自 CheckedListBox 的类来显示标志枚举。

FlagCheckedListBox 类

FlagCheckedListBox 类派生自 CheckedListBox,包含 FlagCheckedListBoxItem 类型的项目。每个项目都有一个值和对与枚举成员的值和名称相对应的值的描述。 EnumValue 属性用于将枚举值与 FlagCheckedListBox 相关联:

public Enum EnumValue
{
    get
    {
        object e = Enum.ToObject(enumType,GetCurrentValue());
        return (Enum)e;
    }
    set
    {
        
        Items.Clear();
        enumValue = value; // Store the current enum value
        enumType = value.GetType(); // Store enum type
        FillEnumMembers(); // Add items for enum members
        ApplyEnumValue(); // Check/uncheck items depending on enum value
     }
}

FillEnumMembers 和 ApplyEnumValue 函数如下:

// Adds items to the checklistbox based on the members of the enum
private void FillEnumMembers()
{
    foreach ( string name in Enum.GetNames(enumType))
    {
        object val = Enum.Parse(enumType,name);
        int intVal = (int)Convert.ChangeType(val,typeof(int));

        Add(intVal,name);
    }
}

// Checks/unchecks items based on the current value of the enum variable
private void ApplyEnumValue()
{
    int intVal = (int)Convert.ChangeType(enumValue,typeof(int));
    UpdateCheckedItems(intVal);

}
        
// Checks/Unchecks items depending on the give bitvalue
protected void UpdateCheckedItems(int value)
{

    isUpdatingCheckStates = true;

    // Iterate over all items
    for(int i=0;i<Items.Count;i++)
    {
        FlagCheckedListBoxItem item = Items[i] as FlagCheckedListBoxItem;

        // If the bit for the current item is on in the bitvalue,check it
        if( (item.value & value)== item.value && item.value!=0)
            SetItemChecked(i,true);
        // Otherwise uncheck it
        else
            SetItemChecked(i,false);
    }

    isUpdatingCheckStates = false;

}

CheckListBox 的 OnItemCheck 函数被覆盖以在选中或取消选中任何项目时更新所有其他项目:

protected override void OnItemCheck(ItemCheckEventArgs e)
{
    base.OnItemCheck(e);

    if (isUpdatingCheckStates)
        return;

    // Get the checked/unchecked item
    FlagCheckedListBoxItem item = Items[e.Index] as FlagCheckedListBoxItem;
    // Update other items
    UpdateCheckedItems(item,e.NewValue);
}

// Updates items in the checklistbox
// composite = The item that was checked/unchecked
// cs = The check state of that item
protected void UpdateCheckedItems(FlagCheckedListBoxItem composite,CheckState cs)
{

    // If the value of the item is 0,call directly.
    if(composite.value==0)
        UpdateCheckedItems(0);


    // Get the total value of all checked items
    int sum = 0;
    for(int i=0;i<Items.Count;i++)
    {
        FlagCheckedListBoxItem item = Items[i] as FlagCheckedListBoxItem;

        // If item is checked,add its value to the sum.
        if(GetItemChecked(i))
            sum |= item.value;
    }

    // If the item has been unchecked,remove its bits from the sum
    if(cs==CheckState.Unchecked)
        sum = sum & (~composite.value);
    // If the item has been checked,combine its bits with the sum
    else
        sum |= composite.value;

    // Update all items in the checklistbox based on the final bit value
    UpdateCheckedItems(sum);

}

最后,当用户编辑完属性后,通过GetCurrentValue方法返回CheckListBox中所有选中项对应的位值。

// Gets the current bit value corresponding to all checked items
public int GetCurrentValue()
{
    int sum = 0;

    for(int i=0;i<Items.Count;i++)
    {
        FlagCheckedListBoxItem item = 
                   Items[i] as FlagCheckedListBoxItem;

        if( GetItemChecked(i))
            sum |= item.value;
    }

    return sum;
}

使用代码

在您自己的项目中使用代码非常简单。只需将 FlagEnumEditor.cs 文件添加到您的项目中,然后将 UI 类型编辑器应用于您的枚举或属性,如下所示:

[Editor(typeof(Utils.FlagEnumUIEditor),typeof(System.Drawing.Design.UITypeEditor))]