【问题标题】:Is it possible to hide properties of a class by default, e.g. in a DataGridView?是否可以默认隐藏类的属性,例如在 DataGridView 中?
【发布时间】:2015-12-08 00:55:50
【问题描述】:

我很清楚<System.ComponentModel.Browsable("False")> 属性可以应用于类的每个属性。是否可以将所有属性的Browsable属性的默认值设置为False

以下代码编译并说明了我想要实现的目标,但遗憾的是它没有按预期工作:

<Browsable(False)>
Public Class SomeClass

    Public Property HiddenByDefaultProperty1 As Object
    Public Property HiddenByDefaultProperty2 As Object

    ...

    <Browsable(True)> Public Property BrowsableProperty As Object

End Class

为了实现我想要的,我必须将&lt;Browsable(False)&gt; 应用于我不想在我的 DataGridView 中显示的所有属性,这是很多代码混乱。

如果我只需为要显示的属性指定&lt;Browsable(True)&gt;,那就太好了。但是:有可能吗?

【问题讨论】:

  • 不可能的。但是,请注意,如果您要设置 Browsable(False),请考虑是否还需要设置 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)

标签: c# vb.net datagridview componentmodel


【解决方案1】:

不,我认为使用 browsable 属性是不可能的。但是,您可以通过实现 ICustomTypeDescriptor 接口来控制 DataGridView 将绑定到哪些属性。

型号

C#:

public class Foo : ICustomTypeDescriptor
{

    public string P1 { get; set; }
    public string P2 { get; set; }
    public string P3 { get; set; }
    public string P4 { get; set; }
    public string P5 { get; set; }
    public string P6 { get; set; }
    public string P7 { get; set; }
    public string P8 { get; set; }
    public string P9 { get; set; }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
    {

        var properties = new[] { "P1", "P3", "P7" };

        var descriptors = TypeDescriptor
            .GetProperties(typeof(Foo))
            .Cast<PropertyDescriptor>()
            .Where(p => properties.Any(s => s == p.Name))
            .ToArray();

        return new PropertyDescriptorCollection(descriptors);

    }

    AttributeCollection ICustomTypeDescriptor.GetAttributes()
    {
        return new AttributeCollection(null);
    }

    string ICustomTypeDescriptor.GetClassName()
    {
        return null;
    }

    string ICustomTypeDescriptor.GetComponentName()
    {
        return null;
    }

    TypeConverter ICustomTypeDescriptor.GetConverter()
    {
        return null;
    }

    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
    {
        return null;
    }

    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
    {
        return null;
    }

    object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
    {
        return null;
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
    {
        return new EventDescriptorCollection(null);
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
    {
        return new EventDescriptorCollection(null);
    }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
    {
        return ((ICustomTypeDescriptor)this).GetProperties(null);
    }

    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }
}

VB.Net:

Public Class Foo
    Implements ICustomTypeDescriptor

    Public Property P1 As String
    Public Property P2 As String
    Public Property P3 As String
    Public Property P4 As String
    Public Property P5 As String
    Public Property P6 As String
    Public Property P7 As String
    Public Property P8 As String
    Public Property P9 As String

    Private Function GetProperties(attributes() As Attribute) As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties

        Dim properties = {"P1", "P3", "P7"}

        Dim descriptors = TypeDescriptor _
            .GetProperties(GetType(Foo)) _
            .Cast(Of PropertyDescriptor) _
            .Where(Function(p) properties.Any(Function(s) s = p.Name)) _
            .ToArray()

        Return New PropertyDescriptorCollection(descriptors)

    End Function

    Private Function GetAttributes() As AttributeCollection Implements ICustomTypeDescriptor.GetAttributes
        Return New AttributeCollection(Nothing)
    End Function

    Private Function GetClassName() As String Implements ICustomTypeDescriptor.GetClassName
        Return Nothing
    End Function

    Private Function GetComponentName() As String Implements ICustomTypeDescriptor.GetComponentName
        Return Nothing
    End Function

    Private Function GetConverter() As TypeConverter Implements ICustomTypeDescriptor.GetConverter
        Return Nothing
    End Function

    Private Function GetDefaultEvent() As EventDescriptor Implements ICustomTypeDescriptor.GetDefaultEvent
        Return Nothing
    End Function

    Private Function GetDefaultProperty() As PropertyDescriptor Implements ICustomTypeDescriptor.GetDefaultProperty
        Return Nothing
    End Function

    Private Function GetEditor(editorBaseType As Type) As Object Implements ICustomTypeDescriptor.GetEditor
        Return Nothing
    End Function

    Private Function GetEvents() As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
        Return New EventDescriptorCollection(Nothing)
    End Function

    Private Function GetEvents(attributes() As Attribute) As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
        Return New EventDescriptorCollection(Nothing)
    End Function

