随笔 - 48, 文章 - 43, 评论 - 2, 引用 - 0
数据加载中……

2008年4月25日

asp.net控件开发基础(4)

       上篇写了有关回传的一些东西,这次我本来不知道该写什么的,因为各方面的关联太多了,最后我还是想,还是慢慢一点点的写吧.这次讲WebControl

一.从继承WebControl开始

在第二篇教程中,重点介绍了Render()方法的使用,用来呈现控件,但从Control类继承的控件尚未发挥asp.net控件的作用.大家知道web服务器控件分为HTML服务器控件(如<input id="Button2" runat="server" type="button" value="button" />这样的形式)和标准服务器控件(就是<asp:..  id="" runat="server" />这样的形式的控件)

HTML服务器控件的控件从System.Web.UI.HtmlControls.HtmlControl 类派生

标准服务器控件的控件从System.Web.UI.WebControls.WebControl 类派生

HtmlControl 类和WebControl 类则从System.Web.UI.Control 类派生,并扩展.

所以我们说,所有的服务器控件都继承自System.Web.UI.Control 类,即所有的服务器控件都具有Control 类的共同属性,如Visible,EnableViewState属性,HtmlControl 类和WebControl 类则扩充了System.Web.UI.Control 类的功能,如

HtmlControl 类定义了所有 HTML 服务器控件所通用的方法、属性 (Property) 和事件(具体参数参照MSDN)

WebControl 类定义了所有 标准服务器控件所通用的方法、属性 (Property) 和事件(具体参数参照MSDN)

如每个继承了WebControl 类的标准控件都有定义外观和行为的属性,然后不同控件再根据需要扩展功能.

图一

所以我们推荐的做法是直接从WebControl 类派生,而非Control类.我们所做的非并从头开始.从WebControl 类继承可以帮我们省很多工作.

二.重写WebControl类方法,不再是Render()

WebControl类继承了Control类,当然有Render方法,在WebControl类中重写了Render方法,如下代码

示例一
protected override void Render(HtmlTextWriter output)
{
 RenderBeginTag(output);
 RenderContents(output);
 RenderEndTag(output);
}

注意 RebderBeginTag方法并非是HtmlTextWriter类中的方法,而是WebControl类中的方法,表示输出HTML标签头标记,如<table .....>,RenderEndTag方法则输出HTML标签尾标记,如</table>.中间的RenderContents方法则就是Control类的Render方法.
看下面RenderContents方法的定义.

示例二
protected override void RenderContents(HtmlTextWriter output)
 
//使用默认逻辑来呈现子控件,那么一定要调用基类中的方法。 
 base.Render(output);
}


接着再看RenderBeginTag方法的定义

示例三
      public virtual void RenderBeginTag(HtmlTextWriter output)
      
{
          
//添加呈现控件的属性和样式
          
//AddAttributesToRender为WebControl类中的方法
          AddAttributesToRender(output);
          
//呈现控件标签
          
//如label控件呈现<span >
          
//textbox控件呈现<input >
          HtmlTextWriterTag tagKey = TagKey;
          
if (tagKey != HtmlTextWriterTag.Unknown)
          
{
              output.RenderBeginTag(tagKey);
          }

          
else
          
{
              output.RenderBeginTag(
this.TagName);
          }

      }

这里打个比方,假设你要输出一个表格,你就必须定义<table>标签头,然后在其内部定义<tr>,<td>,下面看Control类中Render方法的实现,表明Render方法必须完成所有的任务,包括标签头标记<table>和<table>标签的属性和样式的输出.

示例四
        protected override void Render(HtmlTextWriter writer)
        
{
            
//为table标签定义属性和样式
            writer.AddAttribute(HtmlTextWriterAttribute.Width, "287px");
            writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, 
"0");

            writer.RenderBeginTag(HtmlTextWriterTag.Table);

            
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.Write(
"<strong>" + PaymentMethodText + "</strong>");
            writer.RenderEndTag();
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.AddAttribute(HtmlTextWriterAttribute.Name, 
"PaymentMethod");
            writer.AddAttribute(HtmlTextWriterAttribute.Id, 
"PaymentMethod");
            writer.AddStyleAttribute(HtmlTextWriterStyle.Width, 
"100%");
            writer.RenderBeginTag(HtmlTextWriterTag.Select);

            
