Reflecting Attributes

Attributes are read using Reflection. The CustomAttributeDemo.sln file contains several example projects that use ControlHintAttribute. Several classes in the UserInterfaceGenerator.vbproj project are capable of generating custom user interfaces. (There are two flavors: WindowsInterfaceGenerator creates a Windows Forms user interface, and WebInterfaceGenerator creates a Web Forms user interface.) Listing 5.8 contains the implementation of ExWindowsUserInterface, taking into account the possibility that the control has the ControlHintAttribute applied. ExWindowsUserInterfaceGenerator inherits from WindowsUserInterfaceGenerator, which in turn implements the IUserInterfaceGenerator interface.

Listing 5.8. Creating a Custom User Interface
1:  Option Strict On
2:  Option Explicit On
3:
4:  Imports System.Reflection
5:  Imports System.Drawing
6:  Imports CustomAttributeDemo
7:
8:  Public Interface IUserInterfaceGenerator
9:
10:   ReadOnly Property Parent() As Object
11:   ReadOnly Property List() As IList
12:   Sub ClearControls()
13:   Sub CreateUserInterface(ByVal Type As Type)
14:   Sub AddControl(ByVal PropertyInfo As PropertyInfo, _
15:     ByVal Index As Integer)
16:
17: End Interface
18:
19: Public MustInherit Class UserInterfaceGenerator
20:   Implements IUserInterfaceGenerator
21:
22:   Private FParent As Object
23:   Private FList As IList
24:
25:   Public Sub New(ByVal Parent As Object, _
26:     Optional ByVal List As IList = Nothing)
27:     FParent = Parent
28:     FList = List
29:   End Sub
30:
31:   ReadOnly Property Parent() As Object _
32:     Implements IUserInterfaceGenerator.Parent
33:   Get
34:     Return FParent
35:   End Get
36:   End Property
37:
38:   ReadOnly Property List() As IList _
39:     Implements IUserInterfaceGenerator.List
40:   Get
41:     Return FList
42:   End Get
43:   End Property
44:
45:   MustOverride Sub ClearControls() _
46:     Implements IUserInterfaceGenerator.ClearControls
47:
48:   Sub CreateUserInterface(ByVal Type As Type) _
49:     Implements IUserInterfaceGenerator.CreateUserInterface
50:
51:     Dim properties() As PropertyInfo = Type.GetProperties()
52:
53:     Dim I As Integer
54:     For I = 0 To properties.Length - 1
55:       AddControl(properties(I), I)
56:     Next
57:   End Sub
58:
59:   MustOverride Sub AddControl(ByVal PropertyInfo As PropertyInfo, _
60:     ByVal Index As Integer) _
61:     Implements IUserInterfaceGenerator.AddControl
62:
63: End Class
64:
65: Public Class WindowsInterfaceGenerator
66:   Inherits UserInterfaceGenerator
67:
68:   Public Sub New(ByVal Parent As Object, _
69:     Optional ByVal List As IList = Nothing)
70:     MyBase.New(Parent, List)
71:   End Sub
72:
73:   Public Overrides Sub AddControl( _
74:     ByVal PropertyInfo As PropertyInfo, ByVal Index As Integer)
75:
76:     Dim Label As System.Windows.Forms.Label = _
77:       New System.Windows.Forms.Label()
78:     Label.AutoSize = True
79:     Label.Text = PropertyInfo.Name
80:     Label.Location = New Point(10, (Index + 1) * 25)
81:     Control.Controls.Add(Label)
82:
83:     Dim TextBox As System.Windows.Forms.TextBox = _
84:       New System.Windows.Forms.TextBox()
85:     TextBox.Name = PropertyInfo.Name
86:     TextBox.Location = New Point(100, (Index + 1) * 25)
87:     TextBox.Width = 250
88:     TextBox.DataBindings.Add("Text", List, PropertyInfo.Name)
89:     Control.Controls.Add(TextBox)
90:
91:   End Sub
92:
93:   Overrides Sub ClearControls()
94:     Control.Controls.Clear()
95:   End Sub
96:
97:   Protected ReadOnly Property Control() As _
98:     System.Windows.Forms.Control
99:   Get
100:    Return CType(Parent, System.Windows.Forms.Control)
101:  End Get
102:  End Property
103:
104: End Class
105:
106:
107: Public Class ExWindowsInterfaceGenerator
108:   Inherits WindowsInterfaceGenerator
109:
110:   Public Sub New(ByVal Parent As Object, _
111:     Optional ByVal List As IList = Nothing)
112:     MyBase.New(Parent, List)
113:   End Sub
114:
115:   Public Overrides Sub AddControl( _
116:     ByVal PropertyInfo As PropertyInfo, ByVal Index As Integer)
117:
118:     Dim Label As System.Windows.Forms.Label = _
119:       New System.Windows.Forms.Label()
120:     Label.AutoSize = True
121:     Label.Text = PropertyInfo.Name
122:     Label.Location = New Point(10, (Index + 1) * 25)
123:     Control.Controls.Add(Label)
124:
125:     Dim Instance As System.Windows.Forms.Control = _
126:       CreateControl(PropertyInfo)
127:
128:     Instance.Name = PropertyInfo.Name
129:     Instance.Location = New Point(100, (Index + 1) * 25)
130:     Instance.Width = 250
131:     Instance.DataBindings.Add("Text", List, PropertyInfo.Name)
132:
133:     Control.Controls.Add(Instance)
134:   End Sub
135:
136:   Protected Function CreateControl( _
137:     ByVal PropertyInfo As PropertyInfo) _
138:       As System.Windows.Forms.Control
139:
140:     Dim Attributes() As Object = _
141:       PropertyInfo.GetCustomAttributes( _
142:       GetType(ControlHintAttribute), False)
143:
144:
145:     If Attributes.Length = 0 Then
146:       Return New System.Windows.Forms.TextBox()
147:     End If
148:
149:     Dim ControlHintAttribute As ControlHintAttribute = _
150:       CType(Attributes(0), ControlHintAttribute)
151:
152:     Dim Instance As Object = _
153:       Activator.CreateInstance(ControlHintAttribute.ControlType)
154:
155:     Return CType(Instance, System.Windows.Forms.Control)
156:
157:   End Function
158:
159: End Class

