欢迎大家访问我的BLOG,我会多多的出原创文章,希望大家支持我,为我祈祷,让我实现我的三个梦想!再30岁能成为一个名优秀的软件架构师!

DataGrid大全

创建完全可编辑的 DataGrid

在论坛中我看到过许多相同或相似的问题:我怎样在我的DataGrid的每一行中放置检查框、文本框等等?怎样更新它们的值?答案相当简单,在这篇文章中,我将向你展示如何完成它。

我们都知道,DataGrid是一个功能非常强大的工具。根据我的经验,在90%以上的时间中, DataGrid 都被用来显示数据,并可能一次编辑一行数据。  而某些时候,可能需要一次编辑多行,甚至是所有数据。一个实际的例子就是在网上销售物品的应用程序中, 顾客可能一次要变更他们篮子中的一种或多种物品,单击检查框移去他们不想要的商品。

构想

在这个例子中,我写了一个简单的WebForm来管理存储在XML中的联系人列表。 这个需求是非常简单的:具有添加新联系人,编辑/删除现有联系人的能力。用户可以一次修改或删除多个联系人,我也允许用户按他们选定的列来对数据网格进行排序。

我的例子是用 C# 编写的。 如果你更喜欢这些代码的VB版本,在下载文件中有这两种格式的代码。

Contacts.xml

这个例子中的 XML 数据文件非常简单直观。由于它非常简单,所以我没有创建规划。

<?xml version="1.0" standalone="yes"?>
<Contacts>
  <Contact>
    <Email>myaddress@mycompany.com</Email>
    <FirstName>John</FirstName>
    <LastName>Doe</LastName>
  </Contact>
  <Contact>
    <Email>youraddress@yourcompany.com</Email>
    <FirstName>Jane</FirstName>
    <LastName>Doe</LastName>
  </Contact>
</Contacts>

ContactList.aspx

设置 WebForm 非常简单。我放置了一个新的 DataGrid 到我的窗体中,并且设置它为4列,第一列都包含了用来删除联系人的检查框。你会注意到我在这里做的主要工作就是以模板列( TemplateColumn)的形式创建了每一列。 这允许我放置文本框和检查框对象到数据模板(ItemTemplate)中 . 这是一个在网格每一行中显示文本以外的其它东西的技巧。 除此以外,你还会注意到我使用 FooterTemplate 来使新建联系人变得简单而直观。

我也包含了一个链接按钮(LinkButton),用来保存用户所做的修改及删除操作。但它并不用来添加新联系人。添加新联系人的操作由最后一列的页脚模板中链接按钮(LinkButton)来完成。

<asp:datagrid id="dgContacts" runat="server" ShowFooter="True" AllowSorting="True" Forefont color="Black" GridLines="None" CellPadding="2" Backfont color="LightGoldenrodYellow" BorderWidth="1px" Borderfont color="Tan" Width="499px" AutoGenerateColumns="False" DataKeyField="Email">
  <SelectedItemStyle Forefont color="GhostWhite" Backfont color="DarkSlateBlue"></SelectedItemStyle>
  <AlternatingItemStyle Backfont color="PaleGoldenrod"></AlternatingItemStyle>
  <HeaderStyle Font-Bold="True" Backfont color="Tan"></HeaderStyle>
  <FooterStyle Backfont color="Tan"></FooterStyle>
  <Columns>
    <asp:TemplateColumn SortExpression="FirstName" HeaderText="First Name">
      <ItemTemplate>
        <asp:TextBox id=First runat="server" Width="109px" Text='<%# DataBinder.Eval(Container, "DataItem.FirstName") %>'>
        </asp:TextBox>
      </ItemTemplate>
      <FooterTemplate>
        <asp:TextBox id="NewFirst" runat="server" Width="109px"></asp:TextBox>
      </FooterTemplate>
    </asp:TemplateColumn>
    <asp:TemplateColumn SortExpression="LastName" HeaderText="Last Name">
      <ItemTemplate>
        <asp:TextBox id=Last runat="server" Width="109px" Text='<%# DataBinder.Eval(Container, "DataItem.LastName") %>'>
        </asp:TextBox>
      </ItemTemplate>
      <FooterTemplate>
        <asp:TextBox id="NewLast" runat="server" Width="109px"></asp:TextBox>
      </FooterTemplate>
    </asp:TemplateColumn>
    <asp:TemplateColumn SortExpression="Email" HeaderText="Email">
      <ItemTemplate>
        <asp:TextBox id=Email runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Email") %>'>
        </asp:TextBox>
      </ItemTemplate>
      <FooterTemplate>
        <asp:TextBox id="NewEmail" runat="server"></asp:TextBox>
      </FooterTemplate>
    </asp:TemplateColumn>
    <asp:TemplateColumn HeaderText="Delete Contact">
      <ItemStyle HorizontalAlign="Center"></ItemStyle>
      <ItemTemplate>
        <asp:CheckBox Runat="server" ID="chkDelete"></asp:CheckBox>
      </ItemTemplate>
      <FooterStyle HorizontalAlign="Center"></FooterStyle>
      <FooterTemplate>
        <asp:LinkButton Runat="server" Text="Add" CommandName="Add" ID="Linkbutton1" NAME="Linkbutton1"></asp:LinkButton>
      </FooterTemplate>
    </asp:TemplateColumn>
  </Columns>
</asp:datagrid>


ContactList.cs

当我选择用XML文件来存取数据后,我就决定要使用DataSet来存取它。因为 DataSet 对象有 ReadXmlWriteXml 方法,所以这是非常合理的选择。第一步是在XML中读取数据。正如你从代码中所看到的,  我创建了一个成员用来处理数据排序。

private DataSet _dsContacts;
private string _sSort;

private void Page_Load(object sender, System.EventArgs e)
{
  // 装载 XML 文件.
  _dsContacts = new DataSet();
  _dsContacts.ReadXml(Server.MapPath("Contacts.xml"));
  DataColumn[] dcPk = {_dsContacts.Tables["Contact"].Columns["Email"]};
  _dsContacts.Tables["Contact"].PrimaryKey = dcPk;

  if (!Page.IsPostBack )
  {
    // 如果是第一次装载的话,绑定数据。
    BindContacts();
    _sSort = "FirstName";
  }
  else
  {
    // 否则,从视图状态中读取排序状态.
    _sSort = (string)ViewState["Sort"];
  }
}

第二步,我创建了一个用来绑定数据到网格的方法,它包含了数据排序逻辑以及从磁盘读取数据的方法。

private void BindContacts()
{
  // 保存排序状态到视图状态中.
  ViewState["Sort"] = _sSort;

 
// 绑定网格到已排序的数据视图中.
  DataView dv = new DataView(_dsContacts.Tables["Contact"]);
  dv.Sort = _sSort;
  dgContacts.DataSource = dv;
  dgContacts.DataBind();
}

private void
SaveContacts()
{
  _dsContacts.WriteXml(Server.MapPath(
"Contacts.xml"));
}

ItemCommand 事件用来处理向列表中添加新联系人。注意:我检查了  CommandName 参数是否为 Add.  它是来处理ASPX页中网格最后一列的页脚模板(FooterTemplate)中的链接按钮(LinkButton)的返回值。

private void dgContacts_ItemCommand(object source , System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
  // 添加新数据到 dataset.  这里我使用了数组以提高处理效率.
  if (e.CommandName == "Add")
  {
   
string[] sContact = {"", "", ""};
    sContact[0] = ((TextBox)e.Item.FindControl(
"NewEmail")).Text;
    sContact[1] = ((TextBox)e.Item.FindControl(
"NewFirst")).Text;
    sContact[2] = ((TextBox)e.Item.FindControl(
"NewLast")).Text;

    _dsContacts.Tables[
"Contact"].Rows.Add(sContact);

    SaveContacts();
  }

  BindContacts();
}

我跳过了 SortCommand 代码,因为有许多其它文档已经非常详细地讨论过如何排序了。 如果你下载了这个例子的源代码,它就包含在里面。

最后,我将窗体上链接按钮(LinkButton)的单击事件(OnClick)移到了这里。  这里我通过循环检测DataGrid中的数据项来执行任何必需的删除及更新操作。

private void btnUpdate_Click(object sender, System.EventArgs e)
{
  // 循环处理每个数据项.
  foreach (DataGridItem di in dgContacts.Items)
  {
   
// 确信是数据项而不是页首或页尾.
    if (di.ItemType == ListItemType.Item || di.ItemType == ListItemType.AlternatingItem)
    {
     
// 取得更新或删除操作执行以后的当前行.
      DataRow dr = _dsContacts.Tables["Contact"].Rows.Find(dgContacts.DataKeys[di.ItemIndex]);

     
// 检查是否需要删除某行.
      if (((CheckBox)di.FindControl("chkDelete")).Checked)
      {
        _dsContacts.Tables[
"Contact"].Rows.Remove(dr);//删除指定行
      }
     
else
      {
       
//更新数据行.
        dr["Email"] = ((TextBox)di.FindControl("Email")).Text;
        dr[
"FirstName"] = ((TextBox)di.FindControl("First")).Text;
        dr[
"LastName"] = ((TextBox)di.FindControl("Last")).Text;
      }
    }
  }

  
// 如果有变化则保存它.
   if (_dsContacts.HasChanges())
  {
    SaveContacts();
  }

  BindContacts();//绑定
}

结束语

我可以很容易地通过控件的位置找到控件中每一个 DataGridItem的单元(Cells)。 有多种方法可以实现它, 我确信你可以找到完成这项任务的更好的方法。正如你所看到的, 一次编辑整个数据网格是非常简单的。 同样的方法经过轻微的修改也可用于分页网格。


posted @ 2006-02-25 12:13 井泉 阅读(75) | 评论 (1)编辑 收藏

2006年2月15日

[Oracle]对数据库字段使用默认值

在创建数据库表时,你可以指定一个 DEFAULT 值(即默认值)。对数据库字段使用默认值有助于将数据库设计问题与应用程序代码隔离。

可以在以后某个时候通过一条 ALTER TABLE 命令改变一个字段的默认值,改变之后应用程序代码会立即开始使用新值。

有一点是很重要的,即 DEFAULT 值只有当一个字段没有在 INSERT 或 MERGE 中指定值,或者使用了 DEFAULT 关键字时才会使用。如果你不显式地声明一个 DEFULAT 值,Oracle 将隐式地将默认值定义为 NULL,而且 DEFAULT 关键字也是这样。从 Oracle 9i开始,可以在 DEFAULT 子句中使用诸如 SYSDATE 或 CURRENT_TIMESTAMPE 之类的伪字段。例如:

create table t1
(
    id$ integer not null,
    charcol char default 'Y',
    datecol date default sysdate,
    strcol varchar2(30) default user,
    intcol integer default 12
);
insert into t1 (id$) values (1);
select * from t1;

       ID$ C DATECOL   STRCOL                             INTCOL
---------- - --------- ------------------------------ ----------
         1 Y 28-MAY-04 SCOTT                                  12

DEFAULT 关键字与INSERT、MERGE 或UPDATE 语法比起来可以看上去没有那么必要,但是想一下如果你希望在插入一列数据时使用所有默认值,那么你就不会这么认为了。Oracle 不接受INSERT INTO <table> 或INSERT INTO <table> VALUES () 作为有效的 SQL。必须指定至少一个字段,但是可以使用 DEFAULT 关键字来允许使用默认值,而非硬编码值,所以下面是有效的语法,它将使用所有 DEFAULT 值创建一行记录。

create table t2(charcol char default 'Y',datecol date default sysdate);
insert into t2 (charcol) values (default);
select * from t2;

C DATECOL
- ---------
Y 28-MAY-04

一个常见的问题

一个常见的问题是模拟其它数据库提供商的 Autonumber 功能,该功能是使用某种顺序数字自动地填充某个字段。在 Oracle 数据库中,不能指定一个顺序数字作为一个字段的 DEFAULT 值;然而,可以使用触发器模拟这一功能。即使一个字段声明为 NOT NULL,也依然可以在 INSERT 语句中忽略这个字段,而使用一个触发器来填充该字段的值。注意使用 DEFAULT 关键字比使用显式的 NULL 可读性要好。

create sequence t3_seq;
create table t3(id$ integer constraint t3_pk primary key);
create or replace trigger t3_autonumber
before insert on t3 for each row
begin
    if :new.id$ is null then
        select t3_seq.nextval into :new.id$ from dual;
    end if;
end;
/
show errors;

insert into t3(id$) values (default);
select * from t3;

       ID$
----------
         1

可以使用SYS_CONTEXT 值的集合中的默认值来填充字段,并收集有关某处一个会话的重要信息:

create table t4
(
    when date default SYSDATE,
    db_domain varchar2(200) default SYS_CONTEXT('USERENV','DB_DOMAIN'),
    host varchar2(256) default SYS_CONTEXT('USERENV','HOST'),
    ip_address varchar2(256) default SYS_CONTEXT('USERENV','IP_ADDRESS'),
    language varchar2(256) default SYS_CONTEXT('USERENV','LANGUAGE'),
    protocol varchar2(200) default SYS_CONTEXT('USERENV','NETWORK_PROTOCOL'),
    terminal varchar2(200) default SYS_CONTEXT('USERENV','TERMINAL')
);
insert into t4 (when) values (default);
select * from t4;

