Please first note that there are many similarities in the
example introduced here with the first one in Part 1; however, we will lay
emphasis on the other aspects not yet mentioned previously. Now, you can start
Visual Studio 2005 and still select the "ASP.NET AJAX CTP-Enabled Web
Site" template to create a new website and name it ListEmployees.
The following Figure shows its design-time snapshot.
Figure 2: The design-time sample web page
In this sample we will still use a ListView control to
display an employee list, which is automatically executed when the page has
been loaded completely. Now, let us follow the steps explained above to work.
1. Design the web page
Listing 2
//……omitted
<link rel="stylesheet" type="text/css" href="site.css" />
//……omitted
<span style="font-size: 24pt; color: #6600cc">
<strong>Employee Info Searching Results:</strong>
</span>
<div id="header">
<span >
<strong>EmpID</strong>
</span>
<span >
<strong>Name</strong>
</span>
<span>
<strong>Address</strong>
</span>
</div>
<hr style="width: 457px" align="left" />
<div id="searchResults">
</div>
<div style="visibility:hidden;display: none;">
<div id="searchResults_layoutTemplate">
<div id="searchResults_itemTemplate" >
<span id="searchResults_ID"></span>
<span id="searchResults_Name"></span>
<span id="searchResults_Address"></span>
</div>
<div id="searchResults_separatorTemplate" class="TaskSeparator">
</div>
</div>
<div id="NoDataTemplate">Waiting...</div>
</div>
Here, we first create a constant header line within a div block
to elegantly list the employee records below. Then starting from the line in
bold, we construct the HTML elements in relation to each possible template of
the advanced MS AJAX client-side control—ListView.
Here we have also used the separatorTemplate template
to separate the neighboring records. As mentioned in Part 1, control ListView
shows records in the AJAX way. So here we have also used the emptyTemplate
template—NoDataTemplate
so that when data from the WebService is being loaded the user can get a
friendly hint.
2. Create the corresponding MS AJAX
client-side controls
Here, mainly one MS AJAX client-side control, ListView, is
created to match the HTML elements (mainly a set of divs), with its each
template in correspondence with different div elements.
Additionally, we have also defined three label elements
matching the three span elements above.
Create the Web Service
Right click the project and choose "Add new item"
and create a new Web Service named EmployeeDataService.asmx.
Here, we have also made the Web Service derived from DataService,
and then we have created four CRUD WebMethods. Listing 3 shows the crucial code
snippet.
Listing 3
//……
using System.ComponentModel;
using System.Web.Script.Services;
using Microsoft.Web.Preview.Services;
//……
[ScriptService]
public class EmployeeDataService: DataService
{
static List < Employees > _data;
static int _nextId;
static object _dataLock = new object();
private static List < Employees > Data
{
get
{
if (_data == null)
{
lock(_dataLock)
{
if (_data == null)
{
_data = new List < Employees > ();
_data.Add(new Employees(0, "John Smitch", "1970 Napa Ct."));
_data.Add(new Employees(1, "Mary Smitch", "9833 Mt. Dias Blv."));
_data.Add(new Employees(2, "Mike Jodan", "7484 Roundtree Drive"));
_data.Add(new Employees(3, "Ronald Adina", "9539 Glenside Dr"));
_data.Add(new Employees(4, "Blue Yonder Airlines", "1226 Shoe St."))
;
_data.Add(new Employees(5, "Milton Albury", "1399 Firestone Drive"))
;
_data.Add(new Employees(6, "Phyllis Allen", "5672 Hale Dr."));
_data.Add(new Employees(7, "Stanley Alan", "6387 Scenic Avenue"));
_data.Add(new Employees(8, "Alexander Berger", "8713 Yosemite Ct."))
;
_nextId = 9;
}
}
}
return _data;
}
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Delete)]
public void DeleteRecords(int id)
{
foreach (Employees row in _data)
{
if (row.Id == id)
{
lock(_dataLock)
{
_data.Remove(row);
}
break;
}
}
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Select)]
public Employees[]SelectRecords()
{
return EmployeeDataService.Data.ToArray();
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Insert)]
public Employees InsertRecords(string name, string Address)
{
Employees newRow;
lock(_dataLock)
{
newRow = new Employees(_nextId++, name, Address);
_data.Add(newRow);
}
return newRow;
}
[WebMethod][DataObjectMethod(DataObjectMethodType.Update)]
public void UpdateRecords(Employees updateRow)
{
foreach (Employees row in _data)
{
if (row.Id == updateRow.Id)
{
row.Name = updateRow.Name;
row.Address = updateRow.Address;
break;
}
}
}
}
public class Employees
{
private int _id;
private string _name;
private string _address;
[DataObjectField(true, true)]
public int Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
[DataObjectField(false)][DefaultValue("")]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
[DataObjectField(false)][DefaultValue("")]
public string Address
{
get
{
return _address;
}
set
{
_address = value;
}
}
public Employees()
{
_id = - 1;
}
public Employees(int id, string name, string address)
{
_id = id;
_name = name;
_address = address;
}
}
Here, for simplicity, we have used an in-memory data source,
a static Array with basic element being Employees
object to simulate the web service with states; while in enterprise development
this is not a good habit.
Next, let us dwell upon the mysterious DataService.
About DataService
To be honest, I have scoured the entire Internet with the
word "DataService," but obtained nearly nothing besides several
discontinuous articles (such as this one on http://forums.asp.net)
referring to it and the newest MS AJAX samples shipped with the framework. In
addition, I use the object browser and the reflector tool inside Visual Studio
2005 to grab a little more info about it. So now I have pieced them together and
drawn some conclusions on the mysterious "DataService."
Recently, from the web and MS AJAX tutorials, you can easily
find many related tips on consuming general web services (derived from WebService) from inside the client-side JavaScript of ASP.NET
AJAX framework, while there are few samples on DataService
derived web services accessed from the client side. According to my preliminary
research, DataService should be used under the
following typical circumstance: when we use the advanced client-side rendering controls,
such as ItemView, ListView, etc. and their data sources are database-related
data operations (e.g. CRUD) provided by web services, while under the other kinds
of circumstances we can choose the more common WebService.
Is it true that Microsoft has specially designed DataService
for ListView/ItemView? By examining the assembly—Microsoft.Web.Preview.dll
using the .NET object browser and Lutz Roeder's
advanced .NET Reflector tool (note: this is
an excellent thing; with it you can easily obtain the complete source code for
class DataService). We have found DataService
is derived from WebService and only exposed the
following two Web Methods.
Listing 4
public DataTable <span class=Bold>GetData</span>(object parameters, string loadMethod);
public DataTable <span class=Bold>SaveData</span>(ChangeList changeList, object parameters, string loadMethod);
By examining the source code of Sys.Preview.Data.DataSource.save
and Sys.Preview.Data.DataSource.load in file
PreviewScript.js, you can easily find the following.
1. Method save requires that property
serviceType of DataSource must
be of type of Sys.Preview.Data.ServiceType.DataService and
then the save method is directed to call method SaveData of class DataService.
2, There are two things with method load:
when property serviceType of DataSource
is of type of DataService, then the load
method is directed to call method GetData of class DataService; when property serviceType
of DataSource is not of type of DataService,
i.e. Handler according to discuss with DataSource in Part
1, the system will create a general delegate and then method
load is directed to Sys.Net.WebRequest(). When we further study the methods
SaveData and GetData of class DataService using .NET Reflector, we can find more
interesting things—you will know what
the meaning of property loadMethod of DataSource
is and how the database CRUD operations correspond to the Delete (DeleteRecords), Insert (InsertRecords),
Select (SelectRecords), and Update (UpdateRecords)
methods of your DataService and even more secrets…
For brevity, here we do not give their detailed analyses, but
I leave this to the readers.
Author's Note: I highly recommend you
study the source code of class DataService carefully, so
that you can get the most of you originally want.
With the newly-introduced attributes, DataObjectMethod
and DataObjectField (you can also refer to two classes DataObjectFieldAttribute and DataObjectMethodAttribute
in MSDN for further research), we can easily achieve our aims in providing properties
and methods to be used by control DataSource, such as Fill, Select, Update, Insert,
and Delete operations. All these are to be provided by the easy xml-script
declarative programming in the following section.
Implement the binding
Now, let us see how to use the xml-script declarative mode
to achieve the ListView control data binding. Here is the xml-script part in
correspondence with HTML elements definitions in Listing 2 above.
Listing 5
<script type="text/xml-script">
<page xmlns="http://schemas.microsoft.com/xml-script/2005">
<components>
<dataSource id="EmployeeDataSource" serviceURL="EmployeeDataService.asmx" >
</dataSource>
<listView id="searchResults" itemTemplateParentElementId="searchResults_layoutTemplate" >
<bindings>
<binding dataContext="EmployeeDataSource" dataPath="data" property="data" />
</bindings>
<layoutTemplate>
<template layoutElement="searchResults_layoutTemplate" />
</layoutTemplate>
<itemTemplate>
<template layoutElement="searchResults_itemTemplate">
<label id="searchResults_ID">
<bindings>
<binding dataPath="Id" transform="Add" property="text" />
</bindings>
</label>
<label id="searchResults_Name">
<bindings>
<binding dataPath="Name" property="text" />
</bindings>
</label>
<label id="searchResults_Address">
<bindings>
<binding dataPath="Address" property="text" />
</bindings>
</label>
</template>
</itemTemplate>
<separatorTemplate>
<template layoutElement="searchResults_separatorTemplate" />
</separatorTemplate>
<emptyTemplate>
<template layoutElement="NoDataTemplate" />
</emptyTemplate>
</listView>
<application>
<load>
<invokeMethodAction target="EmployeeDataSource" method="load" />
</load>
</application>
</components>
</page>
</script>
In the first lines of code in bold, we have define a dataSource to be used in the following components. Note here
that although not explicitly declared, the ServiceType
property of this dataSource has been set to DataService. From the second part of code in bold, we start
the concrete data binding, with property dataContext to
specify the data source of ListView, dataPath to specify
the binding property of the source, property to specify
the bound property of the target. And further, we start to bind property Id of to property text of label searchResults_ID, property Name of to
property text of label searchResults_Name,
and property Address of to property text
of label searchResults_Address. In the first binding,
we introduce a transformer of type Add (there are
several types, refer to the source code of class Sys.Preview.BindingBase in file
PreviewScript.js). Since the value of property DataIndex
exposed by DataTable (the data format bound to ListView
when data from DataService arrives at the client side) starts from 0, using of Add transformer can automatically lead to its value plus 1,
which will rather adapt to the flavors of common users. The final <application> block tells the system to automatically invoke
the load method of data source EmployeeDataSource.
Why load and what behind load? Please
find the answer in the DataService discuss above.
Harvesting
It is no use wasting more words, so just see the final
run-time screenshot in Figure 3.
Figure 3
When the sample application starts up, the data bindings
automatically happen and then the ListView control gets rendered on the screen.