Monday, March 26, 2012

UpdatePanel + Repeater + LinkButton: Which Event Will Fire?

I have been pulling my hair out for 8 hours now, trying to get this little piece of code to work. Any help would be greatly appreciated!

It is a seemingly simple excercise of having a LinkButton in a Repeater control, nested inside an UpdatePanel. For the life of me, I can't get a command (or click) event to fire!

I have tried adding event code for the repeater control as well as the linkbuttons. When a linkbutton is clicked, the Load event on the page runs, but I can never get a command event to fire.

There are MANY different things I have tried, and for clarity I have removed most of the abberations I have gone through. The code below is a stripped-down version that just shows the basics of what I am trying to do. I would be grateful if someone who has gone through this before could give me some things to try.

Again, I am simply trying to have some server-side code run in response to a LinkButton command that is nested inside a repeater, which is inside an UpdatePanel. It is data bound, as you will notice below (that is the one apsect that seems to work just fine). Thank you!

Here is the code (a little sanitized):

<atlas:UpdatePanel ID="UpdatePanel1" Mode="Conditional" runat="server"><ContentTemplate><table class="choices" border="0"><asp:Repeater ID="Choices" runat="server"><ItemTemplate><tr><td><asp:LinkButton ID="Choice" CssClass="choice" CommandName='<%#Eval("ID")%>' Text='<%#Eval("Name")%>' runat="server" /></td></tr></ItemTemplate><FooterTemplate><tr><td><asp:LinkButton ID="Choice" CssClass="choice" CommandName="0" Text="Nothing" runat="server" /></td></tr></FooterTemplate></asp:Repeater></table></ContentTemplate></atlas:UpdatePanel>

I dont see your triggers section, controling the action that happens on the click of the linkbutton, are you trying to perform a codebehind function?


How do you implement the code that executes when the button is pressed? Instead of a linkButton Click, fetch the ItemCommand event on the repeater.

Check out:

http://sncore.vestris.com/Source/sncore/SnCore.Web/AccountFeedItemImgsView.aspx
http://sncore.vestris.com/Source/sncore/SnCore.Web/AccountFeedItemImgsView.aspx.cs

There's a PagedList (a multicolumn repeater) with a toggle button inside an UpdatePanel.

<atlas:UpdatePanelID='panelShowHide'Mode="Conditional"runat="Server">
<ContentTemplate>
<asp:LinkButtonText='<%# (bool) Eval("Visible") ? "» Hide" : "» Show" %>'ID="linkToggleVisible"runat="server"
Visible='<%# base.SessionManager.IsAdministrator %>'CommandName="Toggle"CommandArgument='<%# Eval("Id") %>'/>
</ContentTemplate>
</atlas:UpdatePanel>

The handling code changes something in the DB, then updates the button only.

publicvoid gridManage_ItemCommand(object sender, DataListCommandEventArgs e)
{
try
{
switch (e.CommandName)
{
case "Toggle":
TransitAccountFeedItemImg img = SyndicationService.GetAccountFeedItemImgById(
SessionManager.Ticket,int.Parse(e.CommandArgument.ToString()));
img.Visible = !img.Visible;
if (!img.Visible) img.Interesting =false;
SyndicationService.CreateOrUpdateAccountFeedItemImg(SessionManager.Ticket, img);
LinkButton lb = (LinkButton)e.Item.FindControl("linkToggleVisible");
lb.Text = img.Visible ? "» Hide" : "» Show";
UpdatePanel up = (UpdatePanel)e.Item.FindControl("panelShowHide");
up.Update();
break;
}
}
catch (Exception ex)
{
ReportException(ex);
}
}

I don't have a running demo, you need to be admin on foodcandy.com to do this :)

Hope this helps,
dB.


Freakyuno:

I dont see your triggers section, controling the action that happens on the click of the linkbutton, are you trying to perform a codebehind function?

I tried both with and without a trigger.

I figure the code doesn't need a trigger section because it is being triggered from a linkbutton within the UpdatePanel.

However, I just want to make sure I made it clear that I did try it.


dblock,

It looks like the major difference between your code and mine is that you seem to put every linkbutton within its own updatepanel, and then the entire list is enclosed within a "wrapper" updatepanel.

Is that right?

I will give it a try and see if that helps. Do you know if it is required?

Thanks for the help.


speednet:

dblock,

It looks like the major difference between your code and mine is that you seem to put every linkbutton within its own updatepanel, and then the entire list is enclosed within a "wrapper" updatepanel.

Is that right?

I will give it a try and see if that helps. Do you know if it is required?

Thanks for the help.

I definitely had this working without having every linkbutton in its own updatepanel (with just the wrapper one). That would update the entire grid. For me the trick has been to catch the grid item command, not the button click.


I just threw this together (as you probably can tell!) and it works for me. I'm using the June CTP if that may have anything to do with it. Here's the ASPX file:

<%@.PageLanguage="C#"AutoEventWireup="true"CodeFile="Default.aspx.cs"Inherits="_Default" %>
<!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.1//EN""http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<htmlxmlns="http://www.w3.org/1999/xhtml">
<headrunat="server">
<title>Untitled Page</title>
</head>
<body>
<formid="form1"runat="server">
<atlas:ScriptManagerID="ScriptManager1"EnablePartialRendering="true"runat="server"/>
<atlas:UpdatePanelID="UpdatePanel1"Mode="conditional"RenderMode="inline"runat="server">
<ContentTemplate>
<asp:RepeaterID="Repeater1"runat="server"OnItemCommand="Repeater1_ItemCommand">
<ItemTemplate>
<asp:LinkButtonID="LinkButtonTest"runat="server"CommandName='<%# Eval("Name") %>'Text='<%# Eval("Name") %>'></asp:LinkButton><br/>
</ItemTemplate>
</asp:Repeater>
<br/>
<asp:LabelID="Label1"runat="server"></asp:Label>
</ContentTemplate>
</atlas:UpdatePanel>
</form>
</body>
</html>

...and the C# file:

using

System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public

partialclass_Default : System.Web.UI.Page
{
protectedvoid Page_Load(object sender,EventArgs e)
{
List<Test> tests =newList<Test>();

tests.Add(newTest("Apple"));
tests.Add(newTest("Berry"));
tests.Add(newTest("Cherry"));

Repeater1.DataSource = tests;
Repeater1.DataBind();
}

protectedvoid Repeater1_ItemCommand(object source,RepeaterCommandEventArgs e)
{
Label1.Text = e.CommandName;
}
}

public

classTest
{
privatestring _name ="";

publicstring Name
{
get {return _name; }
}

public Test(string name)
{
_name = name;
}
}

Apart from the table tags, this is pretty much the same as you're doing.

(Apologies for the spacing... I'm a spacing freak!)


The inner UpdatePanel did not help. I'll post a complete code sample, without editing out the unnecessary stuff. Maybe someone will know why the Command event is not firing.

<asp:PlaceHolder ID="Content" runat="server"><div class="results"><p class="question"><asp:Literal ID="Question" runat="server" /></p><atlas:UpdatePanel ID="UpdatePanel" Mode="Conditional" runat="server"><ContentTemplate><asp:MultiView ID="Views" runat="server"><asp:View runat="server"><div class="choiceswrapper"><table class="choices" border="0"><asp:Repeater ID="Choices" OnItemCommand="Choices_Click" runat="server"><ItemTemplate><tr><td><atlas:UpdatePanel ID="InnerUpdatePanel" Mode="Conditional" runat="server"><ContentTemplate><asp:LinkButton ID="Choice" CssClass="choice" onmouseover="this.className='choiceon';" onmouseout="this.className='choice';" CommandName='<%#Eval("ID")%>' Text='<%#Eval("Name")%>' runat="server" /></ContentTemplate></atlas:UpdatePanel></td></tr></ItemTemplate><FooterTemplate><tr><td><asp:LinkButton ID="Choice" CssClass="discardoption choice" onmouseover="this.className='discardoption choiceon';" onmouseout="this.className='discardoption choice';" CommandName="0" Text="Discard" runat="server" /></td></tr></FooterTemplate></asp:Repeater></table></div><p class="instructions">After you register your vote the current results will be displayed. Select "Discard Vote" to see the results without voting (your vote is discarded permanently).</p></asp:View><asp:View runat="server"><div class="options"><table><tr><td>Entry 1</td><td> [ <strong>0</strong> ] </td><td><img src="images/bar.gif" width="4" height="11" alt="" /></td></tr></table></div><div class="foot"><div class="center"><strong>You are already logged in.</strong></div></div></asp:View></asp:MultiView></ContentTemplate></atlas:UpdatePanel><div class="tblrowseparator"></div></div></asp:PlaceHolder>

Only the first View in the MultiView is used right now. I kept the extra UpdatePanel in there, from the test I mentioned in my last post. Also, in case anyone is wondering, I tried removing the MultiView from the UpdatePanel, and it still did not work.

The code-behind looks like this:

Sub Choices_Click(ByVal senderAs Object,ByVal eAs System.Web.UI.WebControls.RepeaterCommandEventArgs)Handles Choices.ItemCommandIf (Me.Master.CurrentUser.LoggedInAndActivated)Then'I set a breakpoint on the "If" statement above to see if the code ever entered the event'... Much more code hereEnd IfSetupAndDisplay(Me.Master.CurrentUser.ID)End Sub

Thank you Jason!

Can you take a look at the code I just posted and see if you can see any problems?

I am using the June CTP (which is awesome), so I'm OK there. I was thinking maybe it was the MultiView, but it stll didn't work when I tried removing it.

My ScriptManager is on the Master page (which works fine for another UpdatePanel on the same page).


I'm pretty sure the problem has something to do with the repeater.

I completed removed the repeater, and replaced it with just a single linkbutton in the table, and then wired the code-behind to the linkbutton command (instead of the repeater item command event) and it did execute the code.

Any eagle-eyes see a problem with my repeater code? Should I try a different template control?


Hmm... when I tried your code straight, I got nothing (could be the MultiView for me!). So I removed the MultiView and the inner UpdatePanel (definately not needed) and I was able to get the event to fire.

I'll try adding the the MultiView back in to see if that might be the issue.


Oops... didn't set the ActiveIndex of the MultiView to 0 :|

Anyway, I've added your MultiView code (almost) exactly and I definately getting the Choices_Click event firing. The only things I changed was removing the inner UpdatePanel.

When I re-added the inner UpdatePanel the event did NOT fire. So that's what I believe to be the issue.


Jason,

First of all, thank you very much for taking the time to test out my code - much appreciated!

That inner UpdatePanel was only in there as a test, and I tried testing both with and without it. Didn't work either way.

It is interesting that when I removed the repeater it started to work.

Looking at my code for the 1,000th time today, I'm reasonably sure it is good, valid code. Itshould work. The fact that you successfully tested it on your end would indicate that also.

It is quite possible that something else is interfering with the operation of this code, maybe it's just going to take another day of trial and error to eliminate all the possibilities.

It's also possible that I have some combination of things that is exposing a rare bug. My web site is very complex, so it wouldn't surprise me. I was actually hoping that if I posted the code it might help spark an idea from one of the Atlas developers or contributors.

In any case, if you can think of anything else that may be causing the problem, I would appreciate whatever you could offer. Once I'm able to narrow down the problem, I'll post my solution here.

Thanks again for your help.

-Todd


I believe I have finally nailed down the basic problem, although the exact details are still a little murky.

It turns out that this is one of those very tricky areas in ASP.NET, and not actually and Atlas issue. It has been blogged by Scott Mitchell (of 4guysfromrolla.com) in the past. Here are the relevant links:

http://scottonwriting.net/sowBlog/posts/1263.aspx

http://scottonwriting.net/sowblog/posts/1268.aspx

Although I did not find the exact solution for the ASP.NET issue described in the blog entries, it helped me identify the basic issue so I could code around it.

The solution I came up with works pretty well and is not too kludgy:

1. I put a <asp:HiddenField> tag within the UpdatePanel, and used it to PostBack an ID code (the same ID code that I was originally going to PostBack as a command argument). I included the HiddenField inside the UpdatePanel so that I could reset it to an empty string after each PostBack. If it was outside the UpdatePanel, I would not be able to update it during the partial render.

2. I kept the LinkButtons in the Repeater, but added an OnClientClick event to each one that loads the ID code into the HiddenField. Check out the weird syntax I had to come up with, to overcome all the different quote characters: OnClientClick='<%#String.Format("$({2}{0}{2}).value={2}{1}{2};", HiddenTag.ClientID, Eval("ID"), Chr(39))%>' You may also notice the Atlas shortcut "$()" is used instead of document.getElementById().

3. I stripped out all Repeater and Control Events, and now process everything in the Load event. The Load event looks for a value posted back in the HiddenField, because if the HiddenField contains anything, that means one of the LinkButtons in the repeater must have been clicked. So although the LinkButtons still perform the PostBack (with partial rendering), they are only used to jumpstart the processing in the Load event, and nothing else.

I hope this is helpful to anyone else who encounters the same problem with template controls and PostBacks. It may save you the day-and a half that I have spent on this issue.

No comments:

Post a Comment