Analytics


Google

Saturday, October 25, 2008

Dynamically loading UserControl in ASP.Net

I gathered information from the following sources to come up with the following:

4 Guys from Rolla
ASP.Net
CodeProject

First the UserControl:

This is how it looks like in VS Designer:


The code is as follows:

<%@ Control Language="vb" AutoEventWireup="false" Codebehind="ucAckFrm.ascx.vb" Inherits="testWeb.ucAckFrm" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<asp:panel id="PaneCtrl" runat="server">
<asp:Label id="lblCtrlName" runat="server"></asp:Label>
<BR>
<asp:Panel id="paneDisp" runat="server">
<asp:Label id="lblMsg" runat="server"></asp:Label><BR><BR>
<asp:Button id="btnAck" runat="server" Text="Acknowledge"></asp:Button>
<asp:Button id="btnReject" runat="server" Text="Reject"></asp:Button>
<asp:TextBox id="txtReason" runat="server"></asp:TextBox><BR>
<asp:Label id="lblErrMsg" runat="server" ForeColor="Red"></asp:Label>
<HR>
</asp:Panel>
</asp:panel>


The code behind is as follows:

Public Class ucAckFrm
Inherits System.Web.UI.UserControl

#Region " Web Form Designer Generated Code "

'This call is required by the Web Form Designer.
Private Sub InitializeComponent()

End Sub
Protected WithEvents lblMsg As System.Web.UI.WebControls.Label
Protected WithEvents btnAck As System.Web.UI.WebControls.Button
Protected WithEvents btnReject As System.Web.UI.WebControls.Button
Protected WithEvents txtReason As System.Web.UI.WebControls.TextBox
Protected WithEvents lblCtrlName As System.Web.UI.WebControls.Label
Protected WithEvents lblErrMsg As System.Web.UI.WebControls.Label
Protected WithEvents PaneCtrl As System.Web.UI.WebControls.Panel
Protected WithEvents paneDisp As System.Web.UI.WebControls.Panel

'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub

#End Region

Public Event AckClick As EventHandler
Public Event RjctClick As EventHandler

Public Property RjctReason() As String
Get
Return txtReason.Text
End Get
Set(ByVal Value As String)
txtReason.Text = Value
End Set
End Property

Public Sub setMainPane(ByVal pVisible As Boolean)
PaneCtrl.Visible = pVisible
End Sub

Public Sub setDispPane(ByVal pVisible As Boolean)
paneDisp.Visible = pVisible
End Sub

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblCtrlName.Text = Me.ID
End Sub

Private Sub btnAck_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAck.Click
RaiseEvent AckClick(Me, e)
End Sub


Private Sub btnReject_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReject.Click
Dim tmpStr As String

tmpStr = Trim(txtReason.Text)

If tmpStr.Length > 0 Then
RaiseEvent RjctClick(Me, e)
Else
lblErrMsg.Text = "Reject Reason cannot be blank"
End If
End Sub
End Class


The main aspx page:


The code:

<%@ Register TagPrefix="uc1" TagName="ucAckFrm" Src="ucAckFrm.ascx" %>
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="TestPage.aspx.vb" Inherits="testWeb.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<title>WebForm1</title>
<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
<meta content="Visual Basic .NET 7.1" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClientScript">
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<asp:label id="lblMsg" runat="server"></asp:label><br>
<asp:textbox id="txtMsg" runat="server"></asp:textbox><br>
<br>
<asp:button id="btnSubmit" runat="server" Text="Submit"></asp:button><br>
<asp:Label id="lblErrMsg" runat="server" ForeColor="Red"></asp:Label><br>
<hr>
<br>
<uc1:ucAckFrm id="DefAck" runat="server"></uc1:ucAckFrm><br>
<asp:PlaceHolder id="pHolder" runat="server"></asp:PlaceHolder></form>
</body>
</HTML>


The code behind:

Public Class WebForm1
Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

'This call is required by the Web Form Designer.
Private Sub InitializeComponent()

End Sub
Protected WithEvents lblMsg As System.Web.UI.WebControls.Label
Protected WithEvents txtMsg As System.Web.UI.WebControls.TextBox
Protected WithEvents btnSubmit As System.Web.UI.WebControls.Button
Protected WithEvents defAck As ucAckFrm
Protected WithEvents pHolder As System.Web.UI.WebControls.PlaceHolder
Protected WithEvents lblErrMsg As System.Web.UI.WebControls.Label

'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub

#End Region

Dim vStr As String
Dim ackArray As New ArrayList

Private Sub PageInit2(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
Dim ctrlCnt, tmpStr As String
Dim loopCnt, ackCnt As Integer
Dim vAckCtrl As ucAckFrm
Dim ctrl As Control

ctrlCnt = Request.Item("opt")
vStr = ctrlCnt

If IsNumeric(vStr) Then
ackCnt = CInt(vStr)
If ackCnt > 0 Then
For loopCnt = 0 To (ackCnt - 1)
ctrl = LoadControl("ucAckFrm.ascx")
tmpStr = "Ack_" & loopCnt
ctrl.ID = tmpStr
vAckCtrl = CType(ctrl, ucAckFrm)
AddHandler vAckCtrl.AckClick, AddressOf AckHandlerAck
AddHandler vAckCtrl.RjctClick, AddressOf AckHandlerRjct

ackArray.Add(vAckCtrl)
pHolder.Controls.Add(ctrl)
Next
End If
End If

AddHandler defAck.AckClick, AddressOf AckHandlerAck
AddHandler defAck.RjctClick, AddressOf AckHandlerRjct

End Sub


Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If IsPostBack = False Then
Try
lblMsg.Text += "ctrlCnt value is " & vStr
Catch ex As Exception
lblErrMsg.Text += "<br>" & ex.Message
End Try

End If

End Sub


Private Sub AckHandlerAck(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim idName As String

idName = UCase(CType(sender, Control).ID)

lblMsg.Text += "<br>Acknowledge by " & idName
End Sub

Private Sub AckHandlerRjct(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim idName, reason As String
Dim handledCtrl As ucAckFrm

idName = UCase(CType(sender, Control).ID)
handledCtrl = CType(sender, ucAckFrm)
reason = handledCtrl.RjctReason

lblMsg.Text += "<br>Rejected by " & idName & " due to " & reason
End Sub
End Class


In this example, to simplify testing, I use the url parameter opt to determine how many times I want to load the usercontrol. In the following example, I loaded it twice - i.e. ?opt=2


Key points I learn from the articles are:
  • Init will be called each time the page is refreshed.
  • View State is loaded after the init phase before the page load phase.
  • To load the control, we actually use the .ascx file using Page.LoadControl
  • After loading the control, we need to use CType to change back to the class defined in the codebehind for the usercontrol so we can access the properties, events etc for the user control.
Other Notes on this example:
  • In the example above, I assigned the ID for each of the controls that are dynamically loaded, this is actually for a project I am working on where the requestor will specify how many persons will be informed of a request made. Each person receiving the request will then have an option to either acknowledge the request or reject it. In that situation, I will then name each of the controls with the role for each receipient.
  • I added the mainPane and dispPane to provide an option if to either hide the whole control or to display on the header (but not used in the example above).
  • I use a try catch and throw to practice Error handling I wrote in my previous article.

No comments: