PropertyGrid控件的使用4

目标

PropertyGrid 基本的用法是绑定一个固定的类,显示这个类的属性和值。

但是有些情况,你需要用到 PropertyGrid 去绑定一个属性/值的集合,但是这个属性/值的集合并不适合写成一个固定的类。

比如你想用 PropertyGrid 绑定XML 里的数据。或者数据库的某个表。

假设你有 1000 个XML 文件,每个 XML 所取到的属性集合各不一样,你不可能为每个XML 文件都写一个类 。

或者你的某个数据表有1000 条记录,该表有 a 字段的值表示属性名称, b字段的值表示属性值,你不可能写一个类,定义1000个属性。

这时候,我们就希望是否能够将一个动态的属性/值的集合与Property 绑定。

该 VB 2005 的实例教程就是解决这样的问题。为了简化问题,我们假设你已经将数据源准备成了一个属性/值的集合 (Collection)。

实例界面

首先我们会设计如下界面。

在 Name 和 Value 中输入数据,Name 值表示属性名称,Value 值表示属性的值。点击 Add 按钮后,你输入的信息就会显示在下面的 PropertyGrid 里面;

在 PropertyGrid 中修改一些属性值,点击 Show 以后,会在画面最下面的 TextBox 文本框里看到修改后的值已经存储到用户自定义的属性/值集合 (Collection) 中了。

画面中包含控件和属性设置见下表。

 

对象 控件类型 属性 属性值
XPGridWin Form Text Dynamic PropertyGrid - book.chinaz.com/html
Size 475, 540
Label1 Label Location 12, 12
Size 35, 13
Text Name
TxtName TextBox Location 53, 9
Size 100, 20
Label2 Label Location 177, 12
Size 34, 13
Text Value
TxtValue TextBox Location 217, 9
Size 100, 20
CmdAdd Button Location 380, 7
Size 75, 23
Text Add
CmdShow Button Location 380, 36
Size 75, 23
Text Show
PGrid PropertyGrid Location 12, 70
Size 443, 283
TxtShow TextBox Location 13, 360
Multiline True
Size 443, 134

程序设计

实现这个目标的基本方法是,建立一个属性/值的集合 (Collection) 的类,并将这个类的对象和 PropertyGrid 关联。与此同时,要让 PropertyGrid 不去分析这个类的属性,而使用用户自定义的属性名称和属性值。

System.ComponentModel 中定义了一个接口 ICustomTypeDescriptor,任何 Implements 了这个接口的类和 PropertyGrid 绑定的时候,PropertyGrid 就不去分析这个类的属性,而使用接口中实现的成员来构建属性窗口。

根据以上的分析,基本的程序设计是:

  • 定义一个 XProp 类来保存一对属性名称/值,该类有两个属性,一个是 Name,表示属性名称;另外一个属性是 Value,表示属性值。
  • 再定义一个 XProps 类作为 XProp 的集合 (Collection),并实现 ICustomTypeDescriptor 接口。
  • 另外为了实现需要的接口,还需要定义一个 XPropDescriptor 类把 XProp 中属性名称和属性值对应到 PropertyGrid 上去。XPropDescriptor 类要 Inherits ICustomTypeDescriptor, ICustomTypeDescriptor 是 .Net Framework 提供的一个接口。

代码

建立一个名字叫 XProps.vb 文件,使用Class模板。文件前面包含下面的Imports。

Imports System.ComponentModel
Imports System.Collections.Generic
Imports System.Text

这个文件中将建立下面3个类:

  • XProp
  • XPropDescriptor
  • XProps

建立Class XProp

XProp 就是用户将具体使用的属性。PropertyGrid 很炫的一点就是属性的定制可以有很大的自由度。在这里,给出一种最最简单的形式,属性包括 Name 和 Value 两个部分,在 PropertyGrid 的属性窗口中,Name 将代表左边的属性名称,Value 将代表右边的属性值。

Public Class XProp
 
    Private theName As String = ""
    Private theValue As Object = Nothing
 
    Public Property Name() As String
        Get
            Return theName
        End Get
        Set(ByVal value As String)
            theName = value
        End Set
    End Property
 
    Public Property Value() As Object
        Get
            Return theValue
        End Get
        Set(ByVal value As Object)
            theValue = value
        End Set
    End Property
 
    Public Overrides Function ToString() As String
        Return "Name: " & Name & ", Value: " & Value
    End Function