//以下代码省略
        }


在WebControl类中重写了Render方法后,直接帮你定义好了标签,默认情况下为<span>,可通过重写TagKey属性来修改标签,然后AddAttributesToRender方法为标签定义样式和属性

示例五
    protected override HtmlTextWriterTag TagKey
    
{
      
get return HtmlTextWriterTag.Table; }
    }

示例六
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        
{
            
//为table标签定义属性和样式
            writer.AddAttribute(HtmlTextWriterAttribute.Width, "287px");
            writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, 
"0");

            
base.AddAttributesToRender(writer);

        }


接着重写RenderContents方法,对比上面的Render方法,实现效果是一样的

示例七
    protected override void RenderContents(HtmlTextWriter writer)
    
{
      
//注意,此处无没有table标签,只定义其内部标签
      writer.RenderBeginTag(HtmlTextWriterTag.Tr);
      writer.RenderBeginTag(HtmlTextWriterTag.Td);
      writer.Write(PaymentMethodText);
      writer.RenderEndTag();
      writer.RenderBeginTag(HtmlTextWriterTag.Td);
      writer.AddAttribute(HtmlTextWriterAttribute.Name, PaymentMethodSelectName);
      writer.AddAttribute(HtmlTextWriterAttribute.Id, PaymentMethodSelectId);
      writer.AddStyleAttribute(HtmlTextWriterStyle.Width, 
"100%");
      writer.RenderBeginTag(HtmlTextWriterTag.Select);
      
//以下省略

    }


所以说重写后的Render方法在默认情况下加了一个标签,而你可以重写这个标签(默认情况下为<span>).可能大家对其感到奇怪,即然可以实现同样效果,有必要重写Render方法,再加一个RenderContents方法吗?

三.Render方法和RenderContents方法的区别

当你继承WebControl类,在RenderContents方法中实现示例四代码时,(我们再以以前的例子展示)呈现后的代码如下,看到没有,控件ID为<span>
在RenderContents方法输入的标签将成为其内部标签.再看看这个控件的属性面板,你会看到很多的继承自WebControl类的属性,设置其属性,即是设置<span>标签的属性.TagKey的作用大概就在于此吧,为了使用WebControl类的公共属性.(我是这么认为的)而非定义在<table>标签上
示例八
<span id="CreditCardForm5_1"><table style="border-width:0;">
    
<tr>
        
<td><strong>信用卡类型</strong></td><td><select name="PaymentMethod" id="PaymentMethod" style="width:100%;">
......


假设我们称<span>里面的<table>等标签为子标签,在RenderContents方法应该定义控件的子标签,如果你只定义标签属性的话,只需重写AddAttributesToRender方法即可,可不须重写RenderContents方法.

下面再讲下重写标签的方法

(1) 重写TagKey属性,下面重写label控件的标签

  public class Ch4Label: Label
    
{
        
protected override HtmlTextWriterTag TagKey
        
{
            
get return HtmlTextWriterTag.Div; }
        }

    }

(2)重写基类构造函数,此方法只有在继承Control类后适用

public CreditCardForm5() : base(HtmlTextWriterTag.Table) { }


最后总结下:
1.控件继承自WebControl类                    主要原因是WebControl类公共的东西比Control类
2.TagKey                                                   表示控件的标签,默认情况下为<span>,可以重写此属性修改或者重写WebControl类的构造函数
3.AddAttributesToRender方法                  为标签添加属性和样式
4.RenderContents方法                               在标签内呈现内容

如果控件不复杂,则可直接从标准控件继承(如label),再根据需要扩展,重写AddAttributesToRender方法,还可以重写
TagKey更改默认标签,而无须重写RenderContents方法.如果控件比较复杂,不是单一的,则需要在RenderContents方法输出控件的内部的内容.

