The Concept of Callbacks

http://www.devexpress.com/Support/Center/kb/p/K18387.aspx

Article Details
Solution
Example: ASPxGridView - how to update an external control during a callback
Example: How to load a file on the callback of the ASPxGridView using the ASPxWebControl.RedirectOnCallback method
Article Details
ID: K18387
Version(s): all
Updated: 2011/4/3
 Product Group: .NET (ASP.NET)
Product: n/a

Description:
While using DX controls, people usually face the following issues:
1. I have two ASPxGridView controls. How do I refresh the second grid when the focused row within the first grid is changed?
2. When I click a button, the ASPxCallbackPanel refreshes a part of the page. How do I disable the button after the callback?
3. In the ASPxLabel, I need to show information that the ASPxTreeList has been switched to edit mode...
4. Do DX callbacks differ from AJAX callbacks?
and many other...

This Knowledge Base article provides information regarding the callbacks nature.

Solution
Standard ASP.NET controls don't use callbacks, and all their internal processing is performed via postbacks only. This approach is good when a page content is simple, so a browser can download the light page and render it quickly. However, when web sites contain many heavy-weight controls, this approach isn't suitable, because it might reduce page performance. When the Microsoft Atlas (ASP.NET AJAX) framework was introduced, a new UpdatePanel control was provided. This control intercepts postbacks that occur within a page, and converts them to asynchronous postbacks. During an asynchronous postback, the AJAX only renders a panel that initiated the postback, for example, if you have three asp:UpdatePanel controls, and one of them causes the postback, only this panel will be rendered.

Callbacks differ from asynchronous postbacks even if they provide almost the same results (in case of callbacks, only part of a page is refreshed).

The main difference is how the ASP.NET page life cycle changes based on whether postbacks and callbacks are used. When a postback/callback occurs, the ASP.NET builds a page from scratch, by raising the following events: Page_Init and Page_Load. Between these events, the ViewState and post data (Postback Data) are loaded. During a callback, a control (that is "in callback" state) raises the callback event, and then obtains the callback result.

The callback result always contains a part of a page (HTML tags, scripts, styles, etc.), the part that was updated. As compared with the AJAX behavior, the callback result is very similar to what the asp:UpdatePanel returns from the server when an asynchronous postback is processed. As the page life cycle during callbacks is shorter than during postbacks, you should always remember that the ViewState isn't updated on callbacks, as well as the PreRender event isn't raised on callbacks, too.
These two points aren’t usually taken into account when a user moves from asp:UpdatePanel controls to the ASPxCallbackPanel ones.

Note:
Most of "large" DevExpress ASP.NET controls, such as ASPxGridView, ASPxTreeList, ASPxPivotGrid, use their own callbacks (since the EnableCallBacks property equals "true") to update their own rendering during a round-trip to the server, which is intended to change a corresponding control's state.

It is, however, possible to disable the controls' callbacks by setting the EnableCallBacks property to "false", to force the controls to work in the standard postback mode (in the same manner as the standard controls work), and put the controls, for example, into the UpdatePanel, (as it is illustrated in the ASP.NET AJAX - Update Panel Online Demo), so that the controls can work with MS AJAX / ControlTookit together.

During a partial update, the result obtained from the server contains HTML page markup, which will be placed in the updated area. For example, you have an ASPxButton, a label within the asp:UpdatePanel and a label outside the asp:UpdatePanel. On the button's click event, you want to update both labels. What will be the result? On the server side, two labels will be updated. However, as it is stated above, the result obtained from the server will have the ASPxButton and the first label. So, after a postback, you'll get the updated first label only.

This statement can be easily applied to the ASPxCallbackPanel: assume you have both the ASPxButton and a label outside the ASPxCallbackPanel, and a single label within the callback panel. The button has the ASPxButton.AutoPostBack property disabled, and the callback panel is refreshed using the ASPxClientCallbackPanel.PerformCallback method:

[JScript]

function OnClick (s, e) {
   callbackPanel.PerformCallback ();
}

