首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当PropertyGrid的所有属性都是只读的时,collection<T>的类别不会在<T>中显示。

当PropertyGrid的所有属性都是只读的时,collection<T>的类别不会在<T>中显示。
EN

Stack Overflow用户
提问于 2018-12-18 10:12:08
回答 4查看 1.7K关注 0票数 11

正如标题所述,我注意到,当类"T“的所有属性都为只读时,类别没有显示在集合( T)的**PropertyGrid* (在其默认集合编辑器中)中。

下面的代码表示我的代码结构:

C#:

代码语言:javascript
运行
复制
[TypeConverter(typeof(ExpandableObjectConverter))]
public class TestClass1 {

    public TestClass2 TestProperty1 {get;} = new TestClass2();
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class TestClass2 {

    [TypeConverter(typeof(CollectionConverter))]
    public ReadOnlyCollection<TestClass3> TestProperty2 {
        get {
            List<TestClass3> collection = new List<TestClass3>();
            for (int i = 0; i <= 10; i++) {
                collection.Add(new TestClass3());
            }
            return collection.AsReadOnly();
        }
    }
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class TestClass3 {

    [Category("Category 1")]
    public string TestProperty3 {get;} = "Test";
}

VB.NET:

代码语言:javascript
运行
复制
<TypeConverter(GetType(ExpandableObjectConverter))>
Public Class TestClass1

    Public ReadOnly Property TestProperty1 As TestClass2 = New TestClass2()

End Class

<TypeConverter(GetType(ExpandableObjectConverter))>
Public NotInheritable Class TestClass2

    <TypeConverter(GetType(CollectionConverter))>
    Public ReadOnly Property TestProperty2 As ReadOnlyCollection(Of TestClass3)
        Get
            Dim collection As New List(Of TestClass3)
            For i As Integer = 0 To 10
                collection.Add(New TestClass3())
            Next
            Return collection.AsReadOnly()
        End Get
    End Property

End Class

<TypeConverter(GetType(ExpandableObjectConverter))>
Public NotInheritable Class TestClass3

    <Category("Category 1")>
    Public ReadOnly Property TestProperty3 As String = "Test"

End Class

问题在于TestProperty3.当它是只读的,类别(“类别1")没有显示在属性网格中.

但是如果我做了可编辑的属性,那么分类就会显示.

C:#

代码语言:javascript
运行
复制
[Category("Category 1")]
public string TestProperty3 {get; set;} = "Test";

VB.NET:

代码语言:javascript
运行
复制
<Category("Category 1")>
Public Property TestProperty3 As String = "Test"

不仅如此,让我们假设在TestClass3中声明了10个属性(而不是这个示例中的1个),其中9个属性是只读的,1个是可编辑的,那么,在这种情况下,将显示所有类别。另一方面,如果所有10个属性都是只读的,则不会显示类别。

PeopertyGrid的这种行为对我来说是非常恼人和意外的。我希望看到我的自定义类别,不管在我的类中是用setter声明的属性还是没有它的属性。

我必须向类别显示哪些选项具有类的所有属性为只读?也许编写自定义TypeConverter或集合编辑器可以修复这种恼人的视觉表示行为?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-12-21 20:01:10

这不是PropertyGrid的错误,而是特性(错误?)CollectionForm of CollectionEditor.

如果将TestClass3的实例直接分配给属性网格,您将看到属性网格按预期在类别下显示属性。但是,当CollectionForm试图在其属性网格中显示TestClass3实例时,由于它没有任何可设置属性,而且它的集合转换器不支持创建项实例,所以它决定将对象包装到另一个派生自定义类型描述符的对象中,在类名相同的类别下显示所有属性。

正如其他答案所建议的,您可以通过

  • 向类中添加不可浏览的虚拟可写属性。
  • 或者通过注册一个新的类型描述符,当请求返回属性列表时,该描述符返回一个虚拟的不可浏览的可写属性。

但是,我不希望仅仅因为CollectionForm错误而更改类或其类型描述符。

由于问题与CollectionFormCollectiorEditor有关,作为另一种选择,您可以通过创建从CollectionEditor派生的集合编辑器并重写其CreateCollectorForm方法来解决问题,并在尝试在集合编辑器表单中设置属性网格的选定对象时更改其行为:

代码语言:javascript
运行
复制
public class MyCollectionEditor<T> : CollectionEditor
{
    public MyCollectionEditor() : base(typeof(T)) { }
    public override object EditValue(ITypeDescriptorContext context, 
        IServiceProvider provider, object value)
    {
        return base.EditValue(context, provider, value);
    }
    protected override CollectionForm CreateCollectionForm()
    {
        var f = base.CreateCollectionForm();
        var propertyBrowser = f.Controls.Find("propertyBrowser", true)
            .OfType<PropertyGrid>().FirstOrDefault();
        var listbox = f.Controls.Find("listbox", true)
           .OfType<ListBox>().FirstOrDefault();
        if (propertyBrowser != null && listbox !=null)
            propertyBrowser.SelectedObjectsChanged += (sender, e) =>
            {
                var o = listbox.SelectedItem;
                if (o != null)
                    propertyBrowser.SelectedObject =
                        o.GetType().GetProperty("Value").GetValue(o);
            };
        return f;
    }
}

然后就可以用这个属性来装饰TesProperty2了:

代码语言:javascript
运行
复制
[Editor(typeof(MyCollectionEditor<TestClass3>), typeof(UITypeEditor))]
票数 3
EN

Stack Overflow用户

发布于 2018-12-18 11:15:04

这的确是一种非常令人讨厌的行为。但是,我不认为您可以绕过它:错误的不是属性--描述符--它报告的是正确的类别--您可以通过以下方法进行验证:

代码语言:javascript
运行
复制
var props = TypeDescriptor.GetProperties(new TestClass3());
foreach(PropertyDescriptor prop in props)
{
    Console.WriteLine($"{prop.Category}: {prop.Name}");
}

输出Category 1: TestProperty3

因此,这只是集合编辑器UI控件的一个怪癖。奇怪的是,如果添加第二个可写属性,它将开始显示这两个属性的类别。但是如果添加第二个只读属性:它不显示类别。这既适用于get-only属性,也适用于标记为[ReadOnly(true)]的属性。

所以:我不认为这里有一个好的解决方案,除了使用不同的属性-网格实现,或者添加一个虚拟的可写属性-对不起!

请注意:在使用{get;set;} = "initial value";样式初始化(或构造函数初始化)时,最好也向该属性添加[DefaultValue("initial value")],以便使ShouldSerialize*()行为正确(或以PropertyGrid术语表示:以便使其粗体/不粗体),但是.这解决不了你看到的问题,抱歉。

票数 2
EN

Stack Overflow用户

发布于 2018-12-20 15:52:40

向类中的虚拟可写但不能浏览的属性问好。

当然,这是属性网格的bug(?)的一个解决方法。但是,考虑到创建自定义集合编辑器表单和实现自定义UITypeEditor所需的开销,而定制表单又将使用您的自定义表单来克服这种行为,它至少应该被命名为一个半优雅的解决方案。

代码:

代码语言:javascript
运行
复制
Imports System.Collections.ObjectModel
Imports System.ComponentModel

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim tc1 As New TestClass1
        PropertyGrid1.SelectedObject = tc1
    End Sub

    <TypeConverter(GetType(ExpandableObjectConverter))>
    Public Class TestClass1
        Public ReadOnly Property TestProperty1 As TestClass2 = New TestClass2()
    End Class

    <TypeConverter(GetType(ExpandableObjectConverter))>
    Public NotInheritable Class TestClass2
        <TypeConverter(GetType(CollectionConverter))>
        Public ReadOnly Property TestProperty2 As ReadOnlyCollection(Of TestClass3)
            Get
                Dim collection As New List(Of TestClass3)
                For i As Integer = 0 To 10
                    collection.Add(New TestClass3())
                Next
                Return collection.AsReadOnly()
            End Get
        End Property
    End Class

    <TypeConverter(GetType(ExpandableObjectConverter))>
    Public NotInheritable Class TestClass3
        <Category("Category 1")>
        Public ReadOnly Property TestProperty1 As String = "Test 1"
        <Category("Category 1")>
        Public ReadOnly Property TestProperty2 As String = "Test 2"
        <Category("Category 1")>
        Public ReadOnly Property TestProperty3 As String = "Test 3"
        <Category("Category 2")>
        Public ReadOnly Property TestProperty21 As String = "Test 21"
        <Category("Category 2")>
        Public ReadOnly Property TestProperty22 As String = "Test 22"
        <Category("Category 2")>
        Public ReadOnly Property TestProperty23 As String = "Test 23"
        'We use the following dummy property to overcome the problem with the propertygrid
        'that it doesn't display the categories once all the properties in the category
        'are readonly...
        <Browsable(False)>
        Public Property DummyWriteableProperty As String
            Get
                Return String.Empty
            End Get
            Set(value As String)

            End Set
        End Property
    End Class

End Class

这是有和没有虚拟属性的结果:

如果仍然希望为集合实现自定义编辑器,请在这条线中签出接受的答案。它没有贯穿整个过程,但它是一个很好的起点。

希望这能有所帮助。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53830738

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档