其实最大的区别就是默认情况下WebControl类为你加了一个标签,方便添加WebControl类的一些公共的东西,如果你重写Render()方法,而舍弃RenderContents方法,你就无福享受WebControl类给你提供的这么多属性和方法了.

大家多熟悉下WebControl类的一些公共属性,然后再多改改,可以明白的更加深刻.

posted @ 2008-04-25 16:37 nacarat 阅读(35) | 评论 (0)编辑

2008年4月23日

JS代码的格式化和压缩

     摘要: <HTML><HEAD><TITLE>Format</TITLE><METAcontent="MSHTML6.00.2800.1528"name=GENERATOR><METAcontent=""name=Author><METAcontent=""name=Keywords><METAcontent=""n... 阅读全文

posted @ 2008-04-23 15:48 nacarat 阅读(14) | 评论 (0)编辑

2008年4月19日

asp.net控件开发基础(3)

     摘要: 本次来介绍控件的事件处理. 我们知道Button控件有OnClick事件,DropDownList控件有SelectedIndexChanged事件.一.回发事件和客户端回发下面来看一个最简单的例子按钮单击事件1protectedvoidButton1_Click(objectsender,EventArgse)2{3Label1.Text="你好:"+TextBox1.Text;4}大家知道We... 阅读全文

posted @ 2008-04-19 10:23 nacarat 阅读(34) | 评论 (0)编辑

2008年4月15日

asp.net控件开发(2)

或许大家还对为何要重写Render方法存有疑惑,希望大家看看我举的例子,能够明白Render方法和其他两个方法的作用,然后真正明白为何一般情况下只须重写Render方法

我们知道我们每次编写控件时,都需要重写Render方法,我们发现在Control类中很多方法可以重写,但我们没有去重写他们,我们需要遵循一个原则,在需要重载的时候再去重写他们

我们还是先来看看与Render方法相关的两个方法
//RenderControl方法的基本实现
 public void RenderControl(HtmlTextWriter writer)
 
{
 
if(Visible)
 
{
 Render(writer);
 }

 }

 
//Render方法基本实现
 protected virtual void Render(HtmlTextWriter writer)
 
{
 RenderChildren(writer);
 }

 
//RenderChildren方式基本实现
 protected virtual void RenderChildren(HtmlTextWriter writer)
 
{
 
foreach (Control c in Controls)
 
{
 c.RenderControl(writer);
 }

 }

相信看过"ASP.NET服务器控件开发技术与实例"这本书的人,肯定看过上面的一段代码.

假设你不理解上面的流程(我也不一定理解,希望我的思路对你有帮助),我认为有一种很好的方式来理解上面的流程,跟大家分享一下.

现在抛开上面的代码,我们来建一个简单的页面,随意的拖几个控件到界面上,注意最后一个三panel控件,如下图


图一

我们知道,每个控件都有Visible和EnableViewState属性,Visible用来设置控件是否被呈现.


图二

现在我们把button控件的Visible属性设置为flase,我们看到了我们预期的效果,接着请启用页面跟踪,这个很重要


图三

在服务器上运行这个页面,大家可以在控件树上看到下面画面


图四

(1)System.Web.UI.LiteralControl

大家可以看到,在我们定义的每个控件之间都有System.Web.UI.LiteralControl.
这里需要说明的是,要理解任何不需要在服务器上处理的任何其他字符串.

如何理解呢?大家打开这个运行页面的源代码页面,如下代码,大家看到没有,除了服务器控件外,我们有其他元素(不需要在服务器上处理的任何其他字符串),包括空格.

示例一
<!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><title>
  鏃犳爣棰橀〉
</title></head>
<body>
 
<form name="form1" method="post" action="Default1.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTExNTUxMDYxODdkZHVaWm47e5anDettRKviGvS0nDWQ" />
</div>

 
<div>
 
<span id="Label1">Label</span><br />
 
<br />
 
<input name="TextBox1" type="text" id="TextBox1" /><br />
 
<br />
 
<br />
 
