Telerik RadListView inside asp.net update panel does not refresh - c#

I've been banging my head against the wall for the last couple of days trying to get this scenario to work.
I have an <asp:UpdatePanel> on a page which has an external trigger and contains a Telerik RadListView:
<asp:UpdatePanel runat="server" ID="RationUpdatePanel" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<telerik:RadListView ID="RationListView runat="server"
DataSourceId="RationDataSource"
OnItemCanceling="ItemCancelingHandler"
OnItemEditing="ItemEditingHandler"
OnItemUpdating="ItemUpdateHandler">
<LayoutTemplate>
<table>
<thead>
<tr>...</tr>
</thead>
<tbody>
<tr id="itemPlaceHolder" runat="server"/>
</tbody>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<asp:LinkButton ID="EditButton" runat="server" CausesValidation="False" CommandName="Edit" Text="Edit" ToolTip="Edit" />
<asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False" CommandName="Delete" Text="Delete" ToolTip="Delete" />
</td>
<td>...</td>
</tr>
</ItemTemplate>
<EditItemTemplate>
<tr>
<td>
<asp:LinkButton ID="SaveButton" runat="server" CausesValidation="False" CommandName="Save" Text="Save" ToolTip="Save" />
<asp:LinkButton ID="CancelButton" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel" ToolTip="Update" />
</td>
<td>...</td>
</tr>
</EditItemTemplate>
</telerik:RadListView>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="UseRationButton" EventName="Click" />
</Triggers>
</UpdatePanel>
Code behind looks like this:
protected void RationListView_ItemEditing(object sender, RadListViewCommandEventArgs e)
{
((RadListViewDataItem)e.ListViewItem).Edit = true;
}
protected void RationListView_ItemCanceling(object sender, RadListViewCommandEventArgs e)
{
RationListView.ClearEditItems();
((RadListViewDataItem)e.ListViewItem).Edit = false;
}
Clicking on the EditButton or CancelButton produced the expected events and the ObjectDataSource routine to rebind the list is called, yet the update panel is not refreshed. Calling RationUpdatePanel.Update() from the event handlers does not change the situation. The code works properly outside of an update panel: the mode changes and the buttons from the other template are displayed.
In either scenario clicking on the external trigger causes the list to be displayed.
Update: I added a couple of javascript callbacks to the PageManager class in the browser for beginRequest and endRequest. These just display an alert when the event occurs.
The page contains several elements which basically look like this (details omitted):
<div>
<div>
<asp:DropDownList ID="RationDdl"/>
<asp:Button ID="UseRationButton"/>
</div>
<div>
<asp:DropDownList ID="AnimalGroupDdl"/>
<asp:UpdatePanel ID="AnimalWeightUpdatePanel"/>
<ContentTemplate>
<asp:DropDownList ID="AnimalWeightDdl"/>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</div>
<div>
<telerik:RadListBox>
</telerik:RadListBox>
</div>
<div>
<asp:UpdatePanel ID="FeedPreviewUpdatePanel">
<ContentTemplate>
<asp:Repeater>
</asp:Repeater>
</ContentPanel>
</asp:UpdatePanel>
</div>
<div>
<!-- this contains the update panel shown above -->
</div>
The UseFeedRationButton in the first div is an external trigger for the RationUpdatePanel. AnimalGroupDdl is an external trigger for AnimalWeightUpdatePanel. The handler for the SelectedIndexChanged event on AnimalGroupDdl populates AnimalWeightDdl with available weight classes for the
selected group. It has no interaction with RationUpdatePanel or FeedPreviewUpdatePanel.
Here is the markup for UseRationButton:
<asp:Button ID="UseRationButton" runat="server"
Text="Use This Ration"
OnClick="UseRationButton_Click"
CausesValidation="true"
ValidationGroup="UseRation"
UseSubmitBehavior="false">
</asp:Button>
And here is the code behind:
protected void UseRationButton_Click(object sender, EventArgs e)
{
string text = this.RationDdl.SelectedItem.Text;
this.RationNameLabel.Text = text;
this.RationDataSource.SelectParameters["RationId"].DefaultValue = this.RationDdl.SelectedValue;
}
There are two scenarios I have been testing:
1) Choose a ration from RationDdl and click UseRationButton
2) Choose an animal group and then execute scenario 1.
In scenario 1 an async postback is initiated by the client side asp.net code and the button event is handled server side. The RationListView is populated but the client side endRequest code is never executed (the alert is not displayed). Subsequent clicks on the controls in the RadListView trigger an async postback but the state of the list is not changed and the client side endRequest code is not executed.
In scenario 2 an async postback is initiated, the event is handled on the server side and AnimalWeightDdl is populated. The client side endRequest code is executed in this case (the alert IS displayed). Subsequent to this selecting a ration and clicking on the use ration button initiates a postback, the list view is populated and the endRequest code is executed. At his point clicking on buttons in the list view causes an async postback to be initiated, the state of the list is changed and the endRequest code is executed.
My working theory is that something is causing the state of the page request manager class on the client to become fubared, but I can't figure out what might be causing it.