End Class

这个Class没有特别的地方,对 ToString 的 Overrides 是为了方便后面对整个类的内容显示。

建立Class XPropDescriptor

XPropDescriptior 是建立在 XProp 基础上的,同时是 PropertyDescriptor 的一个派生类。PropertyDescriptor是在 System.ComponentModel 中的,包含了 PropertyGrid 中每个属性所对应的各种接口。XPropDescriptior 就是把 XProp 中的属性和这些接口对应起来。

Public Class XPropDescriptor
    Inherits PropertyDescriptor
 
    Private theProp As XProp
 
    Public Sub New(ByVal prop As XProp, ByVal attrs() As Attribute)
        MyBase.New(prop.Name, attrs)
        theProp = prop
    End Sub
 
    Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
        Return False
    End Function
 
    Public Overrides ReadOnly Property ComponentType() As System.Type
        Get
            Return Me.GetType
        End Get
    End Property
 
    Public Overrides Function GetValue(ByVal component As Object) As Object
        Return theProp.Value
    End Function
 
    Public Overrides ReadOnly Property IsReadOnly() As Boolean
        Get
            Return False
        End Get
    End Property
 
    Public Overrides ReadOnly Property PropertyType() As System.Type
        Get
            Return theProp.Value.GetType()
        End Get
    End Property
 
    Public Overrides Sub ResetValue(ByVal component As Object)
        ' Do Nothing
    End Sub
 
    Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
        theProp.Value = value
    End Sub
 
    Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
        Return False
    End Function
 
End Class

这一段程序中,大部分都是返回缺省值。需要注意的是下面的部分。

  • New
    这个部分把 XProp 的对象导入到类中,存储在变量 theProp 里面,成为将用户属性和 PropertyGrid 使用的属性对应的基础。
  • GetValue
    把存储在 XProp 中的属性值放到 PropertyGrid 里面。
  • SetValue
    把用户通过画面在 PropertyGrid 中修改了的属性值存储到 XProp 里面。
  • PropertyType
    指定这个属性的类型。PropertyGrid 是可以利用不同的类型来选择对应的属性值对话框的。

建立Class XProps

XProps 其实就是 XProp 的 Collection。这很容易,只要用一行代码,继承 System.Collections.Generic.List(Of XProp) 就搞定了。(希望你是了解 Generic 类的,这玩意是 .Net Framework 2.0里面的。)

另一方面,XProps 又需要实现 ICustomTypeDescriptor 的接口,这样把这个类的对象和 PropertyGrid 关联起来的时候,PropertyGrid 就会使用用户自定义的部分来构成属性窗口,而不是用这个类本身的程序Property来构成属性窗口。

Public Class XProps
    Inherits List(Of XProp)
    Implements ICustomTypeDescriptor
 
#Region "ICustomTypeDescriptor Implementation"
    Public Function GetAttributes() As AttributeCollection _
    Implements ICustomTypeDescriptor.GetAttributes
        Return TypeDescriptor.GetAttributes(Me, True)
    End Function
 
    Public Function GetClassName() As String _
    Implements ICustomTypeDescriptor.GetClassName
        Return TypeDescriptor.GetClassName(Me, True)
    End Function
 
    Public Function GetComponentName() As String _
    Implements ICustomTypeDescriptor.GetComponentName
        Return TypeDescriptor.GetClassName(Me, True)
    End Function
 
    Public Function GetConverter() As TypeConverter _
    Implements ICustomTypeDescriptor.GetConverter
        Return TypeDescriptor.GetConverter(Me, True)
    End Function
 
    Public Function GetDefaultEvent() As EventDescriptor _
    Implements ICustomTypeDescriptor.GetDefaultEvent
        Return TypeDescriptor.GetDefaultEvent(Me, True)
    End Function
 
    Public Function GetDefaultProperty() As PropertyDescriptor _
    Implements ICustomTypeDescriptor.GetDefaultProperty
        Return TypeDescriptor.GetDefaultProperty(Me, True)
    End Function
 
    Public Function GetEditor(ByVal editorBaseType As System.Type) As Object _
    Implements ICustomTypeDescriptor.GetEditor
        Return TypeDescriptor.GetEditor(Me, editorBaseType, True)
    End Function
 
    Public Function GetEvents() As EventDescriptorCollection _
    Implements ICustomTypeDescriptor.GetEvents
        Return TypeDescriptor.GetEvents(Me, True)
    End Function
 
    Public Function GetEvents(ByVal attributes() As System.Attribute) _
    As EventDescriptorCollection _
    Implements ICustomTypeDescriptor.GetEvents
        Return TypeDescriptor.GetEvents(Me, attributes, True)
    End Function
 
    Public Function GetProperties() As PropertyDescriptorCollection _
    Implements ICustomTypeDescriptor.GetProperties
        Return TypeDescriptor.GetProperties(Me, True)
    End Function
 
    Public Function GetProperties(ByVal attributes() As System.Attribute) _
    As PropertyDescriptorCollection _
    Implements ICustomTypeDescriptor.GetProperties
 
        Dim props(Count) As PropertyDescriptor
        Dim i As Int32
        For i = 0 To Count - 1
            props(i) = New XPropDescriptor(Item(i), attributes)
        Next
        Return New PropertyDescriptorCollection(props)
    End Function
 
    Public Function GetPropertyOwner(ByVal pd As PropertyDescriptor) _
    As Object _
    Implements ICustomTypeDescriptor.GetPropertyOwner
        Return Me
    End Function
