动态添加控件的事件可以写在类中

本文主要介绍的是,如何对PropertyGrid中某个属性定义下拉框的功能。如下图所示。

建立一个简单的Class并和PropertyGrid关联

打开 Visual Studio。在文件 (File) 菜单上,单击新建项目 (New Project)。 在新建项目 (New Project) 对话框的模板 (Templates) 窗格中,单击 Windows 应用程序 (Windows Application)。在名称 (Name) 框中键入 PropGrid,再单击确定 (OK)。

在 Solution Explorer 里选中 Form1.vb,改名为PropGridEx.vb。选中该 Form,在 Properties 窗口将该 Form 的 Text 属性设为 PropGridEx - www.admin5.com/html

在 Toolbox 的 All Windows Forms 里选中 PropertyGrid 控件,将其拖到 Form 上。在 Properties 窗口将 PropertyGrid 的 Name 属性改为 PGrid。

点击Solution Explorer上的 View Code 按钮进入代码编辑器,你会看到如下代码。

Public Class PropGridEx
End Class

在上述代码的最后追加下面的代码,创建 Class Demo。

Public Class Demo
 
#Region "Private Variables"
    Private theCountry As String
    Private theLanguage As String
#End Region
 
#Region "Public Properties"
    Public Property Country() As String
        Get
            Return theCountry
        End Get
        Set(ByVal value As String)
            theCountry = value
        End Set
    End Property
 
    Public Property Language() As String
        Get
            Return theLanguage
        End Get
        Set(ByVal value As String)
            theLanguage = value
        End Set
    End Property
#End Region
 
End Class

注意,以上代码不能写在文件的开始。因为这是一个 Windows Form 的 vb 文件,第一个类必须是表示窗体的类。

然后在窗体事件 Load 中将 Demo 的一个对象和 PGrid 关联起来。整个窗体部分的代码如下。

Public Class PropGridEx
 
    Private theDemo As Demo = New Demo
 
    Private Sub PropGridEx_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Me.Load
        PGrid.SelectedObject = theDemo
    End Sub
 
End Class

运行程序,得到下面的画面。

把Country的属性值变成下拉式的

先把代码显摆出来再慢慢解释吧。

第一步,在文件的开始增加对System.ComponentModel的Imports;

Imports System.ComponentModel

第二步,在文件的结尾增加一个类StrChoice;

Public Class StrChoice
    Inherits StringConverter
 
    Dim theValues As TypeConverter.StandardValuesCollection
 
    Public Overrides Function GetStandardValuesSupported( _
        ByVal context As ITypeDescriptorContext) As Boolean
        Return True
    End Function
 
    Public Overrides Function GetStandardValuesExclusive( _
        ByVal context As ITypeDescriptorContext) As Boolean
        Return True
    End Function
 
    Public Overrides Function GetStandardValues( _
        ByVal context As ITypeDescriptorContext) _
        As TypeConverter.StandardValuesCollection
        Return Values
    End Function
 
    Private ReadOnly Property Values() As
        TypeConverter.StandardValuesCollection
        Get
            If theValues Is Nothing Then
                theValues = New TypeConverter.StandardValuesCollection( _
                    New String() {"China", "USA", "Germany"})
            End If
            Return theValues
        End Get
    End Property
End Class

第三步,在前面建立出来的 Class Demo 中修改 Country 的定义为下面的代码。

    ......
    <TypeConverter(GetType(StrChoice))> _
    Public Property Country() As String
    ......

运行代码,就可以看到下拉框了。

