正如标题所述,我注意到,当类"T“的所有属性都为只读时,类别没有显示在集合( T)的**PropertyGrid* (在其默认集合编辑器中)中。
下面的代码表示我的代码结构:
C#:
[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:
<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:#
[Category("Category 1")]
public string TestProperty3 {get; set;} = "Test";
VB.NET:
<Category("Category 1")>
Public Property TestProperty3 As String = "Test"
不仅如此,让我们假设在TestClass3中声明了10个属性(而不是这个示例中的1个),其中9个属性是只读的,1个是可编辑的,那么,在这种情况下,将显示所有类别。另一方面,如果所有10个属性都是只读的,则不会显示类别。
PeopertyGrid的这种行为对我来说是非常恼人和意外的。我希望看到我的自定义类别,不管在我的类中是用setter声明的属性还是没有它的属性。
我必须向类别显示哪些选项具有类的所有属性为只读?也许编写自定义TypeConverter或集合编辑器可以修复这种恼人的视觉表示行为?
发布于 2018-12-21 20:01:10
这不是PropertyGrid
的错误,而是特性(错误?)CollectionForm
of CollectionEditor
.
如果将TestClass3
的实例直接分配给属性网格,您将看到属性网格按预期在类别下显示属性。但是,当CollectionForm
试图在其属性网格中显示TestClass3
实例时,由于它没有任何可设置属性,而且它的集合转换器不支持创建项实例,所以它决定将对象包装到另一个派生自定义类型描述符的对象中,在类名相同的类别下显示所有属性。
正如其他答案所建议的,您可以通过
但是,我不希望仅仅因为CollectionForm
错误而更改类或其类型描述符。
由于问题与CollectionForm
或CollectiorEditor
有关,作为另一种选择,您可以通过创建从CollectionEditor
派生的集合编辑器并重写其CreateCollectorForm
方法来解决问题,并在尝试在集合编辑器表单中设置属性网格的选定对象时更改其行为:
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
了:
[Editor(typeof(MyCollectionEditor<TestClass3>), typeof(UITypeEditor))]
发布于 2018-12-18 11:15:04
这的确是一种非常令人讨厌的行为。但是,我不认为您可以绕过它:错误的不是属性--描述符--它报告的是正确的类别--您可以通过以下方法进行验证:
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
术语表示:以便使其粗体/不粗体),但是.这解决不了你看到的问题,抱歉。
发布于 2018-12-20 15:52:40
向类中的虚拟可写但不能浏览的属性问好。
当然,这是属性网格的bug(?)的一个解决方法。但是,考虑到创建自定义集合编辑器表单和实现自定义UITypeEditor所需的开销,而定制表单又将使用您的自定义表单来克服这种行为,它至少应该被命名为一个半优雅的解决方案。
代码:
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
这是有和没有虚拟属性的结果:
如果仍然希望为集合实现自定义编辑器,请在这条线中签出接受的答案。它没有贯穿整个过程,但它是一个很好的起点。
希望这能有所帮助。
https://stackoverflow.com/questions/53830738
复制相似问题