Section 9.1. Using a ListView Control


9.1. Using a ListView Control

The best way to display data in Atlas is using the ListView control (in xml-script, the <listView> element). This control can iterate through a list so that the user can view the resultthat's where the name of the control comes from.

Within a <listView> xml-script element, you can define two display templates:


<layoutTemplate>

To specify the layout and appearance of the data


<itemTemplate>

To specify the layout for each individual element (item) of the data

In addition, you set a number of attributes (which will be detailed in the following section), and can bind the data to the elements. As a target element, you can choose from any suitable HTML element. Static lists (numbered or bulleted), selection lists (<select> element), and tables are the elements most commonly used, because HTML provides these elements precisely to display a lot of data.

9.1.1. Binding Data to a ListView Control

An obvious choice for displaying data from a server data source is an un-ordered list. The following example will query data from a server database and display it as an HTML bulleted list.

Before we dig deep into xml-script, let's add the HTML markup used to display the data from the data source. First of all, you'll need a container to hold the data-display list. Here's the markup:

 <div >   vendor list goes here</div> 

Next, you need to put the templates (layout and item) in a container. The style of this container will be set to invisible (display: none). Note that the data will be displayed in a different HTML element, the container that was just mentioned for holding the data, which initially functions only as a placeholder.

In the layout container, we need a couple of elements (and associated IDs):

  • An outer container that represents the <layoutTemplate> element

  • An inner container that reflects the <itemTemplate> element

  • Placeholders for data items (such as <span> elements) from the data source

The following snippet presents an example that can be used for an un-ordered list (a <ul> element). As an outer container, a <div> element is used. The individual data item is displayed using a <li> element. As its parent element, the <ul> element can be used. This leads to the following markup serving as the placeholder:

 <div style="display: none;"> <!-- hide the placeholders -->   <div > <!-- layout template container -->     <ul > <!-- item template container -->       <li ><span >vendor name goes here         </span> </li>     </ul>   </div> </div> 

You cannot eliminate an element by merging the outer, invisible <div> with the layout template element (vendorsLayout, in the example). If you do, the output will be invisible, too, even after being inserted into the output element. You need the additional <div> element (reflecting <itemTemplate>), which itself is not hidden via CSS (only the outer <div> is).


Before we continue creating the page to display the data, we need to create the data to work with, which we will do by creating a web service. You need something that exposes the data you want as properties of the object returned by the web service The Atlas data binding mechanism for the listView element does not accept ADO.NET datasets directly. The two most used options are:

  • A DataTable object

  • A custom class in which all data is put in class members

The custom class gives you more flexibility, but probably also means more code. Using a DataTable object, on the other hand, is rather easy: create a DataSet object and then access its Table[0] property to return the desired data table with all data in it. As noted in Chapter 1, we will use the AdventureWorks database for sample data. In this case, the fields AccountNumber and Name are from the Vendor table are queried. The code shown in Example 9-1 shows the web service that returns the AdventureWorks data as a DataTable object.