Firstly , If you use telerik rad control , I suggest you to change your asp:button to telerik:RadButton .
Here is example
<telerik:RadButton ID="v" runat="server" Text="Use This Ration"
AutoPostBack="true"
ValidationGroup="UseRation" OnClick="UseRationButton_Click">
</telerik:RadButton>
protected void UseRationButton_Click(object sender, EventArgs e)
{
string text = this.RationDdl.SelectedItem.Text;
this.RationNameLabel.Text = text;
this.RationDataSource.SelectParameters["RationId"].DefaultValue = this.RationDdl.SelectedValue;
}
Note that , you should add AutoPostBack="true" into your radButton .
And this links , MSDN Reference1 and MSDN Reference2 will show details of using update Panel .

The research I did late Friday and Saturday morning provided the answer to this one.
I had some code attached to the Sys.WebForms.PageRequestManager endRequest event which updated a text box which contained a temporary title for a new ration. The code looked like this:
///
/// reregister the change handlers after reloading the animal weight ddl and create a
/// temporary name for the ration based on animal choice and weight.
///
function animalWeightDdlOnLoad() {
var ddl = document.getElementById("<%= AnimalWeightDdl.ClientID %>");
//
// add event handler in a browser agnostic fashion
//
if (ddl.addEventListener) {
ddl.addEventListener("change", animalWeightDdlOnChange);
}
else if (ddl.attachEvent) {
ddl.attachEvent("onchange", animalWeightDdlOnChange);
}
else {
ddl.onchange = animalWeightDdlOnChange;
}
animalWeightDdlOnChange();
}
animalWeightDdlOnChange() referenced the options list of the animal weight ddl:
///
/// create a temporary name for the ration
///
function animalWeightDdlOnChange() {
var ddl1 = document.getElementById("<%= AnimalGroupDdl.ClientID %>");
var ddl2 = document.getElementById("<%= AnimalWeightDdl.ClientID %>");
var text = document.getElementById("<%= RationNameText.ClientID %>");
text.value = ddl1.options[ddl1.selectedIndex].text + ' ' + ddl2.options[ddl2.selectedIndex].text + ' - Copy';
}
However if the list had not yet been populated the reference to options caused a fault which halted endRequest processing. Rather than having this code run on every async request I changed it to be a handler for the change event on the animal group ddl, like this:
function animalGroupDdlOnSelectedChanged() {
//
// add a handler for the endRequest event which will be triggered by
// fetching the contents of the weight ddl.
//
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(animalWeightDdlOnLoad);
}
Then in animalWeightDdlOnLoad() I added a line to remove the handler once the request had
run. This ensured that the change handler for the weight ddl would only run once the list had been populated. Problem solved. But just in case, I added a check on the length of the animal weight ddl's options list.
So even seemingly innocent javascript can mess up your async request handling in the browser.

Related

Why is my ListView losing data on postback?

