白桦的天空

第一次的心动,永远的心痛!
  首页  :: 新随笔  :: 联系 :: 管理

网页局部刷新技术

Posted on 2005-05-13 11:19  白桦的天空  阅读(1203)  评论(0编辑  收藏  举报
来自于:http://ajax.schwarz-interactive.de/csharpsample/default.aspx

Ajax.NET - A free library for the Microsoft .NET Framework

Asynchronous JavaScript with XmlHttpRequest - C# Example

Download the latest Ajax.dll (5.5.12.4) to use it for free in your web projects.

To learn more about AJAX, visit:
Wikipedia
A New Approach to Web Applications

Visit my blog at http://weblogs.asp.net/mschwarz/.

To download this C# example click here.
Switch to the VB.NET example.

There is a usage guide (will be updated in the next days) available for the first version of the free Ajax.NET:
AjaxGuide.doc
quickGuide.txt

Use my contact form at http://weblogs.asp.net/mschwarz/ to give feedback, ideas and if you got any error.




Examples using the free Ajax.NET library

Send feedback form with Unicode chars

This example will post a form to the server, invoke the Test1 method there and return a HTML string.

On the server you will have a typical method with an additional attribute:

[Ajax.AjaxMethod]
public string Test1(string firstName, string familyName, string email, string comment)
{
  string html = "";
	
  html += "Hello " + firstName + " " + familyName + "<br>";
  html += "Thank you for your comment <b>";
  html += System.Web.HttpUtility.HtmlEncode(comment);
  html += "</b>.";
	
  return html;
}
FirstName:
FamilyName:
Email:
My Comment:
Submit
 

Send a custom type as argument

The second sample will show you how to pass a real .NET object as an argument for your method. The Test2 method will have a System.DateTime argument. The IAjaxObjectConverter will give you a simple to use client script to create the wrapper for .NET. On the client script you can use following script to create such an object:

var d = new DateTime(2005, 4, 20, 0, 0, 0);	// April 20th 2005

To call the AJAX.NET method you will write:

DemoMethods.Test2(d, callback);

The AJAX.NET wrapper on the server will check which converter can decode the Javascript object and passes the method a correct object. You can create your own IAjaxObjectConverters to allow every object to be returned or accepted.

[Ajax.AjaxMethod]
public string Test2(DateTime d)
{
  d = d.AddDays(1);
  return "The next day will be " + d.ToLongDateString() + ".";
}
DateTime (YYYY.MM.DD):
Submit
 

Currently I have following IAjaxObjectConverters:
- System.Collections.ArrayList
- System.Data.DataSet/DataTable/DataRow
- System.DateTime/TimeSpan
- System.Array


Use a System.Data.DataSet to fill a drop down box

Because you can return any object it is possible to fill a drop down box with only two lines. The list of countries will be fetched from the server after you have clicked on the link.

function callback(res)
{
  var html = [];
	
  for(var i=0; i<res.value.Tables[0].Rows.length; i++)
    html[html.length] = "<option>" + res.value.Tables[0].Rows[0].Country + "</option>";
		
  document.getElementById("display").innerHTML = "<select id=\"sel\">" + html.join("") + "</select>";
}
Country: Click here to load contry list...


Exception handling with Ajax.NET

The next example will throw a security exception on the server (System.Security.SecurityException). The callback handler will check if the error property is filled an will show the exception.

function callback(res)
{
  if(res.error != null)
    alert(res.error);
}

Click here to throw an exception on the server.

The error object will have three properties: name will be the type of the exception, description the .Message property of the exception thrown on the server, and number will be a unique ID (not yet implemented!). There is a toString() method that will output the name and the description as one string.


Session State handling with Ajax.NET

A lot of developers asked for the session station access. Now, we you can access your session variables.

First click on write to save the value i.e. "demo" to the session on the server. Now, you can press F5 (reload) and click on read. You should see the same value as you have written to the session state.

Session Handling Demo
Write value Write
Read value Read

On the server the C# method looks like following code:

[Ajax.AjaxMethod(HttpSessionStateRequirement.ReadWrite)]
public void Test5(string value)
{
  System.Web.HttpContext.Current.Session["example"] = value;
}

[Ajax.AjaxMethod(HttpSessionStateRequirement.Read)]
public string Test6()
{
  if(System.Web.HttpContext.Current.Session["example"] != null)
    return (string)System.Web.HttpContext.Current.Session["test"];

  return "First set the value...";
}

Is it possible to wait for events?

Yes, you can use System.Threading.Thread to wait for events instead of pooling every second. Because the server process does not get informed when a client browser has been closed you should not use a infinite loop.

Why to use a loop at the server instead of polling every second? You will save client requests to the server that will use the Internet bandwidth and will create a new request on the server. So, you will have 10 requests if you will poll and one request if you loop at the server for 10 seconds!

Click here to start the process at the server. There is no timer at the client's javascript. In 10 seconds the request will response and call the client callback function which will show a alert box. There will be no network traffic until the method returns. Click on the link two times and you will get two alert boxes!

Note: You can have simultaneous requests at the same time, there are no restrictions.

[Ajax.AjaxMethod]
public void Test7()
{
  int c = 0;

  do
  {
    System.Threading.Thread.Sleep(1000);
    c++;
  }
  while(c < 10);
}

Using a context on the client-side Javascript

If I want to update several elements (i.e. DIV) on the screen I had to use different callback functions in the last release. Now, you can add a context that is accessable in the callback function.

The following sample will update the element you clicked on. There is only one Javascript function that will handle the callback:

<script language="javascript">
function test8(ele)
{
  DemoMethods.Test8(test8_callback, ele);  // the server-side method has no arguments!
}

function test8(res)
{
  if(res.error == null && res.context != null)
    res.context.innerHTML = res.value;
}
</script>

<span onclick="test8(this);">Element1</span>
<span onclick="test8(this);">Element2</span>

DIV elements: Click here Click here Click here


Return arrays and System.Collections.ICollection objects

The free Ajax.NET library is supporting arrays and objects the using the ICollection interface.

[Ajax.AjaxMethod]
public System.Collections.Specialized.StringCollection Test9()
{
  System.Collections.Specialized.StringCollection s = new System.Collections.Specialized.StringCollection();

  s.Add("Michael");
  s.Add("Hans");

  return s;
}

[Ajax.AjaxMethod]
public object[] Test10()
{
  object[] o = new object[3];

  o[0] = "Michael";
  o[1] = DateTime.Now;
  o[2] = true;

  return o;
}

[Ajax.AjaxMethod]
public System.Collections.ArrayList Test11()
{
  System.Collections.ArrayList a = new System.Collections.ArrayList();

  a.Add("Michael");
  a.Add(DateTime.Now);
  a.Add(true);
  
  Person p1 = new Person();
  p1.FirstName = "Michael";
			
  Person p2 = new Person();
  p2.FirstName = "Tanja";

  a.Add(p1);
  a.Add(p2);

  return a;
}

On the client-side Javascript you will get an array with each item:

function test11_callback(res)
{
  if(res.value[2])                           // bolean
    alert(res.value[1].toLocaleString());    // date
    
  alert(res.value[3].FirstName + " + " + res.value[4].FirstName);
}

Click here to get the result of the method DemoMethods.Test11.


Enable tracing for Ajax.NET requests

If you want to enable tracing for your Ajax.NET requests enable the tracing in your web.config file:

<configuration>
  <system.web>
    <trace enabled="true" requestLimit="100" pageOutput="false" />
  </system.web>
</configuration>

To view the trace open a new browser window and open the trace.axd page. This is the default ASP.NET page that will display tracing information off all your requests.

Category  Message                                                    From First(s) From Last(s)
Ajax.NET  Begin ProcessRequest   
Ajax.NET  Invoking CSharpSample.DemoMethods.Test11                   0,000181      0,000181 
Ajax.NET  JSON string: ['Michael',new Date(2005,4,10,9,39,27),true]  0,000365      0,000184 
Ajax.NET  End ProcessRequest                                         0,000387      0,000022

Use your own common Javascript file

Some developers asked for a debug version of the common.ashx (Javascript) file. With one setting in the web.config you can use your own common Javascript file:

<configuration>
  <configSections>
    <sectionGroup name="ajaxNet">
      <section name="ajaxSettings" type="Ajax.AjaxSettingsSectionHandler, Ajax" />
      <section name="ajaxConverters" type="Ajax.AjaxConverterSectionHandler, Ajax" />
    </sectionGroup>
  </configSections>

  <ajaxNet>
    <ajaxSettings>
      <commonAjax enabled="true" path="ajax.js" language="javascript" />
    </ajaxSettings>
  </ajaxNet>

The example above will include the ajax.js instead of the common.ashx.


Add/remove your own IAjaxObjectConverters

The free Ajax.NET library has already some useful build-in object converters. Currently there are following IAjaxObjectConverters available:

  • Ajax.JSON.ArrayListConverter (obsolete, replaced by IEnumerableConverter)
  • Ajax.JSON.DataRow/DataTable/DataSetConverter
  • Ajax.JSON.DateTime/TimeSpanConverter
and the Ajax.JSON.DefaultConverter which will use the .ToString() method to get the value of the object.

All of them are enabled be default. To remove or add your own IAjaxObjectConverters you can modify your web.config file:

<configuration>
  <configSections>
    <sectionGroup name="ajaxNet">
      <section name="ajaxSettings" type="Ajax.AjaxSettingsSectionHandler, Ajax" />
      <section name="ajaxConverters" type="Ajax.AjaxConverterSectionHandler, Ajax" />
    </sectionGroup>
  </configSections>

  <ajaxNet>
    <ajaxConverters>
      <add type="Namespace.Class1, Assembly" />
      <remove type="Ajax.JSON.DataSetConverter, Ajax" >
    </ajaxConverters>
  </ajaxNet>
</configuration>

The example above will add a converter Namespace.Class, Assembly and remove the default converter that will handle the DataSet objects.

To implement your own IAjaxObjectConverters the source code of the IEnumerableConvert.cs will be available in this exmaple. Feel free to download the source code here.

Return your own classes (Updated: 10-05-2005)

With Ajax.NET you can return your own classes you are already using in C#. You only have to add the [Serializable] attribute to the class:

[Serializable]
public class Person
{
  public string FirstName;
  public string FamilyName;
  public int Age = 0;

  public Person NewChild()
  {
    Person p = new Person();
    p.FamilyName = FamilyName;

    return p;
  }

  public Person[] Children = null;
}

On the client-side you can use it like you will do it on the server:

function test12_callback(res)
{
  var s = res.value.FirstName + " " + res.value.FamilyName + ":\r\n";
	
  for(var i=0; i<res.value.Children.length; i++)
    s += "\t" + res.value.Children[i].FirstName + "\r\n";
	
  alert(s);
}

Click here to run the method above.


Use a System.Data.DataSet to retreive a SQL query

A lot of web applications are using the MSDE or Microsoft SQL Server to store data. In some case you want to return a complete DataSet (or DataTable/DataRow) to the client. With the free Ajax.NET library you can directly return a DataSet to the client without any need to convert. There is no sourc code change, only add the Ajax.AjaxMethod to the method:

[Ajax.AjaxMethod]
public DataSet Test15()
{
  SqlConnection conn = new SqlConnection("server=(local);Integrated Security=true;Initial Catalog=master;");
  SqlCommand cmd = new SqlCommand("SELECT [name], [filename] FROM dbo.sysdatabases", conn);
  SqlDataAdapter da = new SqlDataAdapter(cmd);

  DataSet ds = new DataSet();

  try
  {
    conn.Open();

    try
    {
      da.Fill(ds);    // fill DataSet with data
    }
    catch(Exception)
    {
      return null;
    }
    finally
    {
      conn.Close();
      conn.Dispose();
    }
  }
  catch(Exception)
  {
    return null;
  }

  return ds;
}

On the client-side Javascript I made a object that will have similar properties as the object in .NET. A DataTable will have the .Tables array, and each table will have their .Rows. The columns will be properties instead of an array, see the example below:

<script language="Javascript">

function test15_callback(res)
{
  if(typeof(res) == 'object' && typeof(res.Tables) == 'object')
  {
    var html = [];
    
    for(var i=0; i<res.Tables[0].Rows.length; i++)
      html[html.length] = '<p>' + res.Tables[0].Rows[i].ColumnName + '</p>';
   }
}

</script>

Each column value will have its correct data type: an integer will be a number, a DateTime will be a correct date object can be used in Javascript:

alert(res.Tables[0].Rows[0].DateTimeColumn.toLocaleString());
		
var y = res.Tables[0].Rows[0].IntegerColumn +10;

if(res.Tables[0].Rows[0].BooleanColumn == false)
  alert(res.Tables[0].Rows[0].StringColumn + 'my sample');

Build a simple to use NewsTicker

Some developers asked me how to build a news ticker with the Ajax.NET library. I build a small example how you can do this. On the server-side we will have a database (or xml file, whatever) that stores the T to be displayed, the URL and the Seconds each news ticker will be on the screen. I'm using a simple array of 10 NewsTicker objects that hold these information and select one with System.Random:

[Ajax.AjaxMethod]
public NewsTicker Test13()
{
  NewsTicker[] nt = new NewsTicker[10];

  // following code will be replaced by getting a random item
  // from a database or xml file
  
  nt[0] = new NewsTicker("Ajax.NET Library", "http://ajax.schwarz-interactive.de", 20);
  nt[1] = new NewsTicker("Google Search", "http://www.google.com", 20);
  [...]
  nt[9] = new NewsTicker("Free Download!!", "http://ajax.schwarz-interactive.de", 20);

  Random r = new Random(System.DateTime.Now.Second);
  int i = r.Next(0, nt.Length);

  return nt[i];
}

The NewsTicker will be a own class the has the Serializable attribute:

[Serializable]
public class NewsTicker
{
  public NewsTicker(string text, string url, int duration)
  {
    this.Text = text;
    this.URL = url;
    this.Duration = duration;
  }

  public string Text;
  public string URL;
  public int Duration = 60;
}

On the client-side I will use the window.setTimeout command to wait the time is specified in the .Duration property:

function test13()
{
  // call the Ajax.NET method on the server to get
  // a new news ticker object
  
  DemoMethods.Test13(test13_callback);
}

function test13_callback(res)
{
  if(typeof(res.value) == 'object')
  {
    // display the news ticker
    
    document.getElementById('newsticker').innerHTML = '<a href="' + res.value.URL + '">' + res.value.Text + '</a>';
    window.setTimeout(test13, res.value.Duration * 1000);
  }
}

Here you can have a look on the live news ticker using Ajax.NET: ASP.NET Weblogs


Is it possible to return images?

Currently I have implemented a ImageConverter that will support System.Drawing.Bitmap objects. On the server-side method you can create a Bitmap with GDI+ methods. On the client-side you will get a Image object that you can append to HTML elements. The Image object will have the .src property where you can get the URL of the image:

[Ajax.AjaxMethod]
public System.Drawing.Bitmap Test16()
{
  Bitmap bmp = new Bitmap(200, 50);
  Graphics g = Graphics.FromImage(bmp);
		
  g.FillRectangle(new SolidBrush(Color.Yellow), 0, 0, 199, 49);
  g.DrawString(DateTime.Now.ToString(), new Font("Arial", 10), new SolidBrush(Color.Red), 10, 10);

  return bmp;
}

On the client you will get a Image object that you can add to an element or where you can read the .src property:

function test16_callback(res)
{
  if(typeof(res.value) == 'object')
    document.getElementById("imageholder").appendChild(res.value);
}

Click here to create a bitmap showing the current time:

I got some requests what new on this? We could refresh images in the past, too! Ok, the difference is that you can return more than one image, or you can return a class with a lot of properties and one image. Others asked me if they should use it for MouseOvers. No, I think onmouseover is used with static images instead of dynamic rendered images. You can use it i.e. to return an object with a diagram and some values in a DataSet.