Posts Tagged ‘asp.net’

ListView控件的Insert、 Edit 和 Delete功能(第二部分)

September 2, 2007 Tags: , , ,

本系列文章将通过一个简单的实例,结合我自己使用ListView的情况,展示如何用ASP.NET 3.5 ListView控件进行基本的Insert、Edit和Delete操作。

系统要求:

  • Windows XP SP2 or higher
  • VS2008 Beta 2 or Visual Web Developer 2008 Express Edition Beta 2

在本系列的第一篇中,介绍了如何实现ListView的Insert功能。本篇介绍如何实现Edit功能。

实现Edit功能

在ListView中加入Edit功能与Insert的方式非常相似。我们需要添加一个EditItemTemplate,在里边放置两个TextBox用于接收Name和Value的用户输入,以及一个Save按钮。这个Save按钮的CommandName设置为Update:


<EditItemTemplate>
    <li>Name: <asp:TextBox runat="server" ID="Name" Text=‘<%# Bind("Name") %>’ />
     Value: <asp:TextBox runat="server" ID="Value" Text=‘<%# Bind("Value") %>’ />
     <asp:Button runat="server" ID="Update" Text="Save" CommandName="Update" />
     </li>
</EditItemTemplate>

同时,为ItemTemplate加上一个Edit按钮,其CommandName设置为Edit:

<ItemTemplate>
    <li><%# Eval("Name") %>(#<%# Eval("Value") %>)
    <asp:Button runat="server" ID="Edit" CommandName="Edit" Text="Edit" />
    </li>
</ItemTemplate>
 

然后为ListView添加ItemUpdating事件的处理方法。在这个方法里,我们如果得到当前修改的数据对象?我用到过的方法有三个:

  1. 为Save按钮添加CommandArgument属性,其值设为当前数据对象的Value值;不过,在ItemUpdating里并不能直接取得CommandArgument值,所以这种方法的处理代码,应该挪至ItemCommand事件的处理方法中;
  2. 在EditItemTemplate中放置一个Literal,其Text属性设置为数据对象的Value值,并将该Literal隐藏。在ItemUpdating中,对当前Item使用FindControl方法取得该Literal,进而取得Value值;
  3. 使用ListView的DataKeyNames属性;

我们将使用第3种方法。为ListView添加DataKeyNames属性,并将其值设为Value。这样,在ListView被数据填充时,ListView的DataKeys属性会被自动填充上每个数据对象的Value值。

在ItemUpdating处理方法中,我们以事件参数ListViewUpdateEventArgs的ItemIndex值为下标,从ListView的DataKeys中取得当前编辑的数据对象的Value值,然后进行保存操作,最后,将ListView的EditIndex设为-1以退出Edit状态:


protected void ListView1_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
    int value = int.Parse(ListView1.DataKeys[e.ItemIndex].Value.ToString());
    List<DataEntry> data = Data;
    DataEntry entry = data.Single(d => d.Value == value);
    entry.Name = (ListView1.Items[e.ItemIndex].FindControl("Name") as TextBox).Text;
    entry.Value = int.Parse((ListView1.Items[e.ItemIndex].FindControl("Value") as TextBox).Text);
    Data = data;
    ListView1.EditIndex = -1;
    BindData();
}
 

万事俱备,为ListView的ItemEditing事件添加处理方法,并在其中设置ListView的EditIndex属性以进入Edit状态,EditIndex值可以从事件参数ListViewEditEventArgs的NewEditIndex中获得:


protected void ListView1_ItemEditing(object sender, ListViewEditEventArgs e)
{
    ListView1.EditIndex = e.NewEditIndex;
    BindData();
}

运行示例网站,测试Edit功能,貌似正常。贴图就略了:))

注:我们假定数据对象的Value值是唯一(相当于主健)的,为了不引入过多的细节,添加、修改数据时不作对该值的唯一性检查。

ListView控件的Insert、 Edit 和 Delete功能(第一部分)

August 28, 2007 Tags: , , ,

ListView是ASP.NET 3.5新提供的一个控件,它支持GridView类似的功能,并将HTML渲染的责任交由使用者来把控,而这正是GridView的一大劣势(它生成的乱七八糟的HTML输出,即使通过CSS Friendly Control Adapters来净化,也还是让人头痛得不行)。使用ListView两周以来,我已经没有一丁点儿再次使用GridView的欲望了。

由于VS2008还属于Beta2阶段,关于ListView的文档似乎不大容易找到。ScottGu正在写一个关于ListView的系列文章,不过目前还只完成了第一篇Rick Strahl写的ListView and DataPager in ASP.NET 3.5较深入地讲述了ListView的一些特性。这两篇文章,是学习ListView很精彩的资料。

本系列文章将通过一个简单的实例,结合我自己使用ListView的情况,展示如何用该控件进行基本的Insert、Edit和Delete操作。

