views:

40

answers:

2

This has been driving me crazy for 2 days now - hope someone has seen this before.

I have this issue where the first click of a control within a repeater or grid view fails to fire the ItemCommand event, all subsequent clicks work. The controls are being loaded into a placeholder on Base.aspx like so

    private void LoadUserControl()
    {
        string controlPath = LastLoadedControl;

        if (!string.IsNullOrEmpty(controlPath))
        {
            ph.Controls.Clear();

            UserControl uc = (UserControl)LoadControl(controlPath);
            ph.Controls.Add(uc);
        }

    }

I'm wondering if this is a viewstate issue, and in which page events I should use DataBind(). I've experimented with databind in different page events, enabling viewstate on page and control without variation in result.

here's the GridView template , but I have also the same behaviour with a repeater control so I don't believe its the control which is this issue.

<ItemTemplate>
       <asp:RadioButton ID="rbEnable" GroupName="MyGroup" runat="server" Text="Enabled" Checked="<%# ((EducateMe.BaseTypes.AbstractLink)Container.DataItem).IsActive == true %>" />
       <asp:RadioButton ID="rbDisable" runat="server" GroupName="MyGroup" Text="Disabled" Checked="<%# ((EducateMe.BaseTypes.AbstractLink)Container.DataItem).IsActive != true %>" />
       <asp:Button ID="btnEnable" runat="server" CommandArgument="<% # Container.DataItemIndex %>" CommandName="Enable" ToolTip="Enable" Text="Save" />
       <asp:Button ID="btnDisable" runat="server" CommandArgument="<% # Container.DataItemIndex %>" Visible="false" CommandName="Disable" ToolTip="Disable" Text="Disable" />
    </ItemTemplate>

Some further info which might be relevant:

What I've noticed is in the Page_Load event of the usercontrol is where I'm rebinding the control. This is probably the cause as the control state gets rewritten, but if I add a if(!IsPostback) to this area in the ascx, this code section doesnt fire at all like it does on an aspx page. That would be the correct section to rebind the control I think.

A: 

I'm not sure about the rest of your code, but usually when I have a problem of "first action behaves different than subsequent actions" like this is because I placed something in the wrong side of the IsPostBack section.

For example, I find myself binding when it's a postback, so my events don't work until the second postback, coz the first postback is when they were born, and the initial page load never did that part.

I think you're on the right track; make sure you're binding and wiring up your events at the right moment.

Beemer
Thanks for response. I added some more info which might be relevant at the bottom of my post re: !IsPostback.
MikeW
Try moving that rebinding code from Page_Load to Page_PreRender of the control
Beemer
No joy there. Viewstate is turned on for the control and the page btw.
MikeW
Can you post the full markup declaration of the GridView?
Beemer
btw.... correct me if I'm wrong, but GridViews don't have ItemCommand; they have RowCommand.. unless there's a bigger picture I'm missing.
Beemer
you are correct, i found that the behaviour is the same regardless of control.. to make things a bit clearer i put together a barebones solution of the behaviour which is available at 202.53.7.235/PostBackTest.rar, any ideas or suggestions welcome :-)\
MikeW
Some Additional info about the solution file: Contains a usercontrol on base page which dynamically loads in Control A or Control B. Control A repeater ItemCommand works first time, but if you load Control B then next time Control A loads, ItemCommand event fails to fire ('Apply' button) until a postback happens.
MikeW
I had to post another answer, since having this answer could be helpful to others, too.
Beemer
A: 

MikeW,
After hours and hours of fiddling with this, I found the root of your issue.
It has nothing particular to do with the repeater or the gridview.
It is simply a dynamic-controls-on-a-postback issue.

To solve your issue:
Amazingly, just one line is missing in your code.
Right when you load the control, assign something to its ID, like so:

UserControl uc = (UserControl)LoadControl(controlPath);
uc.ID = "mycontrol";
ph.Controls.Add(uc);

That way, when you postback, the page knows which control is which.

To explain this more easily, let's simplify the problem.
Here's a scenario of clicking a button twice in a row that dynamically creates another button:

  • Make a single button, Button1, which when clicked will load another button dynamically, Button2. Assume Button2's action is as simple as displaying the current time.
  • Now, click Button1.
  • To retain Button2, you have to utilize ViewState to tell the page there's a dynamic control. Like you did, we can memorize the control's path, or the name of its class.
  • Right when the page posts back, in Page_Load, we look at the ViewState to see that there is a control we're trying to retain, so we load it right there (this is equivalent to your LoadUserControl() function above).
  • By now, Button2 is visible, and when clicked, it will do its action just fine.
  • Don't click Button2, just click Button1 again (which is equivalent to your case switching between two different dynamic controls).
  • Guess what happens now: Page_Load will load Button2 from the ViewState. And the Click event of Button1 will load ANOTHER Button2 instance, after clearing the placeholder.
  • Since you didn't assign it an ID, it will assign itself one in UniqueID, and those two Button2s will have something like ctl02 and ctl03
  • Now click Button2.
  • You would say "that's fine, we're overwriting the old one". Yes, but not without an ID.
  • Since you didn't give it an ID to identify it on the postback, it will utilize the UniqueID, which is sequentially generated.
  • Now the page is looking for ctl03, which doesn't exist, so the Click doesn't fire.
  • But, by now, we have a brand new Button2, with UniqueID ctl02.
  • Clicking this new Button2 will work just fine, coz on the postback, it's the only Button2, so it would coincidentally have a UniqueID of ctl02.

So, how does assigning an ID make it work?
That way, each new control generated will have the same ID, so on the postback, it can find what it's looking for, whether it was generated in Page_Load or on another button's Click event.

Hope that explains why it works, but as far as you care, just assign it an ID and all will be good.
I thought it would be interesting to share the mechanism behind it and why that's the case. =)

Beemer
Ha - awesome job Beemer - of course thats fixed it, thanks heaps for your help - I'll definitely watch that one in future :-D
MikeW