<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ComplexObjects.aspx.cs" Inherits="ComplexObjects"
    EnableViewState="false" EnableEventValidation="false" %>
<%@ Register Namespace="Westwind.Web.Controls" Assembly="Westwind.AjaxToolkit" TagPrefix="ww" %>
<%@ Register Src="~/ShowCode.ascx" TagName="ShowCode" TagPrefix="uc1" %>
<!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>wwMethodCallback Complex Objects</title>
    <link href="WestWind.css" rel="stylesheet" type="text/css" />
</head>
<body>
<ww:wwMethodCallback ID="CustomerCallback" runat="server"                
    PostBackMode="Post"
    ScriptLocationType="WebResource" >
</ww:wwMethodCallback>

    <form id="form1" runat="server">
        <h1>wwMethodCallback Complex Objects</h1>

        <a href="default.aspx">Home</a> | <a href="ComplexObjects.aspx">Reset Page</a> |
        <small> Page generated at: <%= DateTime.Now %></small>
        
        <div style="margin-left: 15px">
            <p />
            The following examples call methods on the server from the client, returning typed
            result values that are displayed on the client without PostBacks. The text beside
            the header shows the server side method signature called. To see what's happening
            I recommend you hook up an HTTP viewer like <a href="http://www.fiddlertool.com/">Fiddler</a>
            to watch the HTTP traffic.<br />
            <br />
            <br />
            <strong>Returning a DataTable from the server<br />
            </strong><span style="font-size: 8pt">protected DataTable GetCustomerList()</span><br />
            <hr />
            
            <!-- DataTable GetCustomerList DropDown Sample -->
            <asp:DropDownList ID="lstCustomerList" runat="server" 
                              Width="350px" 
                              onchange="$w('txtCustomerId').value = this.value"
            />
            
            <input id="btnGetCustomerList" type="button" value="Load" 
                   onclick="GetCustomerList();" 
                   style="width: 85px" />
            <input type="button" id="btnClearCustomerList" value="Clear" 
                   onclick="ClearCustomerList();"
                   style="width: 85px" />
            <br />
            <br />
            
            <!-- Returning an object from the server to the client -->
            <strong>Returning an Object from the server</strong><br />
            <span style="font-size: 8pt">protected customersEntity GetCustomerEntity(string
                CustomerId) &nbsp; (customersEntity is an object)</span><br />
            <hr />
            
            <asp:TextBox ID="txtCustomerId" runat="server" Width="126px">ALFKI</asp:TextBox>
            
            <input id="Button1" type="button" 
                    value="Return Customer Object" 
                    onclick="GetCustomerEntity();"
                    style="width: 221px" />
                    
            <br />
            <asp:CheckBox ID="chkReturnOrderEntities" runat="server" Text="Return Order Entities" /><br />
            <br />
            <table>
                <tr>
                    <td valign="top">
                        Name:<br />
                        <asp:TextBox ID="txtContactName" runat="server" Width="350px"></asp:TextBox><br />
                        Company:<br />
                        <asp:TextBox ID="txtCompanyName" runat="server" Width="350px"></asp:TextBox><br />
                        Address:<br />
                        <asp:TextBox ID="txtAddress" runat="server" Width="350px" Height="66px" TextMode="MultiLine"></asp:TextBox><br />
                        Phone:<br />
                        <asp:TextBox ID="txtPhone" runat="server" Width="350px"></asp:TextBox></td>
                    <td valign="top">
                        <!-- Order Display Pane - hidden until you return order child objects -->
                        <div id="OrderDetailPane" style="margin-left: 40px; display: none">
                            Customer's Orders:<br />
                            <asp:ListBox ID="lstOrders" runat="server" Height="155px" Width="400px"></asp:ListBox>
                        </div>
                    </td>
                </tr>
            </table>
            <br />
            
            
            <!-- Pass back a complex object to the server demo -->
            <strong>Passing the object &nbsp;back to the server<br />
            </strong><span style="font-size: 8pt">protected string ReturnCustomerEntity(CustomerEntity
                Customer)</span><br />
            <input type="button" name="btnReturnObject" value="Update Customer Object" 
                   onclick="ReturnObject();"
                style="width: 208px" />
            <br />
            <hr />
            <br />
            Posting back to the server also still works as usual - although you will see nothing
            much<br />
            happen here other than the Postback flash.<br />
            <asp:Button ID="btnServerPost" runat="server" Text="Server POST" /><br />
            <blockquote>
                <hr />
                <small>
                This sample demonstrates using the wwMethodCallback control to call methods of the
                current page. To use this control all you do is drop it on the page and the control
                then exposes each server method of the page (or user or server control) marked with
                the [CallbackMethod] attribute and makes it accessible via a proxy object. The proxy
                is a global object in JavaScript that has the same name as the callback control and
                exposes all of the methods marked with the [CallbackMethod] attribute.
                <br />
                <br />
                This example demonstrates passing complex objects. Here a DataTable, a simple Entity 
                object, a complex entity object (with subitems and an sub-collection) are passed
                to the client. Objects on the client can be accessed with standard object syntax so
                you still use Customer.Company for objects. DataTables are presented as an array of
                objects, so Rows can be accessed as Table.Rows[x].Company. 
                <br />
                <br />
                Objects can also be passed back to the server, so you can download an object, modify it
                then send it back to the server. The wwMethodCallback control supports two-way JSON 
                marshalling.
                <br />
                <br />
                The first example returns a simple DataTable and loads up the list with the result values
                by looping throught he rows and using the DOM to create list items.
                The second example returns a complex object from the server. 
                The object contains a child object (PhoneNumbers)
                and an array of Order objects which are returned only if the Return Order Entries
                checkbox is checked. The object
                is passed from the server to the client where the values are displayed in the textboxes.
                You can change values
                and then Update the object, which sends the updated values that are stored back
                into the object to the server.
                The client side object is JSON encoded and sent back to the server. The server then
                merely echos the values
                back as a string. If you changed the values in the fields you should see those values
                returned from the server.
                <br />
                <br />
                Note also that exception handling carries exception to an error handler that can be
                specified as the last parameter of a callback method. A server side exception is picked
                up and returned to the client as a simplified object that contains the error message. 
                You can easily route the error handler to a single page function or use specific error
                handlers if generic operation is not adequate. 
                </small>
            <hr />
            <uc1:ShowCode ID="ShowSource" runat="server" CodeFile="~/ComplexObjects.aspx.cs">
            </uc1:ShowCode>
            <uc1:ShowCode ID="ShowAspx" runat="server" CodeFile="~/ComplexObjects.aspx"
                Text="Show ASPX"></uc1:ShowCode>
            </blockquote>
            
<script type="text/javascript">

function GetCustomerList()
{
    var ToolTip = new wwToolTip('lstCustomerList');
    ToolTip.show("Updating...",0);
  
    // *** Call the Server GetCustomerList method using the generated client Proxy 
    CustomerCallback.GetCustomerList(GetCustomerList_Callback,OnPageError);    
    
//    // *** 'Manual' callback without proxy
//    var Callback = new wwCallbackMethod();

//    // *** serverUrl is not required for same page callbacks. But allows calling another page in the app
//    // *** which essentially allows you to create a 'service page'
//    Callback.serverUrl = "ComplexObjects.aspx";  
//    Callback.postbackMode = "PostMethodParametersOnly";
//    Callback.callbackHandler = "GetCustomer_Callback";

//    // *** Call method: Method name, Array of parameters, Callback and Error handlers
//    Callback.callMethod("GetCustomerList",[],OnPageError);
//    
}   
function GetCustomerList_Callback(Result)
{
    // *** Clear the tooltip set when the request started
    var ToolTip = new wwToolTip('lstCustomerList');
    ToolTip.hide();

    // *** Our Result here is a DataTable which is now 
    // *** an object that has an array of rows and properties
    // *** for each of the column values.
    var DataTable = Result;  
    
    // *** Use a list control to databind easily
    var List = new wwList("lstCustomerList");
    List.dataValueField = "CustomerId";
    List.dataTextField = "CompanyName";
    List.setData(DataTable);
    
    // *** Manual binding walking the DataTable structure looks like this:
    //List.clear();
    //for(x = 0; x < DataTable.Rows.length; x++)
    //{
    //    var Row = DataTable.Rows[x];
    //    List.addItem(Row.CompanyName,Row.CustomerId");
    //}    
}
function ClearCustomerList()
{
    // *** Again use the list control
    var List = new wwList("lstCustomerList");
    List.clear();
}

// *** Global Customer reference so we can update the customer
// *** and pass it back to the server
var Customer = null;
function GetCustomerEntity()
{
    CustomerCallback.GetCustomerEntity($w('txtCustomerId').value,GetCustomerEntity_Callback,OnPageError);
}
function GetCustomerEntity_Callback(Result)
{
    // *** Update global Customer reference
    Customer = Result;    

    // *** Note the $w() function is equal to document.getElementById()
    $w('txtCompanyName').value = Customer.Companyname;
    $w('txtContactName').value = Customer.Contactname;
    $w('txtAddress').value = Customer.Address;
    
    // *** Child object value
    $w('txtPhone').value = Customer.PhoneNumbers.HomePhone;           
    
    var Panel = $w('OrderDetailPane');
    if ( !document.getElementById('chkReturnOrderEntities').checked )
    {
        Panel.style.display='none';            
        return;
    }

    Panel.style.display='';
    
    // *** This works, but can't do the compound expression of OrderId + OrderDate
//    var List = new wwList("lstOrders");
//    List.dataTextField = "Orderid"; // can't do Order + OrderDate
//    List.setData(Customer.Orders);  
    
    // *** To demonstrate object layout, here's the manual
    // *** that you write for looping through the object
    
    var List = new wwList("lstOrders");
        
    List.clear();
        
    for (x=0; x < Customer.Orders.length; x++ )
    {
        var Order = Customer.Orders[x]; 
        List.addItem( Order.Orderid.toString() + ' - ' + Order.Orderdate.toLocaleDateString() );
     }
}    

// Pass the Customer Object loaded on the page back to
// the server.
function ReturnObject()
{
    // *** Take the object with it's updated data
    // *** and send it to the server.
    if (Customer) 
    {   
        Customer.Companyname = $w('txtCompanyName').value;
        Customer.Contactname = $w('txtContactName').value;
        Customer.Address = $w('txtAddress').value;
        Customer.PhoneNumbers.HomePhone = $w('txtPhone').value;               
        
        CustomerCallback.ReturnCustomerEntity(Customer,ReturnObject_Callback,OnPageError);
    }
    else
        alert('Customer Object doesn\'t exist');
}

function ReturnObject_Callback(Result)
{
    alert(Result);
}
function OnPageError(Result)
{
    alert("*** A callback error occurred:\r\n" + Result.message);
}
</script>
    </form>
</body>
</html>
