JScript runtime error when using UpdatePanel - c#

I am trying to create a dynamic UI using UpdatePanel of AJAX toolkit.
Let me explain the context of my application a bit. There are many profiles. Each profile has a number of product classes. Each product class has a number of products.
I am implementing a comparison of products.
Profiles are represented by RadioButtonList (dynamically created). For each product class of selected profile a DropDownList need to
be dynamically created that allows user to select a product of that class.
Everything is working fine on the first selection. However when I try to select another product and click submit the second time, the following error is thrown:
Microsoft JScript runtime error:
Sys.WebForms.PageRequestManagerServerErrorException: Invalid postback
or callback argument. Event validation is enabled using in configuration or <%# Page
EnableEventValidation="true" %> in a page.
Any hints on why this error is thrown would be very helpful.
protected void Page_Load(object sender, EventArgs e)
{
pnlDynamic.Visible = false;
if (!this.IsPostBack)
{
LoadProfiles(); //Data binding is done for radio button list
}
else
{
btnSubmit.Enabled = true;
GenerateProductUI(ProfileID); //DropDownLists are dynamically created and populated from database
//ProfileID is the selected profile id
}
}
<asp:UpdatePanel runat="server" id="UpdatePanel" updatemode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger controlid="rblProfiles" eventname="SelectedIndexChanged" />
</Triggers>
<ContentTemplate>
<asp:Panel ID="Panel1" runat="server">
<asp:RadioButtonList ID="rblProfiles" runat="server" AutoPostBack="True"
onselectedindexchanged="rblProfiles_SelectedIndexChanged">
</asp:RadioButtonList>
</asp:Panel>
<br />
<asp:Panel ID="pnlDynamic" runat="server" Visible="false">
<asp:Panel ID="Panel2" runat="server">
<asp:Table ID="table" runat="server"/> <!-- Product dropdowns generated in table -->
</asp:Panel>
</asp:Panel>
<br />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Button ID="btnSubmit" runat="server" Text="Submit" onclick="btnSubmit_Click" CssClass="btnSubmit" AutoPostBack="True" />
<asp:UpdatePanel runat="server" id="UpdatePanel1" updatemode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger controlid="btnSubmit" eventname="Click" />
</Triggers>
<ContentTemplate>
<asp:Panel ID="pnlResult" runat="server" Visible="false">
<!--Result displayed-->
</asp:Panel>
<br />
</ContentTemplate>
</asp:UpdatePanel>

IMPORTANT! EVERYONE WHO EVER DISABLES EVENT VALIDATION SHOULD UNDERSTAND WHAT HE IS DISABLING!
Imagine that you have a blogging application (or a banking application) and there is a list of comments (or bank accounts) all users can see the comments (or the accounts) but only moderators (or managers) can delete them. So you write a check to switch the delete button visible property depending on the access of the user. Users who are not moderators (managers) cannot delete the comment (account). Problem solved.
HOWEVER if you disable event validation a malicious user can send a post request claiming to have pressed the delete button despite the fact that it was never enabled for him. Normally the event validation would kick in and produce an exception but if it is disabled ASP.NET would not check if the button was visible and will just run the button click handler code and the comment (account) would be deleted. This can be prevented if you check if the user can delete in the button click event but you should also consider that another developer may come later and add another button (edit?) without noticing that event validation was disabled. Also now all other buttons on the page require the appropriate checks. Be very careful if you disable event validation!
The error occurs because you are triggering a postback from a control which does not exist when the original page was rendered. One way to fix it would be to disable event validation for this page but you should make sure to check the credentials of the user requesting the change in the event itself otherwise it is a security issue because a user will be able to "press" a button that was not rendered at all (for example on products he does not have access to).
You can disable event validation through the page directive:
<%# Page EnableEventValidation="false" ... %>
I believe it is also possible to set the update mode of the update panel to always instead of conditional which maybe will fix the issue (can you try it and tell us?) but this will increase the data sent over the network. If it is a good trade off depends on your use case, on the number and size of the update panels you have on the page (the more you have the worse). If it is an internal app or this UI is accessed relatively rarely I would go for this approach (assuming it works because I have not validated that).
BTW this is not actually a script error. You are getting a client side script error simply because the error surfaces through an AJAX request. This is purely a server error that is raised because of a potential security issue with your code.

Add <%# Page EnableEventValidation="false" %> at the top of the page.

Once dynamic dropdown lists are created you have to recreate them or any dynamically created control on every page init or page load. In such case you don't need to use EnableEventValidation="false".
protected void Page_Load(object sender, EventArgs e)
{
pnlDynamic.Visible = false;
if (!this.IsPostBack)
{
LoadProfiles(); //Data binding is done for radio button list
}
else
{
btnSubmit.Enabled = true;
}
GenerateProductUI(ProfileID); //DropDownLists are dynamically created and populated from database
//ProfileID is the selected profile id
}
Or just move GenerateProductUI(ProfileID) from Page_Load to Page_Init

