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

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

Related

Full postback on select & remove within update panel

I'm having issues with the asp.net textbox within an update panel. It works perfectly fine when adding or removing each individual character but if I highlight all of the text within the textbox, and then remove it a full postback occurs, not a partial postback which is expected.
Why is this happening? I haven't found anything related to this particular problem so it's likely I'm doing something wrong.
Example aspx:
<asp:UpdatePanel ID="updExample" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:Repeater ID="rptExample" runat="server" .... >
<ItemTemplate>
<asp:TextBox ID="txtExample" runat="server" ClientIDMode="static" Text='<%# Eval("Example") %>' OnTextChanged="txtExample_TextChanged" AutoPostBack="true"></asp:TextBox>
</ItemTemplate>
</asp:Repeater>
</ContentTemplate>
</asp:UpdatePanel>
Example TextChanged Event:
protected void txtExample_TextChanged(object sender, EventArgs e)
{
updExample.Update();
}
Additional Notes:
Switching UpdateMode to 'Always' doesn't work.
Karthikeyan Nagaraj pointed out in the comments to try adding triggers alongside what I already had. I did in fact already have this, however, I was assigning the trigger in the ItemDataBound event which I realized was incorrect after reinvestigating. The ItemCreated event was much more suited.
I had no issues finding the control in the ItemCreated event, however adding a new async postback trigger to the update panel gave me grief and said the control couldn't be found when changing the text. To resolve this, I used the script managers RegisterAsyncPostBackControl(); method as shown below.
protected void rptExample_ItemCreated(object sender, RepeaterItemEventArgs e)
{
var input = e.item.FindControl("txtExample");
if (input != null) {
ScriptManager sm = ScriptManager.GetCurrent(this);
sm.RegisterAsyncPostBackControl(input);
}
}

Nested UpdatePanel refresh parent

I have a case that I'm using an update panel that when updated it will load a user control containing a child update panel, and its code is as follows:
<asp:UpdatePanel runat="server" ID="Parent" UpdateMode="Conditional" ChildrenAsTriggers="false" >
<ContentTemplate>
<asp:PlaceHolder runat="server" ID="ChildUPNL"></asp:PlaceHolder>
</ContentTemplate>
</asp:UpdatePanel>
In the code behind I got an object then use the Update method to refresh the parent update panel
Then It' should have my user control filled with data
in the user control I have an update panel also like:
<asp:UpdatePanel runat="server" ID="Child" OnLoad="ChildLoad">
<ContentTemplate>
<asp:LinkButton Text="Link button" runat="server" />
<asp:Button Text="Button" runat="server" OnClick="ButtonClick"/>
</ContentTemplate>
</asp:UpdatePanel>
When I click on the link button the full page reloaded even the parent and my user control disappered as it not in the page direct
and if I click on the normal button nothing done !
didn't go to the update panel on load event nor the button click event !
Any idea why this action done !
I need when click on button to change some values or save values to database so I need to go throw the server side code without post back the page
You have to load every time usercontrol on page_init or page_load. You can use MultiView to achieve your goal.
You can also use Events and delegate and call custom event and bind parent control after click on child panel button.
**//On Page Behind Code**
protected void Page_Load(object sender, EventArgs e)
{
UserControl11.EventCallfromOtherUserControl += new UserControl1.CallfromOtherUserControl(CallEvent);
}
public void CallEvent()
{
//Load Parent Control
}
**//On User Control Behind Code**
public delegate void CallfromOtherUserControl();
public event CallfromOtherUserControl EventCallfromOtherUserControl;
protected void Button1_Click(object sender, EventArgs e)
{
if (EventCallfromOtherUserControl != null)
{
EventCallfromOtherUserControl();
}
}

JScript runtime error when using UpdatePanel

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

AJAX Update Panel - Action button click on drop down selection

Evening all
I have the following scenarion. I have a range of drop down menus where a clietn can select. The code below is wrapped in an update panel but without this, the button click fires a method to retrieve the number of products. So for example, an item is selected from ddlCategory, the btnValidate is clicked and the label returns the number of products within that category.
I have the following code for an update panel - I'm just not sure how to implement if effectively.
<asp:UpdatePanel ID="UpdatePanel1" runat="Server">
<ContentTemplate>
<asp:Label ID="lblSearchResultsStatus" runat="server" Text="Number of results found: "></asp:Label>
<asp:Label ID="lblSearchResults1" runat="server" Text=""></asp:Label>
<br />
<br />
<asp:Button ID="btnValidate" runat="server" Text="Validate Search"
OnClick="btnValidate_Click" Width="120px" />
</ContentTemplate>
</asp:UpdatePanel>
How do I go about wiring the update panel so that when a drop down list item is selected, the buttong is effectively clicked?
Do I have to implement something on each of the ddlSelectedIndexChanged event or is there a property within the update panel that does?
Apologies for the noob question.
The point of the UpdatePanel is to update a portion of the page with an AsyncPostBack instead of reloading the whole page, but in order for the drop-down lists to trigger an AsyncPostBack automatically, they must be on an UpdatePanel. In order to update the labels, they must be on the same UpdatePanel with the labels.
A common pattern to implement what you want to accomplish:
Put the DDLs on the UpdatePanel, and set AutoPostBack="true" on each DDL, so they trigger AsyncPostBacks.
Add an event handler for SelectedIndexChanged on each DDL (can be the same event handler).
Move whatever you do in btnValidate_Click to another method.
Call the new method from btnValidate_Click and the SelectedIndexChanged event handler(s) so they all perform the same function.
You can call your btnValidate_Click event from codebehind at any point, i.e. Page_Load
protected void Page_Load(object sender, EventArgs e)
{
btnValidate_Click(btnValidate, new EventArgs());
}

How Do I Get a Dynamic Control's Value after Postback?

I have a listview that adds controls in the ItemDataBound Event. When the postback occurs, I cannot find the new controls. After a bit of research, I found that ASP .NET needs these controls created every time, even after postback. From there I moved the function to bind the ListView outside of the if (!Page.IsPostBack) conditional. Now I get the dynamic controls values but the static controls I have are set to their defaults. Here is a sample of what I am trying to accomplish:
For brevity, I left some obvious things out of this example.
<asp:ListView runat="server" ID="MyList" OnItemDataBound="MyList_ItemDataBound">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</LayoutTemplate>
<ItemTemplate>
<asp:PlaceHolder runat="server" ID="ProductPlaceHolder">
<asp:TextBox runat="server" ID="StaticField" Text="DefaultText" />
<asp:PlaceHolder ID="DynamicItems" runat="server" />
</asp:PlaceHolder>
</ItemTemplate>
</asp:ListView>
and here is the codebehind:
protected void MyList_ItemDataBound(object sender, System.Web.UI.WebControls.ListViewItemEventArgs e) {
PlaceHolder DynamicItems = (PlaceHolder)e.Item.FindControl("DynamicItems");
DynamicItems.Controls.Add(textbox);
}
So, like I said, if I only databind when Page != PostBack then I cant find my dynamic controls on postback. If I bind every time the page loads then my static fields get set to their default text.
Try moving the data binding of the ListView into the OnInit() event.
Very similar question (instead of populating a ListView the guy is generating a set of buttons). Briefly, you'll find that you have to store the items in the list in your Viestate - than fish it out on Postback and re-populate the list.
Note that this solutions implies dropping data-binding (which you might not wanna do for others reasons).
Hope it helps.

Categories