DECORATing Web Controls
I'm sure you've written similar code (the mix of VB & C# comments is so it is clear, it's a comment):
Private Sub PersonDataGrid_ItemCommand(s as object, e as DataGridEventArgs)
Dim thePerson as Person '//or Load the Person object
select case e.CommandName.ToLower().Trim()
case "update"
'//Find all the edit controls
thePerson.Name = DirectCast(e.Item.FindControl("EditNameField"), TextBox).Text
thePerson.Age = Integer.Parse(DirectCast(e.Item.FindControl("EditAgeField"), TextBox).Text
'//other controls obmitted
case "insert"
'//Find all the controls
thePerson.Name = DirectCast(e.Item.FindControl("NewNameField"), TextBox).Text
thePerson.Age = Integer.Parse(DirectCast(e.Item.FindControl("NewAgeField"), TextBox).Text)
'//other controls obmitted
end select
'//other possible logic removed
End Sub
Honestly, that's some ugly, mind-numbing code. Plus, while it might be hard to see at first, there's a good bit of duplication as well. Let's see how the DECORATOR pattern, in conjunction with another pattern, can brighten our day...
Public Interface IControlLocator
Function FindControl(controlId as String) as Control
End Interface
Public Class DataGridItemControlLocator
Implements IControlLocator
private dgItem as DataGridItem
private controlPrefix as String = ""
Public Sub New(dgi as DataGridItem)
If IsNothing(dgi) Then Throw New AgrumentNullException("dgi")
dgItem = dgi
End Sub
Public Sub New(dgi as DataGridItem, prefix as String)
Me.New(dgi)
If IsNothing(prefix) OrElse String.Empty.Equals(prefix.Trim()) Then Throw New ArgumentNullException("prefix")
controlPrefix = prefix.Trim()
End Sub
Public Function FindTextBox(controlId as String) as TextBox
Return DirectCast(FindControl(controlName), TextBox)
End Function
Public Function FindControl(controlId as String)as Control Implements IControlLocator.FindControl
Return dgItem.FindControl(prefix + controlId)
End Function
End Class
Public Class PersonBuilder
Protected controlLocator as IControlLocator
Public Sub New(aControlLocator as IControlLocator)
If IsNothing(aControlLocator) Then Throw New ArgumentNullException("aControlLocator")
controlLocator = aControlLocator
End Sub
Public Function MakePerson() as Person
dim thePerson as Person = New Person()
thePerson.Name = controlLocator.FindTextBox("NameField").Text
thePerson.Age = Integer.Parse(controlLocator.FindTextBox("AgeField").Text)
Return thePerson
End Function
End Class
Boy...that really simplifies the code in the ItemCommand event, doesn't it?
Private Sub PersonDataGrid_ItemCommand(s as object, e as DataGridEventArgs)
Dim thePerson as Person '//or Load the Person object
Select Case e.CommandName.ToLower().Trim()
Case "update"
thePerson = New PersonBuilder(New DataGridItemControlLocator(e.Item, "Edit")).MakePerson()
case "insert"
thePerson = New PersonBuilder(New DataGridItemControlLocator(e.Item, "New")).MakePerson()
end select
'//other possible logic removed
End Sub
Personally, I feel that's a ton better. The IControlLocator can be implemented for any ASP.NET control. Objects that implement that interface, like the DataGridItemControlLocator implementation, allow the duplication of the control names - and the corresponding type casts - to be removed from the ItemCommand event of the DataGrid. Finally, the PersonBuilder is my attempt at demonstrating the BUILDER pattern - one of many patterns that helps centralize complex object creation code. It might be a bit contrived, as you would not want to tightly couple it to a given UI framework as this implementation does.
I appreciate any comments, thoughts and suggestions anyone has to offer on this example.