    Private Function GetProperties() As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
        Return DirectCast(Me, ICustomTypeDescriptor).GetProperties(Nothing)
    End Function

    Private Function GetPropertyOwner(pd As PropertyDescriptor) As Object Implements ICustomTypeDescriptor.GetPropertyOwner
        Return Me
    End Function

End Class

DataGridView:

C#:

var list = new List<Foo>();

list.Add(new Foo());
list.Add(new Foo());
list.Add(new Foo());

this.dataGridView1.DataSource = list;

VB.Net:

Dim list As New List(Of Foo)

list.Add(New Foo())
list.Add(New Foo())
list.Add(New Foo())

Me.DataGridView1.DataSource = list

PropertyGrid:

C#:

this.propertyGrid1.SelectedObject = new Foo();

VB.Net:

Me.PropertyGrid1.SelectedObject = New Foo()

【讨论】:

  • 非常感谢您的回复!不幸的是,它有很多样板代码:o/
【解决方案2】:

正如另一个答案中提到的,ICustomTypeDescriptor 让您可以完全控制对象的元数据,但它需要大量样板代码。对于列表绑定控件(如DataGridViewListView 等),可以通过列表源类实现ITypedList 接口,这更简单,但仍需要一些编码。在这两种情况下,如果您不能创建一些基类并从它们继承所有类,那么只标记“不可浏览”成员会容易得多。
无论如何,还有另一种方法,有点骇人听闻,但(几乎)完全符合您的要求。该行为由我称为NoBrowsableAttribute 的自定义类提供。用法很简单:

// To turn it on
NoBrowsableAttribute.Enabled = true;
// To turn it off
NoBrowsableAttribute.Enabled = false;

请注意,当打开时,它将更改应用程序内所有类的BrowsableAttribute 默认行为。这是一个示例测试:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace Samples
{
    public sealed class NoBrowsableAttribute : TypeDescriptionProvider
    {
        public static bool Enabled
        {
            get { return instance.enabled; }
            set { instance.enabled = value; }
        }
        private static readonly NoBrowsableAttribute instance = new NoBrowsableAttribute();
        private bool enabled;
        private NoBrowsableAttribute() : base(TypeDescriptor.GetProvider(typeof(BrowsableAttribute)))
        {
            TypeDescriptor.AddProvider(this, typeof(BrowsableAttribute));
        }
        public override Type GetReflectionType(Type objectType, object instance)
        {
            if (enabled && objectType == typeof(BrowsableAttribute)) return typeof(NoBrowsableAttribute);
            return base.GetReflectionType(objectType, instance);
        }
        public static readonly BrowsableAttribute Default = BrowsableAttribute.No;
    }

    static class Test
    {
        class Person
        {
            public int Id { get; set; }
            [Browsable(true)]
            public string Name { get; set; }
            [Browsable(true)]
            public string Description { get; set; }
            public int Age { get; set; }
        }
        [STAThread]
        static void Main()
        {
            NoBrowsableAttribute.Enabled = true;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var data = Enumerable.Range(1, 10).Select(i => new Person { Id = i, Name = "Name" + i, Description = "Description" + i }).ToList();
            var form = new Form { StartPosition = FormStartPosition.CenterScreen, ClientSize = new Size(500, 300) };
            var split = new SplitContainer { Dock = DockStyle.Fill, Parent = form, FixedPanel = FixedPanel.Panel2, SplitterDistance = 300 };
            var dg = new DataGridView { Dock = DockStyle.Fill, Parent = split.Panel1 };
            var pg = new PropertyGrid { Dock = DockStyle.Fill, Parent = split.Panel2, ToolbarVisible = false, PropertySort = PropertySort.NoSort };
            dg.BindingContextChanged += (sender, e) => {
                var bm = dg.BindingContext[data];
                pg.SelectedObject = bm.Current;
                bm.CurrentChanged += (_sender, _e) => pg.SelectedObject = bm.Current;
            };
            dg.DataSource = data;
            Application.Run(form);
        }
    }
}

【讨论】:

  • 这是一个有趣的方法!遗憾的是我不能完全理解它是如何工作的,即为什么它会影响所有类?
  • 所有 UI 组件都使用 TypeDescripor 服务而不是直接反射 API。 TypeDescriptor 允许您修改现有类型的元数据。首先,在类构造函数中,我们将类注册为BrowsableAttribute 类型的元数据提供者。然后,在GetReflectionType 方法中,我们说类似“嘿,你想在BrowsableAttribute 上使用反射,但你应该反射我们的类型)。然后我们提供不同的默认值(BrowsableAttribute 具有确切的静态字段,但价值是)。所以这是骇人听闻的部分。
  • 它会影响所有类,因为我们正在更改属性的默认值,而不管它应用在哪里。当没有为字段、属性等定义属性时,使用任何属性的默认值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多