Starbucks-fueled Developer

Monday, September 19, 2005

DECORATing Web Controls

In reading Jeremy's post on the DECORATOR pattern, I actually found another application of the pattern in my own code (gasp!). While I don't particularly enjoy working with in-place editing on the Data* web controls, I didn't have the choice this time. However, as you'll see, I turned my frown upside down by implementing this very useful pattern...

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.

0 Comments:

Post a Comment

<< Home