4
votes

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

NullPointer Error

Page Construction

1
In the actual screenshot you provided, showing the actual NullReferenceException, the exception occurs on the last line (before the break) of the code. Why is that? If Page is null, why does it not fail in one of the earlier lines?Kai Hartmann

1 Answers

2
votes

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);
        }
    }
}