Container.RetrieveItems() calls a service which takes a while so I would like to call it asynchronously (after the items are retrieved they are set to the List property of the Container class). Upon completion of retrieving the items, I would like it to update a gridView which is inside an updatePanel (updatePanel Mode="Conditional" and ScriptManager EnablePartialRendering="true". UpdatePanel has no trigger items).
I have set breakpoints and stepped through each step. The items are retrieved, grid is databound then it calls update. No exceptions are being thrown but the grid is not updating with the content. If I set the UpdatePanel to update using a trigger and Timer.OnTick event it works perfect, however I only need it to update after the items are retrieved so firing the manual UpdatePanel.Update() upon completion of the service call would be ideal.
I have done quite a bit of searching, but all the answers are 'You forgot to call DataBind()'
Is there anything I am missing?
private void UpdateGrid()
{
grid.DataSource = Container.List;
grid.DataBind();
updatePanel.Update();
}
protected void Page_Load(object sender, EventArgs e)
{
var task = Task.Factory.StartNew(Container.RetrieveItems);
task.ContinueWith((x) => UpdateGrid());
}
Update: I set up a simpler test to try to identify the issue. I created a label whose Text property would be updated upon completion of a method. When the page loaded, it called the method and when the method finished it called updatePanel.Update() but no change.
Per Jaimes' advice, I then tried calling the manual update inside the postback of a Button_click and it did indeed update the label. So that is why my current setup is not working, although I'm still searching for the best way to update content on completion of an asynchronous task.
Jamie is on the right track. Your server cannot "push" new data to the client after the page is rendered. The client has to initiate a request. I would set a timer or use a JavaScript setTimeout
on the client to regularly poll the server to see if it completed.
Web Service Approach
Another approach is to set up a web service on the server, then call it from your page. This will be executed in a new thread and update the grid asynchronously as soon as results are available.
First you need to set up a WCF Web Service. There are thousands of articles about this if you aren't sure. Here is one of them: http://www.codeproject.com/Articles/16973/Simple-Web-Service-using-WCF-Windows-Communication
Here is some sample code from one of my projects:
namespace UI.Web.WebServices
{
[ServiceContract(Namespace = "WebServices")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class WebServiceName
{
[OperationContract]
[WebInvoke(ResponseFormat = WebMessageFormat.Json)]
public FacilityResult[] MethodName()
{
FacilityService facilityService = new
return facilityService .GetAssignments()).ToArray();
}
}
}
Calling the web service from the client
Next you will need to databind the grid from your page, using JavaScript: http://blog.ashmind.com/2007/06/21/client-side-databinding-with-aspnet-ajax-futures/
Here is some sample code from the same project. This project uses jQuery and not pure JavaScript like the samples in the links:
<script type="text/javascript">
function getGridData() {
var payload = { }; // Put data to include here, if any
$.ajax({
type: "POST",
url: "/WebServiceName.svc/MethodName",
data: JSON.stringify(payload),
contentType: "application/json",
dataType: "json",
success: function (msg) {
bindResultsToGrid(msg.d);
},
error: function (xhr) { alert('error' + xhr.responseText); }
});
}
function bindResultsToGrid(data)
{
...
}
</script>
I wanted to share my full solution for this problem. While msigman's answer was a big help it did not get me all the way there. I have not found a viable solution to binding to a GridView from the client-side, so instead I am creating the grid from javascript.
1 - Setup Controller to handle request
The MVC controller can be called from client-side via AJAX which allows you to call any .NET code from the client side. This is a key part as all the power and heavy-lifting can be done by the controller/service, but you still have the flexibility of javascript. I posted a full example of how you can do this here, Call ASP.NET function from JavaScript? . (Can be used with Webforms or MVC).
public class TimersController : ApiController
{
public HttpResponseMessage<TimerDisplay[]> Get()
{
return new HttpResponseMessage<TimerDisplay[]>(
TimerRepository.Get(), // all your .NET logic can be encapsulated here
new MediaTypeHeaderValue("application/json")
);
}
}
2 - Initiate the request with AJAX
This can be done in the when the page loads by handlding the $(document).ready() event, on a button-click, or periodically with a timer.
$.ajax({
url: "api/timers",
dataType: "json",
success: function (result) { bindGrid(result); }
})
3 - Bind the data to a grid
As I stated, I have not found a way to bind data to a GridView from the client-side. I feel the better solution is to use drop the GridView altogether (this is more a server-side control and not flexible for this type of situation), and in it's place you can generate the grid from Javascript. While you can manually loop through the data and just create the <tr>
and <td>
yourself, there are quite a few grid / ui frameworks that can do this for you. One such UI framework which has a nice grid object which you can directly bind JSON data to is Kendo UI (http://demos.kendoui.com/web/grid/index.html) (among many other useful widgets). This SO post contains more suggestions on javascript UI frameworks https://stackoverflow.com/questions/159025/jquery-grid-recommendations .
function bindGrid(gridData) {
var grid = $("#grid");
grid.html("");
grid.kendoGrid({
columns: [
{ field: "Place", title: "Place" },
{ field: "Time", title: "Time" }
],
dataSource: {
data: gridData
},
scrollable: false
});
}
来源:https://stackoverflow.com/questions/10238215/call-net-method-asynchronously-and-bind-to-grid-upon-completion