On the server side, both labels will be updated using the following ASPxCallbackPanel.Callback event handler:

[C#]

protected void callbackPanel_Callback(object source, CallbackEventArgsBase e) {
    lblInside.Text = "Inside";
    lblOutside.Text = "Outside";
}
[VB.NET]

Protected Sub callbackPanel_Callback(ByVal source As Object, ByVal e As CallbackEventArgsBase)
    lblInside.Text = "Inside"
    lblOutside.Text = "Outside"
End Sub
According to the declared statement, the content within the ASPxCallbackPanel will be updated. After a callback, you'll see the inner label updated, not the outer one.

These examples are simple, but they describe the main idea of partial updates. When other callback controls, such as the ASPxGridView or ASPxTreeList, are used, all these principles can be applied to them. Assume there is an ASPxGridView with a focused row turned on, and two labels on a page. One label is placed in the grid's Title Panel and the other one is placed outside the grid's bounds. A user wants to refresh the labels in the server-side ASPxGridView.FocusedRowChanged event handler. What result will he or she get?
The label outside the grid's bounds won't be updated. However, the label in the Title Panel will be. When a callback occurs, the ASPxGridView refreshes itself, changes the focused row index, and then modifies both labels. However, due to the callback, the grid renders itself (with a single refreshed label only) and sends the HTML page markup to the client side. After that, a user will see the single label being updated only.

These examples are in effect for multiple controls. For example, the following issues have almost the same explanation as the one described above:
1. There are two grids. On the server-side ASPxGridView.FocusedRowChanged event handler, the second grid gets refreshed. Why isn’t the second grid changed even though the code works fine?
This happens because the first grid sends a callback, and the callback result only has the grid that initiated the callback.
2. Why can't I change page layout in the ASPxComboBox.Callback event handler?
The event handler is able to change the current combobox only. Especially, it is used to bind the combobox with a datasource. It is impossible to change page layout of other controls.
3. Why does the ASPxCallback.Callback event handler update neither grids nor even a button? Is it a bug?
The ASPxCallback control is able to send callbacks to the server. It sends a string argument, and returns a string result. The control doesn't refresh a page. Use the ASPxCallbackPanel instead.
4. I have a master-detail grid on the page. The master grid shows the number of detail records in unbound columns. I want to refresh the master grid when a detail row is deleted. Why doesn't the DataBind method work in the ASPxGridView.RowDeleting event handler?
When a row is deleted, the detail grid sends a callback to the server. On its callback, it is possible to update the detail grid only. When you call the DataBind method of the master grid, the callback result doesn't contain the updated layout. That is why you can update the detail grid, but not the master one.

All these and similar issues have an explanation of why they occur. However, there are many solutions that can be easily implemented even when callbacks are used:
1. A general solution for all these tasks is to turn off callbacks. Of course, a control won't work using the callbacks technology, but as an entire page is refreshed, it will be possible to do whatever is required. To avoid unnecessary postbacks, it is useful to wrap required controls in the asp:UpdatePanel.
2. Usually, it is enough to replace server-side code with its client-side equivalent. As DevExpress ASP.NET controls support Client-Side Functionality, it is better (and faster) to update/refresh controls on the client side:
For example, the mentioned labels can be updated using the client-side ASPxClientGridView.FocusedRowChanged event handler and the ASPxClientLabel.SetText method:

[JScript]

function OnFocusedRowChanged (s, e) {
   lblInner.SetText("Inner");
   lblOuter.SetText("Outer");
}

Note: usually it is useful to prevent the server-side data processing by adding the "e.processOnServer = false;" line into the JavaScript event handler (please check the appropriate documentation before writing this line; maybe, the "e" argument doesn't have this property).

3. If a page is simple, and the server-side code is executed fast, you can refresh controls by sending several subsequent callbacks. For example, if you wish to refresh the master grid, you can use the ASPxClientGridView.Refresh method. All you need to know is when this method should be called. Of course, it should be called when the detail grid callback ends (ASPxClientGridView.EndCallback). However, the master grid will always be refreshed. To avoid this, you should "tell" a client-side object when the Refresh callback should be sent, using the JSProperties:

[C#]

protected void detailGrid_RowDeleting(object sender, DevExpress.Web.Data.ASPxDataDeletingEventArgs e) {
   ASPxGridView detailGrid = sender as ASPxGridView;
   detailGrid.JSProperties["cpDelete"] = true;
}
[VB.NET]

Protected Sub detailGrid_RowDeleting(ByVal sender As Object, ByVal e As
DevExpress.Web.Data.ASPxDataDeletingEventArgs)
   Dim detailGrid As ASPxGridView = TryCast(sender, ASPxGridView)
   detailGrid.JSProperties("cpDelete") = True
End Sub
[JScript]

function OnDetailGrid_EndCallback (s, e) {
   if (s.cpDelete) {
        delete s.cpDelete;
        masterGrid.Refresh();
   }
}

In the EndCallback event handler, you can perform any necessary actions: show popups, update editors, implement cascading behavior, etc.

Usually, it is useful to determine the moment when a callback starts and ends. For such purposes, two event handlers are used: BeginCallback and EndCallback. All controls, supporting callbacks, are able to raise the events. These events are useful when you wish to determine the type of a callback by using the e.command attribute, or when you need to show and hide the ASPxLoadingPanel.

Sometimes, it might be useful to have a general callback handler: the ASPxGlobalEvents control. This is an invisible control that raises the same BeginCallback and EndCallback events when any control on a page initiates a callback.

The last point that should be mentioned when speaking about callbacks is that it is strongly recommended against sending parallel (or simultaneous) callbacks to controls that might use each other during (or after) a callback. What does it mean? Let's assume that you have three ASPxGridView controls, and wish to refresh them all:

[JScript]

grid1.Refresh();
grid2.Refresh();
grid3.Refresh();

This code should work fine, because three grids might not depend on each other. For example, when the first grid is updated, it uses its own datasource, the second grid uses its own one, and so on. However, if you want to change some values on the first grid's callback, and then read them on the second grid's callback, this approach will require additional coding. We call this scenario a "callbacks rush":

When the first callback is sent, the server starts to update the page, raising appropriate events. At the same time, the client-side version of the page still exists, and its next JavaScript line sends another callback. The server is a multi-thread system, and one of its threads may complete faster than another one. So, there isn't anything unusual, because if the first callback uses much data, and the second one doesn't, you'll get the second result faster than the first one.

To avoid this and similar situations, it is recommended to send subsequent callbacks. They function in the following way: the first callback is sent, and we wait until its result becomes available (in the EndCallback event handler); when the event is raised, the second callback is sent; when the second callback ends, the third callback is sent, and so on. An approximate code will be the following:

[JScript]

...
   grid1.PerformCallback();
}

function OnGrid1EndCallback (s, e) {
   grid2.PerformCallback();
}

function OnGrid2EndCallback (s, e) {
   grid3.PerformCallback();
}
...

Also, there might occur a situation when you try to use a control that isn't available on the page during callback processing. This is a typical scenario: previous versions of DX controls allowed hiding the ASPxCallbackPanel content during a callback. For example, if you send a callback to the callback panel, and then try to use the control during the callback, you might get the "Object required" exception. This might happen (not necessarily with the ASPxCallbackPanel, but with any control that sends callbacks) because during the callback, the page layout (which will be updated when the callback ends) is cleared, and all client-side objects (as well as the page markup) are deleted too, and can't be used. To avoid this or similar exceptions, simply wait until the callback ends.

Callbacks are interesting and quite helpful as compared to asynchronous postbacks. Unfortunately, in solving many issues that occurred in real projects, the callbacks nature wasn’t taken into account. If under certain conditions, a project doesn't work correctly with callbacks enabled, simply disable them and try to solve the task using UpdatePanel controls only. After that, you can use a similar solution with callbacks again.

See Also:
ASP.NET 2.0 Page Life Cycle
ASP.NET 2.0 Page LifeCycle by Peter A. Bromberg, Ph.D.
MSDN - ASP.NET Page Life Cycle Overview

Example: ASPxGridView - how to update an external control during a callback
          

 ID: E2379
Supported IDEs: Microsoft Visual Studio 2008, Microsoft Visual Studio 2010

Description:
By default, the ASPxGridView works during callbacks and there's no way to update an external control (that isn't a child control of the callback owner) on the server side. The following article describes this limitation in detail:

The Concept of Callbacks

However, the ASPxGridView as any other ASP.NET control has the JSProperties feature that allows passing a value from the server to the client. Also, the client-side EndCallback is raised each time when a callback is executed successfully.
So, it is possible to set a JSProperty on the server, get it on the EndCallback and change a "target" control using its client-side capabilities.

This example illustrates how to use the ASPxLabel to identify that the grid was successfully updated.

A similar approach my be used with any other DevExpress ASP.NET control that can send callbacks (ASPxCallback, ASPxCallbackPanel, ASPxTreeList, ASPxComboBox and so on).

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register assembly="DevExpress.Web.ASPxGridView.v10.1, Version=10.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
namespace="DevExpress.Web.ASPxGridView" tagprefix="dx" %>
<%@ Register assembly="DevExpress.Web.ASPxEditors.v10.1, Version=10.1.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
namespace="DevExpress.Web.ASPxEditors" tagprefix="dx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
     
    <dx:aspxgridview ID="ASPxGridView1" runat="server" AutoGenerateColumns="False"
        DataSourceID="AccessDataSource1" KeyFieldName="CategoryID"
        onrowupdated="ASPxGridView1_RowUpdated">
        <clientsideevents
EndCallback="function(s, e) {
 if (s.cpIsUpdated != '')
 {
    clientLabel.SetText('The category '+s.cpIsUpdated+' is updated successfully');
    clientLabel.GetMainElement().style.backgroundColor = 'green';
    clientLabel.GetMainElement().style.color = 'white';
 }
 else
 {
    clientLabel.SetText('');
 }
}"
/>
        <columns>
            <dx:gridviewcommandcolumn VisibleIndex="0">
                <editbutton Visible="True">
                </editbutton>
            </dx:gridviewcommandcolumn>
            <dx:gridviewdatatextcolumn FieldName="CategoryID" ReadOnly="True"
                VisibleIndex="1">
                <editformsettings Visible="False" />
            </dx:gridviewdatatextcolumn>
            <dx:gridviewdatatextcolumn FieldName="CategoryName" ReadOnly="True"
                VisibleIndex="2">
            </dx:gridviewdatatextcolumn>
            <dx:gridviewdatatextcolumn FieldName="Description" ReadOnly="True"
                VisibleIndex="3">
            </dx:gridviewdatatextcolumn>
        </columns>
    </dx:aspxgridview>
    <asp:accessdatasource ID="AccessDataSource1" runat="server"
        DataFile="~/App_Data/nwind.mdb"
        DeleteCommand="DELETE FROM [Categories] WHERE [CategoryID] = ?"
        InsertCommand="INSERT INTO [Categories] ([CategoryID], [CategoryName], [Description], [Picture]) VALUES (?, ?, ?, ?)"
        SelectCommand="SELECT * FROM [Categories]"
        UpdateCommand="UPDATE [Categories] SET [CategoryName] = ?, [Description] = ?, [Picture] = ? WHERE [CategoryID] = ?">
        <deleteparameters>
            <asp:parameter Name="CategoryID" Type="Int32" />
        </deleteparameters>
        <updateparameters>
            <asp:parameter Name="CategoryName" Type="String" />
            <asp:parameter Name="Description" Type="String" />
            <asp:parameter Name="Picture" Type="Object" />
            <asp:parameter Name="CategoryID" Type="Int32" />
        </updateparameters>
        <insertparameters>
            <asp:parameter Name="CategoryID" Type="Int32" />
            <asp:parameter Name="CategoryName" Type="String" />
            <asp:parameter Name="Description" Type="String" />
            <asp:parameter Name="Picture" Type="Object" />
        </insertparameters>
    </asp:accessdatasource>
    <dx:aspxlabel ID="ASPxLabel1" runat="server" ClientInstanceName="clientLabel">
    </dx:aspxlabel>
     
    </form>
