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.



Visual Basic. NET Power Coding
Visual Basic(R) .NET Power Coding
ISBN: 0672324075
EAN: 2147483647
Year: 2005
Pages: 215
Authors: Paul Kimmel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net