I'm sure there's a really simple explanation for this but I've been tearing my hair out too long now so I appeal to SO for the sake of my sanity!
It's this simple: I have a ListView which is bound when the page first loads (and not again on postback). It's part of a form. On the first load I see the list of items but when I postback I see the EmptyDataTemplate. Is this correct behaviour? I'm sure that ListView as a data binding control should persist its list over postbacks. Here's the code, first front end:
<asp:ListView ID="boxes" runat="server" ItemType="Model.Generic.ILookupEntity<int>" EnableViewState="true">
<LayoutTemplate>
<div>
<ul class="contact-list checkbox-row">
<li id="itemPlaceholder" runat="server" />
</ul>
</div>
</LayoutTemplate>
<ItemTemplate>
<li>
<label>
<asp:CheckBox ID="cb" runat="server" />
<asp:HiddenField ID="value" runat="server" Value="<%# Item.Key %>" />
<span><%# Item.Value %></span>
</label>
</li>
</ItemTemplate>
<EmptyDataTemplate>
<p>No data</p>
</EmptyDataTemplate>
</asp:ListView>
<asp:CheckBoxList ID="boxtemp" runat="server" />
Then back end:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!IsPostBack)
{
var items = new List<Model.Generic.LookupEntity<int>>()
{
new Model.Generic.LookupEntity<int>(1, "One"),
new Model.Generic.LookupEntity<int>(2, "Two"),
new Model.Generic.LookupEntity<int>(3, "Three"),
new Model.Generic.LookupEntity<int>(4, "Four"),
};
boxes.DataSource = items;
boxes.DataBind();
boxtemp.DataSource = items;
boxtemp.DataTextField = "Value";
boxtemp.DataValueField = "Key";
boxtemp.DataBind();
}
}
I've added the boxtemp CheckBoxList as a test. This one retains its data on postback. But the ListView doesn't. What am I doing wrong here?
UPDATE 1
What's even weirder is that if I add the following code and debug, the variable test references the value 4 even on postback. But when the posted page is rendered I still see the EmptyItemTemplate.
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
var test = boxes.Items.Count;
}
UPDATE 2
Having had it confirmed that this is not expected behaviour I have now found the cause I think. The ListView is inside a user control which is calling this.DataBind() on PreRender. I presume this is causing all data bound controls inside to re-bind on postback. The weird thing is that if I handle the ListView's OnDataBinding and OnDataBound events, on post back I can observe that the list still contains 4 items during both events. But is still rendered displaying the EmptyDataTemplate. Given this scenario, and assuming that the parent control's DataBind call is required, can anyone suggest the best resolution?

Refresh ASP.NET ListView from Event handler

So, I have an event that is fired always when a text file is updated. This is the event handler:
private void FileWasChanged()
{
this.runsList.Items.Clear();
runningModels = RunsFile.ReadFile(Constants.active_runs_loc, Constants.run_file_name);
this.runsList.DataSource = runningModels;
this.runsList.DataBind();
this.updatePanel.Update();
}
Within this method I am clearing the ListView (runsList), reading the contents of the file and then binding the new data to the ListView. The updatepanel is then updated. But that does not happen, the page stays static until I press F5. Here's the layout:
<section>
<asp:ScriptManager ID="scriptManager1" runat="server" ></asp:ScriptManager>
<asp:UpdatePanel id="updatePanel" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:ListView ID="runsList" runat="server">
<LayoutTemplate>
<ul>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li>
<asp:Label Text="<%# Container.DataItem %>" runat="server" />
</li>
</ItemTemplate>
</asp:ListView>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger controlid="runsList"/>
</Triggers>
</asp:UpdatePanel>
</section>
So I am trying to give the user of the web site real time information from the text file. For example, when I change a line in the text file, that line should be added to the web page. The event itself works fine (tested with break points and console project), but for some reason the async update does not do anything.
I tried to look for similar problems, but everyone is using a button for the asyncpostbacktrigger. However, I am very new to C# so I may be misunderstanding how UpdatePanel and ListView DataBind work.
Any help appreciated!
Cheers,
Tetsii
Edit: The FileWasChanged is a handler that is called from FileSystemWatcher OnChanged event. The handler updates the view if called from Page_Load or an async button trigger.

Fail to continously add new control into Panel/Placeholder during partial postback

I want to create a dynamic form where user has a button to click on it.
When the button clicked, it will trigger a partial postback and add a new control into a placeholder.
My problem is, when the button is clicked for the first time, it able to create a new control in the placeholder. But it will not create more than that even though i have clicked the button for few times.
<asp:ScriptManager ID="ScriptManager1" EnablePartialRendering="true" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Button ID="Button1" runat="server" Text="Add images" OnClick="Button1_Click" />
<asp:HiddenField ID="HiddenField1" runat="server" Value="1" />
<br />
<asp:Panel ID="PlaceHolder1" runat="server">
<asp:FileUpload ID="FileUpload1" runat="server" />
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
Code behind:
protected void Button1_Click(object sender, EventArgs e)
{
PlaceHolder1.Controls.Add(new FileUpload());
}
Unless a dynamically added control is added at the preinit or init stage, it does not persist beyond a post back. So when the button is clicked for the second time, the first added control is lost and then the button logic adds a control again, leaving you with one control every time. For a control added after the init stage, you would need to store its state and recreate it on each postback. This is described int this article:
http://www.codeproject.com/Articles/3684/Retaining-State-for-Dynamically-Created-Controls-i
You may also be able to achieve your goals without using dynamically added controls. One possible approach is to use a ListView control, add a FileUpload control to its ItemTemplate and add a new record to the list view data source every time the button is clicked.