</body>
</html>

In order to give you a simpler experience in managing and running our samples locally, we are supplying sample projects in special *.DXSAMPLE files. To handle them, we built a special tool for you called Example Runner, which automatically extracts downloaded projects into a specified folder and automatically runs them in the IDE you've chosen. Please download and install this tool first.

Example: How to load a file on the callback of the ASPxGridView using the ASPxWebControl.RedirectOnCallback method
          
Description:
By default, it is impossible to load files on callbacks:
The Concept of Callbacks
However, there's a solution that allows you to overcome this problem. Our ASPxWebControl class has the RedirectOnCallback method that allows redirect to another page during a callback.
In our situation, we can create a specific "download" page and call a method which loads a file in the Page_Load event handler of this page.
This sample shows how to load a file in the click of a custom button of the ASPxGridView.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ register Assembly="DevExpress.Web.v10.1, Version=10.1.7.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
    Namespace="DevExpress.Web.ASPxCallback" TagPrefix="dx" %>

<%@ register Assembly="DevExpress.Web.ASPxGridView.v10.1, Version=10.1.7.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
    Namespace="DevExpress.Web.ASPxGridView" TagPrefix="dx" %>
<%@ register Assembly="DevExpress.Web.ASPxEditors.v10.1, Version=10.1.7.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
    Namespace="DevExpress.Web.ASPxEditors" TagPrefix="dx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
 
        <dx:aspxgridview ID="ASPxGridView1" runat="server" AutoGenerateColumns="False" DataSourceID="AccessDataSource1"
KeyFieldName="EmployeeID">
            <columns>
                <dx:gridviewcommandcolumn VisibleIndex="0">                   
                    <custombuttons>
                        <dx:gridviewcommandcolumncustombutton Text="Load photo">
                        </dx:gridviewcommandcolumncustombutton>
                    </custombuttons>
                </dx:gridviewcommandcolumn>
                <dx:gridviewdatatextcolumn FieldName="EmployeeID" ReadOnly="True" VisibleIndex="1">
                    <editformsettings Visible="False" />
                </dx:gridviewdatatextcolumn>
                <dx:gridviewdatatextcolumn FieldName="LastName" VisibleIndex="2">
                </dx:gridviewdatatextcolumn>
                <dx:gridviewdatatextcolumn FieldName="FirstName" VisibleIndex="3">
                </dx:gridviewdatatextcolumn>
                <dx:gridviewdatadatecolumn FieldName="BirthDate" VisibleIndex="4">
                </dx:gridviewdatadatecolumn>
                <dx:gridviewdatatextcolumn FieldName="Address" VisibleIndex="5">
                </dx:gridviewdatatextcolumn>
            </columns>
            <clientsideevents
CustomButtonClick="function(s, e) {
 clientCallback.PerformCallback(e.visibleIndex);
              e.processOnServer = false;
}"
/>
        </dx:aspxgridview>
        <asp:accessdatasource ID="AccessDataSource1" runat="server" DataFile="~/App_Data/nwind.mdb"
            SelectCommand="SELECT [EmployeeID], [LastName], [FirstName], [BirthDate], [Address], [Photo] FROM [Employees]">
        </asp:accessdatasource>
        <dx:aspxcallback ID="ASPxCallback1" runat="server" ClientInstanceName="clientCallback"
OnCallback="ASPxCallback1_Callback">
        </dx:aspxcallback>
   
 
    </form>
</body>
</html>

原文地址:https://www.cnblogs.com/luoyaoquan/p/2041406.html