系统要求:

  • Windows XP SP2 or higher
  • VS2008 Beta 2 or Visual Web Developer 2008 Express Edition Beta 2

建立Web Site

按默认配置,新建一个名为ListViewDemo的ASP.NET Web Site。

编写数据源

为了演示的简单性,我们创建一个数据对象DataEntry,它仅包含名为Name和Value的两个成员:


[Serializable]
public class DataEntry
{
    public string Name { get; set; }
    public int Value { get; set; }
}

为了方便在演示页面每次PostBack的时候获取数据对象列表,我们会把数据对象列表存放在Session中,所以为DataEntry类设置上Serializable属性。获取数据对象列表的逻辑很简单,从Session里取得,如果为空则进行简单的初始化,非空则直接返回使用:


protected List<DataEntry> Data
{
    get
    {
        List<DataEntry> data = Session[SESSIONKEY] as List<DataEntry>;
        if (data == null)
        {
            data = new List<DataEntry>();
            data.Add(new DataEntry { Name = "James", Value = 1 });
            data.Add(new DataEntry { Name = "Ash", Value = 2 });
            data.Add(new DataEntry { Name = "Lulu", Value = 3 });
            ViewState[SESSION
KEY] = data;
        }

        return data;
    }
    set { Session[SESSIONKEY] = value; }
}

static readonly string SESSION
KEY = "skdataentry_";
 

使用ListView

在页面上添加一个ListView。为其编写LayoutTemplate,使用ul作为输出数据的HTML空器,而每一条数据将使用li来组织。ListView通过将数据输出至ItemContainer来渲染结果。我们在LayoutTemplate中添加一个PlaceHolder作为ItemContainer,这样生成的HTML中不会有任何多余的元素(比方说,如果使用Panel,则会生成一个空的div)。将ListView的ItemContainerID属性设置为这个PlaceHolder的ID:


<asp:ListView ID="ListView1" runat="server" ItemContainerID="Layout">
     <LayoutTemplate><ul><asp:PlaceHolder runat="server" ID="Layout" /></ul></LayoutTemplate> </asp:ListView>

然后编写ItemTemplate,将每一个数据对象的Name和Value值输出到一个li中:


<ItemTemplate>
    <li><%# Eval("Name") %> (#<%# Eval("Value") %>)</li>
</ItemTemplate>
 

在Page_Load方法中为ListView绑定上面编写的数据源:

ListView1.DataSource = Data;
ListView1.DataBind();

