In my previous post, Dealing with GAC atrocities, I talked about code reusability being an important aspect of rapid application development (RAD). User Controls in .NET support code reusability out of the box. Just to remind ourselves, a user control is a control that is created using the same technique we use for creating ASP.NET web pages. We create user controls every now and then in our everyday programming and, at times, we forget to entertain some of its important aspects during the development. For example exposing properties that define its behavior and layout , taking care of the situation where a web page can have multiple instances of a user control etc.
As you probably know that I love to follow the guidelines and like to stick to the checklist whenever possible. I decided to create one for myself containing good practices for user control development. The idea is to come back to this list to see what should go in the user control and what should not. Though, these are not industry standard best practices, I find them very useful in most of the scenarios. Please have a look and let me know what do you have to say about it. Moreover, If there is anything, a procedure, a guideline or even a practice that you think helps you in anyway while writing a user control and saves your time, share it with the world in the comments below.
- Every single behavior of a user control should be represented by a public property. More the properties it has, the more it is customizable for the end user. I never forget to write properties that define the important characteristics of my list-type control e.g. AutoPostBack.
- There will always be only one instance of a user control on a web page is a blind assumption. If the user control looks good and is designed to keep the user friendliness and intuitiveness in mind, people would love to use it than once, sometimes on the same page which might lead to a conflict between their child control IDs. To deal with it, ClientID property can be used in the HTML as well as in the back end.
- All the external script files (JavaScript etc.) should always be registered using Page.ClientScript.RegisterClientScriptInclude and Page.ClientScript.IsClientScriptIncludeRegistered method to avoid registering duplicate scripts resources for multiple instances of user control on a page.
- The user control should have no knowledge (no hard-coding), unless required, of any project/application-wide configurations e.g. resource (image/script) file path, connection strings, database column names of the supplied data source, default values etc. We can maintain the abstractness of a control by passing all these values from the page as properties.
- There is a possibility of a user control being used on a page which does not contain any .NET default server control, hence no sign of __doPostBack JavaScript function definition which can be a serious problem if the user control has to do a PostBack. We can harness GetPostBackEventReference() under ClientScriptManager which returns a string that can be used in a client event to cause Postback to the server.
- There is a consistency in the property names e.g. DataSource, DataTextField, DataValueField for all ASP.NET data bound controls because of their support to the standard Windows Forms data-binding model. This helps developers gain familiarity with the control very quickly. We can try wherever possible to maintain this level of friendliness in our user control as well by keeping the property and method names as close to that of default controls. The same goes for event names as well. For example for a user control that is going to behave like a list-type control, we can think of having properties such as DataTextField, DataValueField, DataSource, SelectedIndex etc. and events like SelectedIndexChanged.
- In addition to method and events, a list-type user control should also expose ListItemCollection that gives complete control of the items and facilitates adding, inserting, removing and finding items.
- We need to make sure the control is able to maintain its state on PostBacks. It goes in line with the best practice of loading controls when the page is being rendered for the first time.
- Instead of inheriting our control class directly from System.Web.UI.UserControl class every time, we should first look for an abstract class if it is available that defines the same properties, methods, and events common that we need for our user control. For example ListControl class can be served as the abstract class for all list-type controls and DataBoundControl class for the controls that display their data in list of tabular form.
- The default values should be specified for the control properties, hence allowing the user to get the control up and running in very little time.
Private _AutoPostback As Boolean
Public Property AutoPostback() As Boolean
Get
Return _AutoPostback
End Get
Set(ByVal value As Boolean)
_AutoPostback = value
End Set
End Property
Private _AllowParentSelect As Boolean
Public Property AllowParentSelect() As Boolean
Get
Return _AllowParentSelect
End Get
Set(ByVal value As Boolean)
_AllowParentSelect = value
End Set
End Property
If AutoPostback = True AndAlso Request.Form("__EVENTTARGET") IsNot _
Nothing AndAlso Request.Form("__EVENTTARGET").Equals(Me.ClientID + "_categoryMenu") Then
RaiseEvent OnSelectedIndexChanged(Me, Nothing)
End If
Private Sub RegisterClientScriptIncludes()
Dim clientScriptMgr As ClientScriptManager = Me.Page.ClientScript
If clientScriptMgr.IsClientScriptIncludeRegistered(Me.GetType(), "mcDropdown") = False Then
clientScriptMgr.RegisterClientScriptInclude(Me.GetType(), "mcDropdown", ClientScriptIncludePath)
End If
End Sub
Private _DropdownListStyles As String
Public Property DropdownListStyles() As String
Get
Return _DropdownListStyles
End Get
Set(ByVal value As String)
_DropdownListStyles = value
End Set
End Property
Private _DataTextField As String
Public Property DataTextField() As String
Get
Return _DataTextField
End Get
Set(ByVal value As String)
_DataTextField = value
End Set
End Property
Private _DataValueField As String
Public Property DataValueField() As String
Get
Return _DataValueField
End Get
Set(ByVal value As String)
_DataValueField = value
End Set
End Property
Private Sub ddlApplicationStatus_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ddlApplicationStatus.OnSelectedIndexChanged
Try
Dim ctlDropdown As mcDropdown = CType(sender, mcDropdown)
Dim selectedVal As Integer = ctlDropdown.SelectedValue
Dim selectedText As String = ctlDropdown.SelectedText
Catch ex As Exception
'log exception here...
End Try
End Sub
If Not Me.IsPostBack Then
InitializeUserControls()
End If
Private _AllowParentSelect As Boolean
Public Property AllowParentSelect() As Boolean
Get
Return _AllowParentSelect
End Get
Set(ByVal value As Boolean)
_AllowParentSelect = value
End Set
End Property
HTH,