Listing 5.8 is a bit on the long side. It shows the interface programming technique again, this time with one interface and three classes. The IUserInterfaceGenerator interface defines what an user interface generator should have. The UserInterfaceGenerator class realizes all of IUserInterfaceGenerator and implements all the code that might be found to be common between all user interface generators (as defined by me).

Lines 19 through 63 implement the UserInterfaceGenerator class, which is an abstract class (as inferred from the MustInherit modifier on line 19). When you see the MustInherit modifier, you should expect to find MustOverride methods. (MustOverride is the VB .NET modifier that indicates a method is purely abstract.) By combining an interface with an abstract class we can implement all the code that all user interface generators might need. The UserInterfaceGenerator class provides a reasonable implementation for everything except IUserInterfaceGenerator. ClearControls and IUserInterfaceGenerator.AddControls. Because Windows Forms and Web Forms need instances of different controls, we will need to implement a specific version of each of these methods. (See the CustomAttributeDemo.sln file for an example of a user interface generator that creates a dynamic Web page.)

Lines 107 through 159 in Listing 5.8 implement the ExWindowsInterfaceGenerator class. All we have to do is implement the AddControl method. AddControl calls the CreateControl method. CreateControl—in lines 136 through 157—requests the custom attributes for the current PropertyInfo object. Lines 140 through 142 request all attributes that are ControlHintAttribute objects. Because we applied the AttributeUsageAttribute with AllowMultiple equal to False (Listing 5.7) there will be at most only one ControlHintAttribute. In lines 145 through 147 of Listing 5.8, we return an instance of the System.Windows.Forms.TextBox control if a ControlHintAttribute was not found.

We pick up on line 149 when there is a ControlHintAttribute. Lines 149 and 150 convert the Attribute object to its specific type. Lines 152 and 153 use the Activator.CreateInstance shared method to create an instance of the control type associated with the attribute, and line 155 casts the generic object type returned by the Activator.CreateInstance method to a System.Windows.Forms.Control object as expected. Listing 5.9 shows a Windows Forms application using the ExWindowsInterfaceGenerator class.

Listing 5.9. Using ExWindowsInterfaceGenerator in a Windows Forms Application
1:  Private Sub Form1_Load(ByVal sender As System.Object, _
2:    ByVal e As System.EventArgs) Handles MyBase.Load
3:
4:    Dim TestTypes() As TestType = _
5:      New TestType() {New TestType("Paul", "Kimmel")}
6:
7:    Dim Generator As IUserInterfaceGenerator = _
8:      New ExWindowsInterfaceGenerator(Me, TestTypes)
9:
10:   Generator.CreateUserInterface(GetType(TestType))
11: End Sub

Lines 4 and 5 create an array of objects called TestType. System.Array (arrays) implement the IList interface, so we can pass the array of objects to the ExWindowsInterfaceGenerator constructor and bind the array to the dynamic controls as they are created. Line 10 creates the user interface. If we apply the ControlHintAttribute to any of the properties in TestType (see Listing 5.10), that kind of control will be created.

Listing 5.10. Applying the ControlHintAttribute
<ControlHint(GetType(Label))> _
Public Property FirstName() As String
Get
  Return FFirstName
End Get
Set(ByVal Value As String)
  FFirstName = Value
End Set
End Property

A Label control will be created for the FirstName property.

Clearly you can extend the user interface generators in several useful ways. You could write code that inserts spaces between Pascal-cased property names. You could insert some clever code to manage spacing between labels and controls, and you could use the data bindings to implement navigation. None of these things represent real difficulties. The real difficulty is how to add code to dynamically generated controls and forms. I think this is the real reason we don't see a lot of general-purpose form generators. However, you can quickly create some very advanced dynamic user interfaces.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.188.120