如何使用表单中的属性网格来编辑任何类型

发布于 2024-07-27 04:54:40 字数 1926 浏览 3 评论 0原文

我有一个应用程序,我希望能够在运行时编辑任何类型(字体、颜色、点等)并使用任何 .Net 默认类型编辑器。 (例如,字体/颜色选择器)。

我决定使用属性网格控件,而不是重新发明轮子。

如果我将一个对象(例如字体)传递到网格,它会单独列出所有字段,并且没有打开字体选择器的选项。

因此,我创建了这个通用包装器类:

Private Class Wrapper(Of T)
    Private _Value As T
    Public Property Value() As T
        Get
            Return Me._Value
        End Get
        Set(ByVal value As T)
            Me._Value = value
        End Set
    End Property

    Public Sub New(ByVal Value As T)
        Me._Value = Value
    End Sub
End Class

我传递了包装器的实例,而不是将字体对象传递到网格。 然后属性网格的行为就像我想要的那样。

这是可行的,但问题是,对象可以是任何类型,我无法编写类似的代码 -

Dim MyWrapper = New Wrapper(of T)(myObject).

基本上,我拥有的信息是类型的程序集限定名称和对象的字符串表示形式。 然后,我使用类型转换器来创建对象:

Dim ID As String = "System.Drawing.Font, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Dim PropertyValue As String = "Arial, 12px, style=Bold, Strikeout"
Dim T As Type = System.Type.GetType(ID)
Dim tc As TypeConverter = TypeDescriptor.GetConverter(T)
Dim o As Object = tc.ConvertFromString(PropertyValue)

如果我将对象传递给属性网格,它可以工作,但如果我传递包装器的实例,它就不起作用。

我已经通过使用 Reflection.Emit 动态创建所需类型的非通用包装器解决了这个问题,但我怀疑这已经过头了。

有任何想法吗?

ETA:

如果我使用网格编辑尚未定义的属性(例如字体),我会遇到一个问题,该怎么办。

如果我定义:

Dim f as Font = Nothing

并将其传递给包装器,属性网格将按预期显示 (none) 和一个带有 ... 的按钮来选择字体。

我的问题是如何在运行时执行相当于 Dim myObject As 'Type' = Nothing 的操作。

我找不到办法做到这一点,但幸运的是,对于包装器和我的类型,这不是问题。 我将普拉迪普的代码(查看答案)更改为:

Dim genericType As Type = GetType(Wrapper(Of ))
Dim specificType As Type = genericType.MakeGenericType(T)
Dim ci As ConstructorInfo = specificType.GetConstructor(New Type() {T})
Dim wrappedObject As Object = ci.Invoke(New Object() {Nothing})
Me.PropertyGrid1.SelectedObject = wrappedObject

问题解决了!

I have an App where I'd like to be able to edit any type (font, colour, point etc.) at run time and use any of the .Net default type editors. (e.g., font/ colour picker).

Rather than re-invent the wheel, I decided to use the property grid control.

If I pass an object of, say font, to the grid, it lists all the fields separately, with no option to open the font picker.

Therefore, I created this generic wrapper class:

Private Class Wrapper(Of T)
    Private _Value As T
    Public Property Value() As T
        Get
            Return Me._Value
        End Get
        Set(ByVal value As T)
            Me._Value = value
        End Set
    End Property

    Public Sub New(ByVal Value As T)
        Me._Value = Value
    End Sub
End Class

Instead of passing a font object to the grid, I pass an instance of the wrapper. The property grid then behaves as I would like.

This works, but the problem is, the object could be of any type and I can't code something like -

Dim MyWrapper = New Wrapper(of T)(myObject).

Basically, the information I have is the type's assembly qualified name and a string representation of the object. I then use a type converter to create the object :

Dim ID As String = "System.Drawing.Font, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Dim PropertyValue As String = "Arial, 12px, style=Bold, Strikeout"
Dim T As Type = System.Type.GetType(ID)
Dim tc As TypeConverter = TypeDescriptor.GetConverter(T)
Dim o As Object = tc.ConvertFromString(PropertyValue)

If I pass the object to the property grid, it works but it doesn't work if I pass an instance of the wrapper.

I've solved the problem by using reflection.Emit to create a non generic wrapper of the required type on the fly, but I suspect this is over kill.

Any ideas?

ETA:

I had a problem with what do to if I was using the Grid to edit a property, say a Font, that was not already defined.

If i define:

Dim f as Font = Nothing

, and pass that to the wrapper, the property grid displays as expected with (none) and a button with ... to select the font.

My problem was how to do the equivalent of Dim myObject As 'Type' = Nothing, at run-time.

I couldn't find a way to do this, but luckily with the wrapper and my type, it wasn't a problem.
I changed Pradeep's code (look at the answers) to :

Dim genericType As Type = GetType(Wrapper(Of ))
Dim specificType As Type = genericType.MakeGenericType(T)
Dim ci As ConstructorInfo = specificType.GetConstructor(New Type() {T})
Dim wrappedObject As Object = ci.Invoke(New Object() {Nothing})
Me.PropertyGrid1.SelectedObject = wrappedObject

Problem solved!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夏了南城 2024-08-03 04:54:40

我认为这应该有效。 我已经在 C# 中对其进行了测试,并使用转换器在 VB.net 中获取代码

中的代码

Type generic = typeof(Wrapper<>);
Type specific = generic.MakeGenericType(o.GetType());
ConstructorInfo ci = specific.GetConstructor(new Type[] { o.GetType() });
object o1 = ci.Invoke(new object[] { o });
propertyGrid1.SelectedObject = o1;

这是 C# VB.NET

Dim generic As Type =  Type.GetType(Wrapper<>) 
Dim specific As Type =  generic.MakeGenericType(o.GetType()) 
Dim ci As ConstructorInfo =  specific.GetConstructor(New Type() {o.GetType() })
Dim o1 As Object =  ci.Invoke(New Object(){  o })
propertyGrid1.SelectedObject = o1

I think this should work. I've tested it in C# and used a converter to get code in VB.net

This is the code in C#

Type generic = typeof(Wrapper<>);
Type specific = generic.MakeGenericType(o.GetType());
ConstructorInfo ci = specific.GetConstructor(new Type[] { o.GetType() });
object o1 = ci.Invoke(new object[] { o });
propertyGrid1.SelectedObject = o1;

VB.NET

Dim generic As Type =  Type.GetType(Wrapper<>) 
Dim specific As Type =  generic.MakeGenericType(o.GetType()) 
Dim ci As ConstructorInfo =  specific.GetConstructor(New Type() {o.GetType() })
Dim o1 As Object =  ci.Invoke(New Object(){  o })
propertyGrid1.SelectedObject = o1
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文