4

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

Autonomic
  • 150
  • 4
  • 15
  • 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 Jan 29 '21 at 14:49

1 Answers1

2

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);
        }
    }
}
VDWWD
  • 35,079
  • 22
  • 62
  • 79
  • Hi thank you very much for the response. The point of this exercise is to prevent postback hence the use of AJAX and the update panel. As far as the parent page goes, its just a big placeholder for the controls. The controls know which controls to load. But I cannot get the control to talk to the parent pages placeholder. When I did successfully manage to, it loaded the first 2 controls, and then looped back to the first control. – Autonomic Jan 27 '21 at 19:43
  • Unfortunately this is not the implementation I am looking for. I have found dozens of examples of controls talking to the parent page, got me in the same state but not even close to the ball park. – Autonomic Jan 27 '21 at 19:45
  • If you work with UpdatePanels there is always a PostBack as it would with a "normal" PostBack. The only difference is that the content within the UpdatePanel is send to the browser with Ajax. If you do not want to have PostBack you would need MVC or Core. – VDWWD Jan 27 '21 at 21:04
  • And what do you mean by "talk"? This example delegates a command to the Parent Page `UC_Button1_Click` from there you can communicate all you want with other objects. Try this example on a separate page and it will continue to add UserControls without resetting – VDWWD Jan 27 '21 at 21:05
  • The registered method delegate has an explicit control registered "WebUserControl1.ascx" where this needs to be generic. 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); } – Autonomic Jan 28 '21 at 15:59
  • I replaced the "WebUserControl1.ascx with public void LoadUserControl(string controlName, int index) { var uc = (UserControl)LoadControl(controlName); uc.index = index; OaaSPlaceholder.Controls.Add(uc); uc.UserControlButton1 += new EventHandler(UC_Button1_Click); } but the UserControl doesn't contain a definition of index. – Autonomic Jan 28 '21 at 16:05