In this post, I will show you how to load different user control in UpdatePanel from different menu item click. I have found a lot of request in Asp.net Ajax Forum and some of them are having misconception about this. Once you complete reading this post you will be able to load controls dynamically and learn how to employ different helper controls like UpdateProgress, ModalPopupExtender while the UserControl is loading. First let us create plain page without the ajax support:
Markup:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="PlainSampleMenu.aspx.cs" Inherits="PlainSampleMenuPage" %>
<!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>Plain Sample Menu</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Menu ID="Menu1" runat="server" OnMenuItemClick="Menu1_MenuItemClick">
<Items>
<asp:MenuItem Text="File">
<asp:MenuItem Text="Load Control1"></asp:MenuItem>
<asp:MenuItem Text="Load Control2"></asp:MenuItem>
<asp:MenuItem Text="Load Control3"></asp:MenuItem>
</asp:MenuItem>
</Items>
</asp:Menu>
<br />
<br />
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</form>
</body>
</html>
Code Behind:
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class PlainSampleMenuPage : System.Web.UI.Page {
private const string BASE_PATH = "~/DynamicControlLoading/";
private string LastLoadedControl {
get { return ViewState["LastLoaded"] as string; }
set { ViewState["LastLoaded"] = value; }
}
private void LoadUserControl() {
string controlPath = LastLoadedControl;
if (!string.IsNullOrEmpty(controlPath)) {
PlaceHolder1.Controls.Clear();
UserControl uc = (UserControl)LoadControl(controlPath);
PlaceHolder1.Controls.Add(uc);
}
}
protected void Page_Load(object sender, EventArgs e) {
LoadUserControl();
}
protected void Menu1_MenuItemClick(object sender, MenuEventArgs e) {
MenuItem menu = e.Item;
string controlPath = string.Empty;
switch (menu.Text) {
case "Load Control2":
controlPath = BASE_PATH + "SampleControl2.ascx";
break;
case "Load Control3":
controlPath = BASE_PATH + "SampleControl3.ascx";
break;
default:
controlPath = BASE_PATH + "SampleControl1.ascx";
break;
}
LastLoadedControl = controlPath;
LoadUserControl();
}
}
As you can see we are loading the UserControls in the PlaceHolder Control on different menu item click. If you are wondering why I am also loading the controls in Page Load event based upon the ViewState, the reason is otherwise those dynamically loaded controls will not be visible in the consequent postbacks. There are few good articles written by Scott Mitchell on dynamically loading Controls which you will find in the following links:
- Dynamic Controls in ASP.NET
- Working with Dynamically Created Controls
- Dynamic Web Controls, Postbacks, and View State
Now let us ajaxify this page, lets wrap the PlaceHolder with an UpdatePanel control. The following shows the markup:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SampleMenu1.aspx.cs" Inherits="SampleMenuPage1" %>
<!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>Sample Menu</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Menu ID="Menu1" runat="server" OnMenuItemClick="Menu1_MenuItemClick">
<Items>
<asp:MenuItem Text="File">
<asp:MenuItem Text="Load Control1"></asp:MenuItem>
<asp:MenuItem Text="Load Control2"></asp:MenuItem>
<asp:MenuItem Text="Load Control3"></asp:MenuItem>
</asp:MenuItem>
</Items>
</asp:Menu>
<br />
<br />
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Menu1" />
</Triggers>
</asp:UpdatePanel>
</form>
</body>
</html>
Since the Menu control resides outside the UpdatePanel we will also need a AsyncPostBackTrigger for the Menu Control. Now run this page, you will find the controls are loaded without a full postback.
Now let us further enhance this page with UpdateProgress and AjaxControlToolKit’s ModalPopupExtender. First with the UpdateProgress Control.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SampleMenu1.aspx.cs" Inherits="SampleMenuPage1" %>
<!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>Sample Menu - UpdateProgress</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Menu ID="Menu1" runat="server" OnMenuItemClick="Menu1_MenuItemClick">
<Items>
<asp:MenuItem Text="File">
<asp:MenuItem Text="Load Control1"></asp:MenuItem>
<asp:MenuItem Text="Load Control2"></asp:MenuItem>
<asp:MenuItem Text="Load Control3"></asp:MenuItem>
</asp:MenuItem>
</Items>
</asp:Menu>
<br />
<br />
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
Loading....
</ProgressTemplate>
</asp:UpdateProgress>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Menu1" />
</Triggers>
</asp:UpdatePanel>
</form>
</body>
</html>
As you can see we have added a UpdateProgress Control in the UpdatePanel ContentTemplate. But the UpdateProgress does not get visible when you click any of the menu item. This is the true nature of UpdatePanel, it does not shows the UpdateProgress if the control is out side the UpdatePanel which caused the postback. We have to manually show the UpdateProgress by the JavaScript code like the following:
<script type="text/javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(initializeRequest);
prm.add_endRequest(endRequest);
var _postBackElement;
function initializeRequest(sender, e)
{
if (prm.get_isInAsyncPostBack())
{
e.set_cancel(true);
}
_postBackElement = e.get_postBackElement();
if (_postBackElement.id.indexOf('Menu1') > -1)
{
$get('UpdateProgress1').style.display = 'block';
}
}
function endRequest(sender, e)
{
if (_postBackElement.id.indexOf('Menu1') > -1)
{
$get('UpdateProgress1').style.display = 'none';
}
}
</script>
Now run the page, you will find the UpdateProgress Control is showing the loading message no matter which control caused the postback.
In this final section we will implement the AjaxControlToolKit’s ModalPopupExtender to show the loading message, this will give you a similar look of CodePlex Site. The following shows the source:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SampleMenu2.aspx.cs" Inherits="SampleMenuPage2" %>
<!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>Sample Menu - ModalPopupExtender</title>
<style type="text/css">
.modalBackground
{
background-color:#dcdcdc;
filter:alpha(opacity=60);
opacity:0.60;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<asp:Menu ID="Menu1" runat="server" OnMenuItemClick="Menu1_MenuItemClick">
<Items>
<asp:MenuItem Text="File">
<asp:MenuItem Text="Load Control1"></asp:MenuItem>
<asp:MenuItem Text="Load Control2"></asp:MenuItem>
<asp:MenuItem Text="Load Control3"></asp:MenuItem>
</asp:MenuItem>
</Items>
</asp:Menu>
<br />
<br />
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Menu1" />
</Triggers>
</asp:UpdatePanel>
<asp:Panel ID="Panel1" runat="server" style="background-color:#ffffff;display:none;width:400px">
<div style="padding:8px">
<h2>Loading...</h2>
</div>
</asp:Panel>
<ajaxToolKit:ModalPopupExtender ID="ModalPopupExtender1" runat="server" TargetControlID="Panel1" PopupControlID="Panel1" BackgroundCssClass="modalBackground" DropShadow="true" />
<script type="text/javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(initializeRequest);
prm.add_endRequest(endRequest);
var _postBackElement;
function initializeRequest(sender, e)
{
if (prm.get_isInAsyncPostBack())
{
e.set_cancel(true);
}
_postBackElement = e.get_postBackElement();
if (_postBackElement.id.indexOf('Menu1') > -1)
{
$find('ModalPopupExtender1').show();
}
}
function endRequest(sender, e)
{
if (_postBackElement.id.indexOf('Menu1') > -1)
{
$find('ModalPopupExtender1').hide();
}
}
</script>
</form>
</body>
</html>
As you can see we first created a custom style which will be shown as the background style when the modal popup is visible, next we have added a regular Asp.net Panel control which will be shown as the modal popup. We have initially set the Panel control display mode to none, so there will be no flicker when the modal popup is shown. At last, we have added a ModalPopupExtender and sets its required property. The previous rule also applies here, we have to manually show/hide the ModalPopup which we are doing in the above JavaScript code. You will also found few cool techniques of ModalPopupExtender in this link.
To run the sample first create a AjaxControlToolKit enable site website then create a folder DynamicControlLoading folder and copy the files in the following attachment.
Download: Complete Source