<br />
 
<div id="Panel1" style="height:50px;width:125px;">
  
 
</div>
 
&nbsp;</div>
 
<div>

  
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgK/5/fTBwLs0bLrBrVw7YrSp5G/l4sJGPkKN/asFj2W" />
</div></form>
</body>
</html>

为了让大家更加明白System.Web.UI.LiteralControl的意思的,让我们来修改HTML页面,说明:以上代码为运行后的HTML源代码.而不是我们所说的源代码,大家应该明白我所指的源代码的意思.

我们来修改代码,注意:我把<form..以下的标签无空格的写在了一起.看下面修改后的代码

示例二
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" Trace="true" %>

<!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>无标题页</title>
</head>
<body>
 
<form id="form1" runat="server"><asp:Label ID="Label1" runat="server" Text="Label"></asp:Label><asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><asp:Button ID="Button1" runat="server" Text="Button" Visible="False" /><asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px"></asp:Panel></form>
</body>
</html>

运行效果


图五

现在发现控件之间已经没有System.Web.UI.LiteralControl了,因为我去掉了空格.这个也说明了一点,如果代码很乱的话会影响速度.现在大家应该明白System.Web.UI.LiteralControl的意思了吧.

(2)大家继续看图四的Button1,大家会发现它呈现的大小字节数为0,因为我们设置了Button1的Visible值为False,所以未呈现此控件.

下面我们来理解这一点,大家重新看到RenderControl方法,如果Visible值为True则呈现此控件.
if(Visible)
 Render(writer);

为了理解这个方法,我们来重写此方法,我们以第一次讲的CreditCardForm3控件为例

我们重写RenderControl方法,把Render方法的代码全部拷贝到RenderControl方法中,然后去掉Render方法

然后在asp.net页面使用此控件,定义其Visible值为False

图六

运行这个例子以后,你会发现控件还是呈现了,就是因为你重写了RenderControl方法,使控件的Visible值无效了

所以我们就要加上一个判断
if(Visible) {}


否则的话,此方法呈现的内容没有Visible值.为了更加深刻理解这一点,我们重写基类的RenderControl方法的方法.

base.RenderControl(writer);