接下来解释一下这些代码。

  • 所谓 TypeConverter,就是"将某一种类型的值列表转换为另外一种类型的值"。名字和解释都是挺拧巴的。简单的理解就是下拉列表。
  • 定义一个类继承 TypeConverter,这里就是 StrChoice,就可以在具体的 Property 前面用TypeConverter(GetType(......)) 来定义了。PropertyGrid 在需要的时候,就会调用这个类里面的相应成员来获得列表中的属性值。
  • 这里的 StrChoice 继承的是 StringConverter。StringConverter 本身就是 TypeConverter 的派生类,Overrides 了不少和String相关的操作。如果定义的是 String 类型的下拉列表,从 StringConverter 继承可以比从 TypeConverter 少写不少代码,因为微软都帮你写好了嘛。
  • GetStandardValuesSupported 用来告诉 PropertyGrid 可以从列表中选取的一组候选值。这样解释也挺拧巴的,简单的说,这个函数返回 True,就有下拉框,是 False 就没有下拉框了。
  • GetStandardValuesExclusive 用来告诉 PropertyGrid 是否允许用户能够输入下拉列表中没有包含的值。 这个函数返回 True,表示你只能选择下拉框里面的值;返回 False,表示你可以直接输入下拉内容以外的值。
  • GetStandardValues 用来把候选值的 Collection 提供给 PropertyGrid,这个 Collection 的类型为TypeConverter.StandardValuesCollection,是可以从字符串数组转换过去的。
  • 私有属性 Values 就是将一个字符串数组转换成为 TypeConverter.StandardValuesCollection。

除了这些解释之外,还要补充两件事。

第一,在代码编辑器里面,一输入 Overrides,后面就会自动弹出一个可以 Overrides 的成员列表。TypeConverter 或者 StringConverter 可以 Overrides 的成员有很多,和上面提到的三个成员名字相似的还有两个:GetProperties 和 GetPropertiesSupported。这些成员的名字都是老长的英文,参数也差不多,一不小心就会选错了。我第一次写这个代码的时候就选错了,任凭无数脏话在我胸中翻腾,下拉框死也不出来。后来看了个BT下载的片儿后头脑冷静的细看代码才找到问题,你写的时候就别受罪了。

第二,Values那个函数中有一句这么写的,

    theValues = New TypeConverter.StandardValuesCollection( _
        New String() {"China", "USA", "Germany"})

这里多少有些卖弄技巧的成分。如果老老实实的写代码应该是下面这个样子。

    Dim strs(2) As String
 
    strs(0) = "China"
    strs(1) = "USA"
    strs(2) = "Germany"
    theValues = New TypeConverter.StandardValuesCollection(strs)

用一个类来处理多个下拉框

如果有好几个属性值都需要下拉框,而且下拉框的内容都不相同,那是不是要写很多个类呢?并非如此,可以用下面的方法把下拉列表的值写到属性的定义中去。

第一步,在文件的最后再追加一个类 StrListAttribute;

Public Class StrListAttribute
    Inherits Attribute
 
    Private theList As String()
 
    Public Sub New(ByVal list As String())
        theList = list
    End Sub
 
    Public ReadOnly Property List() As String()
        Get
            Return theList
        End Get
    End Property
End Class

第二步,在类 StrChoice 中删除 Values 属性,并修改 GetStandardValues 的代码;

Public Class StrChoice
    Inherits StringConverter
 
    Dim theValues As TypeConverter.StandardValuesCollection
 
    Public Overrides Function GetStandardValuesSupported( _
        ByVal context As ITypeDescriptorContext) As Boolean
        Return True
    End Function
 
    Public Overrides Function GetStandardValuesExclusive( _
        ByVal context As ITypeDescriptorContext) As Boolean
        Return True
    End Function
 
    Public Overrides Function GetStandardValues( _
        ByVal context As ITypeDescriptorContext) _
        As TypeConverter.StandardValuesCollection
        If theValues Is Nothing Then
            Dim slist As StrListAttribute = _
                context.PropertyDescriptor.Attributes(GetType(StrListAttribute))
            theValues = New TypeConverter.StandardValuesCollection(slist.List)
        End If
        Return theValues
    End Function
End Class

第三步,修改Demo中的属性定义,把下拉列表值定义进去。

    ......
    <TypeConverter(GetType(StrChoice)), _
    StrList(New String() {"China", "USA", "Germany"})> _
    Public Property Country() As String
    ......
    <TypeConverter(GetType(StrChoice)), _
    StrList(New String() {"Chinese", "English", "German"})> _
    Public Property Language() As String
    ......