WHEN
---------
DB_DOMAIN
------------------------------------------------------------------------------
HOST
------------------------------------------------------------------------------
IP_ADDRESS
------------------------------------------------------------------------------
LANGUAGE
------------------------------------------------------------------------------
PROTOCOL
------------------------------------------------------------------------------
TERMINAL
------------------------------------------------------------------------------
28-MAY-04
scott.bn
MSHOME\SCOTT-LAP
AMERICAN_AMERICA.AL32UTF8
SCOTT-LAP

还可以使用伪字段SYS_GUID 来填充一个字段;它具有全局唯一性的优点,并且不需要顺序数字或触发器开销:

create table t5(id$ raw(16) default sys_guid()
    constraint t5_pk primary key);
insert into t5(id$) values (default);
select * from t5;

ID$
--------------------------------
643718A07DCC43F2AC95312FD43617BA

posted @ 2006-02-15 09:02 井泉 阅读(41) | 评论 (0)编辑 收藏

2006年2月14日

VS2005中使用C#的新特性:可空类型

随着C#语言最新标准的出炉,现在它也提供了对可空类型的支持。这个小变化将会在处理那些包括可选项的数据库记录时非常有用。当然在其他地方,它也是非常有用的。

  简单说来,可空数据类型就是包含了所定义的数据类型或者值的空(null)的类型。C#的ECMA-334标准提供了对所有C#值类型的可空版本的描述。

  定义可空类型

  定义可空类型和非可空类型基本类似,不同的是采用了?来表示。如定义一个整型,你可以使用简单的语句:

int myInt = 1;

  为了使得myInt能够存储一个空值,你可以这样声明它:

int? myNullableInt = 1;

  你可以看到,这两个变量看上去好像是一样的。但是,可空类型的版本是非常不同的。可空的版本事实上是一个结构,它将值类型和一个标记该值是否为空的标志位结合在一起。一个可空类型有两个公共可读的属性,HasValue和value。如果存储了一个值那么HasValue这个布尔型变量就为true。否则,如果变量是空值就是false。如果HasValue是true,你可以获取这个变量的值。如下有两个对可空变量的有效赋值:

double? myDouble = 3.1415926;
double? myOtherDouble = null;

  你可以看到,myDouble被赋值了,但是也可以被赋为空。在第二个语句里,myOtherDouble被初始化一个空值,这在一个非可空类型里不能这样做的。

  使用可空类型

  可空类型可以像普通值类型一样的使用。事实上,可以使用内建的隐式转换来转换相同类型的可空变量和非可空变量。这意味着你可以在一个标准整型和可空整型之间相互转换:

int? nFirst = null;
int Second = 2;

nFirst = Second; // 有效
nFirst = 123; // 有效
Second = nFirst; // 同样有效

nFirst = null; // 有效
Second = nFirst; // 例外,后者是非空类型

  在以上的语句里,你可以看到如果可空变量不包含空值的话是可以和非可空变量交换值的。如果它是一个空值,那么就会抛出例外。为了防止例外,你可以使用可空变量的HasValue属性:

if (nFirst.HasValue) Second = nFirst;

  你可以看到,如果nFirst有值赋值就会发生,否则程序会跳过此句语句。

  使用可空类型的操作符

  虽然可以使用相同值类型的可空和非可空变量的转换,也必须对操作符进行一些改变使得它们可以处理可空和非可空值。这些操作符被称为提升的操作符。

  考虑如下代码:

int ValA = 10;
int? ValB = 3;

int? ValC = ValA * ValB;

  在ValC里存储了什么?ValC中存储了30。标准操作符被扩展使得它们能够处理可空类型。考虑到如下的变化:

int ValA = 10;
int? ValB = null;

int? ValC = ValA * ValB;

  ValC这次值为多少?ValC为空。无论哪个操作数为空,提升的操作符的结果为空。即使进行加法或减法,结果也为空。

  如果ValC不为可空类型呢?如下的代码会有什么样的结果?

int ValA = 10;
int? ValB = null;

int ValC = ValA * ValB; // ValC 不为可空类型


  代码将会抛出一个异常。ValA*ValB结果为空,但是不能赋值为非可空类型,这将会导致程序异常的抛出。

比较

  比较将会和数学计算操作类似的方式处理。比较的操作数将同时被提升为可空的。这样就可以比较了,如果某个操作数为空,那么比较结果为false。

  如果对比是否相等,两个同为空的变量将被认为是相等的。一个空变量和其他任意值的变量相比的结果是不相等。下面是一些比较的例子:

int abc = 123;
int xyz = 890;
int? def = null;
int? uvw = 123;

Comparison Result
abc == xyz // false
abc == def // false
def == null // true
abc == uvw // true
uvw == null // false
uvw != null // true

  在所有的比较中,结果都是布尔型值true或者false。在做大小比较的时候,如果操作数的任意一个或者都是空值,那么结果返回的是false。如下展示了一些例子:

Comparison Result
abc > uvw // false, they are equal
abc < def // false, def is null
uvw < def // false, because def is null
def > null // false, because right side is null
uvw > null // false, because right side is null

  可空性的移去

  C#在新版本中加入了一个新的操作符,它被称为空接合操作符,使用如下的格式:

returnValue = first second;

  这样,如果first不为空,那么它的值将返回作为returnValue的值。如果first为空,那么second的值将被返回。注意:returnValue可以为可空变量或者非可空变量。

  如果你希望可空变量的值到一个非可空的版本,你可以这样做:

int? ValA= 123;
int? ValB = null;

int NewVarA = ValA ?? -1;
int NewVarB = ValB ?? -1;

  NewVarA的值将会为123因为ValA不是空值。NewVarb的值是-1因为ValB是空值。你看一看到,这里你将可以将变量从一个空值转化成一个缺省值。这里缺省值是-1。

  结束语

  总得来说,最新的C#允许一个可空类型的存在。语言内部建立了对可空类型的处理机制。可空类型使得数据库记录和其他可选信息更加的容易处理。

  可空类型是C# ECMA-334版本的一个特性。你需要一个支持这个版本的C#的编译器。Visual Studio 2005支持这个版本。

posted @ 2006-02-14 09:12 井泉 阅读(22) | 评论 (0)编辑 收藏

2006年2月9日

如何通过需要验证的邮件服务器发送邮件?

在.NET Framework 推出以后,大家一直在为这个问题而伤脑筋。的确,在1.0的时候,我们是不能实现此方案的,大部分人选择了使用Socket底层自己重写。但是,在1.1的时候,其实Microsoft已经提供了验证功能了,只是一直没有公开。

恰好我在读.Text 0.95的源代码的时候找到了这段代码,感觉应该提供给大家咧嘴笑脸

private void Page_Load(object sender, System.EventArgs e)
{
       MailMessage mail = new MailMessage();
       mail.To = "me@mycompany.com";
       mail.From = "you@yourcompany.com";
       mail.Subject = "this is a test email.";
       mail.Body = "Some text goes here";
       mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1"); //basic authentication
       mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", "my_username_here"); //set your username here
      mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "super_secret"); //set your password here

    SmtpMail.SmtpServer = "mail.mycompany.com";  //your real server goes here
    SmtpMail.Send( mail );
}

参考资料:

http://vaultpub.sourcegear.com/VaultService/VaultWeb/Blame.aspx?repid=7&path=$/Dottext/Dottext.Framework/Email/SystemMail.cs&version=1&includedversions=20 (用户名及密码为guest)

http://www.systemwebmail.com/faq/3.8.aspx

posted @ 2006-02-09 13:10 井泉 阅读(37) | 评论 (0)编辑 收藏

2006年1月12日

DataGrid/DataList,你会用了吗? 作者- heone

很久以前就想写一些关于DataGrid/DataList的东西,但是一直以来,一方面自感所学未深,另一方面,总觉无从下笔,一拖再拖,离刚开始的念头已距一年有余。
DataGrid/DataList在ASP.NET中的重要性,想必就不用我再强调了,凡显示Table类型的数据,大多会使用这两个控件(当然,如果谁还像ASP那样写ASP.NET,那我也没有办法),所以,每个人可能都有自己的领悟,这篇文章,算是抛砖引玉,为大家做个铺垫。

 

一、方法
1、DataBind
很简单、最常用的方法。绑定数据用。需要注意的只有一点:执行了这个方法后,DataGrid(由于DataGrid和DataList极为相似,所以下面的介绍虽然是针对DataGrid,但与DataList也相差不远)里面所有的显示绑定数据的控件,都会显示DataSource里的数据,其余控件也将初始化成.aspx里设计的状态。


二、属性
1、DataSource
有DataBind的地方,就应该有DataSource。如果没有指定DataSource而执行DataBind,那DataGrid将什么也不会显示。
DataSource一般是DataSet、DataTable或者DataView。当然也可以绑定DataReader或者其他实现IEnumerable的类。

2、DataKeyField,DataKeys
当你在DataGrid中定位一行之后,肯定想知道这行在数据表里的位置,至少有五种方法可以做到这一点,设置DataGrid的DataKeyField就是这几种方法之一。
DataKeyField一般设置为数据表的Unique字段(否则就没意义了),通过DataKey可以得到这一行对应的关键字段的值。
DataKeys是DataKey的集合,通过行的索引来读取相应行的DataKey。

3、EditItemIndex,SelectedIndex,CurrentPageIndex,SelectedItem
这些属性都很好理解,看名字就知道是什么意思,需要注意的是,设置了EditItemIndex或者CurrentPageIndex后需要重新执行DataBind方法(当然,前面提到过,还需要设置DataSource)。

4、Columns
没什么好解释的,Columns就是Columns,列的集合,可以设置列的属性,包括Visible、HeaderText、FooterText、SortExpression等。
严重注意:自动生成的列,是不包含在Columns中的。只有在.aspx中显示声明的列和在代码中添加的列才会被包含在其中。

5、Items
俗话说,最后的都是最重要的,把Items作为最后一个属性来介绍,正式基于这样的理由。
Items是DataGridItem的集合,可以遍历当前DataGrid中显示数据的DataGridItem。
5.1、DataGridItem
每一个DataGridItem就是DataGrid中显示的一行,其中包括:
Header   DataGrid 控件的标题部分
Item   DataGrid 控件中的项
AlternatingItem  DataGrid 控件中的交替项
SelectedItem    DataGrid 控件中的选定项(由SelectedIndex设置,通过SelectedItem属性或者Items[SelectedIndex]来读取)
EditItem    DataGrid 控件中处于编辑状态的项(由EditItemIndex设置,通过Items[EditItemIndex]来读取)
Separator    DataGrid 控件中项之间的分隔符
Footer    DataGrid 控件的脚注部分
Pager     DataGrid 控件的页选择节
注意,DataGrid的Items属性中不会包含Header、Footer、Pager这三类DataGridItem的。
5.1.1、DataGridItem的属性
ItemIndex —— 得到行在Items中的索引
ItemType —— 返回行的类型,也就是上面列出的Header、Item、...、Pager
Cells —— 返回行包含的所有TableCell(不管是显示声明的,还是自动生成的,不管是可以看见的,还是隐藏掉的),通过TableCell,可以读取Cell中显示的文本、包含的控件
严重注意:只有BoundColumn列和自动生成列,才可以通过TableCell.Text属性读取显示的文本。HyperLinkColumn、ButtonColumn、EditCommandColumn都需要将目标控件转换成相应的控件。
比如:
假设DataGrid的第一列声明如下
<asp:HyperLinkColumn DataTextField="au_id" HeaderText="au_id" DataNavigateUrlField="au_id" DataNavigateUrlFormatString="Edit.aspx?id={0}"></asp:HyperLinkColumn>
读取的时候可以用:
//Items[0]表示第一行,Cells[0]表示第一列,Controls[0]表示Cell中的第一个控件(也只有这个控件可以用)
HyperLink link = (HyperLink)DataGrid1.Items[0].Cells[0].Controls[0]);
Response.Write(link.Text);
至于模板列(TemplateColumn),当然也可以通过DataGrid1.Items[i].Cells[j].Controls[n]来获取,然后转换成原来的控件类型再操作,但是还有个更好的办法,就是用FindControl来查找控件。
FindControl是System.Web.UI.Control的方法,可以根据子控件ID来查找子控件
比如:
假设DataGrid的某一列声明如下
<asp:TemplateColumn>
   <ItemTemplate>
      <asp:TextBox Runat="server" ID="txtID" Text=''<%# DataBinder.Eval(Container.DataItem,"au_id") %>''>
      </asp:TextBox>
   </ItemTemplate>
</asp:TemplateColumn>
读取方法:
TextBox txt = (TextBox)DataGrid1.Items[1].FindControl("txtID");
Response.Write(txt.Text);
注意:DataList中是没有Cell的


三、事件
1、ItemCommand、CancelCommand、DeleteCommand、EditCommand、UpdateCommand
也就是DataGrid中,点击Button、LinkButton后执行的事件,执行的事件取决于按钮的CommandName。其实最主要的一个是ItemCommand,而后面四个都只是ItemCommand的一小部分,
比如一个按钮的CommandName为"Cancel",当返回后,首先执行的是ItemCommand事件,然后才是CancelCommand事件。

2、PageIndexChanged
如果你的DataGrid是分页的,那当你在DataGrid上点击Pager上的1、2、3或者<、>时,就会激发这个事件。
在这个事件里面,你可以用e.NewPageIndex来读取要改变的页,然后赋值给DataGrid的CurrentPageIndex属性,最后不要忘了,还要设置DataSource,还要执行DataBind。
注意:DataList中没有这个事件,如果需要在DataList中分页,可以一段一段的读取数据,然后把当前段的数据绑定到DataList上。