你会发现在页面呈现时的控件有两个,一个在RenderControl方法方法输出,一个在Render方法输出,因为
base.RenderControl方法调用了Render方法,当设置控件Visible属性为False时,Render方法输出的内容被隐藏(未被呈现,而RenderControl方法输出的内容仍然存在.现在大家应该了解RenderControl方法的作用了吧.

如果服务器控件的 Visible 属性设置为 true,则向页呈现服务器控件的内容,所以一般情况下我们不重写此方法.因为一般控件都需要Visible 属性,除非特殊情况.
图七

(3)RenderChildren方法

再重新看到图四,大家可以看到,我们拖放的控件是在属于form1的子控件,panel控件是一个容器控件,因为下面没拖放控件,任何其他显示的字符串表现为System.Web.UI.LiteralControl,大家可以拖几个控件到panel里再重新运行看看,会发现拖进去的控件变为panel的子控件.最明显的的测试方法是Wizard控件,拖放一个Wizard控件然后再测试你就会明白了.

RenderChildren方法则判断当前控件是否有子控件,如果有,则根据RenderControl方法判断控件的Visible值来呈现控件.所以大家在重写Render方法时,不重写基类Render方法时,将无法实现RenderChildren方法.带来的后果将是无法呈现子控件.

下面我们来测试一下.我们还是以CreditCardForm3控件为例子(请先把RenderControl方法的内容全注释掉),当未重实现RenderChildren方法时则无法呈现子控件内容,请启动跟踪

将发现其子控件呈现字节为0

图八

由于CreditCardForm3继承了CreditCardForm2,所以重写基类Render方法将会重复输出,我们可以直接在Render方法中重写RenderChildren方法.再来测试.将会发现有些变化

发现其子控件呈现字节并非为0,而是10

图九

说明其子控件还是存在东西的,只不过没有用而已,所以大家可以根据实际需求来确实是否要重写RenderChildren方法,一般的话都会重写Render方法,这样保险一点

好了,现在再来回顾下刚开始给出的代码,通过上面的试验,你是否明白了?

呈现控件的步骤(注意:下面三个方法都可以呈现,不过我们已经说过了,像在RenderControl方法用HtmlTextWriter预先输出的话,就丧失Visible的功能(说不定你就不需要这个功能,那时你就可以重写这个方法了)

(1)RenderControl方法

先判断其Visible然后调用Render方法

(2) Render方法

使用HtmlTextWriter将标记字符和文本输出然后调用RenderChildren方法

(3)RenderChildren方法

判断当前控件是否有子控件,然后再调用RenderControl方法根据子控件的Visible值输出子控件.

我们了解上面三个方法后,就会知道,一般情况下,我们无须重写RenderControl方法和RenderChildren方法.所以最合适的就是重写Render方法了.说了一大堆.目的就是为了说明为什么要重写Render方法.

上次,忘了把代码传上了,不小心只上传了dll文件,不好意思.这次就写这么多.希望大家能够真正明白.大家可以适当的修改代码,这样你会发现更多.

参考文章:ASP.NET2.0服务器控件之Render方法

posted @ 2008-04-15 08:36 nacarat 阅读(31) | 评论 (0)编辑

2008年4月14日

asp.net控件开发1

     摘要: p.net本身提供了很多控件,提供给我们这些比较懒惰的人使用,我认为控件的作用就在此,因为我们不想重复工作,所以要创建它,这个本身便是一个需求的关系,所以学习控件开发很有意思.虽然自己对控件开发还不是很熟悉,但我感觉以下几点很重要,是我自己总结的1.了解控件之间的继承关系 最好是先看看看System.Web.UI命名空间(1)Control 类,所有的控件都共享的一个类,你需要去看下其里面受保护的... 阅读全文

posted @ 2008-04-14 10:43 nacarat 阅读(28) | 评论 (0)编辑

2008年3月3日

windows server 2008 iis7下配置asp.net 1.1

最进将开发环境升级到windows server 2008。在配置iis7时死活出现DEP崩溃。查了下原来是没装.net framework 1.1 sp1所引起的。
郁闷,以为是系统挂了还重装了次。-_-

具体的配置步骤如下:
  1. 确认 .NET 1.1 SP1 已经正常安装.
    • Vista 默认不包含.NET v1.1, 当然也不包括 v1.1 SP1;
    • 如果没有 SP1环境, 在v1.1下运行一个app池将因为DEP而导致W3WP.exe 崩溃;
    • 要确认这一点, 检查"c:\Windows\Microsoft.NET\Framework\v1.1.4322\mscorsvr.dll" 文件的版本是"1.1.4322.2032" 或更高.
  2. 启用 IIS 6.0 compatibility
    • 打开 Control Panel - Programs and Features
    • 选中 Internet Information Services - Web Management Tools - IIS 6 Management Compatibility
  3. 在IIS中注册 v1.1
    • 以Administrator身份运行命令行的High Mandatory Level;
    • 进入c:\Windows\Microsoft.net\Framework\v1.1.4322
    • 运行 aspnet_regiis -ir -enable, 其中"ir"选项保证在IIS7中注册v1.1时不改变现有的脚本映射, "enable" 打开 ISAPI and CGI Restrictions 的 Allow;
    • aspnet_regiis 同时在Application Pools下创建一个新的名为"ASP.NET 1.1"的App池, 并将配置为 Classic pipline, 如果是64位操作系统将Enable32BitAppOnWin64设为 true;
  4. 确认新"ASP.NET 1.1" app 池为默认池
    • 打开IIS manager - Web Sites;
    • 在右上的Actions区点击Set Web Site Defaults...;
    • Application Pool设为"ASP.NET 1.1"
  5. 用Visual Studio创建v1.1的asp.net项目后设置Application Pool (步骤4/5选一)
    • 用Visual Studio创建v1.1的asp.net项目. 如果v1.1 app池不是缺省池时运行这个项目将失败;
    • 打开IIS manager;
    • 在新建的APP目录上右键选择 Advanced Settings;
    • 将 Application Pool 设为 "ASP.NET 1.1";
    • 回到Visual Studio, 运行或调试这个项目.

posted @ 2008-03-03 16:43 nacarat 阅读(688) | 评论 (0)编辑

2008年1月29日

使用ASP.NET 2.0 Profile存储用户信息[翻译]

     摘要: 作者: Stephen Walther原文地址:http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/dnvs05/html/UserProfiles.asp概要:许多ASP.NET应用程序需要跨访问的用户属性跟踪功能,在ASP.NET1.1中,我们只能人工实现这一功能。但如今,使用 ASP.NET 2.0的Profi... 阅读全文

posted @ 2008-01-29 13:44 nacarat 阅读(42) | 评论 (0)编辑

关于asp.net Session丢失问题的总结

asp中Session的工作原理:
asp的Session是具有进程依赖性的。ASP Session状态存于IIS的进程中,也就是inetinfo.exe这个程序。所以当inetinfo.exe进程崩溃时,这些信息也就丢失。另外,重起或者关闭IIS服务都会造成信息的丢失。

asp.net Session的实现
asp.net的Session是基于HttpModule技术做的,HttpModule可以在请求被处理之前,对请求进行状态控制,由于Session本身就是用来做状态维护的,因此用HttpModule做Session是再合适不过了。

原因1:
bin目录中的文件被改写,asp.net有一种机制,为了保证dll重新编译之后,系统正常运行,它会重新启动一次网站进程,这时就会导致Session丢失,所以如果有access数据库位于bin目录,或者有其他文件被系统改写,就会导致Session丢失

原因2:
文件夹选项中,如果没有打开“在单独的进程中打开文件夹窗口”,一旦新建一个窗口,系统可能认为是新的Session会话,而无法访问原来的Session,所以需要打开该选项,否则会导致Session丢失

原因3:
似乎大部分的Session丢失是客户端引起的,所以要从客户端下手,看看cookie有没有打开

原因4:
Session的时间设置是不是有问题,会不会因为超时造成丢失

原因5:
IE中的cookie数量限制(每个域20个cookie)可能导致session丢失

原因6:
使用web garden模式,且使用了InProc mode作为保存session的方式

解决丢失的经验
1. 判断是不是原因1造成的,可以在每次刷新页面的时候,跟踪bin中某个文件的修改时间
2. 做Session读写日志,每次读写Session都要记录下来,并且要记录SessionID、Session值、所在页面、当前函数、函数中的第几次Session操作,这样找丢失的原因会方便很多
3. 如果允许的话,建议使用state server或sql server保存session,这样不容易丢失
4. 在global.asa中加入代码记录Session的创建时间和结束时间,超时造成的Session丢失是可以在SessionEnd中记录下来的。
5. 如果有些代码中使用客户端脚本,如javascript维护Session状态,就要尝试调试脚本,是不是因为脚本错误引起Session丢失

参考:
[1] http://jinglecat.cnblogs.com/archive/2005/11/14/275587.aspx
[2] http://www.0dsf.com/bbs/dispbbs.asp?boardID=30&ID=6032&page=5
[3] http://spaces.msn.com/eparg/Blog/cns!1pnPgEC6RF6WtiSBWIHdc5qQ!447.entry
[4] http://jinlinc.cnblogs.com/archive/2005/11/29/287181.aspx
[5] http://blog.joycode.com/ghj/archive/2004/06/23/25521.aspx
[6] http://www.365base.com/Article/343/347/2005/2005091534671.html

posted @ 2008-01-29 13:41 nacarat 阅读(33) | 评论 (0)编辑

2008年1月23日

页面间传递数据的几种方法

在页面间传递数据时,我们有以下几种选择:

1、Query String

    一个很常见的方法,Query String是URL中问号之后的那一部分。其优点在于它是轻量级的,不会给服务器带来任何负担。而它也有几个缺点:传递的信息仅限于简单的字符串,而且必须是合法的URL字符;信息是对用户是可见的,因而存在安全性问题;用户可能会尝试手动修改查询字符串,这可能是程序未预料到或不能防范的;很多浏览器对URL的长度都有所限制(通常为1KB到2KB)。

2、Cookie

    Cookie是创建在客户端硬盘上(或者,如果它们是临时的,则在内存中)的小文件。其优点在于使用时不易被用户察觉,可被程序中每个页面使用,并且可将数据长期保存。但它也有一些与Query String 相同的缺点:仅限于简单的字符串信息;一旦用户找到了相应的文件,它们也是易于访问和阅读的。所以Cookie最好不要用于保存复杂的或私密的信息。

3、Session

    可以在源页面中将数据保存在Session中,然后在目标页面中读取这些数据。注意:将大量的信息存储在Session中会严重影响服务器的性能。

4、Server.Transfer

    要进行服务器端的重定向,可以使用Server.Transfer。因为在服务器端执行,Server.Transfer方法不需要请求另一页面。使用HttpContext,我们可以在目标页面中访问源页面中的数据。其缺点是,浏览器并不了解返回给它的是另外一个页面,它在地址栏中会显示第一个页面的URL,这会让用户陷入混乱,在他们使用书签的时候也会产生麻烦。所以不推荐该方法。

5、其它

    还可以使用缓存(Cache)来存储数据,可在程序的任意处访问缓存。建议仅对那些修改不太频繁但经常使用的数据使用缓存。 另外在某些特定情况下还可以使用Application变量,如统计页面的点击数等。

使用Cross-Page Postback

ASP.NET 2.0中引入了一个新的方法:跨页面提交,即postback触发在另一个页面。这种技术听起来很是简单,但却存在隐患。一不小心,就会导致你创建的页面紧密耦合,难以维护和调试。

支持跨页面提交的机制是一个名为PostBackUrl的属性,该属性由IButtonControl接口定义,实现这个接口的按钮控件包括ImageButton,LinkButton,Button。将PostBackUrl属性值设置为另一个web窗体的名称(即URL),当用户点击按钮时,页面将被提交到新的URL。

看下面这个示例,该示例包括源页面CrossPage1.aspx和目标页面CrossPage2.aspx:

CrossPage1.aspx

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>Source Page</title>
</head>
<body>
    
<form id="form1" runat="server">
    
<div>
        FirstName: 
<asp:TextBox runat="server" ID="txtFirstName"></asp:TextBox>
        
<br />
        LastName:
&nbsp;<asp:TextBox runat="server" ID="txtLastName"></asp:TextBox> 
        
<br />
        
<asp:Button runat="server" ID="cmdSubmit" PostBackUrl="~/CrossPage2.aspx" Text="Cross-Page Postback" />
    
</div>
    
</form>
</body>
</html>

CrossPage1.aspx不包含任何代码,效果如下:

现在点击按钮,该页面就被提交到CrossPage2.aspx了。此时CrossPage2.aspx页面可以使用Page.PreviousPage属性与CrossPage1.aspx进行交互了,下面这个事件处理函数演示了如何获取源页面的标题并显示它:

 

if (PreviousPage != null)
{
    lblInfo.Text 
= "You came from a page titled " + PreviousPage.Header.Title;
}



注意:该方法在访问PreviousPage对象前先对其作了null检查,如果结果为false,表明没有跨页面提交发生,也就是说,CrossPage2.aspx是被直接请求的,或者由其自身提交,此时PreviousPage对象不可用。

从源页面中获取更多数据

上面那个示例作了一个有趣的尝试,但仅仅如此,我们还是不能传递任何有用的信息。

要获取源页面中控件的值,可以使用FindControl方法:

 

if (PreviousPage != null)
{
    lblInfo.Text 
= "You came from a page title " + PreviousPage.Header.Title;

    
string firstName = (PreviousPage.FindControl("txtFirstName"as TextBox).Text;
    
string lastName = (PreviousPage.FindControl("txtLastName"as TextBox).Text;
    lblInfo.Text 
+= "<br />";
    lblInfo.Text 
+= "your full name: " + firstName + " " + lastName;
}

要获得更多信息,我们需要将PreviousPage引用转换为适当的页面类(本示例中是CrossPage1类):

 

if (PreviousPage != null)
{
    CrossPage1 prevPage 
= PreviousPage as CrossPage1;
    
if (prevPage != null)
    {
        
// 此时可以访问源页面的公共属性
    }
}

另外,除了在代码中进行类型转换,还可以在.aspx页面中添加PreviousPageType指示字:

<%@ PreviousPageType VirtualPath="~/CrossPage1.aspx" %>

此时,PreviousPage属性会自动使用CrossPage1类型,编辑器中的智能提示也可以使用了。但是这种方法相当脆弱,因为你只能使用一个页面类!因此,出于灵活性考虑,使用类型转换的方法会更好。

好了,不管怎样,现在已经将PreviousPage对象转换为合适的页面类型了,但是你还是不能直接访问它包含的控件对象。这是因为这些控件都被声明为保护类型(protected),此时的解决方案是使用属性。

比如,如果希望公开源页面上两个文本框控件的值,可以添加属性来封装控件对象,如在CrossPage1类中添加属性:

public TextBox FirstNameTextBox
{
    
get { return txtFirstName; }
}

public TextBox LastNameTextBox
{
    
get { return txtLastName; }
}

但是,这通常不是最好的方法。其问题在于它公开了太多的细节,目标页面可以读取文本框控件的所有内容了。如果过段时间需要修改源页面,决定使用不同的输入控件,维护这些属性就相当困难了,因为你不得不修改两个页面的代码。

更好的方法是定义更具体的属性,它们应当仅仅提供你需要的东西。比如,你可以考虑添加一个FullName属性,该属性读取两个文本框的值:

public string FullName
{
    
get { return this.txtFirstName.Text + " " + this.txtLastName.Text; }
}

这样做,两个页面的关系就变得清晰、简单并易于维护了。如果你决定在CrossPage1中使用新的输入控件,只要修改CrossPage1页面就好了。CrossPage2中的代码也相应地修改如下:

 

if (PreviousPage != null)
{
    lblInfo.Text 
= "You came from a page titled " + PreviousPage.Header.Title + "<br />";

    CrossPage1 prevPage 
= PreviousPage as CrossPage1;
    
if (prevPage != null)
    {
        lblInfo.Text 
+= "You typed in this: " + prevPage.FullName;
    }
}

 

下面是CrossPage2的最终结果:



跨页面提交确实非常有用,但它们也会使页面变得复杂。如果你允许多个源页面提交到同一目标页面,你就得编写代码逻辑以判断页面来自何处,然后作出相应处理。要避免这种烦恼,简单的方法就是只在两个特定的页面间使用它。

posted @ 2008-01-23 18:34 nacarat 阅读(76) | 评论 (0)编辑

2008年1月20日

.NET Framework终于开源了!

期待已久的.NET Framework终于在本周开源了,微软在MS-RL协议下终于公开了.NET Framework源代码,我们只可以自由查看,不允许直接进行修改。第一批开放的源代码包括:


.NET基本类库:
System,System.CodeDom,System.Collections,System.ComponentModel, System.Diagnostics, System.Drawing, System.Globalization, System.IO, System.Net, System.Reflection, System.Runtime, System.Security, System.Text, System.Threading

ASP.NET:
System.Web, System.Web.Extensions

Windows Forms:
System.Windows.Forms

Windows Presentation Foundation:
System.Windows

ADO.NET和 XML:
System.Data
System.Xml

详情请访问ScottGu的文章:http://weblogs.asp.net/scottgu/archive/2008/01/16/net-framework-library-source-code-now-available.aspx

如何在Visual Studio 2008中进行配置请参考这篇文章:Configuring Visual Studio to Debug .NET Framework Source Code

posted @ 2008-01-20 19:50 nacarat 阅读(20) | 评论 (0)编辑