Example 9-1. A web service that returns a DataTable object

 ListViewVendors.asmx <%@ WebService Language="C#"  %> using System; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using System.Data; using System.Data.SqlClient; [WebService(Namespace = "http://hauser-wenz.de/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Vendors : System.Web.Services.WebService {   [WebMethod]public DataTable GetVendors()   {     SqlConnection conn = new SqlConnection(       "server=(local)\\SQLEXPRESS; Integrated Security=true; Initial Catalog=AdventureWorks");     conn.Open();     SqlCommand comm = new SqlCommand(       "SELECT TOP 10 AccountNumber, Name FROM Purchasing.Vendor",        conn);     SqlDataAdapter adap = new SqlDataAdapter(comm);     DataSet ds = new DataSet();     adap.Fill(ds); return ds.Tables[0];   } } 

Alternatively, the web service can be written to return an array of a custom type based on the data, instead of returning a DataTable object directly. Since the example requires the AccountNumber and Name fields of the AdventureWorks database, a class with two string properties must be used. The following code snippet shows how you might implement the custom type:

 public class Vendor {   string _AccountNumber;   string _Name;   public string AccountNumber   {     get     {       return _AccountNumber;     }     set     {       _AccountNumber = value;     }   }   public string Name   {     get     {       return _Name;     }     set     {       _Name = value;     }   }   public Vendor(string AccountNumber, string Name)   {     this._AccountNumber = AccountNumber;     this._Name = Name;   }   public Vendor()   {   } } 

The empty constructor public Vendor() { } is required so that the class is serializable. If you omit this class constructor, you get an error when calling the .asmx file directly in your browser. However, the web service still works and can be called from the script. This additional constructor just makes testing easier, but does not add any functionality to the script that is required.


The web service that uses the custom type queries the Purchasing.Vendors table in AdventureWorks and selects the first 10 entries, like the first web service example does:

 [WebMethod] public Vendor[] GetVendors() {   SqlConnection conn = new SqlConnection(     "server=(local)\\SQLEXPRESS; Integrated Security=true; Initial Catalog=AdventureWorks");   conn.Open();   SqlCommand comm = new SqlCommand(     "SELECT TOP 10 AccountNumber, Name FROM Purchasing.Vendor",     conn);   SqlDataReader dr = comm.ExecuteReader(); 

Then the code iterates through the list and creates a Vendor element for each entry in the data table. This list is finally converted into an array and returned from the service:

   List<Vendor> v = new List<Vendor>();   while (dr.Read())   {     v.Add(new Vendor(       dr["AccountNumber"].ToString(),       dr["Name"].ToString()));   }   return v.ToArray(); } 

This example uses a construct that's new in the .NET Framework Version 2.0: generics. To be able to use generics, you have to import the associated namespaces (System.Collections for List support, and System.Collections.Generic).Example 9-2 shows the code you get in the end for the web service.

Example 9-2. This web service returns a custom type

 ListViewVendorsCustom.aspx <%@ WebService Language="C#"  %> using System; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using System.Data; using System.Data.SqlClient; using System.Collections; using System.Collections.Generic; public class Vendor {   string _AccountNumber;   string _Name;   public string AccountNumber   {     get     {       return _AccountNumber;     }     set     {       _AccountNumber = value;     }   }   public string Name   {     get     {       return _Name;     }     set     {       _Name = value;     }   }   public Vendor(string AccountNumber, string Name)   {     this._AccountNumber = AccountNumber;     this._Name = Name;   }   public Vendor()   {   } } [WebService(Namespace = "http://hauser-wenz.de/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Vendors : System.Web.Services.WebService {   [WebMethod]   public Vendor[] GetVendors()   {     SqlConnection conn = new SqlConnection(       "server=(local)\\SQLEXPRESS; Integrated Security=true; Initial Catalog=AdventureWorks");     conn.Open();     SqlCommand comm = new SqlCommand(       "SELECT TOP 10 AccountNumber, Name FROM Purchasing.Vendor",       conn);     SqlDataReader dr = comm.ExecuteReader();     List<Vendor> v = new List<Vendor>();     while (dr.Read())     {       v.Add(new Vendor(         dr["AccountNumber"].ToString(),         dr["Name"].ToString()));     }     return v.ToArray();   } } 

In the source code downloads for this book, both variants of the web serviceone using a DataTable and one using a custom typeare included under ListViewVendors.asmx and ListViewVendorsCustom.asmx. You can use both of them for the following examples; they are interchangeable.


Now back to the ASP.NET page, where the web service is called. Web services will be covered in greater detail in Chapter 10, so here is just a recap of what must be done to use them (you first saw them in Chapter 1). First, the .asmx file must be referenced in the xml-script; then, a client-side proxy is generated: a local object behaving like the remote web service. That means that the local object has the same methods the remote service has; calling the local methods in turn calls the remote methods. This call is done asynchronously (just like the XMLHttpRequest calls were done in Chapter 3); once again, a callback function is used once the web service returns data.

First, when including the Atlas ScriptManager, be sure to reference the web service's .asmx file. Here's the markup you need:

 <atlas:ScriptManager runat="server"><Services>     <atlas:ServiceReference Path="ListViewVendors.asmx" /></Services> </atlas:ScriptManager> 

When the page has been loaded, you have to call the web service. However, the term "when the page has been loaded" is a bit misleading. The following code, for instance, would not work:

 <script language="JavaScript" type="text/javascript">   window.onload = function() {     Vendors.GetVendors(callComplete);   } </script> 

The load event of an HTML page occurs when the HTML of the page has been fully loaded. However at this point, it is possible that the Atlas library and the web service proxy have not been fully loaded yet. Therefore, this code could fail with a JavaScript error message such as "Vendors is not defined." Therefore it is better to add a delay. You could use JavaScript's window.setTimeout() method, or you wait and have the user click a button to get the data, using syntax like the following (the function loadVendors() will be implemented in the next step):

 <input type="button" value="Load Vendors" onclick="loadVendors();" /> 

The best way is to use the special pageLoad() method that Atlas provides:

 <script language="JavaScript" type="text/javascript">   function pageLoad() {     Vendors.GetVendors(callComplete);   } </script> 

Then, you can call the web service:

 <script language="JavaScript" type="text/javascript">   function loadVendors() {     Vendors.GetVendors(callComplete);   } 

and receive the results in the callback function. In the callback function, you have to do the following:

  1. Get a reference to the element you want to use to display the data (in the example, that's <div />).

  2. Access its control property and call its set_data() method, submitting the result of the web service call.

This leads to the following code:

   function callComplete(result) {     document.getElementById("output").control.set_data(result);   } </script> 

There is only one thing left to do, which is a little tricky: create the xml-script markup. Starting off is easy: create a <script> element, nest a <page> element, and then nest a <components> element:

 <script type="text/xml-script">   <page xmlns="http://schemas.microsoft.com/xml-script/2005">     <components>     ...     </components>   </page> </script> 

Now within <components>, you can place the <listView> element. This tag requires several attributes:


itemTemplateParentElementId

The ID of the element that is the parent of the individual item elements; sounds confusing, but basically it references the <ul> element in the example


id

The ID of the element where the result will be put.

The following markup is the result for the unordered list example:

 <listView itemTemplateParentElement > ... </listView> 

Within <listView>, the layout template and the item template must be defined. The former is easyyou just have to reference the outer <div>:

 <listView itemTemplateParentElement > <layoutTemplate>     <template layoutElement="vendorsLayout" /></layoutTemplate>   ... </listView> 

The <itemTemplate> is a bit trickier. This time, you have to reference the individual item; in the example, that's the <li> element.

 <listView itemTemplateParentElement >   <layoutTemplate>     <template layoutElement="vendorsLayout" /> </layoutTemplate><itemTemplate>     <template layoutElement="vendorsItem">     ...     </template></itemTemplate> </listView> 

Within the <template> element, you have to define the bindings for each item. Since you want to output text, you can use the <label> element, which provides a representation of the Atlas Label web control. In the markup code, the following two properties are required:


dataPath

The name of the class property you want to bind


property

The property of the Label control you want to bind to

This leads to the following markup:

 <listView itemTemplateParentElement >   <layoutTemplate>     <template layoutElement="vendorsLayout" />   </layoutTemplate>   <itemTemplate>     <template layoutElement="vendorsItem">     <label >     <bindings>     <binding dataPath="Name" property="text" />     </bindings>     </label>     </template>   </itemTemplate> </listView> 

A lot of work, unfortunately without any IntelliSense support. But nevertheless, the result is rewarding. Example 9-3 shows the complete markup and script for the page.

Example 9-3. Binding data to an HTML list

 ListViewUnorderedList.aspx <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">   <title>Atlas</title> <script language="JavaScript" type="text/javascript">     function loadVendors() {     Vendors.GetVendors(callComplete);     }     function callComplete(result) {     document.getElementById("output").control.set_data(result);     } </script> </head> <body>   <form  runat="server">     <atlas:ScriptManager runat="server">     <Services>     <atlas:ServiceReference Path="ListViewVendors.asmx" />     </Services>     </atlas:ScriptManager>     <input type="button" value="Load Vendors" onclick="loadVendors();" />     <div >     vendor list goes here</div>     <div style="display: none;">     <div >     <ul >     <li ><span >vendor name goes here</span> </li>     </ul>     </div>     </div>   </form> <script type="text/xml-script">     <page xmlns="http://schemas.microsoft.com/xml-script/2005">     <components>     <listView itemTemplateParentElement >     <layoutTemplate>     <template layoutElement="vendorsLayout" />     </layoutTemplate>     <itemTemplate>     <template layoutElement="vendorsItem">     <label >     <bindings>     <binding dataPath="Name" property="text" />     </bindings>     </label>     </template>     </itemTemplate>     </listView>     </components>     </page> </script> </body> </html> 

Figure 9-1 displays the results of loading the page and clicking on the Load Vendors button.

Figure 9-1. Upon clicking the button, the list is populated


What happens now is the following:

  1. When you click the button, the web service is called.

  2. Once the web service returns data, the callback function is executed.

  3. The JavaScript code iterates through the result set from the web service.

  4. According to the data in the xml-script, the placeholders are filled with data and the list is created in the invisible <div> element.

  5. The list is copied (using DOM functions) to the final destination, the output <div> element.

9.1.2. Binding Data to an HTML Table

Instead of a list, you could use an HTML table to display dataan Atlas version of an ASP.NET GridView data control, so to speak. To do so, you have to change the HTML markup a bit. Instead of the <ul> and <li> elements, you need <table> and <tr> elements. Also, since a table is used now, all the data from the web service can be used, including both the Name and AccountNumber fields.

For every data item, you create a table row (<tr>). Within this row, create two cells (<td>), one for each database column returned from the web service.

Using an HTML Selection List

Unfortunately, the approach from Example 9-3 does not work with HTML <select> list elements. Have a look at what a <select> element normally looks like:

 <select>   <option value="1">one</option>   <option value="2">two</option>   <option value="3">three</option> </select> 

Within an <option> element, no other HTML is allowed. So you might very well try something like this:

 <select>   <option value="1"><span >one</span></option>   <option value="2">two</option>   <option value="3">three</option> </select> 

but it will not work. Therefore, you cannot use the approach from Example 9-3 to fill a selection list with data from a data source. You can, however, use one of the other Atlas techniques covered in this book to fill the list dynamically: Atlas's Select client-side control also supports data binding!


Here is the (hidden) placeholder to which Atlas binds server-side data:

 <div style="display: none;">   <div >     <table >       <tr><th>Account Number</th><th>Name</th></tr>       <tr >         <td><span >vendor account number goes here</span> </td>         <td><span >vendor name goes here</span></td>       </tr>     </table>   </div> </div> 

However, this will not work. Mozilla browsers do show the table, but in Internet Explorer, the browser remains blank. Internet Explorer is very particular about the structure of the dynamically generated HTML tablean interesting fact, since Internet Explorer has a history of being very gentle to incorrect HTML markup.

So to make the data-bound HTML table work, you have to create the table with a <thead> and a <tbody> section. The <tbody> section is the parent element of each data item, as rendered using a <tr> element.

You could also add a <tfoot> element, but this must occur before the <tbody> element.

 <table>   <thead>     <tr><th>Account Number</th><th>Name</th></tr>   </thead>   <tbody >     <tr >       <td >vendor account number goes here</td>       <td >vendor name goes here</td>     </tr>   </tbody> </table> 

In xml-script, you have to add the additional binding for the new placeholder element. Then, the example works as before: when you click the HTML button, the web service is called, its result is parsed into the vendorsLayout element, and the result is copied into the ouput element. Example 9-4shows the complete code, with changes highlighted in bold.

Example 9-4. Binding data to an HTML table

 ListViewTable.aspx <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">   <title>Atlas</title>   <script language="JavaScript" type="text/javascript">     function loadVendors() {       Vendors.GetVendors(callComplete);     }     function callComplete(result) {       document.getElementById("output").control.set_data(result);     }   </script> </head> <body>   <form  runat="server">     <atlas:ScriptManager runat="server">       <Services>         <atlas:ServiceReference Path="ListViewVendors.asmx" />       </Services>     </atlas:ScriptManager>     <input type="button" value="Load Vendors" onclick="loadVendors();" />     <div >       vendor list goes here</div>     <div style="display: none;">       <div >     <table>     <thead>     <tr><th>Account Number</th><th>Name</th></tr>     </thead>     <tbody >     <tr >     <td >vendor account number goes here</td>     <td >vendor name goes here</td>     </tr>     </tbody>     </table>       </div>     </div>   </form>   <script type="text/xml-script">     <page xmlns="http://schemas.microsoft.com/xml-script/2005">       <components>         <listView itemTemplateParentElement >           <layoutTemplate>             <template layoutElement="vendorsLayout" />           </layoutTemplate>           <itemTemplate>             <template layoutElement="vendorsItem">     <label >     <bindings>     <binding dataPath="AccountNumber" property="text" />     </bindings>     </label>               <label >                 <bindings>                   <binding dataPath="Name" property="text" />                 </bindings>               </label>             </template>           </itemTemplate>         </listView>       </components>     </page>   </script> </body> </html> 

Figure 9-2 shows the results of displaying the page.

Figure 9-2. Clicking the button generates and fills the table





Programming Atlas
Programming Atlas
ISBN: 0596526725
EAN: 2147483647
Year: 2006
Pages: 146

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net