PostBackTrigger on multiple LinkButtons in an update panel

I have a search form in an updatePanel which retrieves a list of users in a grid in the same UpdatePanel. The name of each user is a commandLink. I want to make the commandLinks as PostBackTriggers.
But when I do it I get an error at the pageLoad time that the controlId does not exist and its true because the grid of users does not render at the load time but through an ajax call.
Any ideas on how can I make the multiple command buttons in a grid retrieved through ajax call as post back triggers?
When adding the items to the grid, within the ItemDataBound event handler, you should register the postback for each specific control (the static identifiers in your HTML declarations are essentially placeholders - not all things repeated in the grid can actually have the same ID). You do this using the ScriptManager.RegisterAsyncPostBackControl method:
The RegisterAsyncPostBackControl method enables you to register Web
server controls as triggers so that they perform an asynchronous
postback instead of a synchronous postback. When the
ChildrenAsTriggers property of an UpdatePanel control is set to true
(which is the default), postback controls inside the UpdatePanel
control are automatically registered as asynchronous postback
controls.
As stated above, using ChildrenAsTriggers is a possibility, too, but this is commonly set to false for more stringent management.
I have found the solution. Here is the code on asp
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox>
<asp:Button ID="btnSearch" runat="server" OnClick="btnSearch_Click" Text="Search" />
<asp:GridView ID="gvSearchResult" runat="server" OnRowCommand="gvSearchResult_RowCommand"
OnRowDataBound="gvSearchResult_RowDataBound">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:LinkButton ID="lnkbtnDetail" runat="server" CommandArgument='<%# Bind("CNIC") %>' CommandName="Detail">
<asp:Label ID="lblName" Text='<%# Bind("Employee_Name") %>' runat="server</asp:Label>
</asp:LinkButton>
</ItemTemplate>
<ItemStyle HorizontalAlign="Left" VerticalAlign="Middle"Height="25px"Width="30%" />
</asp:TemplateField>
</Columns>
</asp:GridView>
Ihad to place OnRowDataBound="gvSearchResult_RowDataBound" on gridView and that function looks like below. So I had to register the iterative control in Scriptmanager as PostBackControl in RowDataBound event of GridView.
protected void gvSearchResult_RowDataBound(object sender, GridViewRowEventArgs e)
{
try
{
if ((e.Row.RowType == DataControlRowType.DataRow))
{
LinkButton lnkbtnDetail = (LinkButton)e.Row.FindControl("lnkbtnDetail");
ScriptManager.GetCurrent(this).RegisterPostBackControl(lnkbtnDetail);
}
}
catch (Exception ex)
{
}
}

C# codebehind not getting hit when button clicked

<asp:UpdatePanel runat="server" ID="userRemovalUpdatePanel">
<ContentTemplate>
<p><label>Remove: </label>
<asp:DropDownList runat="server" ID="removeUserList" /></p>
<br />
<asp:Button runat="server" ID="removeUserBtn" Text="Remove User"
onclick="removeUserBtn_Click" CssClass="buttons" />
<p><label for="deleteStatus">Delete status: </label></p><br />
<asp:Label runat="server" ID="deleteStatusLbl" Text="" Font-Size="Medium" Width="100" ForeColor="Red" />
</ContentTemplate>
</asp:UpdatePanel>
I put a break point on the below code behind and it never gets hit. The code behind for this is:
protected void removeUserBtn_Click(object sender, EventArgs e)
{
string userToDelete = removeUserList.SelectedValue;
Business.User deleteUser = new Business.User();
deleteStatusLbl.Text = deleteUser.DeleteUser(userToDelete);
fillUserDropDown();
}
It's not: onclick="removeUserBtn_Click"
It is: OnClick="removeUserBtn_Click"
Do you have any validators on the page? Also place the Application_Error event in your Global.asax and try to log any errors that take place.
Validators can prevent the postback and behave strangely with update panels sometimes.
Do you have any code running in Page_Load()?
Code here runs before any event handlers are fired, so if something was interrupting your page loading here, say redirecting to another page etc. the click handler would never be fired.
The page lifecycle is described here.
Page_PreRender() is an alternative place to put code that you want to run after the event handlers fire.

Categories

Resources