.net 在Visual Studio designer中公开枚举(标志)集合

.net 在Visual Studio designer中公开枚举(标志)集合,.net,visual-studio,winforms,.net,Visual Studio,Winforms,我有一个可能在.NETForms控件中显示的数据类型枚举,我想为控件的使用者提供一个接口来过滤某些类型(设置一些标志)。位字段似乎是这样做的合乎逻辑的方式,不幸的是,枚举从0开始,而不是1(0,1,2,4,8,…),并且无法更改 如何公开这组标志,以便以编程方式或通过VisualStudio设计器轻松配置 您需要编写一个UITypeEditor来完成这项工作,并通过[EditorAttribute]将其与属性相关联 编辑现在使用示例—恐怕是一个相当长的示例—但幸运的是,大多数代码可以在类型之间共

我有一个可能在.NETForms控件中显示的数据类型枚举,我想为控件的使用者提供一个接口来过滤某些类型(设置一些标志)。位字段似乎是这样做的合乎逻辑的方式,不幸的是,枚举从0开始,而不是1(0,1,2,4,8,…),并且无法更改


如何公开这组标志,以便以编程方式或通过VisualStudio设计器轻松配置

您需要编写一个
UITypeEditor
来完成这项工作,并通过
[EditorAttribute]
将其与属性相关联

编辑现在使用示例—恐怕是一个相当长的示例—但幸运的是,大多数代码可以在类型之间共享

您不能使用单个复合枚举值,因为该值为零。因此,如果您使用.NET 2.0/3.0,那么在这里我使用一个
哈希集来保存所选的枚举,这非常容易重新处理
列表

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;

public class MyControl : UserControl
{
    public MyControl()
    {
        Values = new HashSet<MyEnum>();
    }
    [Editor(typeof(MyEnumSetEditor), typeof(UITypeEditor))]
    [TypeConverter(typeof(MyEnumSetConverter))]
    public HashSet<MyEnum> Values { get; set; }
}

public enum MyEnum
{  // numbers as per the question...
    A = 0, B = 1, C = 2, D = 4, E = 8
}
class MyEnumSetEditor : EnumSetEditor<MyEnum> { }
class MyEnumSetConverter : EnumSetConverter<MyEnum> { }

// from here down is shared between types
abstract class EnumSetConverter<T> : TypeConverter where T : struct
{
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
    }
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if(destinationType == typeof(string))
        {
            HashSet<T> set = (HashSet<T>)value;
            if (set == null) return "(null)";

            StringBuilder sb = new StringBuilder();
            foreach (T item in Enum.GetValues(typeof(T)))
            {
                if (set.Contains(item))
                {
                    if (sb.Length > 0) sb.Append(", ");
                    sb.Append(item);
                }
            }
            return sb.ToString();
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

public abstract class EnumSetEditor<T> : UITypeEditor where T : struct
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.DropDown;
    }
    public override bool IsDropDownResizable
    {
        get { return true; }
    }
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        IWindowsFormsEditorService svc = (IWindowsFormsEditorService)
            provider.GetService(typeof(IWindowsFormsEditorService));
        HashSet<T> set = value as HashSet<T>;
        if (svc != null && set != null)
        {
            UserControl ctrl = new UserControl();
            CheckedListBox clb = new CheckedListBox();
            clb.Dock = DockStyle.Fill;
            Button btn = new Button();
            btn.Dock = DockStyle.Bottom;
            foreach (T item in Enum.GetValues(typeof(T)))
            {
                clb.Items.Add(item, set.Contains(item));
            }
            ctrl.Controls.Add(clb);
            ctrl.Controls.Add(btn);
            btn.Text = "OK";
            btn.Click += delegate
            {
                set.Clear();
                foreach (T item in clb.CheckedItems)
                {
                    set.Add(item);
                }
                svc.CloseDropDown();
            };
            svc.DropDownControl(ctrl);
        }

        return value;
    }
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统、绘图、设计;
使用系统文本;
使用System.Windows.Forms;
使用System.Windows.Forms.Design;
公共类MyControl:UserControl
{
公共MyControl()
{
值=新的HashSet();
}
[编辑器(typeof(MyEnumSetEditor)、typeof(UITypeEditor))]
[TypeConverter(typeof(MyEnumSetConverter))]
公共哈希集值{get;set;}
}
公共枚举髓鞘
{//根据问题的编号。。。
A=0,B=1,C=2,D=4,E=8
}
类MyEnumSetEditor:EnumSetEditor{}
类MyEnumSetConverter:EnumSetConverter{}
//从这里开始,类型之间共享
抽象类EnumSetConverter:TypeConverter,其中T:struct
{
公共覆盖布尔CanConvertTo(ITypeDescriptorContext上下文,类型destinationType)
{
返回destinationType==typeof(string)| | base.CanConvertTo(context,destinationType);
}
公共重写对象转换为(ITypeDescriptorContext上下文,System.Globalization.CultureInfo区域性,对象值,类型destinationType)
{
if(destinationType==typeof(string))
{
HashSet=(HashSet)值;
如果(set==null)返回“(null)”;
StringBuilder sb=新的StringBuilder();
foreach(Enum.GetValues中的T项(typeof(T)))
{
if(集合包含(项目))
{
如果(sb.Length>0)sb.Append(“,”);
某人附加(项目);
}
}
使某人返回字符串();
}
返回base.ConvertTo(上下文、区域性、值、destinationType);
}
}
公共抽象类EnumSetEditor:UITypeEditor,其中T:struct
{
公共重写UITypeEditorEditStyle GetEditStyle(ITTypeDescriptorContext上下文)
{
返回UITypeEditorEditStyle.DropDown;
}
公共覆盖布尔值可降大小
{
获取{return true;}
}
公共重写对象EditValue(ITypeDescriptorContext上下文、IServiceProvider提供程序、对象值)
{
IWINDOWSFormsEditor服务svc=(IWINDOWSFormsEditor服务)
GetService(typeof(IWindowsFormsEditorService));
HashSet set=作为HashSet的值;
if(svc!=null&&set!=null)
{
UserControl ctrl=new UserControl();
CheckedListBox clb=新的CheckedListBox();
clb.Dock=DockStyle.Fill;
按钮btn=新按钮();
btn.Dock=DockStyle.Bottom;
foreach(Enum.GetValues中的T项(typeof(T)))
{
clb.Items.Add(item,set.Contains(item));
}
ctrl.Controls.Add(clb);
ctrl.Controls.Add(btn);
btn.Text=“确定”;
点击+=委托
{
set.Clear();
foreach(clb.CheckedItems中的T项)
{
设置。添加(项目);
}
svc.CloseDropDown();
};
下拉控件(ctrl);
}
返回值;
}
}