#End Region
 
    Public Overrides Function ToString() As String
        Dim sbld As StringBuilder = New StringBuilder
        Dim i As Int32
        For i = 0 To Count - 1
            sbld.Append("[" & i & "] " & Item(i).ToString & vbNewLine)
        Next
        Return sbld.ToString
    End Function
End Class

这段代码虽然比较冗长,不过读起来并不费力的。

ICustomTypeDescriptor 的实现部分中绝大部分是用 TypeDescriptor中 的成员实现的。只有 GetProperties 是另外写的,这个部分也很简单,就是把 Collection 里面的内容构成一个 PropertyDescriptor 集合,在用这个数组构成 PropertyDescriptorCollection 返回。

需要指出的是,也可以用 Dictionary 或者其他类型的 Collection 来构成这个类,差别部分也就是GetProperties 的写法不同而已。

剩下的部分就是对 ToString 的 Overrides 了,是为了方便后面对整个类的内容显示。这段程序里面使用了 StringBuilder,是属于 System.Text 的。在进行很多字符串组合的时候,这比用运算符 (&) 速度快很多。

界面代码

在 XPGridWin.vb 文件中加入下面的代码。

Public Class XPGridWin
    Private XProps As XProps = New XProps
 
    Private Sub XPGridWin_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
        PGrid.SelectedObject = XProps
    End Sub
 
    Private Sub CmdAdd_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles CmdAdd.Click
        Dim xprop As XProp = New XProp
        xprop.Name = TxtName.Text
        xprop.Value = TxtValue.Text
        XProps.Add(xprop)
        PGrid.Refresh()
    End Sub
 
    Private Sub CmdShow_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles CmdShow.Click
        TxtShow.Text = XProps.ToString
    End Sub
End Class

这段代码中包含了下面这些内容。

  • 定义一个私有的全局对象 XProps,并在定义的时候创建对象;
  • 在 Form.Load 的时候,把这个对象和 PropertyGrid 关联起来;
  • 在按钮 CmdAdd 点击的时候,创建一个 XProp 对象,赋值 Name 和 Value 后加入到 XProps 中,然后刷新 PropertyGrid;
  • 在按钮 CmdShow 点击的时候,将 XProps 的内容在窗体底部的文字框中显示出来。

测试

  • 在 Name 和 Value 中输入数据,点击 Add 后,这些内容就会加入到 PropertyGrid 里面去;
  • 在 PropertyGrid 中修改一些属性值,点击 Show 以后,会看到修改的值已经存储到用户自定义的Collection 中了。

扩展XProps

在 XProps 上增加几个成员就可以扩展 XProps 的能力。例如增加 LoadFromXml 和 SaveToXml 来实现 Xml 文件到 PropertyGrid 的对应,增加 LoadFromDB 和 SaveToDB 来实现数据库和 PropertyGrid 的对应。这些就不赘述了,相信这些对你不是难题。

 

你可能感兴趣的:(object,function,String,Class,Dictionary,attributes)