Reputation: 170
Thank you in advance for assistance. It has been too long since I've worked in this, my memory isn't the greatest but I know it can be done.
I have a page, the ask is to never leave the page (no postback) which is why I'm using AJAX and an update panel to being with, and basically stream menus. I went and created multiple UserControls to be loaded individually on the page.
On the Parent Page, I placed the update panel, and placeholder.
<asp:UpdatePanel ID="UpdatePanelContent" runat="server">
<ContentTemplate>
<asp:PlaceHolder ID="OaaSPlaceholder" runat="server">
<!-- section loading area -->
</asp:PlaceHolder>
</ContentTemplate>
</asp:UpdatePanel>
In the parent page "OaaS" I am able to successfully load the first control in the OaaSPlaceholder "tier_2_start.ascx" probably because its being called from the OaaS parent itself on Page_Load. The issue is loading the subsequent controls when trying to access the same methods OaaS used. Directionality is the control is calling the OaaS main pages public method to load a control. The button controls are on the UserControls themselves and each loaded UserControl knows which control to load next. The parent page has 0 awareness of the controls being swapped in and out and that is the intended action. I'm talking about 2 dozen controls in total.
The issue is accessing the Parent OaaS page public method "LoadControl" to load the next control, from the UserControl where the button is. The behavior I'm seeing is this solution currently loads the first 2 controls, but then loads the first control when the 3rd is called. So something is missing.
I created a public method in the OaaS page
public void LoadUserControl(string controlName)
{
OaaSPlaceholder.Controls.Clear();
UserControl uc = (UserControl)LoadControl(controlName);
OaaSPlaceholder.Controls.Add(uc);
}
public void ClearControl()
{
OaaSPlaceholder.Controls.Clear();
}
In each user control (child), I want to call this method to load the next User Control. Below on the Page_Load of the parent (OaaS), this is working great.
switch (tier)
{
case "tier1" :
FULL_PATH = BASE_PATH + "Tier1/";
break;
case "tier2" :
FULL_PATH = BASE_PATH + "Tier2/";
controlPath = FULL_PATH + "tier_2_start.ascx";
break;
case "tier3" :
FULL_PATH = BASE_PATH + "Tier3/";
break;
case "tier4" :
FULL_PATH = BASE_PATH + "Tier4/";
break;
default:
FULL_PATH = BASE_PATH + "Tier1/";
break;
}
ClearControl();
LoadUserControl(controlPath);
The page loads the first UserControl, is the next controls that are problematic, since I'm trying to access the parent page OaaS forms placeholder, from the child control.
tier_2_new.ascx control
protected void NextBtn_Click(object sender, EventArgs e)
{
string action = ActionSelect.SelectedValue; // dropdown menu value from user selection
string BASE_PATH = "~/OaaS_Controls/Tier2/";
string controlPath = "";
switch (action)
{
case "New":
controlPath = BASE_PATH + action + "/tier_2_step_1.ascx"; // new control to be loaded
((OaaS)this.Page).ClearControl();
((OaaS)this.Page).LoadUserControl(controlPath);
break;
case "Decomission":
// nothing yet
break;
case "Consultation":
// nothing yet
break;
}
}
I'm getting NullPointer exceptions and all kind of errors, when its not behaving like expected. That said I know I'm doing it wrong. Any help appreciated.
System.NullReferenceException: 'Object reference not set to an instance of an object.'
The ".Page" is null
Upvotes: 4
Views: 605
Reputation: 35514
When using Dynamic Controls (UserControl or otherwise) you need to keep track of how many there are currently and add them all before adding a new one on PostBack, otherwise the previous ones will be lost. So here is a simple example. The trick is to delegate the Button click on the UserControl to the Page containing that UserControl.
So first the Page containing the UserControls
<asp:UpdatePanel ID="UpdatePanelContent" runat="server">
<ContentTemplate>
<asp:PlaceHolder ID="OaaSPlaceholder" runat="server">
</asp:PlaceHolder>
</ContentTemplate>
</asp:UpdatePanel>
Code behind
public partial class Default1 : System.Web.UI.Page
{
//set the initial number of user controls
int controlCount = 1;
protected void Page_Load(object sender, EventArgs e)
{
//check if the viewstate with the controlcount already exists (= postback)
if (ViewState["controlCount"] != null)
{
//convert the viewstate back to an integer
controlCount = Convert.ToInt32(ViewState["controlCount"]);
}
else
{
//set the first viewstate
ViewState["controlCount"] = controlCount;
}
//create the required number of user controls
for (int i = 1; i <= controlCount; i++)
{
LoadUserControl("WebUserControl1.ascx", i);
}
}
//add a new user control to the page
public void LoadUserControl(string controlName, int index)
{
var uc = (WebUserControl1)LoadControl(controlName);
uc.index = index;
OaaSPlaceholder.Controls.Add(uc);
uc.UserControlButton1 += new EventHandler(UC_Button1_Click);
}
//the delegated button click
private void UC_Button1_Click(object sender, EventArgs e)
{
controlCount++;
LoadUserControl("WebUserControl1.ascx", controlCount);
ViewState["controlCount"] = controlCount;
}
}
Then the UserControl
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="TestOmgeving.WebUserControl1" %>
<div style="border: 1px solid red; margin: 10px; padding: 10px;">
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
</div>
Code behind User Control
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public event EventHandler UserControlButton1;
public int index { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = "This is UserControl " + index;
Button1.Text = "Load UserControl " + (index + 1);
WebUserControl1 control = Page.Master.FindControl("UserControlOnMaster") as WebUserControl1;
}
//the button click from the user control
protected void Button1_Click(object sender, EventArgs e)
{
Button1.Visible = false;
OnUserControlButtonClick();
}
//the delegated control to the method of the parent page
private void OnUserControlButtonClick()
{
if (UserControlButton1 != null)
{
UserControlButton1(this, EventArgs.Empty);
}
}
}
Upvotes: 2