Related

UpdatePanel with AsyncPostback tryes to update control outside UpdatePanel

I need to have UpdatePanel with asyncpostback, but in my case it seems that no partial postback happens, but fullpostback. I am new to web forms, please, check the code:
<%# Register TagPrefix="Cust" TagName="CompanyInformationView" Src="~/CustomControls/CompanyInformationView.ascx" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="Server">
<asp:UpdatePanel runat="server" ID="UpdatePanel1" ChildrenAsTriggers="true" UpdateMode="Conditional">
<ContentTemplate>
<asp:Button ID="Button1" runat="server" OnClick= "Button1_Click" Text="test" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
<Cust:CompanyInformationView ID="CompanyInformationView" runat="server" />
</asp:Content>
So I have Test Button. OnClick it should do "nothing" for test. Also there is custom control on web form. Here is server side code for this form:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// fill in custom control
CompanyInfo c = GetInfo();
CompanyInformationView.Company = c;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
var i = 1;
}
CompanyInformationView is custom control with property "Company". There is no ViewState added for this property (so it cannot be loaded properly if postback is done). When I click on Test Button, the page fails, because "CompanyInformationView.Company" is not set (it is not set, because it cannot be loaded from ViewState, I guess).
Instead, I think that it should not work like this. AsynPostback should deal only with UpdatePanel.
Why it wants to reload custom control? Doesn't it mean that Full postback happen or maybe I do not understand Asyncpostback?
PageLoad and all other events are raised on every get\postback, no matter if it's async or full,
In asyncpostback, the response which the server renders includes only the content inside the updatepanel.
Try to Put <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
Before
<asp:UpdatePanel runat="server" ID="UpdatePanel1" ChildrenAsTriggers="true" UpdateMode="Conditional">
Async post back in web forms still proceeds the whole page life cycle as if it's a traditional post. the difference is only the updated content inside target update panel that will be sent in response. so in this case, async post back will still give you exception unless you populate the custom control no matter get/post.
you need to also put the custom control inside another update panel or in the same update panel as the button in order see partial update to happen.

How can I prevent a page from going to the top every time a button with OnClick event is clicked?

How can I prevent a page from going back to the top every time a button is click? I still want to execute the code on the "OnClick" event for the button.
Example:
Almost all of our forms have a button on the bottom of the page which are suppose to bring some data back from the DB and populate some labes and textboxes located on the bottom of the page, when the button is click it takes user back to the top of the page. How can I prevent this from happening?
Any help will be really appreciate it.
Assuming the OnClick event is handled on the server and not in javascript, the easiest thing is wrap the controls to be updated in an UpdatePanel and have the update panel trigger on the button's click event.
Here is an example from Microsoft's documentation:
<asp:Button ID="Button1"
Text="Refresh Panel"
runat="server" />
<asp:ScriptManager ID="ScriptManager1"
runat="server" />
<asp:UpdatePanel ID="UpdatePanel1"
UpdateMode="Conditional"
runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" />
</Triggers>
<ContentTemplate>
<fieldset>
<legend>UpdatePanel content</legend>
<%=DateTime.Now.ToString() %>
</fieldset>
</ContentTemplate>
</asp:UpdatePanel>
There are two options:
Use ASP.NET Ajax, in particular the UpdatePanel control
Set MaintainScrollPositionOnPostback to true on page level or in web.config
in your page declaration you can add MaintainScrollPositionOnPostback="true" ie:
<%# Page Language="C#" ... MaintainScrollPositionOnPostback="true" %>
you can also put that in your web.config under system.web or declare it from codebehind Page.MaintainScrollPositionOnPostback=true;
In the function handler for the button make sure you return false. You might be actually posting the page with those buttons rather than just doing something on the click event.

How can I trigger UpdatePanel from Event within a dynamically loaded UserControl?

