.NET (296) administrative (41) Ajax (42) AngularJS (2) ASP.NET (144) bicycle (2) books (180) browser (8) C# (134) cars (1) chess (28) CodePlex (10) Coma (8) database (47) deployment (3) Entity Framework (2) essay (112) flash/shockwave (2) flex (1) food (3) friend (2) game (20) idea (5) IIS (8) javascript (82) LInQ (2) Linux (6) management (4) manga (42) misc (672) mobile (1) movies (91) MsAccess (1) murder (2) music (64) mysql (1) news (100) permanent (1) personal (68) PHP (1) physics (2) picture (307) places (12) politics (13) programming (504) rant (120) religion (3) science (43) Sharepoint (3) software (58) space (1) T4 (2) technology (11) Test Driven Development (4) translation (2) VB (2) video (97) Visual Studio (44) web design (46) Windows API (8) Windows Forms (3) Windows Server (5) WPF/Silverlight (63) XML (11)

Monday, February 25, 2008

Templated Controls / Add UpdatePanels programatically

I named this post so because I started researching something that a chat user asked me: how do you add UpdatePanels programatically to a page. You see, the actual problem was that he couldn't add controls to the UpdatePanel after adding it to the page and that was because the UpdatePanel is a templated control, in other words it contains one or more objects that inherit from ITemplate and all the control's children are part of these templates.

So, the required application is like this: A page that has a button that does nothing but a regular postback and another button that adds an UpdatePanel. Each update panel must contain a textbox and a button. When the button is pressed, the textbox must fill with the current time only in that particular UpdatePanel. If the regular postback button is pressed, the UpdatePanels must remain on the page.

What are the possible issues?
First of all, the UpdatePanels must survive postbacks. That means that you have to actually create them every time the page loads, therefore inside Page_Load. Note: we could add them in Page_Init and in fact that's where they are added when getting the controls from the aspx file of a page, but during Init, the ViewState is not accessable!
Then, there is the adding of the UpdatePanels. It is done in a Click event from a button, one that is done AFTER the Page_Load, therefore adding of an UpdatePanel must also be done there. Note: we could put the CreatePanels method in Page_LoadComplete, but then the controls in the update panel will not respond to any events, since the Load phase is already complete.
There is the matter of how we add the TextBox and the Button in each UpdatePanel. The most elegant solution is to use a Web User Control. This way one can visually control the content and layout of each UpdatePanel and also (most important for our application) add code to it!
Now there is the matter of the ITemplate object that each UpdatePanel must have as a ContentTemplate. This is done via the Page.LoadTemplate method! We you give it the virtual path to the ascx file and it returns an ITemplate. It's that easy!

Update:if you by any chance want to add controls programatically to the UpdatePanel, use the ContentTemplateContainer property of the UpdatePanel like this:
updatePanel.ContentTemplateContainer.Controls.Add(new TextBox());


Enough chit-chat. Here is the complete code for the application, the DynamicUpdatePanels page and the ucUpdatePanelTemplate web user control:
DynamicUpdatePanels.aspx.cs
using System;
using System.Web.UI;

public partial class DynamicUpdatePanels : Page
{
private int? _nrPanels;

public int NrPanels
{
get
{
if (_nrPanels == null)
{
if (ViewState["NrPanels"] == null)
NrPanels = 0;
else
NrPanels = (int) ViewState["NrPanels"];
}
return _nrPanels.Value;
}
set
{
_nrPanels = value;
ViewState["NrPanels"] = value;
}
}

protected void Page_Load(object sender, EventArgs e)
{
CreatePanels();
}

private void CreatePanels()
{
for (int i = 0; i < NrPanels; i++)
{
AddPanel();
}
}

private void AddPanel()
{
UpdatePanel up = new UpdatePanel();
up.UpdateMode = UpdatePanelUpdateMode.Conditional;
up.ContentTemplate = Page.LoadTemplate("~/ucUpdatePanelTemplate.ascx");
pnlTest.Controls.Add(up);
}

protected void btnAdd_Click(object sender, EventArgs e)
{
NrPanels++;
AddPanel();
}
}


DynamicUpdatePanels.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DynamicUpdatePanels.aspx.cs"
Inherits="DynamicUpdatePanels" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>
Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:Panel ID="pnlTest" runat="server">
</asp:Panel>
<asp:Button ID="btnAdd" runat="server" Text="Add Panel" OnClick="btnAdd_Click" />
<asp:Button ID="btnPostBack" runat="server" Text="Postback" />
</form>
</body>
</html>


ucUpdatePanelTemplate.ascx.cs
using System;
using System.Web.UI;

public partial class ucUpdatePanelTemplate : UserControl
{
protected void btnAjax_Click(object sender, EventArgs e)
{
tbSomething.Text = DateTime.Now.ToString();
}
}


ucUpdatePanelTemplate.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ucUpdatePanelTemplate.ascx.cs"
Inherits="ucUpdatePanelTemplate" %>

<asp:TextBox ID="tbSomething" runat="server"></asp:TextBox>
<asp:Button ID="btnAjax" runat="server" OnClick="btnAjax_Click" />


That's it, folks!

No comments: