티스토리 툴바

달력

052012  이전 다음

  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  

[출처 : http://www.west-wind.com/presentations/jQuery/jQueryPart2.aspx]

 Using jQuery with ASP.NET
Part 2: Making Ajax Callbacks to the Server

by Rick Strahl
www.west-wind.com/weblog

 

In the first part of this article series I introduced jQuery’s functionality and how it provides a rich client side programming model. This time around I’ll expand on these concepts and show you how you can use jQuery in combination with ASP.NET using it as an AJAX backend to retrieve data. I’ll also discuss how you can create ASP.NET controls and otherwise interact with jQuery content from ASP.NET pages in WebForms.

 

jQuery is just a JavaScript library so it will work seamlessly with ASP.NET both from page code as well as through backend driven code using the Page.ClientScript object or ScriptManager. You can use jQuery on its own as a client side and Ajax library that communicates with ASP.NET or you can use jQuery in combination with ASP.NET AJAX. The two actually complement each other quite well as jQuery provides functionality that the ASP.NET AJAX library does not and vice versa. For the most part the interaction between the two libraries is trouble free except for a few very rare edge cases.

 

In this article I’m not going to be talking much about ASP.NET AJAX since that’s been covered ad finitum in other places – the procedure doesn’t vary much if you’re using it with jQuery. Instead I’ll focus on using only jQuery plus a few small helpers to make callbacks to the server easily. In the process you’ll get to see how some of jQuery’s AJAX features work and how to manage the data coming back from the server in a few different ways.

First Ajax Steps with jQuery

One of the most obvious client side features of any Javascript client library is the ability to make AJAX calls to the server. jQuery includes a host of Ajax functions that make it easy to retrieve content from a Url starting with the low level and do-everything $.ajax() function  plus a number of others that are simpler and more focused to specific tasks. Here’s a list of some of the functions available:

 

$.ajax(opt)
This the low level Ajax function with many, many options that lets you create just about any kind of Ajax request. If you need full control over requests or you want to create a generic component that calls back to the server (like the WCF/ASMX client proxy I’ll discuss later) you’ll want to use this function. For now check out the documentation on the multitude of options available.

 

$(sel).load(url,data,callback)

The .load() function is the only Ajax function that works off a jQuery selector. It calls a URL on the server and loads the result as content into selected element(s). It’s a very quick and easy way to load Html fragments and inject them into the document if your result is HTML. An optional callback can be provided to be notified with the server result text when the callback completes which is useful if you want to visually adjust the retrieved content – like applying an effect to visually cue the user to an update. Note this function is heavily overloaded: If no URL is specified .load() acts as a load event handler that fires when an element has loaded its data (ie. an image or script).

 

$.get(url,callback),$.post(url,data,callback)
These functions are simple helpers that provide basic get and post operations to the server. You specify a URL and a callback which is called with the HTTP response from the server. $.post() also allows you to pass either formatted POST buffer string or an object the properties of which are turned into POST encoded key value pairs.

 

$.getJSON(url,data,callback)

Similar to $.post(), but expects the result to be JSON which is automatically deserialized into a Javascript value and passed to the callback as a parameter. While this function is handy for simple JSON results there are two things to watch out for: Dates are not parsed since there’s no date literal in Javascript, so whatever the server returns will be used (typically a string). $.getJSON() also doesn’t support JSON POST data – only POST encoded variables. This function is useful for simple JSON results returned from arbitrary services, but not usable for calling WCF or ASMX ASP.NET services since they expect JSON POST input. More on this later in the article.

.getJSON() also supports cross domain JSONP callbacks. If you specify a query string parameter of callback=? you can force the result to be routed to the callback you specify in the parameter list.

 

$.getScript(url,callback)

This function loads script code from the server and executes it once downloaded if no callback is specified. If specified the optional handler is fired instead and passed the Javascript, plus the current ajax request. This can be useful for JSONP cross domain callbacks where you have no control over the parameters used.

 

Global Ajax Events

There also a number of global Ajax events that you can take advantage of all of which take callbacks as parameters: ajaxCallback(), ajaxError(), ajaxSend(), ajaxStart(),ajaxStop(),ajaxSuccess(). These are useful for setting up global handlers that can centrally manage Ajax requests. You’re not likely to need these much unless you build components that need to know status of requests.

 

 

Getting started with $().load()

The load() function is the easiest way to retrieve content from the server and display it. Let’s look at a simple stock quote example from the included code samples. It retrieves a stock quote from Yahoo and displays the results using the .load() as shown in Figure 1.

Figure 1: A simple example loading a stock quote as HTML with the .load() function.

 

This page works by utilizing two HTML pages: One to hold the input form (StockAjaxLoad.aspx) and one to display the actual stock chart (StockDisplay.aspx). The StockDisplay form is a standalone server page that renders an HTML fragment and is one that is called from jQuery to return the fragment with the completed stock data.

 

The stock display form can be accessed simply via query string parameters by providing a symbol name, or providing an action=Chart key value pair to renders the stock chart image. The two Urls the form accepts look like this:

 

StockDisplay.aspx?Symbol=msft

StockDisplay.aspx?Symbol=msft&action=Chart

 

The form itself is a plain server form with a few label controls. The codebehind loads up a stock quote by using a StockServer class that goes out to Yahoo from the server and retrieves stock data, parsing it into a stock quote object. The resulting quote data is then assigned to the label fields. The page is not a full HTML document but rather just a fragment that starts with a <div> tag and <table> -  there’s no <html> or <body> tag since the output will be merged into the existing document. Check out and test the StockDisplay.aspx to see how the display and stock retrieval works.

 

The key part of the client page contains only the form fields and a placeholder is very simple and looks like this:

 

<div class="containercontent">

    Stock Symbol:

    <asp:TextBox runat="server" ID="txtSymbol" Text="MSFT" />       

    <input type="button" id="btnSubmit" value="Get Quote"   

           onclick="showStockQuote();" />

    <img src="../images/loading_small.gif" id="imgLoading"
         style="display: none"/>

    <hr />

    <div id="divResult" style="width:420px"></div>

</div>

 

To load a stock quote with jQuery we can now very easily read the symbol and use the .load() function to load the result into divResult.

 

<script type="text/javascript">

    function showStockQuote() {                       

        $("#divResult").load("StockDisplay.aspx",

                             { symbol: $("#txtSymbol" ).val() });  

                      

    }

</script>

 

That’s it! We call .load() with the URL plus the optional data parameter which can be either a string (a raw POST buffer) or an object map, the properties of which turn into POST parameters. Here I’m passing an object with a single symbol property that is turned into a POST var.

 

On the server form the StockDisplay.aspx fragment page code simply picks up the symbol, retrieves the stock quote and updates the HTML display and renders the page. Listing 1 shows the StockDisplay codebehind that handles both the stock display and stock chart image.

 

Listing 1: The StockDisplay codebehind loads a quote and displays it

public partial class StockDisplay : System.Web.UI.Page

{

 

    protected void Page_Load(object sender, EventArgs e)

    {

        string action = Request.Params["Action"];

        if (action == "Chart")

            this.GetStockChart();

       

        this.GetStockQuote();

    }

 

 

    void GetStockQuote()

    {

        string symbol = Request.Params["Symbol"] ?? "";

 

        StockServer stockServer = new StockServer();

        StockQuote quote = stockServer.GetStockQuote(symbol);

 

        this.lblCompany.Text = quote.Company + " - " + quote.Symbol;

        this.lblPrice.Text = quote.LastPrice.ToString();

        this.lblOpenPrice.Text = quote.OpenPrice.ToString();

        this.lblNetChange.Text = quote.NetChange.ToString();           

        this.lblQuoteTime.Text = quote.LastQuoteTimeString;

 

        // *** this will call this page to retrieve the chart     

        this.imgStockQuoteGraph.ImageUrl = "StockDisplay.aspx?Symbol=" +

                                           quote.Symbol + "&action=Chart";

    }

 

    void GetStockChart()

    {

        string symbol = Request.Params["Symbol"] ?? "";

 

        StockServer stockServer = new StockServer();

        byte[] img = stockServer.GetStockHistoryGraph(

                        new string[1] { symbol },

                        "Stock History for " + symbol,

                        400, 250, 2);

        Response.ContentType = "application/jpg";

        Response.BinaryWrite(img);

        Response.End();

    }

 

}

 

Note that the single page handles both the text and image displays by routing via query string and post variables and a little routing.

 

That was easy. Using .load() to call an external page is childishly simple.

 

But we probably should improve the sample a little bit. The first thing to do is to make the display a little more interactive. When you load a stock quote when one is already active it’d be nice to use an effect to ‘clear’ the old quote and display the new one. We can use the .load() method and its callback parameter to coordinate sliding the display up and then down again once the quote has loaded completely. This takes a little coordination as Listing 2 demonstrates.

Listing 2: Indication progress in the stock display with basic animation

function showStockQuote() {

    var div = $("#divResult");

    showProgress();

    div.slideUp(function() {

            div.load("StockDisplay.aspx",

                     { symbol: $("#txtSymbol").val() },

                     function() {

                         $(this).slideDown();

                         showProgress(true);

                     });

        });                                                 

}

function showProgress(hide) {

    var img = $("#imgLoading");

    if (hide)

        img.hide();

    else

        img.show();

}

ASP.NET ClientIds and jQuery Selectors

In the code above I’m simply using the ID of the txtSymbol control and that works fine in this particular example, because the txtSymbol control is not contained inside of any ASP.NET naming container like a MasterPage or UserControl. If it did the code above would fail because the ID would not be found.

 

Specifically inside of a master page you might find that the ID gets mangled by ASP.NET into: ctl00_Content_txtSymbol. I could change my code to read:

{ symbol: $("#ctl00_Content_txtSymbol").val() }

 

which works, but is pretty ugly and volatile because the parent IDs might change if a container is renamed or moved around.

 

Another option is to use a little server side script markup to embed the ClientID:


{ symbol: $("#<%= txtSymbol.ClientID %>").val() }


This is also ugly, but reliable. But this does not work if you end up moving your code into a separate .js script file. If you use client ids like this a lot you might create a list of them as global variables:

 

var txtSymbolId = "<%= txtSymbol.ClientID %>";

which then lets you reuse the variable a little more easily:

 

{ symbol: $("#" + txtSymbolId).val() }

 

These variables are also visible in loaded script files.


Finally, some time ago I created a ScriptVariables component that allows for creating variables on the server and rendering them into the client. Among its features this component can expose all control client IDs as object properties under a named object. You can find out more about this component in this blog post. Using this approach you’d end up with a server variable of your choosing that can be referenced like this:

 

{ symbol: $("#" + serverVars.txtSymbolId).val() }


All of this is pretty ugly and might make you want to consider using master pages and user controls in script heavy pages, but the workarounds are fairly easy, just tedious.

 

In the future ASP.NET 4.0 will bring some relief in this regard with a new Control property that lets you specify how control IDs are rendered with an option to override INamingContainer name mangling.

Using .load() with the same ASPX page

In the last stock example I used a separate ASPX page to display a result, which is great if what you’re rendering warrants a self contained page that you might reuse. But often you simply want to render some HTML to load into the page by simply calling back to the current page to keep things self contained. Having a separate page (or even a user control) for each AJAX callback certainly can be overkill.

 

However, you can use pretty much the same process used in the last example to call back to the same page. The main difference is that you’ll need to do a little bit of routing to determine whether you are in a callback or whether you’re rendering the full page as a standard postback.

 

Let’s look at another example that demonstrates this Page callback process. In this example, I’ll use some server side control rendering to render a ListView Control with data and return the Html fragment result back to the client. Again the client page uses $(sel)..load(). Figure 2 shows what the sample looks like.

 

Figure 2: A server side control’s HTML loaded into the client page via .load()

 

The layout of this portion of the sample is very simple. It contains merely the dropdown list plus a placeholder that receives the result from the server.

 

<fieldset>   

<legend>.load() with ASP.NET Control</legend>

<div class="fieldsetpadding">   

    Show Entries:

    <asp:DropDownList runat="server" id="lstFilter"
                      onchange="showEntries()">

        <asp:ListItem Text="Recent Items" Value="Recent" />

        <asp:ListItem Text="All Items" Value="All" />

        <asp:ListItem Text="Open Items" Value="Open" />    </asp:DropDownList>

   

    <a href="javascript:{}" id="lnkShowEntries">Go</a>

   

    <div id="divEntryDisplay" style="margin: 10px;display:none;">
    </
div>   

</div>   

</fieldset>

The page also contains a non-visible ListView control and some containing markup that is not initially rendered:

 

<!-- EntryList 'Template' rendered for AJAX retrieval -->

<asp:PlaceHolder runat="server" ID="entriesPlaceHolder" Visible="false">

   

<div class="blackborder" style="width:500px;">       

    <div class="gridheader">Select one of the Open Entries</div>

    <div style="height: 300px;  overflow-Y: scroll; overflow-x: hidden;">

 

    <asp:ListView runat="server"  ItemPlaceholderID="layoutContainer" ID="lstEntries">   

    <LayoutTemplate>

            <div id="layoutContainer" runat="server" />           

    </LayoutTemplate>

    <ItemTemplate>

    <div id="itemtemplate"

         onclick="alert('Clicked Entry: ' + $(this).attr('pk')); return false;"

         pk="<%# Eval("pk") %>">       

        <div id="clockimg"></div>

        <b><a href="javascript: alert('clicked');"><%# Eval("Title") %></a></b><br />

        <small><%# Eval("Timein") %></small>   

    </div>

    </ItemTemplate>

    </asp:ListView>

    </div>

    <div id="divListStatus" class="toolbarcontainer" >

        <asp:Label runat="server" ID="lblListCount"></asp:Label>

    </div>

</div>

 

</asp:PlaceHolder>

 

When the selection changes in the list, the showEntries Javascript function runs and calls the server to retrieve the list based on the value selected in the filter drop down:

function showEntries()

{           

    var filter = $("#" + scriptVars.lstFilterId).val();

    var jDiv = $("#divEntryDisplay");

   

    jDiv.load("simplepageCallbacks.aspx?Callback=EntryList",

                               { Filter: filter },

                               function(result,status,xhr) {

                                 jDiv.slideDown(1000);  

                                 $("#lnkShowEntries").text("hide");

                               });

}   

 

On the server side the key to making page callbacks into the same page is routing. Notice the URL I’m using above which includes a Callback=EntryList query string parameter. This sample page contains several different callbacks and each of them has a unique Callback id that I’ll use on the server side to route to the appropriate page method to process the callback appropriately. Listing 3 shows the server page that includes the routing logic

 

Listing 3: Partial Rendering on the Server

public partial class Ajax_SimplePageCallbacks : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        // *** Route to the Page level callback 'handler'

        this.HandleCallbacks();

   }

 

    // Callback routing

    void HandleCallbacks()

    {

        string callback = Request.Params["Callback"];

        if (string.IsNullOrEmpty(callback))

            return;

 

        // *** We have an action try and match it to a handler

        if (callback == "HelloWorld")

            this.HelloWorldCallback();

        else if (callback == "EntryList")

            this.EntryListCallback();

        else if (callback == "StockQuote")

            this.GetStockQuote();

        else if (callback == "StockHistoryChart")

            this.GetStockHistoryChart();

 

        Response.StatusCode = 500;

        Response.Write("Invalid Callback Method");

        Response.End();

    }

 

    void EntryListCallback()

    {

        string Filter = Request.Params["Filter"] ?? "";

 

        // *** Render the data into the listview

        this.LoadEntryList(Filter);

       

        // *** Render just the list view into html

        string html = WebUtils.RenderControl(this.entriesPlaceHolder);

 

        Response.Write(html);

        Response.End();

    }

   

    void LoadEntryList(string Filter)

    {

 

        TimeEntryContext context = new TimeEntryContext();

        IQueryable<TimeEntry> q =

            from ent in context.TimeEntries

            orderby ent.TimeIn descending

            select ent;

 

        if (Filter == "Recent")

            q = q.Take(10);

        else if (Filter == "Open")

            q = q.Where(ent => !ent.PunchedOut);

        else if (Filter == "Closed")

            q = q.Where(ent => ent.PunchedOut);

 

 

        // *** Need a concrete instance so we can count

        List<TimeEntry> custList = q.ToList();

        this.lblListCount.Text = custList.Count.ToString() + " Entries";

 

        this.entriesPlaceHolder.Visible = true;

        this.lstEntries.DataSource = custList;

        this.lstEntries.DataBind();

    }

}

 

The routing is very simple – the Page_Load() early on calls HandleCallbacks() which looks at the Callback query string variable. If passed it goes into callback processing otherwise the code simply resumes normal page processing. If it is a callback the callback takes over page processing which results in full page output being sent and – eventually – a Response.End() to fire to complete page processing resulting in only the partially rendered list.

 

HandleCallbacks simply maps the Callback Id to a specific method in the Page class. If I add a new callback all I have to do is add another Id and map it to another method. Each method should return a full HTTP response – including potentially managing errors.

 

The EntryList processing loads data from Linq to SQL into the ListView control by running a query and data binding the ListView with data. Once bound a utility routine (provided with the samples) called WebUtils.RenderControl() is used to render the entire PlaceHolder containing the list view and headers and return just the HTML output. There’s also a WebUtils.RenderUserControl() which allows you to specify a user control to load dynamically and render. RenderControl is actually quite simple:

 

public static string RenderControl(Control control)

{

    StringWriter tw = new StringWriter();

 

    // *** Simple rendering - just write the control to the text writer

    // *** works well for single controls without containers

    Html32TextWriter writer = new Html32TextWriter(tw);

    control.RenderControl(writer);

    writer.Close();           

 

    return tw.ToString();

}

 

It works great for list controls like ListViews and Repeaters or simple containers like PlaceHolder. But there are a few caveats: It will only work with simple controls that don’t post back to the server even when you don’t plan on using any Postback operations. For more complex controls or containers you’ll have to use the more complete RenderUserControl method in the same WebUtils library. You can read more about these two routines in this blog post and review the code in the samples download.

 

Rendering controls is only one way to generate the HTML fragment output of course. You can hand of course code HTML output, or generate output from canned routines in your library – it doesn’t matter where the HTML comes from as long as you can return it back as  string. The same SimplePageCallbacks.aspx page contains a couple of other examples that return generated HTML data in a few of other ways.

Returning JSON from a Page Method

To demonstrate returning data, let’s reuse the stock display example, but instead of returning HTML let’s return a JSON string back. JSON stands for Javascript Object Notation and it’s an object representation format that Javascript recognizes and can evaluate natively without any manual parsing code. JSON string are simply evaluated and if valid results in a Javascript value or object.

 

If you look back at HandleCallbacks one of the routes calls the GetStockQuote() function in the page which should look familiar. Here though I use the ASP.NET AJAX JavaScriptSerializer() class to create JSON on the server and return it to the client:

 

void GetStockQuote()

{

    string symbol = Request.Params["Symbol"] ?? "";

 

    StockServer stockServer = new StockServer();

    StockQuote quote = stockServer.GetStockQuote(symbol);

 

    JavaScriptSerializer ser = new JavaScriptSerializer();

    string res = ser.Serialize(quote);

 

    Response.ContentType = "application/json";

    Response.Write(res);

    Response.End();

}

 

This code is very simple. It generates the stock quote as before by using the StockServer business object that retrieves the quote data from Yahoo and parses it into a StockQuote object. I then use the JavaScriptSerializer to turn this StockQuote object into a JSON string.

 

The generated JSON string of the serialized StockQuote looks like this:

 

{"Symbol":"VDE","Company":"VANGUARD ENRGY ET",

 "OpenPrice":0,"LastPrice":67.97,

 "NetChange":0.00,
 "LastQuoteTime":"\/Date(1227578400000)\/",

 "LastQuoteTimeString":"Nov 24, 4:00PM"}

 

 This JSON is sent to the client which requests it by calling back to the ASPX page with the Callback=GetStockQuote querystring. The code in Listing 4 demonstrates making a jQuery .getJSON() callback to retrieve and the display the stock quote.

 

Listing 4: Retrieving a JSON stock quote from the current page

function getStockQuote()

{

    var symbol = $("#" + scriptVars.txtSymbolId ).val();

   

    $.getJSON("SimplePageCallbacks.aspx?Callback=StockQuote",

              {Symbol: symbol },                   

               function(result) {    

                  $("#StockName").text(result.Company);

                  $("#LastPrice").text(result.LastPrice.formatNumber("c") ); 

                  $("#OpenPrice").text(result.OpenPrice.formatNumber("c") );

                  $("#NetChange").text(result.NetChange.formatNumber("n2") );

                  $("#QuoteTime").text(result.LastQuoteTimeString );

                  $("#divStockDisplay").slideDown("slow");                       

               });

            

    $("#imgStockQuoteGraph")

        .attr("src",

              "SimplePageCallbacks.aspx?Callback=StockHistoryChart" +

              "&symbol=" + encodeURIComponent(symbol))

        .fadeOut( function() { $(this).fadeIn(1000) });       

}

 

Notice how .getJSON() receieves the result parameter which is the deserialized StockQuote object. The anonymous function that handles the callback simply assigns the object property values to the appropriate DOM elements. Note that a few helper functions are used  from the ww.jquery.js support library you can find in your samples. Here the formatNumber function is used to format numbers to the proper numeric display.

Those pesky JSON Dates

.getJSON() works great in simple scenarios like this where you only receive a JSON result. I’m lucky however in that the date I’m interested in is provided as a string (on purpose <g>). I’m not using the LastQuoteTime property of the stock quote, but rather the preformatted string version LastQuoteTimeString which is generated on the server.

 

Take a look at the LastQuoteTime date in the JSON string above again and notice how it’s formatted. The issue is that Javascript does not a have a standard Date literal, so there’s no effective way to embed a date into JSON that is universally recognizable as a date by a parser. Microsoft uses a string that’s marked up like this:

 

"LastQuoteTime":"\/Date(1227578400000)\/"

 

This value is a special string encoding format that starts with slashes and then has a pseudo date function that contains milliseconds since 1/1/1970, which is the Javascript 0 date. The format is easy to recognize and parse which is why I suspect Microsoft created it, but it’s still a string, so if you read this value with .getJSON() you’d get back… a string rather than a date. Unless you have a parser on the client that understands this date format the date isn’t parsed and .getJSON() won’t parse it for you.

 

I’ll talk about how to address the date issue in the next section, but for now just keep in mind that JSON is a nice lean way to return data back and also send data back to the server.

JSON and Service Based Ajax

Returning JSON is a great way to build client centric applications. One of the big draws of returning and also passing back JSON to the server is that you can make very small and atomic accesses and updates to the server with very little data travelling over the wire. HTML is bulky, where JSON is much more precise and results in much smaller payloads in most cases.

 

Using JSON amounts to a more client centric approach to User Interface development. Rather than using the server to generate HTML from the current page or other pages, you can use the server as a service to return you only data. You can then use client side script code to update or create your user interface which given the flexibility that jQuery provides can often be much easier than generating the same server side HTML.

 

It’s also easier to create JSON on the server side and the approach I showed is only one of ways that you can generate JSON. Since we’re talking about a data format often times you don’t need to use ASP.NET pages (or MVC views for that matter) to generate JSON – instead you can use light weight modules or as we’ll see next WCF or ASMX web services.

 

And this is a perfect segue into the next section.

Using jQuery with WCF and ASMX Services

If you are planning on using .NET as a data service to return data to client applications, there is a rich service infrastructure in the form of Windows Communication Foundation (WCF) or ASMX Web Services available. Both platforms as of .NET 3.5 support exposing services to JSON natively. If you’re using .NET 2.0 the ASP.NET AJAX Server Extensions 1.0 can also be used to provide the same functionality.

 

When using either WCF or ASMX JSON services you have the choice of using ASP.NET AJAX to call these services using the ASP.NET ScriptManager to provide the client service calling infrastructure. I’m not going to cover this method in this article since this is covered in plenty of other places and doesn’t really affect jQuery usage. When you use ScriptManager and the client proxy generated by it you can simply call the Web Service based on the proxy generated using the class and methods exposed by it. You can then use jQuery to apply the retrieved data as shown here or in Part 1 of this article series. I have also provided the BasicWcfServicesScriptManager.aspx example that mirrors the jQuery only code I’ll describe in the next section.

Creating a WCF Service for AJAX Consumption

To create a WCF REST service that can be called with AJAX callbacks you need to do the following:

1. Add a new WCF Service to the Web Application as shown in Figure 3.


Figure 3 – Adding a new WCF service to your project

which results in a new .svc file and codebehind file to be added to your project. Make sure that your Web Application or Project is a .NET 3.5 based project since only .NET 3.5 supports WCF REST Services.


Once you’ve added the service you should see the service in the Web Application Project as shown in Figure 4. Note that if you are using stock Web Projects (rather than WAP as shown) the CodeBehind and interface file will be located in your APP_CODE folder instead.

Figure 4 – the .SVC service as shown in a Web Application.


2. Open up the .SVC file in markup mode add a the WebScriptServiceHostFactory as follows:

 

<%@ ServiceHost Language="C#"

        Service="WcfAjax.BasicWcfService"                

        CodeBehind="BasicWcfService.cs"

        Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

 

This host factory is pre-configured to set ASP.NET AJAX style messaging that allows ASP.NET AJAX as well as your jQuery clients to effectively and consistently communicate with the server. The WebScriptServiceHostFactory configures requests in such a way that all requests must be made with POST and expect JSON objects with parameter properties as input, and wrapped JSON objects as output. Also, any service errors are returned as exception objects rather than raw HTML messages allowing you to effectively marshal service exceptions to the client.


3. Remove any Service configuration settings related to the new service from web.config. When the service was added by default it was added with wsHttpBinding which is a SOAP binding that won’t work with AJAX. Remove all related entries as WebScriptServiceHostFactory provides all the necessary configuration settings for you. You can still configure the service later with custom binding behavior settings if necessary, but in most cases the default behavior of the factory is sufficient.

 

If you’d like to use ASP.NET Compatibility in your Web Service to be able to access the HttpContext.Current object in your code the same way as ASMX services did you can add the following setting into the web.config file:

 

<system.serviceModel>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

</system.serviceModel>


4. Set up your Service Class. By default the WCF wizard will set up your service contract interface and implementation class as two separate classes. Traditionally in WCF you define your service contract in the interface where you specify the [OperationContract] and other attributes and any custom behaviors. You then create a class that implements the interface that provides the actual operational implementation. While you’re free to implement the contract interface and class separately, for AJAX services I prefer to implement only a service class that implements both the contract and implementation on a single class as shown in Listing  6. Unlike typical services that can and often are reused with multiple protocols and different hosts and clients, AJAX services tend to be single focus application services and so to my pragmatic view at least don’t benefit from the extra layer of abstraction – I can’t foresee reusing my AJAX contract anywhere but in the local app. If you do then keep them separate. Having a single class to work with in a changeable AJAX environment is much more productive.

 

Listing 6 – A WCF Service implemented both as Contract and Implementation class

namespace WcfAjax

{

 

    [ServiceContract(Namespace = "BasicWcfService")]

    [AspNetCompatibilityRequirements(RequirementsMode =

              AspNetCompatibilityRequirementsMode.Required)]

#if DEBUG

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]

#endif

    public class BasicWcfService

    {

        [OperationContract]

        public StockQuote GetStockQuote(string symbol)

        {

            StockServer server = new StockServer();

            StockQuote quote;

 

            return server.GetStockQuote(symbol);

        }

 

        [OperationContract]

        public StockQuote[] GetStockQuotes(string[] symbols)

        {

            StockServer server = new StockServer();     

            return server.GetStockQuotes(symbols);

        }

   }

}

Using only jQuery to call WCF

To call this service only using jQuery we’ll need to do a little bit of work. While jQuery has support for basic JSON functionality in the form of  the .getJSON() function, this method isn’t adequate for calling the WCF or ASMX services properly.  There are two problems with .getJSON(): It doesn’t support client side JSON serialization natively and it doesn’t know how to deal with ASP.NET style date formats. To address this we’ll need:

 

  • MS AJAX aware JavaScript JSON Serializer
    jQuery does not include a JSON serializer. Since WCF/ASMX require parameters to be sent as POST JSON objects, a serializer is required on the client. The standard getJSON() deserialization also doesn’t work with the MS date format ("LastQuoteTime":"\/Date(1227578400000)\/")  so special handling is required for deserialization as well. For this task I’ve provided a modified version of Douglas Crockford’s JSON2.js or ww.jQuery.js both of which include JSON parsers that understand the Microsoft date format both for serialization and deserialization.

  • A custom Service Callback Handler
    Making a WCF callback requires setting quite a number of options on the $.ajax() function and the results coming back have to be handled properly in order to yield consistent values. WCF results need to be ‘unwrapped’ and any $.ajax() errors need to be normalized so that an error object is returned. To facilitate this process I’ve provided the small ServiceProxy class in Listing 5 as well as in ww.jquery.js (which provides a slightly more complete version).

 

Let’s take a look and see what this looks like when calling the above Web Service using the helpers I mention above. In the following example I allow the user to enter a set of stock symbols and retrieve a set of StockQuotes objects that are then rendered into a list view like display on the client as shown in Figure 5. This list is client rendered.

 

Figure 5 – Example of a WCF Service providing data to a jQuery client with client rendering.

 

Unlike the Page examples earlier, in this example the server only provides JSON data rather than HTML to the client. The client renders the result by using an empty ‘fake template’ that exists in the HTML document and filling in the ‘data holes’ with the data retrieved from the server for each of the retrieved stock quotes.

 

Listing 7 shows the Javascript code used to make the callback to the server and handle the updating of the display when the callback returns using the ServiceProxy class.

 

Listing 7 – Retrieving a set of stock quotes and displaying them using a ‘template’

// *** Create a global instance

var serviceUrl = "BasicWcfService.svc/";

var proxy = new ServiceProxy(serviceUrl);

function getStockQuotes() {

    var symbols = $("#" + serverVars.txtSymbolsId ).val().split(",");

 

    proxy.invoke("GetStockQuotes",

         { symbols: symbols },  // pass symbol array as 'symbols' parameter

         function(quotes) {  // result is an array for each of the symbols              

             var jCnt = $("#divStockDisplay").fadeIn("slow");

             var jDiv = $("#divStockContent").empty();

 

             // quotes is an array

             $.each(quotes, function(index) {

                 var jCtl = $("#StockItemTemplate").clone();

                 jCtl.attr("id", "stock_" + this.Symbol);
                 var symbol = this.Symbol;

 

                 jCtl.find(".itemstockname").text(this.Company);

                 jCtl.find("#tdLastPrice").text(this.LastPrice.formatNumber("n2"));

                 jCtl.find("#tdOpenPrice").text(this.OpenPrice.formatNumber("n2"));

                 jCtl.find("#tdNetChange").text(this.NetChange.formatNumber("n2"));

                 jCtl.find("#tdTradeDate").text(this.LastQuoteTimeString);

                 jCtl.fadeIn().click(function() { alert('clicked on: ' + symbol); });

                 jCtl.find(".hoverbutton").click(function(e) {

                                    alert("delete clicked on: " +

                                    symbol); e.stopPropagation(); });

 

                 jDiv.append(jCtl);

             });

         },

         onPageError);                         

}

function onPageError(error){

    alert("An error occurred:\r\n" + error.Message);

}


The sample page uses the ServiceProxy class for the callback a global instance of this class is created. ServiceProxy receives the URL of the service to call including the trailing backslash when instantiated. Once instantiated the .invoke() method of the proxy instance can be called to make a callback to the server.

 

When calling the GetStockQuotes method the code first retrieves the input field value containing the comma delimited symbols and splits them up into an array as the server method expects. I use a shortcut here for Client Ids by way of a custom control called ScriptVariables (provided in jQueryControls.dll) that exposes all ClientIds as properties of the serverVars object. So serverVar.txtSymbolsId contains the ClientID of the txtSymbols control. The user enters symbols as a comma delimited list which is .split() and turned into an array of symbol strings that the service method excepts as an input parameter.

 

Next the service is called with proxy.invoke(). You pass this method the name of the service method to call plus any parameters which are provided in the form of an object with each parameter a property of the object. So the service method is defined like this:

 

[OperationContract]

public StockQuote[] GetStockQuotes(string[] symbols)

{

    StockServer server = new StockServer();     

    return server.GetStockQuotes(symbols);

}

 

On the client I’m passing an object that has a symbols property with the symbols array as a value:

 

{ symbols: symbols }

 

If you had multiple parameters you’d express them as an object with multiple properties with each property matching the server parameter names:

 

{ symbol: "MSFT", years: 2 }

 

In the GetStockQuotes call the invoke method serializes the symbols array into JSON and sends it to the server for processing. The server side method in the WCF service class receives the symbol array and then retrieves a set of quotes as an array that is returned to the client. The JSON result is an array of StockQuote objects which WCF returns like this:

 

{"d":

   [{"__type":"StockQuote:#WcfAjax",

     "Company":"LDK SOLAR CO ADR",

     "LastPrice":15.48,

     "LastQuoteTime":"\/Date(1227913260000-1000)\/",

     "LastQuoteTimeString":"Nov 28, 1:01PM",

     "NetChange":1.35,

     "OpenPrice":14.40,

     "Symbol":"LDK"

    },

    {"__type":"StockQuote:#WcfAjax",

     "Company":"MKT BCTR GBL ALT",

     "LastPrice":21.00,

     "LastQuoteTime":"\/Date(1227913140000-1000)\/",

     "LastQuoteTimeString":"Nov 28, 12:59PM",

     "NetChange":0.44,

     "OpenPrice":20.27,

     "Symbol":"GEX"

    }    

   ]

}

 

Note the “root level wrapped property” that’s typical of WCF and ASMX services and accounts for the “wrapped” format I’ve been talking about. The root property then contains the actual result, which is an array of two stock quote objects. When the actual result is returned to the client the callback should receive only the actual result value which is the array of quotes. The ServiceProxy class takes care of this task, so when the callback is made successfully,  only the array of quotes is passed to the callback function not the whole evaluated WCF structure.

 

If you look back on Listing 7 you see that the third parameter is a callback handler and it receives the result as an array of quote objects. The code that follows then parses through the array and effectively adds new items into the following placeholder in the HTML document:

 

<div id="divStockDisplay" class="blackborder" 
        style="display:none;width: 600px;">

    <div class="gridheader">Stock Results</div>           

    <div id="divStockContent" style="overflow-y: scroll;height: 200px;">           

    </div>

</div>

 

The code starts out by making the stock display visible and fading it in as it’s initially not displayed. Then all the content in divStockContent is cleared out since we will be loading a new set of items into the display container. Next the code loops through each of the quote objects using jQuery’s static $.each() function. $.each() loops over each item and calls the specified function in the context of the item parsed – in this case a StockQuote which is exposed as the this pointer.

 

Inside of the .each loop then code then proceeds to load up the ‘template’. I call it a template, but really it’s just a fragment of hidden HTML in the page that holds the empty layout of a stock quote without data:

 

<div id="StockItemTemplate" class="itemtemplate" style="display:none">

    <div class="stockicon"></div>

    <div class="itemtools">

        <a href='javascript:{}' class="hoverbutton" ><img src="../images/remove.gif" /></a>

    </div>

    <div class="itemstockname"></div>

    <div class="itemdetail">

        <table cellpadding="3"><tr>

            <td>Price:</td>

            <td id="tdLastPrice" class="stockvaluecolumn"></td>

            <td>Open:</td>

            <td id="tdOpenPrice" class="stockvaluecolumn"></td>

            <td>Change:</td>

            <td id="tdNetChange" class="stockvaluecolumn"></td>               

            <td id="tdTradeDate" colspan="2"></td>

        </tr></table>

    </div>

</div>


The code picks up the empty template from the page and simply clones it, which creates a new DOM element:

var jCtl = $("#StockItemTemplate").clone().show();

 

The result is a new jQuery object of that new as of yet unattached DOM element. One important thing to set is the ID property of the item so that each element can be uniquely identified later, so

var ctl = jCtl.attr("id","stock_" + this.Symbol;

 

accomplishes that task. The code then stuffs the stock quote data into the appropriate ‘holes’ in the template by using the find command and setting the .text() of the element like this:

 

jCtl.find(".itemstockname").text(this.Company);

 

Finally when the item has been all updated it gets added to the bottom of the list with:

 

jDiv.append(jCtl);

 

which adds the newly cloned and formatted element to the content container. Voila, we’ve just called a WCF service with an array input and output value and nice jQuery logic to dynamicly render the results on the client.

The ServiceProxy simplifies Service Calls

The code above is not much more involved than the code you’d write with a ScriptManager generated proxy. The main difference is that there is no proxy (and no Intellisense) and you end up calling the .invoke() method with a method string and object for parameters rather than a simple method on the proxy. The rest of the behavior is pretty much the same.

 

The ServiceProxy class is basically a wrapper around the jQuery $.ajax() function that uses a slightly modified version of JSON2.js to handle serialization and deserialization and ‘unwrapping’ of success and error responses so the behavior is consistent with object/value results returned in all situations. Listing 5 shows the relatively short implementation of the ServiceProxy class.

 

Listing 5: A WCF/ASMX client ServiceProxy for making AJAX calls with jQuery

this.ServiceProxy = function(serviceUrl) {

    /// <summary>

    /// Generic Service Proxy class that can be used to

    /// call JSON Services using jQuery.

    /// Depends on JSON2.js modified for MS Ajax usage

    /// </summary>

    /// <param name="serviceUrl" type="string">

    /// The Url of the service ready to accept the method name

    /// should contain trailing slash (or other URL separator ?,&)

    /// </param>

    /// <example>

    /// var proxy = new ServiceProxy("JsonStockService.svc/");

    /// proxy.invoke("GetStockQuote",{symbol:"msft"},

    ///              function(quote) { alert(result.LastPrice); },onPageError);

    ///</example>

 

    var _I = this;

 

    this.serviceUrl = serviceUrl;

 

    this.invoke = function(method, params, callback, error) {

        /// <summary>

        /// Calls a WCF/ASMX service and returns the result.

        /// </summary>   

        /// <param name="method" type="string">The method of the service to call</param>

        /// <param name="params" type="object">An object that represents the parameters to pass {symbol:"msft",years:2}      

        /// <param name="callback" type="function">Function called on success.

        /// Receives a single parameter of the parsed result value</parm>

        /// <param name="errorCallback" type="function">Function called on failure.

        /// Receives a single error object with Message property</parm>

 

        // Convert input data into JSON - REQUIRES modified JSON2.js

        var json = JSON2.stringify(params);

 

        // Service endpoint URL       

        var url = _I.serviceUrl + method;

 

        $.ajax({

            url: url,

            data: json,

            type: "POST",

            processData: false,

            contentType: "application/json",

            timeout: 10000,

            dataType: "text",  // not "json" we'll parse

            success: function(res) {

                if (!callback) return;

 

                // Use json library so we can fix up MS AJAX dates

                var result = JSON2.parse(res);

 

                // Wrapped message contains top level object node

                // strip it off

                for (var property in result) {

                    callback(result[property]);

                    break;

                }

            },

            error: function(xhr) {

                if (!error) return;

                var res = xhr.responseText;

                if (res && res.charAt(0) == '{')

                    var err = JSON2.parse(res);

                if (err)

                    error(err);

                else

                    if (xhr.status != 200)

                    error({ Message: "Http Error: " + xhr.statusText });

                else

                    error({ Message: "Unknown Error Response" });

                return;

            }

        });

    }

}

 

Most of the work is handled by the jQuery $.ajax() function that performs the actual AJAX callback. The .invoke method first manually serializes the input parameters using JSON2 before making the $.ajax() call. $.ajax() calls either a success or error function when implemented. On success the HTTP string result is unpacked, deserialized and the first ‘property’ value is used as the result value that is passed to the user provided callback function.

 

The biggest chunk of code deals with errors. $.ajax() errors can come in several different forms from protocol level errors to errors in the service code and the error code basically checks for a JSON object which WCF returns on service level errors. If a protocol error occurs the response is HTML and so the status code is retrieved and returned.

Handling Server Side Exceptions

The way error handling works means that you can throw exceptions on the server and receive those exceptions on the client as part of the error callback. Consider the following service method:

 

[OperationContract]

public string ThrowServerException()

{

    throw new InvalidOperationException(@"User generated Error.
               This error has been purposefully created on the server"
);

    return "Gotcha!";

}

 

which can be called and produce expected results with this code on the client:

 

function throwServerException() {

 

proxy.invoke("ThrowServerException",

             null,

             function(result) {

                 alert("This should never be called.");

             },

             function(error){

                  alert("An error occurred:\r\n\r\n" + error.Message);

             });

}

 

In this case the error function is called error.Message is going to be “User generated Error. This error…”.

 

Using WCF in combination with the ServiceProxy makes it very easy to create new callbacks on the server: Create a method in the service and then simply call with the ServiceProxy class’s .invoke() method.

 

If you want to take a look at a more involved sample application you can check out the JsonStockClient.aspx example, which uses the ServiceProxy class create a rich user interface entirely on the client side against a WCF Service interface.

Figure 6 – The StockPortfolio Sample application demonstrates a host of WCF features with the ServiceProxy jQuery client.

 

This application lets you retrieve stock quotes and manage a simple stock portfolio that lists current pricing of stocks that you have added into the portfolio. A number of the concepts I’ve discussed are used in this project as well as the WCF functionality that allows returning image streams from the WCF service which is used for graphs.

Creating Client Content with Templates

One other feature of interest in this example is templating. I’ve mentioned templating repeatedly in the first part of the article and again earlier when I used the ‘data holes’ templating approach of cloning content and then embedding it into the page. While this approach works it’s still fairly work intensive as you have to explicitly use jQuery expressions to find the ‘holes’ in the template and fill in the blanks.

 

There are a number of jQuery templating solutions available. I’ve used jTemplates (which has a Python like templating language) successfully for some time, but I’ve recently switched over to a customized version of John Resig’s Micro Templating Engine which is very compact but uses plain JavaScript to handle the templating. The beauty of John’s engine is that it’s tiny and entirely uses a language that you should already be familiar with – Javascript.

 

Because it’s so compact I’ve integrated the Micro Templating Engine with a few small modifications into my client library (ww.jquery.js). My customizations from John’s code change the template delimiters from <% %> to <# #> to avoid issues with ASP.NET’s page parser, add some basic error handling and display and fix a small bug that has to do with quote characters. Listing 6 shows my version of the parseTemplate() function that is based on John’s original base code.
 

Listing 6 – A powerful but lightweight Javascript Template Engine

/// based on John Resig's Micro Templating engine

var _tmplCache = {}

this.parseTemplate = function(str, data) {

    var err = "";

    try {

        var func = _tmplCache[str];

        if (!func) {

            var strFunc =

            "var p=[],print=function(){p.push.apply(p,arguments);};" +

                        "with(obj){p.push('" +

            str.replace(/[\r\t\n]/g, " ")

               .replace(/'(?=[^#]*#>)/g, "\t")

               .split("'").join("\\'")

               .split("\t").join("'")

               .replace(/<#=(.+?)#>/g, "',$1,'")

               .split("<#").join("');")

               .split("#>").join("p.push('")

               + "');}return p.join('');";

 

            func = new Function("obj", strFunc);

            _tmplCache[str] = func;

        }

        return func(data);

    } catch (e) { err = e.message; }

    return "< # ERROR: " + err.htmlEncode() + " # >";

}

 

The idea of a template engine is that you can create markup as you normally do, but embed the data as Javascript expressions right into the content using tag delimiters. Hey we know how to do that already in ASP.NET with <% %> tags. The difference is we want to do this on the client rather than on the server.

 

For example, in the StockPortfolio application the individual user portfolio items are rendered using the parseTemplate() function. When the page first loads no data is rendered into the list. Instead client script requests the portfolio items as an array which is then parsed one item at a time using the template. The same template is also used to update items when the user changes a quantity or symbol or adds new items.

 

Templates allow you to design the layout and data display in one place and re-use that template, potentially in multiple places.

 

The template for an individual portfolio item looks like this:

 

<script type="text/html" id="StockItemTemplate">

<div class="itemtemplate" style="display:none">  

    <div class="stockicon"></div>   

    <div class="itemtools">

        <a href="javascript:{}" class="hoverbutton">
          <img src="../images/remove.gif" /></a>

    </div>

    <div class="itemstockname"><#= Symbol #> - <#= Company #></div>

    <div class="itemdetail">

        <table style="padding: 5px;"><tr>

            <td>Last Trade:</td>

            <td id="tdLastPrice" class="stockvaluecolumn"><#= LastPrice.formatNumber("n2") #></td>

            <td>Qty:</td>

            <td id="tdLastQty" class="stockvaluecolumn"><#= Qty #></td>

            <td>Holdings:</td>

            <td id="tdItemValue" class="stockvaluecolumn"><#= ItemValue.formatNumber("c") #></td>               

            <td id="tdTradeDate" colspan="2"><#= LastDate.formatDate("MMM dd, hh:mmt")#></td>

        </tr></table>

    </div>

</div>

</script>   

 

Note the <script> tag which is a great way to hide the template from normal HTML markup. The above is valid HTML and any XHTML parser and robot will simply ignore this script block as if it wasn’t there. Yet the content of the script – the template string - can still be retrieved based on its id. This template is passed a stock quote object and the properties of the quote are accessed inside the <#=  #> expressions. The expressions are plain Javascript expressions and you can see that some of the values call functions like formatNumber and formatDate which are part of the ww.jquery.js library. You can also embed code blocks into the page to loop through items which I’ll show a little later.

 

Let’s look and see how the client code can retrieve the stock quote data. Listing 7 shows the code to retrieve the portfolio items and then iterate through them and merge them into the template one at a time.

 

Listing 7 – Updating a List of Items using Client Templating

function LoadQuotes(noMsg)

{   

    proxy.invoke("GetPortfolioItems",

         { userToken: userToken },

         function(message) {

             $("#lstPortfolioContainer").empty();

                 

             $.each(message.Items, function(i) {

                 var item = this;   // this is the iterated item!

                 var newEl = UpdatePortfolioItem(item);

             });

         },

         OnPageError);

}

function UpdatePortfolioItem(stock) {

   

    // Retrieve the Item template

    var template = $("#StockItemTemplate").html();

   

    // Parse the template and merge stock data into it

    var html = parseTemplate(template, stock);

 

    // Create jQuery object from gen'd html

    var newItem = $(html);

 

    // See if we have an existing item

    var origItem = $("#" + stock.Pk + "_STOCK");   

 

    if (origItem.length < 1)

        newItem.appendTo("#lstPortfolioContainer");

    else

        origItem.after(newItem).remove()                  

 

    newItem.attr("id", stock.Pk + "_STOCK")

      .click(function() { ShowStockEditWindow(this) })     

      .fadeIn(1500);

 

    return newItem;

}

 

There’s not a lot of code in this sample and that’s really the point. The only thing of interest in regards to templating is the retrieval of the template and then merging it with these two lines of code:

 

    var template = $("#StockItemTemplate").html();

    var html = parseTemplate(template, stock);

 

The first line simply returns the template as a string from the script block. The second then merges the stock quote into the template and returns a string result of the merged data for an individual stock item. The html is then turned into a jQuery object that then either replaces an existing item or adds a new one to the list. The UpdatePortfolioItem() function is called from LoadQuotes() as shown, but it’s also called from UpdatePortfolioItem() which updates or adds new items to the list. So this function and the template are re-used in multiple locations without duplicate code or markup.

 

One piece of code, one piece of markup all maintained in one place – that’s the benefit of using templates.

 

parseTemplate() can also work with code blocks so you can do things like loop through a list. The following is another example provided in BookAdmin.aspx. One of the forms displays a search result from Amazon as list that looks as shown in Figure 7.

 

Figure 7 – The Amazon Book list uses client side Javascript templating to render the list

 

In the previous example I used a single item template to add one item at a time to the list and used Javascript code to loop through the items. In this example, a single html string is generated for the entire list based on the template and the template does the iteration. The following template uses code blocks and a for loop to run through all of the books.

 

<script type="text/html" id="amazon_item_template">   

<# for (var i=0; i<bookList.length; i++) {

    var book = bookList[i];

#>

<div class="amazonitem" ondblclick="selectBook(this);" tag="<#= book.Id #>">

    <img src="<#= book.SmallImageUrl #>" class="imgAmazonSearch"/>

    <div><b><#= book.Title #></b></div>

    <div><i><#= book.Publisher #> &nbsp;
           (<#= book.PublicationDate #>)</i></div>

    <small><#= book.Author #></small>

</div>

<# } #>

</script>

 

Note the <# #> blocks that allow any sort of Javascript code to be embedded. parseTemplate effectively works by taking the template and turning it into an executing Javascript function and so just about all Javascript code can be used in code blocks. You can loop, you can use if statements to display content conditionally and you can access any global code that is in scope.

 

Templating is an extremely powerful mechanism for building rich client applications without having to generate HTML by hand, filling holes explicitly and most importantly not repeating yourself when creating HTML markup.

 

parseTemplate() is only one templating mechanism but the general concepts are similar in most template engines. There are a host of other template solutions available for jQuery. I’ve also used jTemplates for quite some time prior to this implementation and it works well, and there are several other engines available that I haven’t used.

Using AjaxMethodCallback in jQueryControls

The BookAdmin example actually uses yet another callback mechanism which is provided as part of the jQueryControls project. This project contains an AjaxMethodCallback control which can make JSON callbacks to the same page, a user or server control on the same page or an external HttpHandler to handle JSON callbacks. The control is based on a combination of client script and a server wrapper control that combine to provide the callback capability. The server control is optional – you can also use the AjaxMethodCallback client library on its own and it operates similar to the way ServiceProxy works.

 

The easiest way to use this functionality is to use the control and drop it onto the page or insert it via markup:

<ww:AjaxMethodCallback ID="Proxy" runat="server" />

 

This uses all default settings which allow to call back to the current page and post back only method parameters. Alternately you can call an external handler and specify how POST data is to be sent.

 

<ww:AjaxMethodCallback ID="Proxy" runat="server"

        ServerUrl="AdminCallbackHandler.ashx"

        PostBackMode="PostNoViewstate" />

 

In the BookAdmin page callbacks are made using an ASHX handler which is the most efficient way, but realize that the methods of the handler shown below could easily have been created as methods of the current form.

 

Like WCF and ASMX services you can create simple methods that are called from the client and receive JSON parameters and return a JSON response. Here’s is a single method in the BookAdmin.ashx handler implementation:

 

/// <summary>

/// Callback handler for Amazon Books Page

/// </summary>

public class AdminCallbackHandler : CallbackHandler

{

    private busBook books = new  busBook();

 

 

    [CallbackMethod]

    public List<AmazonBook> GetBooks(string filter)

    {

        if (filter == null)

            filter = string.Empty;

 

        IQueryable<AmazonBook> bookList = null;

 

        if (filter == "Highlighted")

            bookList = books.GetHighlightedBooks();

        else if (filter == "Recent")

            bookList = books.GetRecentBooks();

        else

            bookList = books.GetBooks();

 

        return bookList.ToList();

    }
}

 

The handler needs to inherit from CallbackHandler which provides all the necessary plumbing to marshal the request to the appropriate method, call it and return a result back to the client.

 

On the client the server code is easy to call and uses a client proxy that works in the same way that the WCF/ASMX proxy works. The proxy is created with the same name as the AjaxMethodCallback control’s Id – Proxy in this case and has methods for each of the exposed server methods. Each method has the same parameter signature as the server method plus the callback and errorCallbacks – just like service proxies discussed earlier. To call the server is as easy as:

 

function editBook(ctl)

{
    var bookPk = $(ctl).attr("id").replace("book_","");
    bookPk = parseInt(bookPk);

    Proxy.GetBook(bookPk,

                  function(book) {  // object result

                      alert(book.Title + " " + book.AmazonUrl);

                      ShowBookForm(book);

                  },

                  onPageError);

}

 

The actual page code in the sample of course is a bit more complex and it uses jQuery to pop up and display the edit window and populate its form fields. What is nice about AjaxMethodCallback is that it’s fully self contained and loads up jQuery.js and the ww.jquery.js script library (optionally – you can override resource load behavior) so you drop the control add methods and you’re on your way, and it doesn’t require any of the ASP.NET AJAX or WCF components.

 

One additional benefit of this control is also the ability to use it to handle callbacks in page and control code. This means if you build a custom ASP.NET server control that needs to retrieve data from the client directly (rather than through a service reference), it’s easy to do by simply adding an AjaxMethodCallback to the page’s controls collection and pointing it at the class that will handle callbacks. Any class can be pointed at to handle callback events.

Summary

Phew – a lot of options for you to calling back to the server using jQuery. In this article I’ve covered both manually using ASP.NET pages to handle callback routing and returning both HTML and JSON data to the client. I’ve also covered three – count ‘em – mechanisms for making JSON based server callbacks that let you pass data between client and server.

 

Which approach is best? As always it depends.

 

For existing applications that need to add a little bit of AJAX functionality page level callbacks that return HTML fragments can be a quick way to provide little bits of content to update the client. Control or user control rendering can provide reuse of server side components, or you can generate HTML from scratch to return to the client. Personally I prefer the ‘raw’ Ajax approach which uses the server as a data service for serving JSON data to the client and then update the client using either small chunks of DOM manipulation or for more complex or markup intensive items using templates. It’s a little more work but you can do so much more interesting stuff and you have way more control over your user interface.

 

When it comes to callbacks to the server you have lots of choices. I discussed three callback mechanisms – WCF, ASMX and AjaxMethodCallback. WCF or ASMX are no brainers if you’re already using ASP.NET AJAX. If you’re not using ASP.NET AJAX you can still use either WCF/ASMX on the server and use the ServiceProxy class and the JSON parsers I introduced in the article on the client. Doing so lets you avoid loading the ASP.NET AJAX client library which is overkill if you only need to make service callbacks.  AjaxMethodCallback is very easy and fully self contained so you can just drop a control and you’re ready to go. It also is a bit more flexible since you can use the server component on a Page, a user or server control, in a high performance Http Handler or even your own custom handling code via the generic JsonCallbackMethodProcessor class which does all the dirty work of parsing input and output and generating JSON.

 

All approaches make it easy to make callbacks to the server with literally just a single line of code, so that you can focus on using the data returned effectively and use jQuery to update the UI. After all that’s really what it’s all about – service callbacks should be just plumbing and the logic of applying the results is where the real work usually comes in and that’s where jQuery really excels. If you’re building complex or repetitive UI layouts client side make sure you check out templating for merging Javascript based templates with data. Templates make sure you don’t repeat yourself and keep your layout in one place even though you are working on the client side.

 

I hope Part 2 of this article has been useful to you. In Part 3 I’ll look a bit closer at integration between ASP.NET and jQuery in terms of creating server controls. I touched on this topic a little with the AjaxMethodCallback control which integrates client jQuery components and plug-ins and a server control. I’ll look deeper into how you can build reusable ASP.NET components that interact with jQuery. Until have fun calling server side code with jQuery.


 

Resources

Sample Download:

Components described in this article are also part of the
West Wind Web Toolkit for ASP.NET

 

Talk Back


jQueryControls


 

Web Sites

 

Printable Cheat Sheets

 

Books

Posted by 불펭

[출처: http://www.codeproject.com/KB/aspnet/ASPDOTNETPageLifecycle.aspx]

 ASP.NET application and page life cycle

 

Introduction
The Two step process
Creation of ASP.NET environment
Process request using MHPM events fired
In What event we should do what?
A sample code for demonstration
Zooming ASP.NET page events
Source code
References
 

 

Introduction
 

In this article we will try to understand what are the different events which takes place right from the time the user sends a request, until the time request is rendered on the browser. So we will first try to understand the two broader steps of an ASP.NET request and then we will move in to different events emitted from ‘HttpHandler’, ‘HttpModule’ and ASP.NET page object. As we move in this event journey we will try to understand what kind of logic should go in each every of these events.
This is a small Ebook for all my .NET friends which covers topics like WCF,WPF,WWF,Ajax,Core .NET,SQL etc you can download the same from Click here  or else you can catch me on my daily free training @ Click here 
 

The Two step process
 

From 30,000 feet level ASP.NET request processing is a 2 step process as shown below. User sends a request to the IIS:-
• ASP.NET creates an environment which can process the request. In other words it creates the application object, request, response and context objects to process the request.
• Once the environment is created the request is processed through series of events which is processed by using modules, handlers and page objects. To keep it short lets name this step as MHPM (Module, handler, page and Module event), we will come to details later.
 

In the coming sections we will understand both these main steps in more details.
 

Creation of ASP.NET environment
 

Step 1:- The user sends a request to IIS. IIS first checks which ISAPI extension can serve this request. Depending on file extension the request is processed. For instance if the page is an ‘.ASPX page’ then it will be passed to ‘aspnet_isapi.dll’ for processing.

Step 2:- If this the first request to the website then a class called as ‘ApplicationManager’ creates an application domain where the website can run. As we all know application domain creates isolation between two web applications hosted on the same IIS. So in case there is issue in one app domain it does not affect the other app domain.

Step 3:- The newly created application domain creates hosting environment i.e. the ‘HttpRuntime’ object. Once the hosting environment is created necessary core ASP.NET objects like ‘HttpContext’ , ‘HttpRequest’ and ‘HttpResponse’ objects are created.

Step 4:- Once all the core ASP.NET objects are created ‘HttpApplication’ object is created to serve the request. In case you have a ‘global.asax’ file in your system then object of the ‘global.asax’ file will be created. Please note ‘global.asax’ file inherits from ‘HttpApplication’ class.
Note: The first time an ASP.NET page is attached to an application, a new instance of ‘HttpApplication’ is created. Said and done to maximize performance, ‘HttpApplication’ instances might be reused for multiple requests.

Step 5:- The ‘HttpApplication’ object is then assigned to the core ASP.NET objects to process the page.

Step 6:- ‘HttpApplication’ then starts processing the request by http module events , handlers and page events. It fires the MHPM event for request processing.
Note: - For more details http://msdn.microsoft.com/en-us/library/ms178473.aspx
 

Below image explains how the internal object model looks like for an ASP.NET request. At the top level is the ASP.NET runtime which has creates an ‘Appdomain’ which in turn has ‘HttpRuntime’ with ‘request’, ‘response’ and ‘context’ objects.

Process request using MHPM events fired
 

Once ‘HttpApplication’ is created it starts processing request it goes through 3 different sections ‘HttpModule’ , ‘Page’ and ‘HttpHandler’. As it moves through these sections it invokes different events which the developer can extend and add customize logic to the same.
Before we move ahead lets understand what are ‘HttpModule’ and ‘HttpHandlers’. They help us to inject custom logic before and after the ASP.NET page is processed. The main differences between both of them are:-
• If you want to inject logic based in file extensions like ‘.ASPX’ , ‘.HTML’ then you use ‘HttpHandler’. In other words ‘HttpHandler’ is an extension based processor.
 

• If you want to inject logic in the events of ASP.NET pipleline then you use ‘HttpModule’. ASP.NET . In other word ‘HttpModule’ is an event based processor.

You can read more about the differences from http://computerauthor.blogspot.com/2009/09/two-interceptors-httpmodule-and.html 
Below is the logical flow of how the request is processed. There are 4 important steps MHPM as explained below :-

Step 1(M  HttpModule):- Client request processing starts. Before the ASP.NET engine goes and creates the ASP.NET ‘HttpModule’ emits events which can be used to inject customized logic. There are 6 important events which you can utilize before your page object is created ‘BeginRequest’,’AuthenticateRequest’,’AuthorizeRequest’,’ResolveRequestCache’,’AcquireRequestState’ and ‘PreRequestHandlerExecute’.

Step 2 (H  ‘HttpHandler’ ) :- Once the above 6 events are fired , ASP.NET engine will invoke ‘ProcessRequest’ event if you have implemented ‘HttpHandler’ in your project.

Step 3 (P – ASP.NET page):- Once the ‘HttpHandler’ logic executes the ASP.NET page object is created. While the ASP.NET page object is created many events are fired which can help us to write our custom logic inside those page events. There are 6 important events which provides us placeholder to write logic inside ASP.NET pages ‘Init’ , ‘Load’ , ‘validate’ , ‘event’ , ‘render’ and ‘unload’. You can remember the word ‘SILVER’ to remember the events S – Start ( does not signify anything as such just forms the word ) , I – (Init) , L ( Load) , V ( Validate) , E ( Event) and R ( Render).

Step4 (M  HttpModule):- Once the page object is executed and unloaded from memory ‘HttpModule’ provides post page execution events which can be used to inject custom post-processing logic. There are 4 important post-processing events ‘PostRequestHandlerExecute’, ‘ReleaserequestState’, ‘UpdateRequestCache’ and ‘EndRequest’.
Below figure shows the same in a pictorial format.
 

In What event we should do what?
 

The million dollar question is in which events we should do what? . Below is the table which shows in which event what kind of logic or code can go.
 

Section Event Description
HttpModule BeginRequest This event signals a new request; it is guaranteed to be raised on each request.
HttpModule AuthenticateRequest This event signals that ASP.NET runtime is ready to authenticate the user. Any authentication code can be injected here.
HttpModule AuthorizeRequest This event signals that ASP.NET runtime is ready to authorize the user. Any authorization code can be injected here.
HttpModule ResolveRequestCache In ASP.NET we normally use outputcache directive to do caching.  In this event ASP.NET runtime determines if the page can be served from the cache rather than loading the patch from scratch.  Any caching specific activity can be injected here.
HttpModule AcquireRequestState This event signals that ASP.NET runtime is ready to acquire session variables. Any processing you would like to do on session variables.
HttpModule PreRequestHandlerExecute This event is raised just prior to handling control to the HttpHandler. Before you want the control to be handed over to the handler any pre-processing you would like to do.
HttpHandler ProcessRequest Httphandler logic is executed. In this section we will write logic which needs to be executed as per page extensions.
Page Init

This event happens in the ASP.NET page and can be used for :-

·     Creating controls dynamically, in case you have controls to be created on runtime.

·     Any setting initialization.

·      Master pages and them settings.

In this section we do not have access to viewstate , postedvalues and neither the controls are initialized.

 

Page Load In this section the ASP.NET controls are fully loaded and you write UI manipulation logic or any other logic over here.
Page Validate If you have valuators on your page, you would like to check the same here.
  Render It’s now time to send the output to the browser. If you would like to make some changes to the final HTML which is going out to the browser you can enter your HTML logic here.
Page Unload Page object is unloaded from the memory.
HttpModule PostRequestHandlerExecute Any logic you would like to inject after the handlers are executed.
HttpModule ReleaserequestState If you would like to save update some state variables like session variables.
HttpModule UpdateRequestCache Before you end if you want to update your cache.
HttpModule EndRequest This is the last stage before your output is sent to the client browser.

 

A sample code for demonstration
 

With this article we have attached a sample code which shows how the events actually fire. In this code we have created a ‘HttpModule’ and ‘Httphandler’ in this project and we have displayed a simple response write in all events , below is how the output looks like.
Below is the class for ‘HttpModule’ which tracks all event s and adds it to a global collection.
 

Collapse Copy Code
public class clsHttpModule : IHttpModule
{
...... 
void OnUpdateRequestCache(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnUpdateRequestCache");
}
void OnReleaseRequestState(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnReleaseRequestState");
}
void OnPostRequestHandlerExecute(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnPostRequestHandlerExecute");
}
void OnPreRequestHandlerExecute(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnPreRequestHandlerExecute");
}
void OnAcquireRequestState(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnAcquireRequestState");
}
void OnResolveRequestCache(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnResolveRequestCache");
}
void OnAuthorization(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnAuthorization");
}
void OnAuthentication(object sender, EventArgs a)
{

objArrayList.Add("httpModule:AuthenticateRequest");
}
void OnBeginrequest(object sender, EventArgs a)
{

objArrayList.Add("httpModule:BeginRequest");
}
void OnEndRequest(object sender, EventArgs a)
{
objArrayList.Add("httpModule:EndRequest");
objArrayList.Add("<hr>");
foreach (string str in objArrayList)
{
httpApp.Context.Response.Write(str + "<br>") ;
}
} 
}

 

Below is the code snippet for ‘HttpHandler’ which tracks ‘ProcessRequest’ event.
 

Collapse Copy Code
public class clsHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
clsHttpModule.objArrayList.Add("HttpHandler:ProcessRequest");
context.Response.Redirect("Default.aspx");
}
}

 

We are also tracking all the events from the ASP.NET page.
 

Collapse Copy Code
public partial class _Default : System.Web.UI.Page 
{
protected void Page_init(object sender, EventArgs e)
{

clsHttpModule.objArrayList.Add("Page:Init");
}
protected void Page_Load(object sender, EventArgs e)
{
clsHttpModule.objArrayList.Add("Page:Load");
}
public override void Validate() 
{
clsHttpModule.objArrayList.Add("Page:Validate");
}
protected void Button1_Click(object sender, EventArgs e)
{
clsHttpModule.objArrayList.Add("Page:Event");
}
protected override void Render(HtmlTextWriter output) 
{
clsHttpModule.objArrayList.Add("Page:Render");
base.Render(output);
}
protected void Page_Unload(object sender, EventArgs e)
{
clsHttpModule.objArrayList.Add("Page:UnLoad");
}}

 

Below is how the display looks like with all events as per the sequence discussed in the previous section.
 

Zooming ASP.NET page events
 

In the above section we have seen the overall flow of events for an ASP.NET page request. One of the most important section is the ASP.NET page, we have not discussed the same in detail. So let’s take some luxury to describe the ASP.NET page events in more detail in this section.
Any ASP.NET page has 2 parts one is the page which is displayed on the browser which has HTML tags , hidden values in form of viewstate and data on the HTML inputs. When the page is posted these HTML tags are created in to ASP.NET controls with viewstate and form data tied up together on the server. Once you get these full server controls on the behind code you can execute and write your own login on the same and render the page back to the browser.
 

Now between these HTML controls coming live on the server as ASP.NET controls, the ASP.NET page emits out lot of events which can be consumed to inject logic. Depending on what task / logic you want to perform we need to put these logics appropriately in those events.
Note: - Most of the developers directly use the ‘page_load’ method for everything, which is not a good thought. So it’s either populating the controls, setting view state, applying themes etc everything happens on the page load. So if we can put logic in proper events as per the nature of the logic that would really make your code clean.
 

Seq Events Controls Initialized

View state

Available

Form data
Available
What Logic can be written here?
1 Init No No No

Note: - You can access form data etc by using ASP.NET request objects but not by Server controls.Creating controls dynamically, in case you have controls to be created on runtime. Any setting initialization.Master pages and them settings.In this section we do not have access to viewstate , posted values and neither the controls are initialized.

2 Load view state Not guaranteed Yes Not guaranteed You can access view state and any synch logic where you want viewstate to  be pushed to behind code variables can be done here.
3 PostBackdata Not guaranteed Yes Yes You can access form data. Any logic where you want the form data to be pushed to behind code variables can be done here.
4 Load Yes Yes Yes This is the place where you will put any logic you want to operate on the controls. Like flourishing a combox box from the database , sorting data on a grid etc. In this event we get access to all controls , viewstate and their posted values.
5 Validate Yes Yes Yes If your page has validators or you want execute validation for your page this is the right place to the same.
6 Event Yes Yes Yes If this is a post back by a button click or a dropdown change then the relative events will be fired. Any kind of logic which  is related to that event can be executed here.
7 Pre-render Yes Yes Yes If you want to make final changes to the UI objects like changing tree structure or property values, before these controls are saved in to view state.
8 Save view state Yes Yes Yes Once all changes to server controls are done this event can be an opportunity to save control data in to view state.
9 Render Yes Yes Yes If you want to add some custom HTML to the output this is the place you can.
10 Unload Yes Yes Yes Any kind of clean up you would like to do here.

Source code
 

This source code shows how the complete ASP.NET request cycle fires. From here
 

References
 

I am not so smart to write this article by myself ;-) , lot of things I have plugged from the below articles.
Intercepting filters http://msdn.microsoft.com/en-us/library/ms998536.aspx 
Explains how to implement Httphandlers and modules http://msdn.microsoft.com/en-us/library/system.web.httpapplication.aspx 
Httphandlers and Httpmodules :- http://www.15seconds.com/Issue/020417.htm 
Implementing security using modules and handlers http://joel.net/articles/asp.net2_security.aspx 
Difference between Httpapplication and global.asax http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx

Posted by 불펭

1. ASP.Net 에서 JavaScript 함수 호출

 

1) ScriptManager.RegisterStartupScript 사용

-.aspx

<script type="text/javascript">
        function AlertMgs(var mgs) {
            alert(msg);  

      }

</script>

-.aspx.cs

ScriptManager.RegisterStartupScript(this, this.GetType(), "Alert", "AlertMgs('" + sMessage + "');", true);

 

이런식으로 호출이 가능하며 이경우 반드시

<asp:ScriptManager ID="ScriptManager1" runat="server"> 구문이 aspx 페이지에 추가되어 있어야 한다.

 

 

2) Page.ClientScript.RegisterStartupScript 사용

Page.ClientScript.RegisterStartupScript(this.GetType(),  "Alert", "AlertMgs('" + sMessage + "');", true);

이경우

if(!Page.ClientScript.IsStartupScriptRegistered(this.GetType(),  "Alert"))

{

   Page.ClientScript.RegisterStartupScript(this.GetType(),  "Alert", "AlertMgs('" + sMessage + "');", true);

}

이런식으로 스크립트가 등록여부를 알아볼수 있다.

 

3) ScriptManager.RegisterStartupScript vs Page.ClientScript.RegisterStartupScript

이 두가지 중 어느것을 어느경우에 사용하는것이 좋은지 궁금할수도 있겠다.

AJAX 기반-UpdatePanel을 사용할텐데 이럴때는

ScriptManager.RegisterStartupScript를 써야한다.

외국의 포스트들을 봐도 그렇다..

보라->

http://gotjeep.net/Blogs/CommentView,guid,4be2f278-12e4-40d5-b154-0e8ecaf18fac.aspx

 

이참에 좀더 확실하게 2가지 기능을 구별해 보자.

 

[출처: http://msdn.microsoft.com/ko-kr/library/asz8zsxy.aspx]

ClientScriptManager.RegisterStartupScript 메서드 (Type, String, String)

형식, 키 및 스크립트 리터럴을 사용하여 시작 스크립트를 Page 개체에 등록합니다.

 

네임스페이스:  System.Web.UI
어셈블리:  System.Web(System.Web.dll)

구문

public void RegisterStartupScript(
Type type,
string key,
string script
)

매개 변수

type
형식: System.Type
등록할 시작 스크립트의 형식입니다.
key
형식: System.String
등록할 시작 스크립트의 키입니다.
script
형식: System.String
등록할 시작 스크립트 리터럴입니다.
설명

클라이언트 스크립트는 해당 키와 형식에 의해 고유하게 식별됩니다.키 및 형식이 같은 스크립트는 중복된 것으로 간주됩니다.지정된 형식과 키 쌍을 사용하는 스크립트 하나만 페이지에 등록할 수 있습니다.이미 등록된 스크립트를 등록하려고 하면 중복 스크립트가 만들어지지 않습니다.

지정한 키와 형식 쌍으로 시작 스크립트가 이미 등록되어 있는지 여부를 확인하여 스크립트를 추가하려는 불필요한 시도를 방지하려면 IsStartupScriptRegistered 메서드를 호출합니다.

RegisterStartupScript 메서드의 이 오버로드에서는 script 매개 변수에 제공된 스크립트가 <script> 요소 블록으로 래핑되어 있는지 확인해야 합니다.

RegisterStartupScript 메서드가 추가한 스크립트 블록은 페이지에서 로드가 끝날 때 OnLoad 이벤트가 발생하기 전에 실행됩니다.스크립트 블록이 항상 등록된 순서대로 출력되는 것은 아닙니다.스크립트 블록의 순서가 중요한 경우 StringBuilder 개체를 사용하여 스크립트를 단일 문자열로 모은 다음 모든 스크립트를 클라이언트 스크립트 블록 하나에 등록합니다.

예제

다음 코드 예제에서는 RegisterStartupScript 메서드의 사용 방식을 보여 줍니다스크립트 태그의 시작과 종료가 script 매개 변수 내에 포함된다는 점에 유의하십시오.추가 매개 변수 설정을 기반으로 스크립트 태그를 추가하려면, RegisterStartupScript 메서드를 참고하십시오.

<%@ Page Language="C#" %>

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

<script runat="server">
  public void Page_Load(Object sender, EventArgs e)
  {
    // Define the name and type of the client scripts on the page.
    String csname1 = "PopupScript";
    Type cstype = this.GetType();

    // Get a ClientScriptManager reference from the Page class.
    ClientScriptManager cs = Page.ClientScript;

    // Check to see if the startup script is already registered.
    if (!cs.IsStartupScriptRegistered(cstype, csname1))
    {
        StringBuilder cstext1 = new StringBuilder();
        cstext1.Append("<script type=text/javascript> alert('Hello World!') </");
        cstext1.Append("script>");

        cs.RegisterStartupScript(cstype, csname1, cstext1.ToString());
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>RegisterStartupScript</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    </div>
    </form>
</body>
</html>
ScriptManager.RegisterStartupScript 메서드 (Page, Type, String, String, Boolean)
ScriptManager 컨트롤을 사용하여 비동기 포스트백이 발생할 때마다 시작 스크립트 블록을 등록하고 페이지에 스크립트 블록을 추가합니다.
 
네임스페이스:  System.Web.UI
어셈블리:  System.Web.Extensions(System.Web.Extensions.dll)
구문

public static void RegisterStartupScript(
	Page page,
	Type type,
	string key,
	string script,
	bool addScriptTags
)

매개 변수

page
형식: System.Web.UI.Page
클라이언트 스크립트 블록을 등록하는 페이지 개체입니다.
type
형식: System.Type
클라이언트 스크립트 블록의 형식입니다.이 매개 변수는 일반적으로 typeof 연산자(C#) 또는 GetType 연산자(Visual Basic)로 스크립트를 등록하는 컨트롤의 형식을 검색하여 지정됩니다.
key
형식: System.String
스크립트 블록의 고유한 식별자입니다.
script
형식: System.String
스크립트입니다.
addScriptTags
형식: System.Boolean
스크립트 블록을 <script></script> 태그로 묶으면 true이고, 그렇지 않으면 false입니다.
설명

이 메서드는 비동기 포스트백이 발생할 때마다 포함된 시작 스크립트 블록을 등록하는 데 사용합니다.UpdatePanel 컨트롤이 업데이트될 때만 스크립트 블록이 등록되도록 UpdatePanel 컨트롤 내에 있는 컨트롤에 대해 스크립트 블록을 등록하려면 이 메서드의 RegisterStartupScript(Control, Type, String, String, Boolean) 오버로드를 사용합니다.

부분 페이지 업데이트에 속하지 않는 시작 스크립트를 등록하고 해당 스크립트를 초기 페이지 렌더링 시 한 번만 등록하려는 경우 ClientScriptManager 클래스의 RegisterStartupScript 메서드를 사용합니다.ClientScriptManager 개체에 대한 참조는 페이지의 ClientScript 속성에서 가져올 수 있습니다.

 

 

 

 

2. JavaScript 에서 ASP.Net 메서드 호출

1) WebMethod를 이용하여 메서드 호출

 

-aspx.cs

        public static string CleanUp()
        {
            string response = ConfigurationManager.AppSettings["CloseOutUrl"];
            if (String.IsNullOrEmpty(response))
            {
                response = "ApplicationClosed.aspx";
            }
            return response;
        }

 

-.aspx

function CloseOut(logout) {
            PageMethods.CleanUp(CloseOutResponse, CloseOutResponse);
        }

여기서 중요한 점은....

메소드가 Static 이라는것과

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />

이게 반드시 필요하다는 거심.

Posted by 불펭

[출처: http://www.codeproject.com/KB/webforms/FileUploadWithProgrss.aspx]

 

 

Introduction

File upload widget that will display real time file upload progress bar

Background

This will allow user to Upload, Download and Delete file with classic user interface and without reloading page 

Using the code  

The upload widget is includes following: 

  • File Upload interface (Default.aspx)
    • IFRAME (which contain upload engine page UploadEngine.aspx).
    • Upload button (that will allow user to upload file on server).
    • Display statistics for uploading file (Filename, Status, Progress and Transferred Bytes)
    • Gird (that will be display list of uploaded files)
    • Hidden field (it will monitor and refresh file list whenever file was successfully uploaded on server).
  • Upload Engine (UploadEngine.aspx)
    • File Upload control

Page structure (Default.aspx)

Upload Engine structure (UploadEngine.aspx)

Page execution flow:

During page loading it will automatically register click event for Upload button.

  1. Choose File to upload
  2. Clicking on Upload button (it will automatically register click event for Upload button)
    • The following function is checking basic validation on client site like
      • File Upload should not be empty.
      • File name should be unique.
    • Executing (UploadEngine) submit event using JavaScript.
    • During execution of (UploadEngine) two processes are executing simultaneously   
      • page it will maintain File Upload details in session with the help of UploadDetail class
      • Every 500 millisecond the PageMethod will call and display progress of current uploading file with Size and Percentage value.
    • Display continuous progress using StartProgress function

3. Display real time progress

4. Update message based on result, result may from following
Success Information Warning Error

5. The two processes are executing simultaneously on server side are as follows:
o Upload Engine

o Default

6. During file upload processes following screen would be appear:

7. Following functionality also available in Grid.

8. I provided classical look and feel (with CSS) and cross browser compatible script (with Javascript)

9. The upload widget are supported following browser:

Points of Interest

Now I will try to support multiple files upload feature.

Posted by 불펭

1. Request.Param["html컨틀롤 명"] 로 받아오기

예)

Default.aspx

<form id="form1" runat="server">    
    <input type="text" id="txt1" name="txt1" />       
    <select id="Select1" name="Select1" runat="Server">       
    </select>
   
    <div>        
        <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
    </div>
 </form>

 

Default.aspx.cs

protected void Button1_Click(object sender, EventArgs e)
        {
            if (Request.Params["txt1"] != null)
            {
                string sss = Request.Params["txt1"].ToString();
                ScriptManager.RegisterStartupScript(Page, GetType(), "Alert", "alert('" + sss+ "');", true);
            }
        }

 

이런식으로 가져올수 있다.

 

2. runat="Server" 사용

단 이경우엔 서버사이드에서 html 컨트롤에 값을 넣어놓은 경우만 가능하다.

<select id="Select1" name="Select1" runat="Server">       
    </select>

의 경우 하부 항목이 없으므로

이상태에서 Select1.Items.Count를 보면 0으로 나온다.

서버쪽에서 값을 넣었을경우엔 그 값을 가져오기가 가능하나

클라이언트에서 서버를 통하지 않고 ajax로 값을 넣었을경우엔 항목을 가져올수 없다.


이벤트도 역시

 <button id="Button1" runat="server" onserverclick="SubmitBtn_Click" >

이런식으로 가능하다.
 

 

 

<참고사항 1 HTML Server Controls>

 

HTML Server Controls

[출처 : http://www.w3schools.com/ASPNET/aspnet_refhtmlcontrols.asp]

 

HTML Server Controls

HTML elements in ASP.NET files are, by default, treated as text. To make these elements programmable, add a runat="server" attribute to the HTML element. This attribute indicates that the element should be treated as a server control.

Note: All HTML server controls must be within a <form> tag with the runat="server" attribute!

Note: ASP.NET requires that all HTML elements must be properly closed and properly nested.

 

HTML Server Control Description
HtmlAnchor Controls an <a> HTML element
HtmlButton Controls a <button> HTML element
HtmlForm Controls a <form> HTML element
HtmlGeneric Controls other HTML element not specified by a specific HTML server control, like <body>, <div>, <span>, etc.
HtmlImage Controls an <image> HTML element
HtmlInputButton Controls <input type="button">, <input type="submit">, and <input type="reset"> HTML elements
HtmlInputCheckBox Controls an <input type="checkbox"> HTML element
HtmlInputFile Controls an <input type="file"> HTML element
HtmlInputHidden Controls an <input type="hidden"> HTML element
HtmlInputImage Controls an <input type="image"> HTML element
HtmlInputRadioButton Controls an <input type="radio"> HTML element
HtmlInputText Controls <input type="text"> and <input type="password"> HTML elements
HtmlSelect Controls a <select> HTML element
HtmlTable Controls a <table> HTML element
HtmlTableCell Controls <td>and <th> HTML elements
HtmlTableRow Controls a <tr> HTML element
HtmlTextArea Controls a <textarea> HTML element

 

 

 

 

 

 

< 참고사항 2 자바스크립트에서 서버컨트롤 이벤트 받아오기>

 

<html xmlns="http://www.w3.org/1999/xhtml">  

<head runat="server">  

    <title>Untitled Page</title>  

     <script type="text/javascript">  

             function getSelectedValue(obj) {   

            var value = "-1" //careful here, this is your default value for "not selected".     

            if (obj) {   

                value = obj.options[obj.selectedIndex].value;   

            }   

            alert(value)   

            return value    

        }    

     </script>  

</head>  

<body>  

    <form id="form1" runat="server">            

        <asp:DropDownList ID="DropDownList1" runat="server" onchange="getSelectedValue(this)">  

        <asp:ListItem Value="1">1</asp:ListItem>  

        <asp:ListItem Value="2">2</asp:ListItem>  

        <asp:ListItem Value="3">3</asp:ListItem>  

        </asp:DropDownList>  

    </form>  

</body>  

</html>  

이경우 DropDownList1 에서 이벤트가 발생해도 서버쪽으로 포스트백이 일어나지 않고

자바스크립트 함수가 클라이언트단에서 발생한다.

Posted by 불펭


protected string Alert_Msg(string msg)
    {        
        string strScript = "";
        strScript += "<script language='javascript'>";
        strScript += "alert('"+msg+"');";
        strScript += "</script>";

        return strScript;
    }

    protected void ImgBtn_Click(object sender, ImageClickEventArgs e)
    {
        if (txtQry.Text == "")
        {
            string strMsg = Alert_Msg(" 수량을 입력하세요");
            Page.RegisterStartupScript("", strMsg);
        }
        else if (txtTags.Text == "")
        {
            string strMsg = Alert_Msg(" 수량을 입력하세요");
            Page.RegisterStartupScript("", strMsg);
        }
        else {
            lblMsg.Text = "OKOK";
        }
    }

Posted by 불펭