3、ItemDataBound,ItemCreated
首先要说的是这两个事件的发生时间。
ItemDataBound嘛,只要执行了DataBind方法,就会马上激发这个事件。
ItemCreated呢,如果页面是第一次访问(Page.IsPostBack = false),那在第一次执行DataBind的时候,会先激发ItemCreated事件,也就是说,执行了DataBind后,首先会用ItemCreated来建立Header行,然后用ItemDataBound来绑定Header行,再用ItemCreated来建立第一行,再调用ItemDataBound来绑定第一行,也就是说ItemCreated和ItemDataBound是交替执行的。
页面返回时,也会执行ItemCreated事件,在Page_Load之前,但是这时候就不会再执行ItemDataBound事件了。
所以,如果你想在DataGrid里动态添加什么控件,就需要在ItemCreated事件中,而不是在ItemDataBound事件中。


四、代码片断
1、DataGrid显示双层表头
假设你的DataGrid有三列,现在想将前两列作为"大类1",第三列作为"大类2",现在,你可以在ItemDataBound事件中加入下面的代码:
if (e.Item.ItemType == ListItemType.Header)
{
 e.Item.Cells[0].ColumnSpan = 2;
 e.Item.Cells[0].Text = "大类1</td><td>大类2</td></tr><tr><td>" + e.Item.Cells[0].Text;
}
用这个方法可以为任意添加新行。

2、设置绑定列或者自动生成列的编辑框宽度
请在你的ItemDataBound事件中加入一下代码:
if (e.Item.ItemType == ListItemType.EditItem)
{
 for (int i = 0; i < e.Item.Cells.Count; i++)
 {
  TextBox txt = (TextBox)e.Item.Cells[i].Controls[0];
  txt.Width = Unit.Pixel(50);
 }
}

3、处理在DataGrid中的DropDownList的事件
DropDownList没有CommandName属性,所以不能用ItemCommand事件,不过你可以这样试试:
在DataGrid的模板列中加入的DropDownList控件
<asp:DropDownList runat="server" id="ddl" AutoPostBack="True" OnSelectedIndexChanged="ddl_SelectedIndexChanged" />
然后你在.aspx.cs中加入一个函数
protected void ddl_SelectedIndexChanged(object sender, System.EventArgs e) //一定要声明成protected或者public,不能是private的。
{
  //在这里就可以加入其他代码
}

3.1、在上面的事件中怎样得到本行其他Cell的值呢?
我们知道,DataGrid完全是一个Table结构的控件,DataGrid包含DataGridItem,每个DataGridItem又包含TableCell,那么,我们就可以在TableCell的某个控件中,利用控件的Parent来得到TableCell,再利用TableCell的Parent,就可以得到DataGridItem了。
protected void ddl_SelectedIndexChanged(object sender, System.EventArgs e) //一定要声明成protected或者public,不能是private的。
{
  DropDownList ddl = (DropDownList)sender;
  TableCell cell = (TableCell)ddl.Parent;
  DataGridItem item = (DataGridItem)cell.Parent;
  Response.Write(item.Cells[0].Text);
}

4、怎样得到Header、Footer、Pager里的控件
方法一:在ItemCreated或者ItemDataBound中,具体代码就不在多写了
方法二:遍历DataGrid的所有Item(注意,不是遍历DataGrid1.Items下的Item)
foreach (DataGridItem item in DataGrid1.Controls[0].Controls)
{
  if (item.ItemType == ListItemType.Header)
  {
    //用item.FindControl查找相应的控件
  }
}
大家可能会注意到,这里有个DataGrid1.Controls[0].Controls,这表示,DataGrid1下,有一个子控件,这个子控件是DataGridTable类型,他下面才是DataGridItem集合
在DataList中,下面的子控件直接就是DataListItem了,而没有Table:

foreach (DataListItem item in DataList1.Controls)
{
  //....
}

posted @ 2006-01-12 16:25 井泉 阅读(57) | 评论 (0)编辑 收藏

2005年11月26日

用ASP.NET建立一个在线RSS新闻聚合器

概要 

  本文讲解了如何使用 XML Web 控件获取远程XML数据并在 ASP.NET 页面显示这些XML数据,以及使用Repeater控件发布数据库中的XML数据。在过去的几年间,随着 异构平台间共享数据的需求不断增长,XML的使用也呈爆炸性增长。意识到这种趋势,微软在整个.NET框架中对 XML 提供了健壮的支持。这意味着,对于 ASP.NET 开发者来说,在Web页面 中显示和处理 XML 数据从来没有这么容易过。本文将通过生成一个 RSS2.0 聚合引擎和在线新闻聚合器来学习 XML 和 ASP.NET 技术。 本文假设读者熟悉 ASP.NET 和 XML。
  简介

  随着办公室和家庭上网在线时间的延长,以及 Web 站点和可访问的互联网应用程序呈持续爆炸性增长,应用程序之间能数据共享变得越来越重要。在 异构平台之间共享数据需要一种平台中立的数据格式,这种数据格式要求能易于通过标准的互联网协议来传输,而这正是XML的用武之地。因为XML文件本质上 只是一个文本文件,其编码格式众所周知,而且现有的XML解析器能为所有主流编程语言所用,所以XML数据能被任何平台轻松使用。 

  Web 网站聚合就是一种使用 XML 来共享数据的范例,在新闻站点和网志中经常可以看到。采用 Web 网站聚合技术,网站能以 XML 格式的 Web 可访问的聚合文件来发布最新内容。网站使用 的聚合格式有很多种,其中最流行的一种格式就是 RSS2.0。( RSS2.0 规范被发布在 Harvard Law 网站 的技术栏目上)。此外,MSDN 杂志有一个聚合文件:MSDN杂志:本期刊物, 其中列出了最新一期 MSDN 杂志上的文章,包括到在线版本文章的链接。 

  一旦 Web 站点有了公开发布聚合文件,那么不同的客户端就可以消费它。消费聚合文件的方式有很多种,比如,某个提供 .NET 技术资源的站点可能希望在网站中 添加最新的 MSDN 杂志文章标题。聚合文件还常常被新闻聚合器程序所用,这种程序被专门设计用来获取和显示不同来源的聚合文件。 

  随着人们越来越注重使用 XML 数据,在 ASP.NET 页面中处理 XML 数据的能力变得比以往更关键。既然 Web 站点聚合如此重要, 本文我们就来创建一个 Web 站点聚合文件生成程序和一个在线新闻聚合器。在建立这两个微型程序的过程中,我们将讲述如何访问和显示XML数据,不论这些数据是来自远端的Web服务器还是本地的文件系统。我们将演示如 何多种不同的方法显示XML数据,比如:用 Repeater 控件以及用 ASP.NET XML Web控件。

  使用 RSS 2.0 规范的聚合内容

  本文我们将要创建的第一个微型程序是一个聚合文件生成器。针对这个迷你程序,假设你是一个大型新闻网站(如 MSNBC.com)的 Web 开发者,所有的新闻内容都保存在 Microsoft SQL Server 2000 数据库中。具体地说,这些文章是 都保存在一个名为 Articles 的表中,表中以下字段与我们的程序密切相关:

  ·ArticleID—主键,自增长的整型字段,用来唯一标识每一篇文章; 

  ·Title— 指定标题,字段数据类型: varchar(50); 

  ·Author—指定作者,字段数据类型: varchar(50); 

  ·Description—新闻内容描述,字段数据类型: varchar(2000); 

  ·DatePublished—新闻发布日期,字段数据类型:datetime 

  请注意,Articles 表中可能还有其它字段,上面所列的只是我们在创建聚合文件的时候所要用到的字段。而且,这只是一个非常简单的数据模型,在 是应用的数据库环境中,你可能会使用更加标准化的数据库模型,比如具备一个单独的 authors (作者)表,有一个建立作者和文章之间多对多关系的表等等。

  下一步,我们将创建一个ASP.NET页面,用格式化好的 RSS2.0 XML 文件显示一个最新的新闻列表。在讲述如何在 ASP.NET 页面 中完成这种转换之前,我们要先介绍一下 RSS2.0 规范的内容。我们应该记住,在整个规范中,RSS 是被设计用来为聚合内容提供一个数据模型。那么 毫无疑问,它会有一系列的 XML 元素,用来描述 Web 站点要聚合的内容信息,以及一系列用来描述某一特定新闻项的 XML 元素。最后,不要忘记 RSS 聚合文件是一个 XML 格式文件,必须符合 XML 格式化的准则, 也就是: 

  ·所有 XML元素必须正确嵌套; 

  ·所有的属性值要用引号包含起来; 

  ·<, >, &, "和’’符号要相应地替换为 &lt;,&gt;, &amp;, &quot; 和 &apos;; 

  而且,XML格式是大小写敏感的,这就意味着,XML元素的起始和终止标签必须匹配,拼写和大小写都必须一致。 

  RSS2.0 的根元素是<rss>元素,这个元素可以有一个版本号的属性,例如:

<rss version="2.0">
...
</rss> 

  <rss>元素只有一个子元素<channel>,用来描述聚合的内容。在<channel>元素里面有三个必需的子元素,用来描述 Web 站点的信息。这三个元素是: 

  ·title—定义聚合文件的名称,一般来说,还会包括Web站点的名称; 

  ·link—Web站点的URL; 

  ·description—Web站点的一段简短的描述。 

  除此之外,还有一些可选元素来描述站点信息。这些元素的更多信息请参见 RSS2.0规范。 

  每一个新闻项目放在一个单独的<item>元素中。<channel>元素可以有任意数量的<item>元素。每个<item>元素可以有多种的子元素,唯一的要求是最少必须包含<title>元素和<description>元素其中一个作为子元素。以下列出了一些相关的<item> 子元素: 

  ·title—新闻项目的标题; 

  ·link—新闻项目的URL; 

  ·description—新闻项目的大纲; 

  ·author—新闻项目的作者; 

  ·pubDate—新闻项目的发布日期 

  下面是一个非常简单的 RSS2.0 聚合文件。你可以从 RSS generated by Radio UserLand 看到其他的RSS2.0文件的例子。

<rss version="2.0">
<channel>
<title>Latest DataWebControls.com FAQs</title>
<link>http://datawebcontrols.com/</link>
<description>
This is the syndication feed for the FAQs 
at DataWebControls.com
</description>
<item>
<title>Working with the DataGrid</title>
<link>http://datawebcontrols.com/faqs/DataGrid.aspx</link>
<pubDate>Mon, 07 Jul 2003 21:00:00 GMT</pubDate>
</item>
<item>
<title>Working with the Repeater</title>
<description>
This article examines how to work with the Repeater 
control.
</description>
<link>http://datawebcontrols.com/faqs/Repeater.aspx</link>
<pubDate>Tue 08 Jul 2003 12:00:00 GMT</pubDate>
</item>
</channel>
</rss>  


  关于<pubDate>元素的格式有一点特别重要,再此要讲一下。RSS 要求日期必须按照 RFC822 日期和时间规范 进行格式化,此格式要求:开头是一个可选的3字母星期缩写加一个逗号,接着必须是日加上3字母缩写的月份和年份,最后是一个带时区名的时间。另外,要注意 <description> 子元素是可选的:上 述文件第一个新闻没有 <description> 元素,而第二个新闻就有一个。

  通过 ASP.NET 页面输出聚合内容

  现在,我们已经知道了如何按照 RSS2.0 规范存储我们的新闻项,我们已经就绪创建一个 ASP.NET 页面,当用户发出请求时,就会返回网站聚合 的内容。更确切地说,我们将建立一个名字叫 rss.aspx 的 ASP.NET 页面,这个页面会按照 RSS2.0 规范的格式返回 Articles 数据库表中的最新的 5 个新闻项 。 

  可以有几种方法来完成这件事,稍后将会讲到。但是现在,我们首先要完成一件事,那就是先要从数据库中获得最新的5个新闻项。这可以用下面的 SQL 查询语句获得:

SELECT TOP 5 ArticleID,Title,Author,Description,DatePublished FROM Articles ORDER BY DatePublished DESC 

  获得了这些信息以后,我们需要把这些信息转换成相应的 RSS2.0 格式聚合文件。要把数据库的数据显示为XML数据最简单、快速的方法就是使用 Repeater 控件。准确地说,Repeater 控件 将在 HeaderTemplate 和 FooterTemplate 模版里显示<rss>元素、<channel>元素以及站点相关的 元素标签,在 ItemTemplate 模版里面显示 <item> 元素。下面是我们这个 ASP.NET 页面(.aspx文件)的 HTML 部分 :

<%@ Page language="c#" ContentType="text/xml" Codebehind="rss.aspx.cs"
AutoEventWireup="false" Inherits="SyndicationDemo.rss" %>
<asp:Repeater id="rptRSS" runat="server">
<HeaderTemplate>
<rss version="2.0">
<channel>
<title>ASP.NET News!</title>
<link>http://www.ASPNETNews.com/Headlines/</link>
<description>
This is the syndication feed for ASPNETNews.com.
</description>
</HeaderTemplate>

<ItemTemplate>
<item>
<title><%# FormatForXML(DataBinder.Eval(Container.DataItem,
"Title")) %></title>
<description>
<%# FormatForXML(DataBinder.Eval(Container.DataItem, 
"Description")) %>
</description>
<link>
http://www.ASPNETNews.com/Story.aspx?ID=<%# 
DataBinder.Eval(Container.DataItem, "ArticleID") %>
</link>
<author><%# FormatForXML(DataBinder.Eval(Container.DataItem, 
"Author")) %></author>
<pubDate>
<%# String.Format("{0:R}", 
DataBinder.Eval(Container.DataItem, 
"DatePublished")) %>
</pubDate>
</item>
</ItemTemplate>

<FooterTemplate>
</channel>
</rss> 
</FooterTemplate>
</asp:Repeater> 

  首先要注意的是:上面这段代码例子只包括 Repeater 控件,没有其它的 HTML 标记或 Web 控件。这是因为我们希望页面只输出 XML 格式的数据。实际上,观察一下 @Page 指令,你就会发现 ContentType 被设置为XML MIME 类型(text/xml)。其次要注意的是:在 ItemTemplate 模版里,当 在 XML 输出中添加数据库字段Title、Description 和 Author 时,我们调用了辅助函数 FormatForXML()。我们 很快就会看到,该函数被定义在后台编码的类中,其作用只是将非法的 xml 字符替换为它们对应的合法的转义字符。最后我们应该注意,在 <pubDate> 元素里面的数据库字段 DatePublished 是用 String.Format 来格式化的。标准的格式描述符“R”对 DatePublished 的值进行相应的格式化 。

  此 Web 页面的后台编码类代码并不复杂。Page_Load 事件处理函数只是将数据库查询结果绑定到 Repeater控件,FormatForXML()函数根据需要做一些简单的字符串替换。为 简单起见,下面的例子只列出了这两个函数的代码:

  在浏览器中访问 rss.aspx 页面的截图参见图一。


图一 通过浏览器访问 Rss.aspx 页面 
  在我们生成在线新闻聚合器之前,让我谈谈这个聚合引擎一些可能的增强功能。首先,每一次访问 rss.aspx 页面的时候,都要访问一次数据库。如果预期可能有大量的人频繁地访问 rss.aspx 页面,使用输出缓存是很有价值的。其次,通常新闻网站会将聚合的内容分为不同的类别。例如:News.com 有一些专门的聚合内容区, 比如针对企业计算、电子商务、通信的内容等等。在数据库表 Articles 中加入表示类别的 Category 字段就可以很容易地提供这种支持。这样 一来,在 rss.aspx 页面中,可以接收一个表示显示分类的查询参数,然后只搜索指定的新闻项分类即可。
 
  在 ASP.NET 页面中使用聚合摘要
  为了测试我们刚建立的聚合引擎,我们将创建一个在线新闻聚合器,允许采集任意数量的聚合内容摘要。聚合器的界面很简单,参见图二。它包括三个框架页面。左边框架以列表形式列出了不同的聚合内容摘要。右上部框架显示所选的聚合内容摘要包含的新闻项以及查看该新闻项的链接。最后,在右下部框架则显示选中的新闻项标题和内容。顺便提及一下,这样的界面基本上是各种类型的聚合器的一个事实上的标准界面,包括新闻聚合器、email客户端软件和新闻组阅读器都是这样的界面。


图二 新闻聚合器用户界面的截图 
 
  第一步是创建一个html页面来建立框架用户界面。幸运的是,在Visual Studio.NET 2003 中,这一过程非常容易。只需要在Web应用程序解决方案中添加一个新 的项目,选择新项目类型为 Frameset。(我在我的工程中将这个新文件命名为 NewsAggregator.htm。我之所以将它设置为 html 文件而不是 asp.net 页面, 是因为这个页面只包括建立框架的 html 代码。每一个单独的框架会显示一个 asp.net 页面)。下一步,参见图三,Frameset 模版向导会启动,简单地选择选项“Nested Hierarchy”,然后按ok按钮就可以了。 


图三 VS2003 中 Frameset 模版向导画面
  然后 Frameset 模版向导会创建一个HTML页面,里面已经加入了框架的源代码。 只要将左边框架的src属性设置为 DisplayFeeds.aspx,它是列表显示聚合摘要 asp.net 页面的 url。至此 NewsAggreator.htm 页面就完成了。 
  以下三个部分,我们将讲述如何创建在线新闻聚合器的三个组件,它们分别是显示聚合摘要列表的 DisplayFeeds.aspx;显示特定聚合摘要新闻项 的 DisplayNewsItems.aspx;以及显示指定聚合摘要特定新闻项具体内容的 DisplayItem.aspx。
  显示聚合摘要列表 
  现在我们需要创建 DisplayFeeds.aspx 页面。该页面要显示订阅的聚合摘要列表。作为示范,我将这些聚合摘要放在一个叫 Feeds 的数据库表中。当然你也可以将它们放在一个XML文件中。表 Feeds 有如下四个字段: 
  ·FeedID—主键,自增长整数类型,唯一标示一个摘要 
  ·Title—摘要名称,数据库字段类型:varchar(50) 
  ·URL—RSS 摘要的 URL,数据库字段类型:varchar(150) 
  ·UpdateInterval—摘要更新频率(分钟),数据库字段类型:int 
  DisplayFeeds.aspx 页面使用一个 DataGrid 控件显示聚合摘要的列表。这个 DataGrid 只有一个 HyperLinkColumn 列,显示 Title 字段的内容并且链接到 DisplayNewsItems.aspx 页面, 在查询字符串中 要传递 FeedID 字段的值。以下是 DataGrid 控件的声明,为简单起见,省略了一些无关的部分):
<asp:DataGrid id="dgFeeds" runat="server" 
AutoGenerateColumns="False" ...>
...
<Columns>
<asp:HyperLinkColumn Target="rtop" 
DataNavigateUrlField="FeedID" 
DataNavigateUrlFormatString="DisplayNewsItems.aspx?FeedID={0}"
DataTextField="Title" HeaderText="RSS Feeds">
</asp:HyperLinkColumn>
</Columns>
</asp:DataGrid>  
  这里要注意的关键是 HyperLinkColumn 列的定义。它的 Target 属性设置为右上部分框架的名称,这样当用户点击的时候,DisplayNewsItems.aspx 页面就会显示在右上部分的框架中。另外, 属性 DataNavigateUrlField、DataNavigateUrlFormatString 和 DataTextField 也做了相应的设置, 以便超链接显示摘要的标题,并且当点击它时,就会将用户带到 DisplayNewsItems.aspx 页面,并在查询串中将 FeedID 字段的内容传 过来。(该页面的后台代码类只访问来自 Feeds 表的摘要清单,按照 Title 字段的字母顺序返回,接着将查询结果绑定到 DataGrid 控件。 由于篇幅所限,本文在此不列出代码。)
 
  显示特定聚合摘要的新闻项
  我们面临的下一个任务是创建 DisplayNewsItems.aspx 页面。这个页面会以链接的形式显示所选聚合摘要的新闻项标题,当点击标题时,新闻的内容就会显示在右下部分的框架中。要完成这一任务,我们会面临以下两个主要的挑战: 
  ·通过指定的 URL 访问 RSS 聚合摘要;
  ·将接收到的 XML 数据转换为相应的 HTML; 
  幸运的是,在.NET 框架中,要实现这两个任务都不是很难。对于第一个任务,只需要两行代码,我们就可以将远程的xml数据装载到一个XmlDocument对象中。而第二个任务呢, 借助 ASP.NET XML Web 控件在ASP.NET 页面中显示XML数据也比较容易。
  XML Web 控件被设计用于在 Web 页面中显示原始或者转换过的 XML 数据。使用 XML Web 控件的第一步是定义XML数据源,通过 定义一系列的属性,用许多方法都可以完成这一工作。使用 Document属性,你可以指定一个 XmlDocument 实例作为 XML Web 控件的 XML 数据源。如果XML数据存在于 Web 服务器文件系统的一个文件中,可以用 DocumentSource 属性,只要提供该 XML 文件的相对或者绝对路径就可以了。最后,如果你 的 XML数据是一个字符串,那么你可以将这个字符串的内容赋给控件的 DocumentContent 属性。这三种办法都可以将 XML 数据与 XML 控件联系起来。
  通常,在将 XML 数据显示到 Web 页面之前,我们会以某种方式转换 XML 数据。XML Web 控件允许我们指定一个 XSLT 样式表来做这个转换工作。与 XML 数据相似,XSLT 样式表可以通过 两个属性之一,以两种不同的方式中的一种来设置,一是 Transform 属性可被赋值给 XslTransform 实例,二是将本地 Web 服务器上 XSLT文件的 相对或绝对路径赋予 TransformSource 属性。
  现在我们来创建 DisplayNewsItems.aspx 页面。在添加 XML Web 控件以及编写后台代码类之前,我们需要在 HTML 部分加入一小段客户端 JavaScript 代码。准确地说,是在 html 部分的 <head> 标签里面 添加如下的<script>代码块:
<script language="javascript">
// display a blank page in the bottom frame when the news items loads
parent.rbottom.location.href = "about:blank";
</script>  
  每当 DisplayNewsItems.aspx 页面装载的时候,这段客户端 JavaScript 代码会在右下角的框架中显示一个空白页。为了理解为什么要加入这段代码,我们来看看省略这段代码,我们会碰到什么情况: 
  ·用户在左边的框架中点击聚合摘要,浏览器会在右上部的框架中装载摘要新闻项;
  ·用户在右上部框架中点击某个新闻项,浏览器会在右下部框架中装载这个新闻项 的详细内容;
  现在用户在左边的框架中点击其它的聚合摘要,浏览器会在右上部分的框架中装载新的摘要新闻项; 
  前一个新闻项的详细内容还显示在右下部的框架中!通过上面的客户端 Javascript 代码,每次点击左面框架的摘要便可以清除右下部框架 的内容,以消除这一瑕疵。
  在我们处理了客户端代码的问题之后,让我们把注意力转到添加 XML Web 控件。一旦加入 XML Web 控件,将其 ID 属性设置为 xsltNewsItems,TransformSourc 属性设置为 NewsItems.xslt(我们将要创建的 XSLT 样式表文件的名称)。现在,在 Page_Load 事件处理函数中,我们需要 在某个 XmlDocument 实例中获取远程 RSS 聚合文件,然后将该 XML Web 控件的 Document 属性赋给该 XmlDocument 实例。
  在 Page_Load 事件处理函数中,与我们要实现的任务有密切关系的代码是最后三行代码。这三行代码创建一个新的 XmlDocument 对象, 加载远程 RSS 摘要内容,然后将这个 XmlDocument 对象赋给 XML Web 控件的 Document 属性。访问远程 XML 数据并 将它们显示在 ASP.NET 页面中就是这么简单,难道给你留下的印象不深吗? 
  剩下我们要做的一件事就是创建 XSLT 样式表,NewsItems.aspx。下面是样式表的第一版的草稿:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
<xsl:output method="html" omit-xml-declaration="yes" />
<xsl:template match="/rss/channel">
<b><xsl:value-of select="title" 
disable-output-escaping="yes" /></b>
<xsl:for-each select="item">
<li>
<a>
<xsl:attribute name="href">
DisplayItem.aspx?ID=<xsl:number value="position()" />
</xsl:attribute>
<xsl:attribute name="target">rbottom</xsl:attribute>
<xsl:value-of select="title"
disable-output-escaping="yes" />
</a>
(<xsl:value-of select="pubDate" />)
</li>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>  
  这个XSLT样式表只有一个模版,用于匹配“/rss/channel”XPath表达式。这个模版先是以粗体显示<title>元素的内容。然后,循环获取每一个<item>元素,对于每一个元素,显示一个到 DisplayItem.aspx 页面的超链接,在查询字符串中传递<item>元素的位置属性。要留意超链接的 target 属性设置为 rbottom,右下部框架的名称。最后,显示每一个新闻项的标题和<pubDate>元素。
  该 XSLT 样式表中有两个项目,并不是每个人都熟悉。首先是 <xsl:value-of> 元素中的 disable-output-escaping="yes" 属性。从本质上讲,这个属性的设置通知 XSLT 引擎不要转义那些非法的 XML 字符,比如:&, < , >, " 和 ’’。为了理解这个设置的意义,就要知道,如果不设置该属性(也就是设置为默认值"no"),那么如果标题包含一个转义的&字符&amp;,那么输出的 html 文件中也会有一个&amp;,而不单单是一个字符&。如果你再仔细想一想,你会发现这种情况会导致很多问题。例如,假设一个聚合文件的标题是“Matt’’s &lt;i&gt;Cool&lt;/i&gt; Blog”,如果输出转义没有被禁止,那么输出就会保留 “Matt’’s &lt;i&gt;Cool&lt;/i&gt; Blog”,在 Web 页面就会显示为 "Matt’’s <i>Cool</i> Blog"。当用 disable-output-escaping="yes"设置禁止输出转义时,输出就不会被转义,上面的内容就会被当作“Matt’’s <i>Cool</i> Blog”,显示在页面上就是我们想要的“Matt’’s Cool Blog”。
  另一个要注意的是元素<a>。这个奇怪的语法会生成下面的输出内容:
