简体   繁体   中英

C# dynamically created user control button click not working

I have a page (company.aspx) that when you click a button on the page the button click event (btnShowDeptsUserControl_Click) dynamically creates a user control and adds to a placeholder. The page reloads with the user control displayed. That works fine.

The issue I am having is that when I click the button on the user control itself, the btnEmailDepts_Click event is not fired, but the main page (company.aspx) is reloaded.

I have read that one way to resolve this is to create the dynamic user control in Page_Init, but I am creating the user control from the main page button click. Also, I am not declaring the user control in the code behind of the main page as you would normally, but do have a placehoder within which the user control is added.

I have also read that you can add a delegate to the main page for the user control button click, but the example seemed to have so much necessary code for such a simple thing that I figured there must be a better way.

Here are the relevant code snippets:

company.aspx:

    <asp:Button id="btnShowDeptsUserControl" runat="server" OnClick="btnShowDeptsUserControl_Click">Show Depts</asp:Button>

    <asp:PlaceHolder ID="phUserControls" runat="server"></asp:PlaceHolder>

company.aspx.cs:

    protected void btnShowDeptsUserControl_Click(object sender, EventArgs e)
    {
        CreateDeptsUserControl();
    }

    private void CreateDeptsUserControl()
    {
        phUserControls.Controls.Clear();
        var uc = (UserControl)LoadControl("~/controls/ucDepartments.ascx");
        phUserControls.Controls.Add(uc);
    }

ucDepartments.ascx:

    <asp:Button ID="btnEmailDepts" runat="server" Text="Send Email" OnClick="btnEmailDepts_Click" />

ucDepartments.ascx.cs:

    protected void btnEmailDepts_Click(object sender, EventArgs e)
    {
        EmailDepts();  // breakpoint here is never hit
    }

    private void EmailDepts()
    {
        // do something
    }

If you read the MSDN , you will notice:

However, the added control does not catch up with postback data processing. For an added control to participate in postback data processing, including validation, the control must be added in the Init event rather than in the Load event.

You are adding your control in the click event which happens after both init and load events, so the postback will not work.

You can call your CreateDeptsUserControl function in the ini event, but there you will have to detect if the btnShowDeptsUserControl was clicked by yourself. It's not hard, you will need to check the submitted values collection and see if there is an item for btnShowDeptsUserControl .

Just wanted to post what I did to make this work. Racil Hilan's answer helped me to arrive at this solution. I did away with the dynamic user control, and went with the more common declared user control in the aspx, but set it to Visible="False" by default.

Notice that the main page button event is empty. Also notice that the user control Page_Load event is empty. All the checking is done in the user control OnInit event, which is executed with each main page load.

In the user control OnInit event I have a couple guard conditions that check to see what EVENTTARGET, if any, caused the page / user control to load. If the EVENTTARGET (control ID) is that of the main page button, then execution will continue and will call LoadSomeData() as well as set the user control Visible = true. Otherwise, if either guard condition evaluates as false, we exit and the user control does not get loaded, so no wasted db / service calls.

company.aspx:

    <asp:Button id="btnShowDeptsUserControl" runat="server" OnClick="btnShowDeptsUserControl_Click">Show Depts</asp:Button>

    <uc1:ucDepartments ID="ucDepartments" runat="server" Visible="False" />

company.aspx.cs:

    protected void btnShowDeptsUserControl_Click(object sender, EventArgs e)
    {
        // empty, just need the event for inspection later in user control OnInit event.
    }

ucDepartments.ascx:

    <asp:Button ID="btnEmailDepts" runat="server" Text="Send Email" OnClick="btnEmailDepts_Click" />

ucDepartments.ascx.cs:

    protected override void OnInit(EventArgs e)
    {
        if (string.IsNullOrWhiteSpace(Request.Params["__EVENTTARGET"]))
            return;

        var controlName = Request.Params["__EVENTTARGET"];
        if (controlName != "btnShowDeptsUserControl")
            return;

        LoadSomeData();  // call method to load the user control
        this.Visible = true;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        // empty
    }

    private void LoadSomeData()
    {
        // get data from database
        // load table / gridview / etc
        // make service call
    }

    protected void btnEmailDepts_Click(object sender, EventArgs e)
    {
        EmailDepts();  // this event is now executed when the button is clicked
    }

    private void EmailDepts()
    {
        // do something
    }

Variation that includes jquery to scroll to user control after postback:

In the main page button click event you can also do something like set a hidden var to a value that can be inspected on main page doc ready to do some jquery stuff, such as scrolling the user control into view if it is far down the page (which I am actually doing in my current task).

Not only will this scroll the now loaded user control into view after clicking the main page button, but notice the code in setupDepts(). I hide the asp button that does a postback to load the user control, and show a regular html button. They both look the same (both say Show Depts), but the regular html button, when clicked, will fire jquery to toggle the div that contains the user control to close, click again, it will open, click again it will close, etc.

This is so that you only load the user control once (make db / service calls once) when the main page button is clicked, and then can toggle show or hide it with subsequent clicks of the alternate button. This approach can be used with multiple buttons or links so long as they all have the same class ids. For example, you may have a Show Depts button / link at top of page and another at the bottom of the page, which is the case in my current task.

company.aspx:

    <asp:Button id="btnShowDeptsUserControl" runat="server" class="btnShowDepts" OnClick="btnShowDeptsUserControl_Click">Show Depts</asp:Button>
    <button class="btnToggleDepts" style="display: none;">Show Depts</button>

   <div id="divShowDepts" style="display: none;">
       <asp:HiddenField runat="server" id="hdnShowDepts"/>
       <uc1:ucDepartments ID="ucDepartments" runat="server" Visible="False" />
   </div>

<script language="javascript" type="text/javascript">
    $(function () {
        toggleDeptsUserControl();
        if ($('#<%=hdnShowDepts.ClientID%>').val() === "show")
            setupDepts();
    });

    function setupDepts() {
        $('.btnShowDeptsUserControl').hide();
        $('.btnToggleDeptsUserControl').show();
        scrollToDepts();
    }

    function scrollToDepts() {
        $('#divShowDepts').toggle(700, function () {
            if ($(this).is(":visible")) {
                $('html, body').animate({ scrollTop: ($(this).offset().top) }, 'slow');
            }
        });
    }

    function toggleDeptsUserControl() {
        $('.btnToggleDeptsUserControl').on('click', function (event) {
            event.preventDefault();
            scrollToDepts();
        });
    }
</script>

company.aspx.cs:

    protected void btnShowDeptsUserControl_Click(object sender, EventArgs e)
    {
        hdnShowDepts.Value = "show";
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM