Testing the Attribute
Any time we write code, it's a good idea to test it. For this test, I implemented a Customer class that uses the custom attribute and a simple generator that reads the attributes if they exist on a type. The generator uses the attributes as a guide to create a basic Windows user interface.
Listing 6 contains the code that demonstrates applying the attribute, and Listing 7 shows a very basic generator that will read the attributes and generate Windows Forms controls.
Listing 6Applying the ControlDesignerAttribute Custom Attribute
Public Class Customer <ControlDesigner( _ "System.Windows.Forms.TextBox, System.Windows.Forms, [ccc]Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", _ Caption:="Name", _ LabelName:="System.Windows.Forms.Label, System.Windows.Forms, [ccc]Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", _ X:=0, Y:=10, Width:=100, Height:=20)> _ Public Property Name() As String Get End Get Set(ByVal Value As String) End Set End Property <ControlDesigner( _ "System.Windows.Forms.TextBox, System.Windows.Forms, [ccc]Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", _ Caption:="Phone Number", _ LabelName:="System.Windows.Forms.Label, System.Windows.Forms, [ccc]Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", _ ValidationExpression:="^(\d\d\d) \d\d\d-\d\d\d\d$", _ X:=10, Y:=50, Width:=100, Height:=20)> _ Public Property PhoneNumber() As String Get End Get Set(ByVal Value As String) End Set End Property End Class
The application of the attribute as defined looks a bit daunting. If I were building a commercial application, I would probably generalize the ControlDesignerAttribute as a series of simpler attributes that provide basic arguments for text boxes, labels, check boxes, etc. to the ControlDesignerAttribute. I would probably revise the custom attribute to use a Type record too.
Listing 7Using Reflection to Read the Custom Attributes and Generate Controls
Public Class FormDesigner Public Shared Sub Generator( _ ByVal Parent As Control, ByVal Type As Type) Dim [Property] As PropertyInfo For Each [Property] In Type.GetProperties() GenerateControl(Parent, [Property]) Next End Sub Public Shared Sub GenerateControl(ByVal Parent As Control, _ ByVal [Property] As PropertyInfo) Dim Attributes() As Attribute = _ [Property].GetCustomAttributes( _ GetType(MyAttribute.ControlDesignerAttribute), True) If (Attributes Is Nothing) Then Exit Sub Dim A As Attribute For Each A In Attributes CreateControl(Parent, CType(A, ControlDesignerAttribute)) Next End Sub Public Shared Sub CreateControl(ByVal Parent As Control, _ ByVal Attribute As ControlDesignerAttribute) Dim Label As Control = _ Activator.CreateInstance(Type.GetType(Attribute.LabelName)) Label.Text = Attribute.Caption Label.SetBounds(Attribute.X, Attribute.Y, _ Attribute.Width, Attribute.Height) Dim Control As Control = _ Activator.CreateInstance(Type.GetType(Attribute.ControlName)) Control.SetBounds(Attribute.X + Attribute.Width, _ Attribute.Y, Attribute.Width, Attribute.Height) Parent.Controls.Add(Label) Parent.Controls.Add(Control) End Sub End Class
I've provided only a basic generator, to show you that it could be done with moderately low difficulty. The code uses reflection to discover the attribute values and from there generates controls.
If you're unfamiliar with how reflection works in .NET, I encourage you to pick up a copy of my Visual Basic .NET Unleashed (Sams, 2002, ISBN 0-672-32234-X), or a comparable book by your favorite author. (Tell them Paul sent you.)