<a href="DisplayItem.aspx?ID=position">news item title</a> 
  之所以要使用这种语法,是因为要给 XSLT 样式表中某个你要创建的元素添加一个属性,然后在该元素的标签里使用 <xsl:attribute> 语法 。有关该语法的一些例子可在 W3Schools 网站上找到:The <xsl:attribute> Element。
  最后要注意的是,超链接的ID查询字符串的值是来自于 <xsl:number> 元素,从 position() 函数中返回的值。<xsl:number> 元素仅仅是输出一个数值。position()函数是一个 XPath 函数,用来返回 XML 文档中当前节点的顺序位置。这意味着对于第一个新闻项,position() 函数返回 1,第二个 新闻项,position函数返回 2,以此类推。我们需要记录这个值并将它通过查询字符串传递出去。这样当 DisplayItem.asp 页面被访问时,就可以知道显示 RSS 聚合摘要的什么项目了。
  聪明的读者可能已经注意到,我们的 XSLT 样式表没有全部完成,因为 FeedID 参数没有通过查询字符串传递到 DisplayItem.aspx 页面。要明白 这是为什么,我们回顾一下在 ID 查询串参数中所传递的是用户拟察看详细信息的<item>元素顺序号。也就是说,如果用户点击第四条新闻项,页面 DisplayItem.aspx?ID=4 就会被 加载到右下部分的框架中。问题在于 DisplayItem.aspx 页面无法确定用户希望查看哪一个摘要。有两个不同的方法可以解决这个问题,比如可以在右下部框架中用客户端 Javascript 代码读取右上部框架的 URL,然后确定FeedID 的值。在我看来,更简单的办法是和 ID 参数一起将 FeedID 的值通过查询字符串传递 。
  这样的话,有一个难题是 XSLT 样式表操纵的 RSS XML 数据中并没有 FeedID 值。但是 DisplayNewsItems.aspx 页面知道 FeedID 值,需要一种方法让 XSLT 样式表也知道这个值。通过使用 XSLT参数可以 实现完成。
   XSLT 参数的使用是非常简单。在 XSLT 样式表中,你需要在 <xsl:template> 元素中加入一个<xsl:param> 元素, 该元素提供参数的名称。下面的代码将这个参数命名为 FeedID:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
<xsl:template match="/rss/channel">
<xsl:param name="FeedID" />
...
</xsl:template>
</xsl:stylesheet> 
  现在,就可以用下面的语法在<xsl:value-of>元素中使用这个参数了:
<xsl:value-of select="$parameterName" />  
  最后,在我们的 XSLT 样式表中加入下面的代码,我们就可以把 FeedID 查询字符串参数加到超链接中了:
<a>
<xsl:attribute name="href">DisplayItem.aspx?ID=<xsl:number value="position()" />&amp;FeedID=<xsl:value-of select="$FeedID" 
/></xsl:attribute> 

  注意在ID查询字符串参数后面我们加了一个&字符(转义&amp;),这样我们就可以传递 FeedID 参数的值到查询字符串的 FeedID 参数中了。 这就是我们要在 XSLT 样式表中添加的内容。
  剩下的工作是在 DisplayNewsItems.aspx 页面的 Page_Load 事件处理函数中设置这个参数的值。通过使用 XsltArgumentList 类可以完成这一工作。这个类有一个 AddParameter() 方法。一旦我们创建了这个类的一个实例,加入了相应的参数,就可以将这个 实例赋给 XML Web 控件的 TransformArgumentList 参数了。下面的代码显示了更新后的 DisplayNewsItems.aspx 页面 Page_Load 事件处理函数:
  显示特定新闻项的详细内容 
  还剩下最后一件需要做的事情是显示用户选择的特定新闻项的详细内容。这些详细内容将显示在右下部的框架中,而且将会显示新闻项的标题,描述和新闻项的链接等信息。和 DisplayNewsItem.aspx 页面 类似,DisplayItem.aspx 页面首先将根据传入的 FeedID 查询字符串参数获取远程的 RSS 聚合摘要,然后它会用 XML Web 控件显示这些详细内容。实际上,DisplayItem.aspx 页面的 Page_Load 事件处理函数和DisplayNewsItem.aspx 页面的 该函数几乎一样,只有以下两个小小的区别: 
  ·DisplayItem.aspx 页面需要读取ID查询字符串参数的值;
  ·DisplayItem.aspx 页面使用一个 XSLT 参数,但是这个参数与 DisplayNewsItem.aspx 页面用的参数是不一样的; 
  DisplayNewsItem.aspx 和 DisplayItem.aspx 页面一样都需要在参数中传递一个 XSLT 样式表。DisplayNewsItem.aspx 页面传递的是 参数 FeedID,而 DisplayItem.aspx 还需要传入 ID 参数,它表示 XSLT 样式表应该显示那个新闻项。这个细小的差别在以下代码中以粗体显示,以下 代码省略了与 DisplayNewsItems.aspx 页面相同的部分:
  以下是转换 XML 数据的 XSLT 样式表:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
<xsl:output method="html" omit-xml-declaration="yes" />
<xsl:param name="ID" />
<xsl:template match="/rss/channel">
<b><xsl:value-of select="item[$ID]/title" 
disable-output-escaping="yes" /></b>
<p>
<xsl:value-of select="item[$ID]/description" 
disable-output-escaping="yes" />
</p>
<a>
<xsl:attribute name="href"><xsl:value-of 
select="item[$ID]/link" /></xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
Read More...
</a>
</xsl:template>
</xsl:stylesheet>  
  注意 <xsl:param> 元素被用于声明 ID XSLT 参数。然后,在几个不同的 <xsl:value-of> 元素中,ID 参数 被用来从 <item> 元素列表中抓取特定的 <item> 元素。在 XPath 的语法中,elementName[i]意思是根据相应元素名 存取第i个元素。例如,item[1]将只获取第一个<item>元素,item[2]则获取第二个元素。所以 item[$ID]是获取由 XSLT 参数 ID 定义的 特定 <item> 元素。
  最后,值得注意的还有在样式表靠近末尾部分的超链接 Read More…,它的target属性设为空,这样的话当用户点击 Read More… 链接的时候,浏览器会打开一个新的窗口。
 
  未来的扩展和当前程序的缺点
  本文讲述的代码中有一个明显的缺点就是每次用户点击左边框架的某个聚合摘要或者在右上部框架点击某个新闻项时,远程聚合摘要都会被装载和解析。每次用户点击远程聚合 摘要时,所有的项都被加载,这样的效率无疑是很差的。每次用户点击一个新闻项标题就重新装载整个远程聚合摘要也是很浪费资源的。这样的方法不仅没有效率,对提供发布服务的个人或者公司也是不礼貌的,因为这些 连续的、不没必要的请求占用了他们的 Web 服务器的负载资源。
  这个缺点在本文附带的源代码中已经得到解决。具体来说,.NET数据缓存可以用来存放不同摘要的 XmlDocument 对象。缓存间隔设置为数据表 Feeds 中 UpdateInterval 字段定义的值。(当然,由于某些原因,摘要的 XmlDocument 对象有可能会被提前清除出缓存)
  这个系统的另外一个缺点是在右上部框架和右下部框架之间没有状态的保存。为了说明这样会引起什么问题,考虑以下的动作: 
  ·用户点击左边框架的某个聚合摘要链接,在右上部框架中装载这个摘要的新闻项目。假设这个摘要的UpdateInterval 的值是30,则表示这些内容在30分钟之 后会过期;
  ·装载右上部框架的新闻项的同时,这些内容被缓存起来;
  ·用户离开去吃午饭;
  ·发布聚合内容的网站增加了一条新的新闻项;
  ·我们的用户一个小时午饭后回来了,这个 摘要的 XmlDocument 的缓存已经过期; 
  ·用户点击右上部框架的第一条新闻项,将会在右下部分框架中装载 DisplayItem.aspx,传入 ID 参数值1; 
  ·DisplayItem.aspx 页面在缓存中没找到 XmlDocument 对象,只好重新获取远程摘要。这样就会获得新的数据了(别忘了,步骤 4 已经加了一个新的新闻项),然后此页面会显示第一条新闻项目(因为ID参数的值为1) ; 
  ·用户看到了新的新闻项,但是内容会令他感到有点困惑,因为已经不是他所点击的那一条新闻了,而且右上部也没有显示那条新的新闻。 
  之所以出现这样的问题,是因为 ID 参数没有唯一地标识一个新闻项,它只是一个特定时间点上新闻项列表中的一个偏移量。解决这个问题的一个好的方法是不要用数据缓存来保存聚合 摘要,而是使用数据库或者持久介质的其它方式(比如 Web 服务器本地文件系统的 XML 文件)。如果使用数据库,每一个新闻项都可以拥有一个唯一的标识号,可以用来传递到右下角的框架中。这种方法可以保证解决上面提到的问题。当然也会增加系统的复杂性,比如需要决定何时从数据库中清除掉旧的新闻项 。
  本文现有的应用程序还缺少异常处理,而这肯定是应该加上的。尤其是当从远程 RSS 聚合摘要文件获取数据并加载到 XmlDocument 对象时,应该加上异常处理。因为远程的文件可能不存在或者格式不正确。
  还有很多增强功能可以轻松地加入到这个在线新闻聚合器。一个明显的功能是需要一个管理页面来允许用户添加,删除和编辑他们现在的聚合摘要。还有,如果能允许用户自定义分类 ,将他们的聚合摘要按类别放在一起就更好了。另外,现在的用户界面还是比较粗糙的,但是通过增加一些 XSLT 样式表生成的 HTML 代码或者在几个框架里面增加一些样式表就可以很容易地美化一下界面。最后,在html标签里面加一些<meta>元素,可以让右上部框架定时地去刷新,使得用户不用自己手工去刷新页面就可以看到最新的新闻项目。
  注解 (2003年8月4日): 在这篇文章发布以后,一些读者用 Email 告诉通知我在显示特定 RSS 聚合项的 <description> 元素时,有两个潜在的问题:
  1、Disable-output-encoding 属性,这个属性用在 <xsl:value-of> 元素中,但是并不是所有的 XSLT解析器都实现了这个功能。.NET XSLT 解析器支持 disable-output-encoding,但是还是要 注意一下,因为读者可能试图将这个应用程序移植到其它平台。
  2、<description> 元素的 HTML 内容是被原封不动地输出的。但是,这些 HTML 内容可能包含恶意代码,比如 <script> 或者 <embed> 代码块。理想情况下,这些代码应该被剔除掉。为了清除掉这些有潜在危险的代码,可能需要用到一些扩展函数(参见 Extending XSLT with JScript, C#, and Visual Basic .NET)。想查看从 RSS 聚合 摘要剔除 HTML 内容的更多信息,可以参见’’Dive Into Mark’’ 日志.
  总结 
  在本文中,我们不仅讲到如何创建一个聚合引擎,还创建了一个在线新闻聚合器。在建立这两个应用程序时,我们都采用了在 ASP.NET 页面显示 XML 数据的技术。在聚合引擎里面,我们使用了 Repeater 控件以 XML格式来显示数据库中的数据。而在新闻聚合器里面,我们使用了 XML Web 控件和 XSLT 样式表。

posted @ 2005-11-26 16:00 井泉 阅读(55) | 评论 (0)编辑 收藏

2005年11月11日

ASP.NET程序中常用的三十三种代码

1. 打开新的窗口并传送参数:

  传送参数:

response.write("<script>window.open(’*.aspx?id="+this.DropDownList1.SelectIndex+"&id1="+...+"’)</script>")
  接收参数:

string a = Request.QueryString("id");
string b = Request.QueryString("id1");

  2.为按钮添加对话框

Button1.Attributes.Add("onclick","return confirm(’确认?’)");
button.attributes.add("onclick","if(confirm(’are you sure...?’)){return true;}else{return false;}")

  3.删除表格选定记录

int intEmpID = (int)MyDataGrid.DataKeys[e.Item.ItemIndex];
string deleteCmd = "DELETE from Employee where emp_id = " + intEmpID.ToString()

  4.删除表格记录警告

private void DataGrid_ItemCreated(Object sender,DataGridItemEventArgs e)
{
 switch(e.Item.ItemType)
 {
  case ListItemType.Item :
  case ListItemType.AlternatingItem :
  case ListItemType.EditItem:
   TableCell myTableCell;
   myTableCell = e.Item.Cells[14];
   LinkButton myDeleteButton ;
   myDeleteButton = (LinkButton)myTableCell.Controls[0];
   myDeleteButton.Attributes.Add("onclick","return confirm(’您是否确定要删除这条信息’);");
   break;
  default:
   break;
 }

}

  5.点击表格行链接另一页

private void grdCustomer_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
 //点击表格打开
 if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
  e.Item.Attributes.Add("onclick","window.open(’Default.aspx?id=" + e.Item.Cells[0].Text + "’);");
}

  双击表格连接到另一页

  在itemDataBind事件中

if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
 string OrderItemID =e.item.cells[1].Text;
 ...
 e.item.Attributes.Add("ondblclick", "location.href=’../ShippedGrid.aspx?id=" + OrderItemID + "’");
}

  双击表格打开新一页

if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
 string OrderItemID =e.item.cells[1].Text;
 ...
 e.item.Attributes.Add("ondblclick", "open(’../ShippedGrid.aspx?id=" + OrderItemID + "’)");
}

  ★特别注意:【?id=】 处不能为 【?id =】
      6.表格超连接列传递参数

<asp:HyperLinkColumn Target="_blank" headertext="ID号" DataTextField="id" NavigateUrl="aaa.aspx?id=’
 <%# DataBinder.Eval(Container.DataItem, "数据字段1")%>’ & name=’<%# DataBinder.Eval(Container.DataItem, "数据字段2")%>’ />

  7.表格点击改变颜色