运行该网站工程,显示效果如下:

  • James (# 1)
  • Ash (# 2)
  • Lulu (# 3)

实现Insert功能

ListView的Insert Mode通过其属性InsertItemPosition来控制,该属性的取值有三种:

  • None:非Insert状态
  • FirstItem:Insert状态,且编辑模板显示于ListView所有item的最前面
  • LastItem :Insert状态,且编辑模板显示于ListView所有item的最后面

我们先添加一个InsertItemTemplate,在里边放置两个TextBox用于接收Name和Value的用户输入,以及一个Save按钮:


<InsertItemTemplate>
    <li>Name: <asp:TextBox runat="server" ID="NewName" Text=‘<%# Bind("Name") %>’ />
    Value: <asp:TextBox runat="server" ID="NewValue" Text=‘<%# Bind("Value") %>’ />
    <asp:Button runat="server" ID="Insert" Text="Save" CommandName="Insert" /> </li>
</InsertItemTemplate>
 

Save按钮的CommandName设置为Insert,这样当用户保存新添加的数据时,Insert相应的事件会被触发。

然后,我们在LayoutTemplate中添加一个New按钮,并将其CommandName设为Create。因为当ListView中数据为空时,不显示LayoutTemplate而是显示EmptyDataTemplate,我们再编写一个EmptyDataTemplate,并在它下面也放一个New按钮。这样当数据被删光时,不至于没有New按钮可用:P


<LayoutTemplate>
    <ul><asp:PlaceHolder runat="server" ID="Layout" /></ul>
    <asp:Button runat="server" ID="Create" CommandName="Create" Text="New" />
</LayoutTemplate>
<EmptyDataTemplate>
    <asp:Button runat="server" ID="Create" CommandName="Create" Text="New" />
</EmptyDataTemplate>
 

我们为New按钮的CommandName设置了Create值。现在为ListView添加ItemCommand的事件处理方法,然后在里边处理New按钮单击事件,将ListView切换至Insert状态:


protected void ListView1_ItemCommand(object sender, ListViewCommandEventArgs e)
{
    if (e.CommandName == "Create")
    {
        ListView1.InsertItemPosition = InsertItemPosition.LastItem;
        BindData();
    }
}
 

注:BindData仅仅是对ListView控件重新进行数据源绑定。

为ListView添加ItemInserting事件处理方法,当Save按钮点击时,在这个方法内处理新数据保存的逻辑。这里遇到一个问题,在这个事件中,如何去获取用户输入的数据?我想ASP.NET应该有很方便又优雅的机制来实现这个功能,比方说这个事件的参数ListViewInsertEventArgs中有一个Values的属性,貌似是用来存储这些数据的。不过我没有弄明白怎使用么用这个属性,只好用土办法来解决:ListViewInsertEventArgs参数中的Item属性以及ListView控件的InsertItem属性,均是当前要插入的Template Item,我们用FindControl方法来获取输入控件并取值保存。保存完毕后,将ListView的InsertItemPosition属性设为None以退出Insert状态:


protected void ListView1_ItemInserting(object sender, ListViewInsertEventArgs e)
{
    ListViewItem item = e.Item;
    try
    {
        string name = (item.FindControl("NewName") as TextBox).Text;
        int value = int.Parse((item.FindControl("NewValue") as TextBox).Text);
        List<DataEntry> data = Data;
        data.Add(new DataEntry { Name = name, Value = value });
        Data = data;

        ListView1.InsertItemPosition = InsertItemPosition.None; ;

        BindData();
    }
    catch  { }
}

运行网站测试一下,功能正常:

listview-insert

接下来

ListView的Insert功能已经实现。在下一篇里,我们将实现Edit功能。

To be continued…

无缝迁移blog

June 23, 2007 Tags: , , ,

我使用的Blog系统是Subtext 1.9.5,原作为一个单独的Application(ashchan.com/blog)“嵌”在网站ashchan.com下面。这样配置有时候会出问题,比如我往ashchan.com上添加一个HttpModule的时候,有可能会造成ashchan.com/blog的出错。

同时,网站使用免费的ISAPI filter modrewrite (该产品网站目前我这里无法访问,原因未知)来处理URL重写(URL Rewriting),曾经测试过,在高并发访问时,该filter可能会出错造成应用程序池死掉。

因此对配置进行了更改,将blog从ashchan.com下迁出建成一个单独的站点(blog.ashchan.com),并将原来使用的modrewrite ISAPI filter去除,改为使用UrlRewritingNet.UrlRewrite。我个人使用,觉得这两个产品的优缺点分别如下:

  moerewrite UrlRewritingNet
费用 免费 免费
发行方式 开源,Delphi,需自己编译,不提供Binary 开源,C#,同时提供源码和Binary
配置 需修改IIS站点配置,加入ISAPI filter 以DLL方式置于网站bin目录下,如果需对非aspx页面进行重写,则需在IIS站点配置下对特定扩展名进行映射
性能 性能高,但测试中出现高并发访问下出错的情况(未查明原因,可能是配置或使用的问题) 性能较高,稳定
对无扩展名URL的支持 支持 部分支持(使用该特性,需将所有扩展名的URL均映射至ASP.NET引擎)
规则编写 以类似Apache modrewrite的正则语法、使用ini文本文件编写 以易读的语法在Web.config中编写
跨站转发 未测试,应该支持 支持

ashchan.com使用的URL,大部分可以直接使用UrlRewritingNet来实现重写,这些URL以.aspx或.html为扩展名,其中以.html为扩展名的需在IIS中设置以ASP.NET来处理。

另外一些无扩展名的URL,如http://ashchan.com/tag/photo,则无法使用,因为我不想让ASP.NET 引擎来处理所有文件的请求。

这就要用到其他的方案来协同了。方案很简单,自定义404错误,然后在处理404错误的aspx页面分析url,发现是上面类型的无扩展名的url则转至相关页面。

最后还剩下blog的url未处理。原来的形如www.ashchan.com/blog/archive/2007/03/16/refresh.aspx的页面,需要转向至blog.ashchan.com/archive/2007/03/16/refresh.aspx,而且最好是301跳转,这样对搜索引擎比较友好。这也好办,使用如下规则即可:

    <add name=“blog_2″  virtualUrl=“^http\://www.ashchan.com/blog/(.*).aspx”
       rewriteUrlParameter=“ExcludeFromClientQueryString”
       destinationUrl=“http://blog.ashchan.com/$1.aspx”
       redirect=“Domain”
       redirectMode=“Permanent”
       ignoreCase=“true” />

大功搞掂!但愿301跳转能把PR给保留转移到新的地址上来。

Microsoft.Web.Extensions.Design.dll 引用错误问题

November 27, 2006 Tags: ,

在开发机器上运行正常的一个ASP.NET站点,发布至测试服务器后出错,提示无法加载Microsoft.Web.Extensions.Design.dll。该站点使用了ASP.NET AJAX 1.0 Beta 2ASP.NET AJAX Control Toolkit,其中AJAX Beta 2安装了core ASP.NET 2.0 AJAX Extensions和ASP.NET 2.0 AJAX Futures November CTP,Control Toolkit为1.0.61106.0版本。

问题原因

这个问题由两个原因引起:

  1. ASP.NET AJAX Control Toolkit引用了Microsoft.Web.Extensions.Design.dll;
  2. core ASP.NET 2.0 AJAX Extensions安装时,如果目标机器上安装了VS,则会安装Microsoft.Web.Extensions.Design.dll,否则不安装。

开发机器必定安装了VS,AJAX Beta 2安装目录下(默认为C:\Program Files\Microsoft ASP.NET\ASP.NET 2.0 AJAX Extensions\v1.0.61025)及GAC中有Microsoft.Web.Extensions.Design.dll。因此开发机器上跑使用了Control Toolkit的站点不会发生任何问题。而测试服务器没有安装VS,就over了。

解决方案

解决方案有两种:

  1. 升级ASP.NET AJAX Control Toolkit至最新的1.0.61121.0版本;该版本去掉了对Microsoft.Web.Extensions.Design.dll的(错误)引用; or
  2. 拷贝一个Microsoft.Web.Extensions.Design.dll至站点的bin目录。

Firefox下带UpdatePanel页面的刷新问题

November 13, 2006 Tags: , , ,

遇到了这样一个问题:在一个asp.net页面上使用UpdatePanel(ASP.NET AJAX为Beta 2版本),当在Firefox下浏览时,如果手动刷新(refresh)页面,会出现“the state information is invalid for this page and might be corrupted.”错误。在IE下无此问题。

问题的原因:有可能为Firefox缓存了页面的JavaScript等内容,造成刷新页面后ViewState被破坏。

解决方法:显式去掉缓存。在Page_Load方法中,加入

Response.Cache.SetNoStore();

ASP.NET AJAX 1 Beta的PageMethods限制

October 24, 2006 Tags: , , ,

今天安装了ASP.NET AJAX 1.0 Beta(见微软发布ASP.NET AJAX 1.0 Beta版),并把一个用到Atlas CTP的项目进行了修改与迁移。迁移并不复杂,只要照着Migration Guide(Doc格式)做就行了。
当然也发现了一些问题。最令人意外的是PageMethods不能正常使用了,这应该是Beta的一个BUG。要正常使用PageMethods,请:

  1. 给要使用的方法加上 Microsoft.Web.Script.Services.ScriptMethod 修饰;
  2. 该方法必须为public和static的;
  3. 该方法不能直接使用当前page的controls;
  4. 该方法不能放在codebehind文件中,而是必须放在aspx文件中(真变态,这应该是BUG的源头,前3个都是PageMethods的正常要求)。
给个例子:
<script runat="server“>
[System.Web.Services.WebMethod]
[Microsoft.Web.Script.Services.ScriptMethod]
public static string HelloWorld()
 {
returnhello world“;
}
</script>

为使用Master的ASP.NET Content页面添加CSS样式

October 19, 2006 Tags: , , ,

使用了Master的ASP.NET Content页面无法直接引用外部样式或内嵌样式,因为CSS样式必须出现在HTML的head标签内,而Content页面自身是不能包含head的。不过通过编程,很容易做到这一点,以下就是解决方案(也可以用相同的手段来添加其他HTML元素)。先定义以下两个方法:

  1. 内嵌样式支持
    protected void AddInlineStyle(string style)
    {
        HtmlGenericControl node = new
            HtmlGenericControl(”style“);
        node.Attributes.Add(”type“, “text/css“);
        node.InnerText = style;
        Page.Header.Controls.Add(node);
    }
  2. 外部样式支持
    protected void AddLinkedStyle(string url)
    {
        HtmlLink link = new HtmlLink();    link.Attributes.Add(”type“, “text/css“);
        link.Attributes.Add(”rel“, “stylesheet“);
        link.Attributes.Add(”href“, url);
        Page.Header.Controls.Add(link);
    }

在 Page_Load 方法中,使用上面两个方法来添加样式:

  1. 添加内嵌样式
    AddInlineStyle("body { padding:10px; margin:5px 0; }“);
  2. 引用外部样式
    AddLinkedStyle("/styles/layout.css");

简单而实用。HtmlGenericControl 是相当有用的类,在ASP.NET中可以用来定制很多输出行为,实在是应该多加利用的好东东。

Blog Reloaded

September 3, 2006 Tags: , , ,

Subtext released its new version 1.9 “Daedelus”,  which “simply be a straight upgrade to ASP.NET 2.0″. I just couldn’t wait to install it side by ashchan.com (which was also built on ASP.NET), and would test it several days before deciding to keep it or not.

Personally I love WordPress most among all the blog platforms, but as I currently program asp.net, Subtext seems to be a better option.