我也遇到了同样的问题:编辑器控件似乎可以工作,但值不会持久。基于马克的答案和一些进一步的研究,我现在已经开始运行了。 虽然我在设计时编辑自己的控件时需要此功能,但在阅读以下引用后,我使用带有PropertyGrid控件的演示项目对其进行了测试:

开发自定义UITypeEditor时,建议您 将生成编号设置为随每个生成而递增。这防止了 无法在中创建UITypeEditor的较旧缓存版本 设计环境

我认为这实际上是一个问题,在试图实现Marc的解决方案时,它导致了问题。在单独的项目中进行测试也很有帮助,因为您可以使用逐步调试来查看控件、编辑器和转换器中发生了什么

基本步骤是:

  • 创建类型(此处:MyFlags)和属性(此处:MyProperty),向 后者
  • 实现用于编辑的控件(此处:EnumEditorControl
  • 实现一个
    UITypeEditor
    类(此处:EnumEditor);它使用
    编辑器
    属性连接到属性。它还可以创建和管理EnumEditorControl的实例
  • 实现一个
    TypeConverter
    类(这里:EnumConverter);它还使用属性(
    TypeConverter
    )连接到属性,并处理将值转换为字符串以在属性网格中显示的操作
现在是相关代码片段的演练:

  • 使用
    Flags
    属性定义
    Enum

    <Flags>
    Public Enum MyFlags
        Flag1 = 2 ^ 0
        Flag2 = 2 ^ 1
        Flag3 = 2 ^ 2
    End Enum
    
  • 此属性稍后将具有att
    Public Property MyProperty() As MyFlags
        ...
    End Property
    
    Imports System.Windows.Forms.Design
    Public Class EnumEditorControl(Of T As Structure)
        Public Sub New(value As Long, editorService As IWindowsFormsEditorService)
            'This call is required by the Windows.Forms Form Designer.
            InitializeComponent()
    
            _value = value
            _editorService = editorService
    
            For Each item As Long In [Enum].GetValues(GetType(T))
                Me.CheckedListBox1.Items.Add([Enum].GetName(GetType(T), item), (_value And item) = item)
            Next
    
        End Sub
    
        Private _value As Long
        Public Property Value As Long
            Get
                Return _value
            End Get
            Set(value As Long)
                _value = value
            End Set
        End Property
        Private _editorService As IWindowsFormsEditorService
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim v As Long = 0
            For Each item As String In Me.CheckedListBox1.CheckedItems
                v = (v Or [Enum].Parse(GetType(T), item))
            Next
            _value = v
            Me._editorService.CloseDropDown()
        End Sub
    End Class
    
    Imports System.ComponentModel
    Imports System.Drawing.Design
    Imports System.Windows.Forms.Design
    
    Public Class EnumEditor(Of T As Structure)
        Inherits UITypeEditor
    
        Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
            Return UITypeEditorEditStyle.DropDown
        End Function
        Public Overrides ReadOnly Property IsDropDownResizable() As Boolean
            Get
                Return True
            End Get
        End Property
        Public Overrides Function EditValue(context As ITypeDescriptorContext, provider As IServiceProvider, value As Object) As Object
            Dim svc As IWindowsFormsEditorService = DirectCast(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
            Dim items As Long = CLng(value)
            If svc IsNot Nothing Then
                Dim c As New EnumEditorControl(Of T)(value, svc)
    
                svc.DropDownControl(c)
    
                value = c.Value
            End If
    
            Return CType(value, T)
        End Function
    End Class
    
    Class EnumConverter(Of T As Structure)
        Inherits TypeConverter
    
        Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
            Return destinationType = GetType(String) OrElse MyBase.CanConvertTo(context, destinationType)
        End Function
        Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object, destinationType As Type) As Object
            If destinationType = GetType(String) Then
                Dim items As Integer = CLng(value)
    
                If items = 0 Then
                    Return ""
                End If
    
                Dim values As New List(Of String)
    
                For Each item As Integer In [Enum].GetValues(GetType(T))
                    If (items And item) = item Then
                        values.Add([Enum].GetName(GetType(T), item))
                    End If
                Next
                Return String.Join(", ", values)
            End If
    
            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End Function
    End Class
    
    <Browsable(True),
    DefaultValue(MyFlags.Flag1),
    Editor(GetType(EnumEditor(Of MyFlags)), 
    GetType(System.Drawing.Design.UITypeEditor)),
    TypeConverter(GetType(EnumConverter(Of MyFlags)))
    >
    Public Property MyProperty() As MyFlags
        ...
    End Property