if (e.Item.ItemType == ListItemType.Item ||e.Item.ItemType == ListItemType.AlternatingItem)
{
 e.Item.Attributes.Add("onclick","this.style.backgroundColor=’#99cc00’;
    this.style.color=’buttontext’;this.style.cursor=’default’;");
}

  写在DataGrid的_ItemDataBound里

if (e.Item.ItemType == ListItemType.Item ||e.Item.ItemType == ListItemType.AlternatingItem)
{
e.Item.Attributes.Add("onmouseover","this.style.backgroundColor=’#99cc00’;
   this.style.color=’buttontext’;this.style.cursor=’default’;");
e.Item.Attributes.Add("onmouseout","this.style.backgroundColor=’’;this.style.color=’’;");
}


  8.关于日期格式

  日期格式设定

DataFormatString="{0:yyyy-MM-dd}"
  我觉得应该在itembound事件中

e.items.cell["你的列"].text=DateTime.Parse(e.items.cell["你的列"].text.ToString("yyyy-MM-dd"))
  9.获取错误信息并到指定页面

  不要使用Response.Redirect,而应该使用Server.Transfer

  e.g

// in global.asax
protected void Application_Error(Object sender, EventArgs e) {
if (Server.GetLastError() is HttpUnhandledException)
Server.Transfer("MyErrorPage.aspx");

//其余的非HttpUnhandledException异常交给ASP.NET自己处理就okay了 :)
}

  Redirect会导致post-back的产生从而丢失了错误信息,所以页面导向应该直接在服务器端执行,这样就可以在错误处理页面得到出错信息并进行相应的处理

  10.清空Cookie

Cookie.Expires=[DateTime];
Response.Cookies("UserName").Expires = 0

  11.自定义异常处理

//自定义异常处理类
using System;
using System.Diagnostics;

namespace MyAppException
{
 /// <summary>
 /// 从系统异常类ApplicationException继承的应用程序异常处理类。
 /// 自动将异常内容记录到Windows NT/2000的应用程序日志
 /// </summary>
 public class AppException:System.ApplicationException
 {
  public AppException()
  {
   if (ApplicationConfiguration.EventLogEnabled)LogEvent("出现一个未知错误。");
  }

 public AppException(string message)
 {
  LogEvent(message);
 }

 public AppException(string message,Exception innerException)
 {
  LogEvent(message);
  if (innerException != null)
  {
   LogEvent(innerException.Message);
  }
 }

 //日志记录类
 using System;
 using System.Configuration;
 using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Threading;

 namespace MyEventLog
 {
  /// <summary>
  /// 事件日志记录类,提供事件日志记录支持
  /// <remarks>
  /// 定义了4个日志记录方法 (error, warning, info, trace)
  /// </remarks>
  /// </summary>
  public class ApplicationLog
  {
   /// <summary>
   /// 将错误信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteError(String message)
   {
    WriteLog(TraceLevel.Error, message);
   }

   /// <summary>
   /// 将警告信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteWarning(String message)
   {
    WriteLog(TraceLevel.Warning, message);  
   }

   /// <summary>
   /// 将提示信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteInfo(String message)
   {
    WriteLog(TraceLevel.Info, message);
   }
   /// <summary>
   /// 将跟踪信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteTrace(String message)
   {
    WriteLog(TraceLevel.Verbose, message);
   }

   /// <summary>
   /// 格式化记录到事件日志的文本信息格式
   /// <param name="ex">需要格式化的异常对象</param>
   /// <param name="catchInfo">异常信息标题字符串.</param>
   /// <retvalue>
   /// <para>格式后的异常信息字符串,包括异常内容和跟踪堆栈.</para>
   /// </retvalue>
   /// </summary>
   public static String FormatException(Exception ex, String catchInfo)
   {
    StringBuilder strBuilder = new StringBuilder();
    if (catchInfo != String.Empty)
    {
     strBuilder.Append(catchInfo).Append("\r\n");
    }
    strBuilder.Append(ex.Message).Append("\r\n").Append(ex.StackTrace);
    return strBuilder.ToString();
   }

   /// <summary>
   /// 实际事件日志写入方法
   /// <param name="level">要记录信息的级别(error,warning,info,trace).</param>
   /// <param name="messageText">要记录的文本.</param>
   /// </summary>
   private static void WriteLog(TraceLevel level, String messageText)
   {
    try
    {
     EventLogEntryType LogEntryType;
     switch (level)
     {
      case TraceLevel.Error:
       LogEntryType = EventLogEntryType.Error;
       break;
      case TraceLevel.Warning:
       LogEntryType = EventLogEntryType.Warning;
       break;
      case TraceLevel.Info:
       LogEntryType = EventLogEntryType.Information;
       break;
      case TraceLevel.Verbose:
       LogEntryType = EventLogEntryType.SuccessAudit;
       break;
      default:
       LogEntryType = EventLogEntryType.SuccessAudit;
       break;
     }

     EventLog eventLog = new EventLog("Application", ApplicationConfiguration.EventLogMachineName, ApplicationConfiguration.EventLogSourceName );
     //写入事件日志
     eventLog.WriteEntry(messageText, LogEntryType);

    }
   catch {} //忽略任何异常
  }
 } //class ApplicationLog
}

   12.Panel 横向滚动,纵向自动扩展

<asp:panel style="overflow-x:scroll;overflow-y:auto;"></asp:panel>
  13.回车转换成Tab

<script language="javascript" for="document" event="onkeydown">
 if(event.keyCode==13 && event.srcElement.type!=’button’ && event.srcElement.type!=’submit’ &&     event.srcElement.type!=’reset’ && event.srcElement.type!=’’&& event.srcElement.type!=’textarea’);
   event.keyCode=9;
</script>

onkeydown="if(event.keyCode==13) event.keyCode=9"

  14.DataGrid超级连接列

DataNavigateUrlField="字段名" DataNavigateUrlFormatString="http://xx/inc/delete.aspx?ID={0}"
  15.DataGrid行随鼠标变色

private void DGzf_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
 if (e.Item.ItemType!=ListItemType.Header)
 {
  e.Item.Attributes.Add( "onmouseout","this.style.backgroundColor=\""+e.Item.Style["BACKGROUND-COLOR"]+"\"");
  e.Item.Attributes.Add( "onmouseover","this.style.backgroundColor=\""+ "#EFF3F7"+"\"");
 }
}

  16.模板列

<ASP:TEMPLATECOLUMN visible="False" sortexpression="demo" headertext="ID">
<ITEMTEMPLATE>
<ASP:LABEL text=’<%# DataBinder.Eval(Container.DataItem, "ArticleID")%>’ runat="server" width="80%" id="lblColumn" />
</ITEMTEMPLATE>
</ASP:TEMPLATECOLUMN>

<ASP:TEMPLATECOLUMN headertext="选中">
<HEADERSTYLE wrap="False" horizontalalign="Center"></HEADERSTYLE>
<ITEMTEMPLATE>
<ASP:CHECKBOX id="chkExport" runat="server" />
</ITEMTEMPLATE>
<EDITITEMTEMPLATE>
<ASP:CHECKBOX id="chkExportON" runat="server" enabled="true" />
</EDITITEMTEMPLATE>
</ASP:TEMPLATECOLUMN>

  后台代码

protected void CheckAll_CheckedChanged(object sender, System.EventArgs e)
{
 //改变列的选定,实现全选或全不选。
 CheckBox chkExport ;
 if( CheckAll.Checked)
 {
  foreach(DataGridItem oDataGridItem in MyDataGrid.Items)
  {
   chkExport = (CheckBox)oDataGridItem.FindControl("chkExport");
   chkExport.Checked = true;
  }
 }
 else
 {
  foreach(DataGridItem oDataGridItem in MyDataGrid.Items)
  {
   chkExport = (CheckBox)oDataGridItem.FindControl("chkExport");
   chkExport.Checked = false;
  }
 }
}

  17.数字格式化

  【<%#Container.DataItem("price")%>的结果是500.0000,怎样格式化为500.00?】

<%#Container.DataItem("price","{0:¥#,##0.00}")%>

int i=123456;
string s=i.ToString("###,###.00");

   18.日期格式化

  【aspx页面内:<%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date")%>

  显示为: 2004-8-11 19:44:28

  我只想要:2004-8-11 】

<%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date","{0:yyyy-M-d}")%>
  应该如何改?

  【格式化日期】

  取出来,一般是object((DateTime)objectFromDB).ToString("yyyy-MM-dd");

  【日期的验证表达式】

  A.以下正确的输入格式: [2004-2-29], [2004-02-29 10:29:39 pm], [2004/12/31]

^((\d{2}(([02468][048])|([13579][26]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|([1-2][0-9])))))|(\d{2}(([02468][1235679])|([13579][01345789]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\s(((0?[1-9])|(1[0-2]))\:([0-5][0-9])((\s)|(\:([0-5][0-9])\s))([AM|PM|am|pm]{2,2})))?$
  B.以下正确的输入格式:[0001-12-31], [9999 09 30], [2002/03/03]

^\d{4}[\-\/\s]?((((0[13578])|(1[02]))[\-\/\s]?(([0-2][0-9])|(3[01])))|(((0[469])|(11))[\-\/\s]?(([0-2][0-9])|(30)))|(02[\-\/\s]?[0-2][0-9]))$
  【大小写转换】

HttpUtility.HtmlEncode(string);
HttpUtility.HtmlDecode(string)

  19.如何设定全局变量

  Global.asax中

  Application_Start()事件中

  添加Application[属性名] = xxx;

  就是你的全局变量

  20.怎样作到HyperLinkColumn生成的连接后,点击连接,打开新窗口?

  HyperLinkColumn有个属性Target,将器值设置成"_blank"即可.(Target="_blank")

  【ASPNETMENU】点击菜单项弹出新窗口

  在你的menuData.xml文件的菜单项中加入URLTarget="_blank",如:

<?xml version="1.0" encoding="GB2312"?>
<MenuData ImagesBaseURL="images/">
<MenuGroup>
<MenuItem Label="内参信息" URL="Infomation.aspx" >
<MenuGroup ID="BBC">
<MenuItem Label="公告信息" URL="Infomation.aspx" URLTarget="_blank" LeftIcon="file.gif"/>
<MenuItem Label="编制信息简报" URL="NewInfo.aspx" LeftIcon="file.gif" />
......

  最好将你的aspnetmenu升级到1.2版

  21.读取DataGrid控件TextBox值

foreach(DataGrid dgi in yourDataGrid.Items)
{
 TextBox tb = (TextBox)dgi.FindControl("yourTextBoxId");
 tb.Text....
}

  23.在DataGrid中有3个模板列包含Textbox分别为 DG_ShuLiang (数量) DG_DanJian(单价) DG_JinE(金额)分别在5.6.7列,要求在录入数量及单价的时候自动算出金额即:数量*单价=金额还要求录入时限制为 数值型.我如何用客户端脚本实现这个功能?

  〖思归〗

<asp:TemplateColumn HeaderText="数量">
<ItemTemplate>
<asp:TextBox id="ShuLiang" runat=’server’ Text=’<%# DataBinder.Eval(Container.DataItem,"DG_ShuLiang")%>’
onkeyup="javascript:DoCal()"
/>

<asp:RegularExpressionValidator id="revS" runat="server" ControlToValidate="ShuLiang" ErrorMessage="must be integer" ValidationExpression="^\d+$" />
</ItemTemplate>
</asp:TemplateColumn>

<asp:TemplateColumn HeaderText="单价">
<ItemTemplate>
<asp:TextBox id="DanJian" runat=’server’ Text=’<%# DataBinder.Eval(Container.DataItem,"DG_DanJian")%>’
onkeyup="javascript:DoCal()"
/>

<asp:RegularExpressionValidator id="revS2" runat="server" ControlToValidate="DanJian" ErrorMessage="must be numeric" ValidationExpression="^\d+(\.\d*)?$" />

</ItemTemplate>
</asp:TemplateColumn>

<asp:TemplateColumn HeaderText="金额">
<ItemTemplate>
<asp:TextBox id="JinE" runat=’server’ Text=’<%# DataBinder.Eval(Container.DataItem,"DG_JinE")%>’ />
</ItemTemplate>
</asp:TemplateColumn><script language="javascript">
function DoCal()
{
 var e = event.srcElement;
 var row = e.parentNode.parentNode;
 var txts = row.all.tags("INPUT");
 if (!txts.length || txts.length < 3)
  return;

 var q = txts[txts.length-3].value;
 var p = txts[txts.length-2].value;

 if (isNaN(q) || isNaN(p))
  return;

 q = parseInt(q);
 p = parseFloat(p);

 txts[txts.length-1].value = (q * p).toFixed(2);
}
</script>

     24.datagrid选定比较底下的行时,为什么总是刷新一下,然后就滚动到了最上面,刚才选定的行因屏幕的关系就看不到了。

page_load
page.smartNavigation=true

  25.在Datagrid中修改数据,当点击编辑键时,数据出现在文本框中,怎么控制文本框的大小 ?

private void DataGrid1_ItemDataBound(obj sender,DataGridItemEventArgs e)
{
 for(int i=0;i<e.Item.Cells.Count-1;i++)
  if(e.Item.ItemType==ListItemType.EditType)
  {
   e.Item.Cells[i].Attributes.Add("Width", "80px")
  }
}

  26.对话框

private static string ScriptBegin = "<script language=\"JavaScript\">";
private static string ScriptEnd = "</script>";

public static void ConfirmMessageBox(string PageTarget,string Content)
{
 string ConfirmContent="var retValue=window.confirm(’"+Content+"’);"+"if(retValue){window.location=’"+PageTarget+"’;}";

 ConfirmContent=ScriptBegin + ConfirmContent + ScriptEnd;

 Page ParameterPage = (Page)System.Web.HttpContext.Current.Handler;
 ParameterPage.RegisterStartupScript("confirm",ConfirmContent);
 //Response.Write(strScript);
}

  27. 将时间格式化:string aa=DateTime.Now.ToString("yyyy年MM月dd日");

  1.1 取当前年月日时分秒

currentTime=System.DateTime.Now;
  1.2 取当前年

int 年= DateTime.Now.Year;
  1.3 取当前月

int 月= DateTime.Now.Month;
  1.4 取当前日

int 日= DateTime.Now.Day;
  1.5 取当前时

int 时= DateTime.Now.Hour;
  1.6 取当前分

int 分= DateTime.Now.Minute;
  1.7 取当前秒

int 秒= DateTime.Now.Second;
  1.8 取当前毫秒

int 毫秒= DateTime.Now.Millisecond;
  28.自定义分页代码:

  先定义变量 :

public static int pageCount; //总页面数
public static int curPageIndex=1; //当前页面

  下一页:

if(DataGrid1.CurrentPageIndex < (DataGrid1.PageCount - 1))
{
 DataGrid1.CurrentPageIndex += 1;
 curPageIndex+=1;
}

bind(); // DataGrid1数据绑定函数

  上一页:

if(DataGrid1.CurrentPageIndex >0)
{
 DataGrid1.CurrentPageIndex += 1;
 curPageIndex-=1;
}

bind(); // DataGrid1数据绑定函数

  直接页面跳转:

int a=int.Parse(JumpPage.Value.Trim());//JumpPage.Value.Trim()为跳转值

if(a<DataGrid1.PageCount)
{
 this.DataGrid1.CurrentPageIndex=a;
}

bind();

   29.DataGrid使用:

  添加删除确认:

private void DataGrid1_ItemCreated(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
 foreach(DataGridItem di in this.DataGrid1.Items)
 {
  if(di.ItemType==ListItemType.Item||di.ItemType==ListItemType.AlternatingItem)
  {
   ((LinkButton)di.Cells[8].Controls[0]).Attributes.Add("onclick","return confirm(’确认删除此项吗?’);");
  }
 }
}

  样式交替:

ListItemType itemType = e.Item.ItemType;

if (itemType == ListItemType.Item )
{
 e.Item.Attributes["onmouseout"] = "javascript:this.style.backgroundColor=’#FFFFFF’;";
 e.Item.Attributes["onmouseover"] = "javascript:this.style.backgroundColor=’#d9ece1’;cursor=’hand’;" ;
}
else if( itemType == ListItemType.AlternatingItem)
{
 e.Item.Attributes["onmouseout"] = "javascript:this.style.backgroundColor=’#a0d7c4’;";
 e.Item.Attributes["onmouseover"] = "javascript:this.style.backgroundColor=’#d9ece1’;cursor=’hand’;" ;
}

  添加一个编号列:

DataTable dt= c.ExecuteRtnTableForAccess(sqltxt); //执行sql返回的DataTable
DataColumn dc=dt.Columns.Add("number",System.Type.GetType("System.String"));

for(int i=0;i<dt.Rows.Count;i++)
{
 dt.Rows[i]["number"]=(i+1).ToString();
}

DataGrid1.DataSource=dt;
DataGrid1.DataBind();

  DataGrid1中添加一个CheckBox,页面中添加一个全选框

private void CheckBox2_CheckedChanged(object sender, System.EventArgs e)
{
 foreach(DataGridItem thisitem in DataGrid1.Items)
 {
  ((CheckBox)thisitem.Cells[0].Controls[1]).Checked=CheckBox2.Checked;
 }
}

  将当前页面中DataGrid1显示的数据全部删除

foreach(DataGridItem thisitem in DataGrid1.Items)
{
 if(((CheckBox)thisitem.Cells[0].Controls[1]).Checked)
 {
  string strloginid= DataGrid1.DataKeys[thisitem.ItemIndex].ToString();
  Del (strloginid); //删除函数
 }
}

  30.当文件在不同目录下,需要获取数据库连接字符串(如果连接字符串放在Web.config,然后在Global.asax中初始化)

  在Application_Start中添加以下代码:

Application["ConnStr"]=this.Context.Request.PhysicalApplicationPath+ConfigurationSettings.
   AppSettings["ConnStr"].ToString();

  31. 变量.ToString()

  字符型转换 转为字符串

12345.ToString("n"); //生成 12,345.00
12345.ToString("C"); //生成 ¥12,345.00
12345.ToString("e"); //生成 1.234500e+004
12345.ToString("f4"); //生成 12345.0000
12345.ToString("x"); //生成 3039 (16进制)
12345.ToString("p"); //生成 1,234,500.00%

  32、变量.Substring(参数1,参数2);

  截取字串的一部分,参数1为左起始位数,参数2为截取几位。 如:string s1 = str.Substring(0,2);

  33.在自己的网站上登陆其他网站:(如果你的页面是通过嵌套方式的话,因为一个页面只能有一个FORM,这时可以导向另外一个页面再提交登陆信息)

<SCRIPT language="javascript">
<!--
 function gook(pws)
 {
  frm.submit();
 }
//-->

</SCRIPT> <body leftMargin="0" topMargin="0" onload="javascript:gook()" marginwidth="0" marginheight="0">
<form name="frm" action=" http://220.194.55.68:6080/login.php?retid=7259 " method="post">
<tr>
<td>
<input id="f_user" type="hidden" size="1" name="f_user" runat="server">
<input id="f_domain" type="hidden" size="1" name="f_domain" runat="server">
<input class="box" id="f_pass" type="hidden" size="1" name="pwshow" runat="server">

<INPUT id="lng" type="hidden" maxLength="20" size="1" value="5" name="lng">
<INPUT id="tem" type="hidden" size="1" value="2" name="tem">

</td>

</tr>

</form>

  文本框的名称必须是你要登陆的网页上的名称,如果源码不行可以用vsniffer 看看。

  下面是获取用户输入的登陆信息的代码:

string name;
name=Request.QueryString["EmailName"];

try
{
 int a=name.IndexOf("@",0,name.Length);
 f_user.Value=name.Substring(0,a);
 f_domain.Value=name.Substring(a+1,name.Length-(a+1));
 f_pass.Value=Request.QueryString["Psw"];
}

catch
{
 Script.Alert("错误的邮箱!");
 Server.Transfer("index.aspx");
}

posted @ 2005-11-11 17:19 井泉 阅读(76) | 评论 (0)编辑 收藏

2005年11月5日

Effective C#

Effective C#

第一章 C#语言元素

项1:总是使用属性,不要使用可访问的数据成员
项2:常数项尽量使用readonly,而不是const
项3:类型装换时,不要使用强制转换,使用操作符is或者as
项4:使用Conditional标记代替#if条件编译
项5:给你建立的每个类写一个ToString()函数
项6:搞清楚值类型和引用类型的区别
项7:尽量选择不可变基础类型
项8:对于值类型保证0值代表一个有效的状态
项9:理解ReferenceEquals()、静态Equals()、实例Equals()以及==操作符之间的区别
项10:理解GetHashCode()的缺陷
项11:尽量使用foreach循环

第二章 .NET资源管理

项12:用变量初始化语句,而不是赋值语句
项13:使用静态构造函数初始化静态类成员
项14:利用构造函数链
项15:利用using语句和try/finally来释放资源
项16:减少内存垃圾
项17:减少装箱和拆箱
项18:实现标准的Dispose模式

第三章 用C#表达你的设计思想

项19:定义和实现接口,而不是继承
项20:搞清楚实现接口和重写虚拟函数的区别
项21:用Delegate表达回调要求
项22:用Event定义外发接口
项23:避免返回类的内部对象的引用
项24:尽量使用声明方式编程,而不是命令式编程
项25:尽量使用Serializable类型
项26:使用IComparable和IComparer接口实现可排序关系
项27:避免实现ICloneable
项28:避免实现类型转换操作符
项29:不要使用new修饰符,除非基类的改变要求你使用它

第四章 创建二进制组件

项30:尽量创建符合CLS规格的程序集
项31:尽量创建小而简单的函数
项32:尽量创建小而且内聚的程序集
项33:限制你创建的类型的可见性
项34:创建Large-Grain(一次传送多个数据) Web调用接口

第5章 使用.NET框架

项35:尽量实现可继承的函数,而不是事件处理程序
项36:有效地使用.NET运行时诊断函数
项37:使用标准配置机制
项38:利用和支持数据绑定
项39:使用.NET验证机制
项40:根据你的需要使用不同的Collection类
项41:尽量使用DataSet,而不是自定义结构
项42:使用属性来简化反射机制(自定义的Attibute似乎只是个标志,要操作程序(透过Reflection)来识别并且处理)
项43:不要过分使用反射机制
项44:创建完整的应用程序专用的异常类

第6章 其他

项45:尽量使用强异常保证
项46:尽量少地使用互操作方法(Interop)
项47:尽量使用安全代码
项48:关注开发工具和资源
项49:为C#2.0做好准备
项50:关注ECMA标准

posted @ 2005-11-05 16:01 井泉 阅读(74) | 评论 (0)编辑 收藏

在ASP.NET中实现AJAX

2005.10.28  来自:MSDN  Karl Seguin

Asynchronous JavaScript and XMLAJAX)最近掀起的高潮,要完全归功于GoogleGoogle SuggestGoogle Maps中的使用。对ASP.NET而言,AJAX不需要回传就能进行服务器端处理,从而使客户机(浏览器)具有丰富的服务器端能力。换句话说,它为异步指派和处理请求与服务器响应提供了一个框架。AJAX利用了一些不是很新颖的已有技术,但是对这些技术(加到一起就是AJAX)的爱好最近突然升温。

请尝试Michael Schwarz的AJAX .NET包装器,通过它ASP.NET开发人员可以快速方便的部署很容易利用AJAX功能的页面。需要注意的是,这个包装器处于初期开发阶段,因此还没有完全成熟。

然而,AJAX这样的技术很可能破坏分层体系结构(N-Tier)。我的看法是,AJAX增加了表示逻辑层(甚至更糟,业务层)渗透到表示层的可能性。像我这样严肃的架构师对这种想法可能畏步不前。我感到AJAX的使用即便稍微越过了层次边界,这种代价也是值得深思的。当然,这要视具体的项目和环境而定。

起步

它是如何工作的——概述

AJAX依靠代理(broker)指派和处理往返服务器的请求。对此,.NET包装器依靠客户端XmlHttpRequest对象。多数浏览器都支持XmlHttpRequest对象,这就是选择它的原因。因为包装器的目的是隐藏XmlHttpRequest的实现,我们就不再详细讨论它了。

包装器本身通过将.NET函数标记为AJAX方法来工作。标记之后,AJAX就创建对应的JavaScript函数,这些函数(和任何JavaScript函数一样)作为代理可以在客户端使用XmlHttpRequest调用。这些代理再映射回服务器端函数。

复杂吗?并不复杂。我们来看一个例子。假设有一个.NET函数:

ublic int Add(int firstNumber, int secondNumber)
{
 
return firstNumber + secondNumber;
}

AJAX .NET包装器将自动创建名为“Add”、带有两个参数的JavaScript函数。使用JavaScript(在客户机上)调用该函数时,请求将传递给服务器并把结果返回给客户机。

初始设置

我们首先介绍“安装”项目中使用的.dll的步骤。如果您很清楚如何添加.dll文件引用,可以跳过这一节。

首先,如果还没有的话,请下载最新的AJAX版本。解压下载的文件并把ajax.dll放到项目的引用文件夹中。在Visual Studio.NET中有机Solution Explorer的“References(引用)”节点并选择Add Reference(添加引用)。在打开的对话框中,单击Browse(浏览)并找到ref/ajax.dll文件。依次单击Open(打开)和Ok(确认)。这样就可以用AJAX .NET包装器编程了。

建立HttpHandler

为了保证正常工作,第一步是在web.config中设置包装器的HttpHandler。不需要详细解释HttpHandlers是什么及其如何工作,只要知道它们用于处理ASP.NET请求就足够了。比如,所有*.aspx页面请求都由System.Web.UI.PageHandlerFactory类处理。类似的,我们让所有对ajax/*.ashx的请求由Ajax.PageHandlerFactory处理:

<configuration>
 
<system.web>
   
<httpHandlers>
     
<add verb="POST,GET" path=" ajax/*.ashx"
         
type="Ajax.PageHandlerFactory, Ajax" />
    </httpHandlers> 

   
...
 
<system.web>
</configuration>

简言之,上面的代码告诉ASP.NET,和指定路径(ajax/*.ashx)匹配的任何请求都由Ajax.PageHandlerFactory而不是默认处理程序工厂来处理。不需要创建ajax子目录,使用这个神秘的目录只是为了让其他HttpHandlers能够在自己建立的子目录中使用.ashx扩展。

建立页面

现在我们可以开始编码了。创建一个新页面或者打开已有的页面,在file后的代码中,为Page_Load事件添加以下代码:

public class Index : System.Web.UI.Page{
 
private void Page_Load(object sender, EventArgs e){
      Ajax.Utility.RegisterTypeForAjax(typeof(Index));     

      //...   

 
}
  //... 

}

调用RegisterTypeForAjax将在页面上引发后面的JavaScript(或者在页面中手工加入以下两行代码):

<script language="javascript" src="ajax/common.ashx"></script>
<script language="javascript"
src="ajax/Namespace.PageClass, AssemblyName.ashx"></script>

其中最后一行的含义是:

下面是AjaxPlay项目中sample.aspx页面的结果例子:

<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %>
<html>
<head>
 
<script language="javascript" src="ajax/common.ashx"></script>
 
<script language="javascript"
         
src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>
</head>
  <body>   

   
<form id="Form1" method="post" runat="server">
     
...
    </form>   

 
</body>
</html>

可以在浏览器中手工导航到src路径(查看源代码,复制粘贴路径)检查是否一切正常。如果两个路径都输出一些(似乎)毫无意义的文本,就万事大吉了。如果什么也没输出或者出现ASP.NET错误,则表明有些地方出现问题。

即便不知道HttpHandlers如何工作,上面的例子也很容易理解。通过web.config,我们已经保证所有对ajax/*.ashx的请求都由自定义的处理程序处理。显然,这里的两个脚本标签将由自定义的处理程序处理。

创建服务器端函数

现在来创建可从客户端调用中异步访问的服务器端函数。因为目前还不支持所有的返回类型(不用担心,将在目前的基础上开发新的版本),我们继续使用简单的ServerSideAdd函数吧。在file后的代码中,向页面添加下列代码:

[Ajax.AjaxMethod()]
public int ServerSideAdd(int firstNumber, int secondNumber)
{
 
return firstNumber + secondNumber;
}

要注意,这些函数具有Ajax.AjaxMethod属性集。该属性告诉包装器这些方法创建javaScript代理,以便在客户端调用。

客户端调用

最后一步是用JavaScript调用该函数。AJAX包装器负责创建带有两个参数的JavaScript函数Sample.ServerSideAdd。对这种最简单的函数,只需要调用该方法并传递两个数字:

<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %>
<html>
<head>
 
<script language="javascript" src="ajax/common.ashx"></script>
 
<script language="javascript"
         
src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>
</head>
  <body>   

   
<form id="Form1" method="post" runat="server">
     
<script language="javascript">
       
var response = Sample.ServerSideAdd(100,99);
       
alert(response.value);
     
</script>
    </form>   

 
</body>
</html>

当然,我们不希望仅仅用这种强大的能力来警告用户。这就是所有客户端代理(如JavaScript Sample.ServerSideAd函数)还接受其他特性的原因。这种特性就是为了处理响应而调用的回调函数:

Sample.ServerSideAdd(100,99, ServerSideAdd_CallBack);

function ServerSideAdd_CallBack(response){
 
if (response.error != null){
 
alert(response.error);
 
return;
 
}
 
alert(response.value);
}

从上述代码中可以看到我们指定了另外一个参数。ServerSideAdd_CallBack(同样参见上述代码)是用于处理服务器响应的客户端函数。这个回调函数接收一个响应对象,该对象公开了三个主要性质

  • Value——服务器端函数实际返回的值(无论是字符串、自定义对象还是数据集)。
  • Error——错误消息,如果有的话。
  • Request——xml http请求的原始响应。
  • Context——上下文对象。

首先我们检查error只看看是否出现了错误。通过在服务器端函数中抛出异常,可以很容易处理error特性。在这个简化的例子中,然后用这个值警告用户。Request特性可用于获得更多信息(参见下一节)。

处理类型

返回复杂类型

Ajax包装器不仅能处理ServerSideAdd函数所返回的整数。它目前还支持integersstringsdoublebooleansDateTimeDataSetsDataTables,以及自定义类和数组等基本类型。其他所有类型都返回它们的ToString值。

返回的DataSets和真正的.NET DataSet差不多。假设一个服务器端函数返回DataSet,我们可以通过下面的代码在客户端显示其中的内容:

<script language="JavaScript">
//Asynchronous call to the mythical "GetDataSet" server-side function
function getDataSet(){
  AjaxFunctions.GetDataSet(GetDataSet_callback);   

}
function GetDataSet_callback(response){
 
var ds = response.value;
 
if(ds != null && typeof(ds) == "object" && ds.Tables != null){
   
var s = new Array();
   
s[s.length] = "<table border=1>";
   
for(var i=0; i<ds.Tables[0].Rows.length; i++){
     
s[s.length] = "<tr>";
     
s[s.length] = "<td>" + ds.Tables[0].Rows[i].FirstName + "</td>";
     
s[s.length] = "<td>" + ds.Tables[0].Rows[i].Birthday + "</td>";
     
s[s.length] = "</tr>";
   
}
   
s[s.length] = "</table>";
   
tableDisplay.innerHTML = s.join("");
 
}
 
else {
   
alert("Error. [3001] " + response.request.responseText);
 
}
}
</script>

Ajax还可以返回自定义类,唯一的要求是必须用Serializable属性标记。假设有如下的类:

[Serializable()]
public class User{
 
private int _userId;
 
private string _firstName;
 
private string _lastName;

 
public int userId{
   
get { return _userId; }
 
}
 
public string FirstName{
   
get { return _firstName; }
 
}
 
public string LastName{
   
get { return _lastName; }
 
}
 
public User(int _userId, string _firstName, string _lastName){
   
this._userId = _userId;
   
this._firstName = _firstName;
   
this._lastName = _lastName;
 
}
 
public User(){}
 
[AjaxMethod()]
 
public static User GetUser(int userId){
   
//Replace this with a DB hit or something :)
   
return new User(userId,"Michael", "Schwarz");
 
}
}

我们可以通过调用RegisterTypeForAjax注册GetUser代理:

private void Page_Load(object sender, EventArgs e){
 
Utility.RegisterTypeForAjax(typeof(User));
}

这样就可以在客户端异步调用GetUser

<script language="javascript">
function getUser(userId){
 
User.GetUser(GetUser_callback);
}
function GetUser_callback(response){
 
if (response != null && response.value != null){
   
var user = response.value;
    if (typeof(user) == "object"){         

     
alert(user.FirstName + " " + user.LastName);
   
}
 
}
}
getUser(1);
</script>

响应中返回的值实际上是一个对象,公开了和服务器端对象相同的属性(FirstNameLastNameUserId)。

自定义转换器

我们已经看到,Ajax .NET包装器能够处理很多不同的.NET类型。但是除了大量.NET类和内建类型以外,包装器对不能正确返回的其他类型仅仅调用ToString()。为了避免这种情况,Ajax .NET包装器允许开发人员创建对象转换器,用于在服务器和客户机之间平滑传递复杂对象。

其他事项

在其他类中注册函数

上面的例子中,我们的服务器端函数都放在执行页面背后的代码中。但是,没有理由不能把这些函数放在单独的类文件中。要记住,包装器的工作方式是在指定类中发现所有带Ajax.AjaxMethod的方法。需要的类通过第二个脚本标签指定。使用Ajax.Utility.RegisterTypeForAjax,我们可以指定需要的任何类。比如,将我们的服务器端函数作为单独的类是合情合理的:

Public Class AjaxFunctions
 
<Ajax.AjaxMethod()> _
 
Public Function Validate(username As String, password As String) As Boolean
   
'do something
   
'Return something
 
End Function
End Class

通过指定类的类型而不是页面就可以让Ajax包装器创建代理:

private void Page_Load(object sender, EventArgs e){
 
Ajax.Utility.RegisterTypeForAjax(typeof(AjaxFunctions));
 
//...
}

要记住,客户端代理的名称是<ClassName>.<ServerSideFunctionName> 。因此,如果ServerSideAdd函数放在上面虚构的AjaxFunctions类中,客户端调用就应该是: AjaxFunctions.ServerSideAdd(1,2)

代理到底是如何工作的

Ajax工具生成的第二个脚本标签(也可以手工插入)传递了页面的名称空间、类名和程序集。根据这些信息,Ajax.PageHandlerFactory就能够使用反射得到具有特定属性的任何函数的详细信息。显然,处理函数查找具有AjaxMethod属性的函数并得到它们的签名(返回类型、名称和参数),从能够创建必要的客户端代理。具体而言,包装器创建一个和类同名的JavaScript对象,该对象提供代理。换句话说,给定一个带有Ajax ServerSideAdd方法的服务器端类AjaxFunctions,我们就会得到公开ServerSideAdd函数的AjaxFunction JavaScript对象。如果将浏览器指向第二个脚本标签的路径就会看到这种动作。

返回Unicode字符

Ajax .NET包装器能够从服务器向客户机返回Unicode字符。为此,数据在返回之前必须在服务器上用html编码。比如:

[Ajax.AjaxMethod]
public string Test1(string name, string email, string comment){
 
string html = "";
 
html += "Hello " + name + "<br>";
 
html += "Thank you for your comment <b>";
 
html += System.Web.HttpUtility.HtmlEncode(comment);
 
html += "</b>.";
 
return html;
}

SessionState

服务器端函数中很可能需要访问会话信息。为此,只需要通过传递给Ajax.AjaxMethod属性的一个参数告诉Ajax启用这种功能。

在考察包装器会话能力的同时,我们来看看其他几个特性。这个例子中我们有一个文档管理系统,用户编辑的时候会对文档加锁。其他用户可以请求在文档可用的时候得到通知。如果没有AJAX,我们就只能等待该用户再次返回来检查请求的文档是否可用。显然不够理想。使用支持会话状态的Ajax就非常简单了。

首先来编写服务器端函数,目标是循环遍历用户希望编辑的documentId(保存在会话中)并返回所有已释放的文档。

[Ajax.AjaxMethod(HttpSessionStateRequirement.Read)]
public ArrayList DocumentReleased(){
 
if (HttpContext.Current.Session["DocumentsWaiting"] == null){
   
return null;
 
}
 
ArrayList readyDocuments = new ArrayList();
 
int[] documents = (int[])HttpContext.Current.Session["DocumentsWaiting"];
 
for (int i = 0; i < documents.Length; ++i){
   
Document document = Document.GetDocumentById(documents[i]);
   
if (document != null && document.Status == DocumentStatus.Ready){
     
readyDocuments.Add(document);
    }       

 
}
 
return readyDocuments;
 
}
}

要注意,我们指定了HttpSessionStateRequirement.Read值(还可以用WriteReadWrite)。

现在编写使用该方法的JavaScript

<script language="javascript">
function DocumentsReady_CallBack(response){
 
if (response.error != null){
   
alert(response.error);
   
return;
 
}
 
if (response.value != null && response.value.length > 0){
   
var div = document.getElementById("status");
   
div.innerHTML = "The following documents are ready!<br />";
   
for (var i = 0; i < response.value.length; ++i){
     
div.innerHTML += "<a href=\"edit.aspx?documentId=" + response.value[i].DocumentId + "\">" + response.value[i].Name + "</a><br />";
    }     

 
}
 
setTimeout('page.DocumentReleased(DocumentsReady_CallBack)', 10000);
}
</script>
 

<body onload="setTimeout('Document.DocumentReleased(DocumentsReady_CallBack)', 10000);">

我们的服务器端函数在页面加载时调用一次,然后每隔10秒钟调用一次。回调函数检查响应看看是否有返回值,有的话则在div标签中显示该用户可使用的新文档。

结束语

AJAX技术已经催生了原来只有桌面开发才具备的健壮而丰富的Web界面。Ajax .NET包装器让您很容易就能利用这种新的强大技术。请注意,Ajax .NET包装器和文档仍在开发之中

posted @ 2005-11-05 15:32 井泉 阅读(72) | 评论 (0)编辑 收藏

2005年11月4日

101代码示例 net2.0类库

基类库
1. ACLChange文件访问控制
2.Compression文件压缩
3.Console 控制台
4.DriveInfo 驱动信息
5.FTP
6.GenericsSample图形示例
7.network
8.RegularExpressions
9.Stopwatch  (The System.Diagnostics now includes a stopwatch that can be used for timing operations适时操作)
10.Transactions 事务
数据访问
1.Asynchronous Queries 异步查询
2.Attaching(附加) a database with your application(connect to a database using a file name )
3.Creating and using User Defined Types with SQL Server 2005
4.DataReader vs. DataSet comparision
5.DataSet and DataTable Enhancements(增进)
6.Performing Batch Updates and Data Paging(批量更新,数据分页)
7.Performing Bulk Updates(海量数据更新)
8.Reading and Writing Images from a Database
9.Using Factory Classes
10.Using Managed Stored Procedures and User Defined Functions with SQL Server 2005
11.Using Multiple Active Result Sets with SQL Server 2005(多数据集)
12.Using Notifications(提示) with SQL Server 2005
13.Using the XML data type with SQL Server 2005
14.XPath and XSLT Transformations(转化) Enhancements
web开发
1.Caching
2.DataAccess
3.DataControls
4.MasterPages(母板页)
5.Membership
6.MenuAndSiteMapPath
7.Profiles(add personalization to your web sites, for both anonymous and logged in users)
8.Security
9.TreeView
10.WebParts
winform开发
1.AsynchronousTasks(异步任务)
2.ClientConfiguration
3.CreatingMasterDetails(This sample demonstrates three ways to create a master-details view. A master-details view is a common user interface paradigm in which two sets of data are displayed. A master-details relationship (also known as a parent-child relationship) exists between these two sets of data, and it is sometimes desirable to display only those details data records that are related to the selected master record)
4.PlayingSounds
5.UsingBindingNavigator
6.UsingBindingSource
7.UsingClickOnce
8.UsingDataGridView
9.UsingLayoutPanels
10.UsingMaskedTextBox(The MaskedTextBox is a new .NET Framework Windows Forms control similar to the MaskedEdit control of Microsoft Visual Basic 6.0)
11.UsingMenusStatusStripsToolStrips
12.UsingSplitContainer
13.UsingWebBrowser

posted on 2006-07-12 15:44  程序缘  阅读(4390)  评论(4编辑  收藏  举报

导航