运行代码后可以看到不同的下拉框出现了不同的下拉列表。

下面是对代码的一些说明。

  • 在VB的编程环境中,只要在一个 Property 的名字前面输入小于号(<),就会自动弹出可以使用的 Attribute 列表。
  • 可以发现,因为 StrListAttribute 是继承了 Attribute,它就会在自动的 Attribute 列表中出现(真智能!); 而且有趣的是,列表里面出现的是 StrList,Attribute 被自动截断了(太智能!)。
  • 如果把 StrListAttribute 的类的名字直接改成 StrList,那在下拉列表中出现的也是 StrList。所以在属性定义中,是否加上 Attribute 都是一样的,不加 Attribute 比较简练些。(如果你和我一样偏执的话,就会建立两个类,一个叫 StrList,一个叫 StrListAttribute,看看反应。结果我不细说了,挺很有趣的。当然具体写程序的时候,就不要偏执了,要么叫 StrList,要么叫 StrListAttribute,千万不要两个类都存在,给自己找拧巴。)
  • StrList(New String() {"Chinese", "English", "German"}) 就是把字符串数组作为参数来创建一个 StrListAttribute,如果是逐字逐句看到这里,应该记得上一节我已经就这种写法卖弄过技巧了。
  • 在 Demo的Property 中使用了 StrListAttribute,这会被传递到 PropertyGrid;在 GetStandardValues 中,是用 StrListAttribute 的类型作为 Key 进行检索,就可以从函数的参数 context 把它给取出来。

对下拉值组合的复用

如果有些下拉值的集合会被很多类的属性反复用到的话,每次都写一堆值的集合一来比较辛苦,二来一旦需要增减个把下拉选项的话,就需要修改N多处程序。要解决这个需求,可以增强 StrListAttribute 这个类如下。

Imports System.Collections.Generic
 
......
 
Public Class StrListAttribute
    Inherits Attribute
 
    Private Shared theListColl As Dictionary(Of String, String())
    Private theList As String()
 
    Public Sub New(ByVal list As String())
        theList = list
    End Sub
 
    Public Sub New(ByVal name As String)
        If ListColl.ContainsKey(name) Then
            theList = ListColl.Item(name)
        Else
            theList = New String() {name}
        End If
    End Sub
 
    Public ReadOnly Property List() As String()
        Get
            Return theList
        End Get
    End Property
 
    Private Shared ReadOnly Property ListColl() As Dictionary(Of String, String())
        Get
            If theListColl Is Nothing Then
                theListColl = New Dictionary(Of String, String())
                theListColl.Add("Country", _
                    New String() {"China", "USA", "Germany"})
                theListColl.Add("Language", _
                    New String() {"Chinese", "English", "German"})
            End If
            Return theListColl
        End Get
    End Property
End Class

这样的话,就可以用下面的代码来定义 Demo 中的属性了。

    ......
    <TypeConverter(GetType(StrChoice)), StrList("Country")> _
    Public Property Country() As String
    ......
    <TypeConverter(GetType(StrChoice)), StrList("Language")> _
    Public Property Language() As String
    ......

说明如下,

  • 这个修改是增强而不是改写,现在这个类具有了2个 New,所以既可以用字符串数组来定义属性,也可以用一个内部定义的名称来定义属性;
  • 这里面用到了 System.Collection.Generic.Dictionary,所以要在代码前加入 Imports System.Collections.Generic;
  • 这里用到的 Dictionary 的 Key 是 String 类型,Value 是一个 String 数组,如果你对 Generic Collection不是很了解的话,请阅读站长网 站长学院里面的相应文章。

扩展到Xml和数据库

如果要把下拉列表的值定义在Xml文件或者数据库中,那只要在 StrListAttribute 增加些成员就 OK了,这已经和 PropertyGrid 的扩展是相对独立的问题了,是如何把 Xml 或数据库和 Dictionary 对应的问题了。希望你自己可以搞定,如果实在搞不定的话,请阅读 www.admin5.com/html 里面有关 Xml、数据库和 Collection 的相关文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值