I am writing a card game app using Ajax, c# and .NET 3.5. Due to the nature of the interface I have numerous update panels that Im trying to manage and update across various user action. I'm having problems with one though.
The players current hand is built by binding a list of Card objects to a repeater and then dynamically creating a Card UserControl and adding it to the Controls of a PlaceHolder when each item is databound. The code is roughly as follows:
On the page
<asp:UpdatePanel ID="pnlInHand" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Repeater ID="rptInHand" runat="server" onitemdatabound="rptInHand_ItemDataBound">
<ItemTemplate>
<asp:PlaceHolder ID="plcInHandCard" runat="server" />
</ItemTemplate>
</asp:Repeater>
</ContentTemplate>
</asp:UpdatePanel>
In code behind
protected void rptInHand_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Card card = (Card)e.Item.DataItem;
PlaceHolder plcCard = (PlaceHolder)e.Item.FindControl("plcInHandCard");
plcCard.Controls.Add(CreateCardControl());
}
private CardControl CreateCardControl()
{
CardControl cardControl = (CardControl)Page.LoadControl("~/UserControls/CardControl.ascx");
//Set control properties here
return cardControl;
}
The Card Control includes a Button. The ClickEvent for this button calls a Method of the Parent Page that needs to update a seperate UpdatePanel as well as remove the card Control from the Panel that it is sitting within.
I have two issues.
When I click the Card Control Button, because it has been created as part of a repeater within an updatePanel, it no longer exists when the page is posted back and so the Click event for the button within the control never fires. I can obviously rebind the repeater on page load, but does this mean I have to essentially do this on every postback?
More importantly I need a way to trigger the update of another updatepanel in the parent page when the Card control's click event is raised. Is there a way of setting a trigger on an update panel that listens out for an event within a dynamicaly loaded UserControl?
Many thanks
Stewart
Sample code from ASP.net site that should address your point 2 problem follows.
I'll leave the translation to your code to you.
I may be misunderstanding what you are trying to do but I believe once you get this working your issue with point 2 is no longer relevant as you'll get the AJAX postback you want from your parent update panel.
Good luck!
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1" OnItemDataBound="itemDataBound">
<ItemTemplate>
<mycontrol:user ID="user1" runat="server" OnCausePostBack="user1_CausePostBack" /> <br />
</ItemTemplate>
</asp:Repeater>
</ContentTemplate>
</asp:UpdatePanel>
protected void itemDataBound(object sender, RepeaterItemEventArgs e)
{
ModalPopup_WebUserControl mw=(ModalPopup_WebUserControl)e.Item.FindControl("user1");
AsyncPostBackTrigger at = new AsyncPostBackTrigger();
at.ControlID = mw.ID;
at.EventName = "CausePostBack";
UpdatePanel2.Triggers.Add(at);
}
protected void user1_CausePostBack(object sender, EventArgs e)
{
// do something
}
just an idea for point 2 : what about add a property in the cardControl to set a reference to the updatepanel/s ? from there you can add triggers or call panel.update in the button event
for point one yes u will have to do it. u will have to re create the controls
for point 2 a simple updatePanel.update() would do the job insode of any event if you are using an event that was binded to a dynamically created control then u will have to rebind the event on every page postback

How to display hidden placeholder without refreshing the page?

I have two placeholders in one page and basically my requirement is that I want to show other placeholder on click of button which is in first placeholder without refreshing the page.
But when I m clicking on the button in 1st placeholder it is refreshing the page and then showing me second placeholder.I m open to hear any other suggestions also if it is not possible through placeholder.
Can anyone tell me how to achieve this?
Thanks,
You could, as suggested, use an UpdatePanel. As long as you ensure that both PlaceHolders exist within the ContentTemplate element, you will be able to switch between the PlaceHolders without the whole page being refreshed.
However, such convenience comes at the cost of control. This isn't a knock on Microsoft. The same problems exist for most ready-rolled solutions.
Whatever route you choose, you're going to need something to refresh part of the current page's DOM. And really, this means Javascript.
Do the actions in PlaceHolder 1 change the content of PlaceHolder 2? If not, you could render both, and simply use CSS to make PlaceHolder 2 invisible on load. You could then use Javascript events to make it visible as desired.
If actions on PlaceHolder 1 do affect PlaceHolder 2, then the above solution won't work, as you'll need to work out what PlaceHolder 2 is going to contain before displaying it.
The real question is whether you employ your own Javascript ( possibly in conjunction with a mature js library like mootools or jQuery ), and maybe learn something in the process, or run with the ASP .NET AJAX stuff for the quick solution, and hope you don't run into any problems.
You could wrap an updatepanel around you PlaceHolder:
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
<ContentTemplate>
<asp:PlaceHolder runat="server" ID="placeholder">hello</asp:PlaceHolder>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" />
</Triggers>
</asp:UpdatePanel>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
Codebehind:
protected void Page_Load(object sender, EventArgs e)
{
placeholder.Visible = false;
}
protected void Button1_Click(object sender, EventArgs e)
{
placeholder.Visible = true;
}
This wil generate a partial postback (via AJAX), so only the content inside my updatepanel gets updated. In the <Triggers>-section, I specify that my Button will trigger the postback.
Either use an UpdatePanel control (part of the MS ajax framework) or use JavaScript. Placeholder would have to render an HTML element, and I'm not sure that it does. If it doesn't, you can use a panel instead, and then in JS do:
function show() {
var panel = document.getElementById("<%= panel2.ClientID %>");
panel.style.display = "true";
}
<asp:Button id="btn1" runat="server" OnClientClick="show();return false;" />
HTH.

UpdatePanel and Repeater render page unresponsive after post-back

I have a page with an UpdatePanel that contains a Repeater and a text box with the number of items in the repeater. When I change the value, the page is supposed to post back and redraw the Repeater with the updated number of items. This works in principle, but the page ends up frozen after post-backs and does not accept any input - in IE 8 only. It works perfectly fine in Firefox. For instance, the context menu does not appear when I right-click in controls, and I cannot enter text in text boxes.
When I take out the UpdatePanel, the page works fine, but of course refreshes on every post-back event. This is not necessarily related to the Repeater on the page. I think I am seeing this on other pages. What's the trick here?
<asp:UpdatePanel ID="uPanel" runat="server" UpdateMode="Conditional"
EnableViewState="true" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:Panel ID="Panel1" runat="server" DefaultButton="btnSubmit">
<asp:TextBox ID="tbItems" runat="server" AutoPostback="true"
OnTextChanged="textchanged_Items"/>
<asp:Repeater id="rptItems" runat="server"
OnItemDataBound="repeaterItem_Databound">
<...>
</asp:Repeater>
protected void textchanged_Items(object sender, EventArgs e) {
try {
// this methods rebinds the repeater to a List after changing
// the number of items in the list
ReflowItemRepeater();
// This is not really necessary, since Databind() appears to
// cause an update. I tried it anyways.
uPanel.Update();
}
catch (Exception ex) {
ShowError(this, "Error displaying the item list.", ex, true);
}
}
I ended up removing the update panel.
One month later, different page, I am still and again fighting this. The situation is the same.
An update panel, a repeater (actually 2 nested repeaters), and a control in the repeater that fires a postback event. The server processes the event correctly and returns control, but the browser (IE8) never refreshes the update panel. The page is unresponsive, as if in some sort of dead-lock situation. I can unlock it by clicking on a button that fires another postback event (also in the update panel). But the text boxes in the panel are not clickable or editable when this happens.
Also, it happens only the first time. Once I have "freed up" the lock, or whatever it is, it will not happen again on this page, even when I repeat the exact same steps that led to it.
When this happens, the JIT debugger does not report anything.
I would actually set Triggers within your updatepanel.
I'm not sure you need to call .Update in your code behind as the updatepanel will be updated when the trigger occurs.
Try this:
My gut feeling is that it has something to do with the use of the OnTextChanged event. For kicks, try adding a button next to the text box, and reflow the repeater when the button is clicked instead. Does IE still freeze?
So I stripped this page down to the minimum and I found out what is doing it - the AjaxToolkit:CalendarExtender. If I take it out, everything works fine. Still, I would be curious to know if there is a workaround.
Here is a link to my test page. I will keep it up for a few days.
To see the issue, select "2" from the drop-down, then type something into the first quantity field and tab out. The cursor will blink in the next field, but it does not allow input. This happened in IE8, not in Firefox.
Edit: Actually, when I went back to the full page and removed the CalendarExtender, it was still not working. I do suspect that this issue has to do with controls posting back in the UpdatePanel, but I just can't pin it down. It seems seems to be one of these things where a combination of x things does not work, while any combination of (x-1) things does work.
Regarding the initial question, here's a working sample. I don't know if it's anyhow helpful, but just to make sure...
<%# Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server"><title>Ajax Test</title></head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" />
<asp:UpdatePanel runat="server" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:Label runat="server" AssociatedControlID="txtTest">
Enter 'fruit' or 'vegetables':
</asp:Label>
<asp:TextBox
runat="server" ID="txtTest" AutoPostBack="true"
OnTextChanged="Handler_Test_TextChanged"
/>
<asp:Repeater runat="server" ID="rptItems">
<HeaderTemplate><ul></HeaderTemplate>
<ItemTemplate><li><%# Container.DataItem.ToString() %></li></ItemTemplate>
<FooterTemplate></ul></FooterTemplate>
</asp:Repeater>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
<script runat="server">
static readonly string[] Fruit = new string[]
{ "Apples", "Oranges", "Bananas", "Pears" };
static readonly string[] Veg = new string[]
{ "Potatoes", "Carrots", "Tomatoes", "Onion" };
void Handler_Test_TextChanged(object s, EventArgs e)
{
if(txtTest.Text == "fruit") rptItems.DataSource = Fruit;
else if(txtTest.Text == "vegetables") rptItems.DataSource = Veg;
else return;
rptItems.DataBind();
}
</script>

Categories