• Textbox的Autopostback="True"引起的误会

    by{ guangboo }, published {2010-03-25}, Tag { asp.net / }

    今天一位网友问我一个问题,内容如下:

    “页面上一个保存按钮 我第一次点击他的时候他跑到重写的Render方法里面 第二次点击他的时候才进去click事件”。

    我首先想到的是,onclick事件是在Render方法里面才绑定的。

    然后我继续问一些问题,进一步了解“现场”情况。了解到事件是在<asp:textbox onclick="btnSave_Click".../>这里就已经绑定了。而且看来他贴出来的Render方法的代码,没有任何相关的代码。然后网友远程,让我看了一下他的演示。然后我看了一下代码,发现TextBox中设置了AutoPostBack的属性为true,因此导致了第一次的点击是TextBox的OnTextChanged事件,当然不是btnSave_Click事件,而第二次,才是Click事件。当然保证第二次点击触发的是Click事件的是,TextBox的Text没有发生变化才行。

    因此在TextBox的AutoPostBack属性设为True时,且当TextBox的值发生了变化,那么页面回发的事件就是TextBox的OnTextChange事件。所以出现这次误会的罪魁祸首就是TextBox的AutoPostBack属性。

     

     

  • Asp.Net Datacontrolrowstate简介

    by{ guangboo }, published {2010-03-24}, Tag { .net / asp.net / }

    DataControlRowState 枚举

    该枚举在.net framework 2.0中新增加的,是指定数据控件,如DetailView,GridView中行的状态的。具有FlagAttribute属性,因此允许其成员值按位组合。

     

    [FlagsAttribute] 
    public enum DataControlRowState

    成员

    成员名称 说明
    Alternate 指示该数据控件行是交替行。 Alternate 状态在任何时候都可以与其他状态组合,例如与 NormalEditInsert 组合。这些行可能会受到数据控件的 AlternateRowStyle 属性影响(若已设置)。
    Edit 指示该行处于编辑状态,这通常是单击行的“编辑”按钮的结果。通常,EditInsert 状态互相排斥。
    Insert 指示该行是新行,这通常是单击“插入”按钮添加新行的结果。通常,InsertEdit 状态互相排斥。
    Normal 指示该数据控件行处于正常状态。Normal 状态与其他所有状态互相排斥。
    Selected 指示该行已被用户选定。

    官方介绍

    DataControlRowState 枚举标识数据控件(例如 DetailsViewGridView)中行的状态。行的状态可以是一个 DataControlRowState 值或值的组合,因此使用按位运算来确定该行的状态是否包括一个 DataControlRowState 值而非一个等效测试。DataControlRowState 枚举用于任何行类型,而不只用于 DataRow 行(通常,标头和脚注行的状态设置为 Normal)。

    当枚举整个 GridViewRowCollectionDetailsViewRowCollection 集合时,可以使用 DataControlRowState 枚举来分别标识 GridViewRowDetailsViewRow 对象的状态。如果正在编写使用行的数据控件,您可以使用 DataControlRowState 枚举来标识何时为行呈现不同的颜色(Alternate 值),或者用它来标识为编辑行而启用或禁用的控件(EditInsert 值)。

    示例:

    判断行在Edit模式

    (row.RowState & DataControlRowState.Edit) != 0

    而不能使用

    row.RowState == DataControlRowState.Edit

    判断行在Insert模式

    (row.RowState & DataControlRowState.Insert) != 0
    而不能使用
    row.RowState == DataControlRowState.Insert

    因为DataControlRowState的声明是:

    [Flags]
        public enum DataControlRowState {
            // 摘要:
            //     指示该数据控件行处于正常状态。System.Web.UI.WebControls.DataControlRowState.Normal 状态与其他状态相互排斥,但
            //     System.Web.UI.WebControls.DataControlRowState.Alternate 状态除外。
            Normal = 0,
            //
            // 摘要:
            //     指示该数据控件行是交替行。
            Alternate = 1,
            //
            // 摘要:
            //     指示该行已被用户选定。
            Selected = 2,
            //
            // 摘要:
            //     指示该行处于编辑状态,这通常是单击行的“编辑”按钮的结果。通常,System.Web.UI.WebControls.DataControlRowState.Edit
            //     和 System.Web.UI.WebControls.DataControlRowState.Insert 状态互相排斥。
            Edit = 4,
            //
            // 摘要:
            //     指示该行是新行,这通常是单击“插入”按钮添加新行的结果。通常,System.Web.UI.WebControls.DataControlRowState.Insert
            //     和 System.Web.UI.WebControls.DataControlRowState.Edit 状态互相排斥。
            Insert = 8,
        }

  • Response.Redirect 产生的“正在中止线程”错误

    by{ guangboo }, published {2010-02-26}, Tag { .net / asp.net / }

    Response.Redirect 产生的“正在中止线程”错误 
    这两天在开发调试过程中,老是会出现在一个 
    "正在中止线程 “(ThreadAbortException)的例外信息。
    例外是 由 Response.Redirect 方法产生的,虽然知道是线程的问题, 但是不知为何 Redirect会出现这样的错误,以前是没有碰到过,转 移到 Asp.net 2.0 开发就遇上了。
    在狂Google了一下后,才发现MS早就发现了这个问题,但提供了解决方法。以下是主要信息的摘要:
    症状
    如果使用 Response.End、Response.Redirect 或 Server.Transfer 方法,将出 现 ThreadAbortException 异常。您可以使用 
    try-catch 语句捕获此异常。 
    原因
    Response.End 方法终止页的执行,并将此执行切换到应用程序的事件管线中 的 Application_EndRequest 事件。不执行 Response.End 后面的代码行。

    此问题出现在 Response.Redirect 和 Server.Transfer 方法中,因为这两种方法均在内部调 用 Response.End。 
    解决方案
    要解决此问题,请使用下列方法之一:• 对于 Response.End,调 用 HttpContext.Current.ApplicationInstance.CompleteRequest 方法而不 是 Response.End 以跳过 Application_EndRequest 事件的代码执行。 
    • 对于 Response.Redirect,请使用重载 Response.Redirect(String url, 
    bool endResponse),该重载对 endResponse 参数传递 false 以取消 对 Response.End 的内部调用。例如: Response.Redirect ("nextpage.aspx"false);

    摘自:http://www.cnblogs.com/wenanry/archive/2007/07/12/815271.html

  • C#创建临时文件

    by{ guangboo }, published {2009-12-08}, Tag { CSharp / .net / asp.net / }

    在使用C1.C1Excel控件将DataTable导出到Excel表格的时候,遇到一个问题。原来的代码是:

    C1XLBook book = ExcelDocument.Instance.ExpertExcel(dt);
    string fileName = Path.GetFileNameWithoutExtension(TMP_PATH + "\\" + dt.TableName) + ".xls";
    book.Save(fileName);
    book.Dispose();
    FileStream fs = null;
    try {
        fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        byte[] buffer = new byte[(int)fs.Length];
        fs.Read(buffer, 0, buffer.Length);
        
        Response.ContentEncoding = Encoding.GetEncoding("GB2312");
        Response.HeaderEncoding = Encoding.GetEncoding("GB2312");
        Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(Path.GetFileNameWithoutExtension(fileName)) + ".xls");
        Response.AppendHeader("Content-Length", buffer.Length.ToString());
        Response.ContentType = "application/octet-stream";
        Response.OutputStream.Write(buffer, 0, buffer.Length);
    }finally{
    	fs.Close();
        try{
        	File.Delete(fileName);
        }catch{}
    }
    

    上面的代码中fileName的值为Path.GetFileNameWithoutExtension(TMP_PATH + "\\" + dt.TableName) + ".xls",其中TMP_PATH是Web.config里面配置的,使用ConfigurationManager.AppSettings[""]获取的,fileName="E:\TMP\****.xls"。

    我其实也没有在E盘下创建TMP文件夹,跟没有给该文件夹添加任何的权限,但是我在使用VS自带的服务器运行网站的时候,完全没有问题,奇怪的是,我在将数据导出到Excel的过程中,一直观察E盘,就是没有发现TMP文件,也没有错误信息,也许是我刷新的不够快吧。

    但到我不网站部署到测试服务器的时候,运行导出程序就出现问题了“System.IO.IOExcetpion:Failed to create storage file.”,问题就出在book.Save(fileName)上,立即想到的是没有给这个TMP_PATH文件夹足够的权限,但是设了半天,问题还是一样。

    然后使用reflector工具,查看C1.C1Excel的源码,根据异常堆栈提供的错误信息,找到C1XLBook的Save(fileName)方法:

    public void Save(string fileName)
    {
        string str;
        bool flag = false;
        if (((str = Path.GetExtension(fileName).ToLower()) != null) && ((str == ".xlsx") || (str == ".zip")))
        {
        	flag = true;
        }
        this.o(fileName, flag);
    }
    

    该方法执行到this.o(fileName, flag)的时候出现异常,然后我又跟踪到o方法:

    internal void o(string A_0, bool A_1)
    {
        if (this.Sheets.Count == 0)
        {
        	n("Cannot save empty book (need at least one sheet).", true);
        }
        bool flag = false;
        foreach (XLSheet sheet in (IEnumerable) this.Sheets)
        {
            if (sheet.Visible)
            {
                flag = true;
                break;
            }
        }
        if (!flag)
        {
        	n("Cannot save book without any visible sheets.", true);
        }
        this.f();
        using (au au = au.a(A_0))
        {
        	using (cg cg = au.c("Workbook"))
            {
            	this.q(cg);
            }
        }
        this.e();
    }
    

    继续向上跟踪,到在using (au au = au.a(A_0))语句的时候出现了异常,然后定位到au类的a方法:

    internal static au a(string A_0)
    {
        int num = 0x1012;
        bg bg = null;
        if ((StgCreateDocfile(A_0, num, 0, out bg) != 0) || (bg == null))
        {
        	throw new IOException(l.e("Failed to create storage file."));
        }
        return new au(bg);
    }
    

    很显然IOException(l.e("Failed to create storage file."))异常被抛出了,然后我又定位到StgCreateDocfile方法:

    [DllImport("OLE32.DLL")]
    private static extern int StgCreateDocfile([MarshalAs(UnmanagedType.LPWStr)] string A_0, int A_1, int A_2, out bg A_3);
    

    该方法是来说OLE32.DLL文件,此时我马上想到也行测试服务器上没有ole32.dll文件,后来我在windows/system32/下面找到ole32.dll文件,虽然文件大小和版本和我的win7上的不一样,但是问题应该不在这里,而且无也不敢随便将这个文件替换,怕影响了sql和其他程序的运行。

    然后我就开始在google上搜寻答案,其中有一个答案最有可能:

      >>>>>>>>>>>>>
      IIS requires file/directory permissions to be set so that the anonymous 
      users have write access to the directory.
      
      IUSR_, IWAM_
      
      By file permissions, I mean the permissions you would set using Windows 
      Explorer file/directory Properties - Security tab. You can also set these 
      using cacls.exe from Run or a command line. Setting them from code is nasty, 
      with all kinds of objects involved. It's so nasty, that even MS 
      installations tend to use calcs.exe from a system or spawn call (that's the 
      little Dos box you see pop up and go away during some installations).
      
      It is also necessary to allow write permission on the Virtual directory - 
      Virtual Directory tab of IIS Website directory properties. Note, that in a 
      live situation, both Write and Execute (scripts or executables) should NOT 
      be granted together. You don't want people writing things that execute.
      
      >>>>>>>>>>>>>
      
      I did write a sample to check this. Here's what my code looks like:
      
      private void Button1_Click(object sender, System.EventArgs e)
      {
          this.c1XLBook1.Clear();
          C1.C1Excel.XLSheet sheet = this.c1XLBook1.Sheets[0];
          sheet[0,0].Value = "Hello from ASP.NET";
          string xlsFile = @"temp\hello.xls";
          this.c1XLBook1.Save(Page.MapPath(xlsFile)); // << map to absolute path
          this.Response.Redirect(xlsFile);
      }
      
      When I ran this, I got the error you described. That was to be expected, 
      since I didn't create or set permissions on the "temp2" folder. Then I did 
      this:
      1) Open the IIS management console, look for the application's virtual 
      directory, right-click, select "New | Virtual Directory".
      2) Type in the alias ("temp") and select the path for the new virtual 
      directory. (Navigate to the app folder, click the "Make New Folder" button, 
      and type "temp" to create the new temp folder).
      3) In the "Access Permissions" step, select "Read" and "Write" (make sure 
      "Run" and "Execute" are not checked).
      
      This takes care of the IIS side of the story. If you run the app now, you 
      will get the same error though. That's because the folder permissions are 
      not set yet. To set the folder permissions, right-click the new folder in 
      the Windows Explorer, select "Sharing and Security...", then pick the 
      "Security" tab.
      
      This is where things get complicated, because you must grant permissions 
      based on how your accounts are set up, and this depends on which OS/IIS/.NET 
      combination you have.
      
      The simplest thing to do (on your development machine) is delete all groups 
      except two: "Administrators" and "Everyone". Members of the "Administrators"
      group should be allowed to do everything. Members of the "Everyone" group 
      should be allowed to "Read" and "Write" (but not execute, list folder 
      contents, etc). To delete the groups, you will probably have to click the 
      "Advanced" button and uncheck the "Inherit" checkbox.
      
      Attached is a screenshot of the Security Properties dialog after this is all 
      setup. After making these changes, my sample web app works fine. The xls 
      files are created under the temp folder and I can redirect the browser to 
      them.
      
      I hope this information helps.
      
      Please note that this is an easy way to set up the securities on your 
      development environment. On production systems, the sysadmin would probably 
      be more restrictive and grant permissions to specific users instead of 
      "Everyone". But since we're not granting execute permissions anyway, this 
      seems OK at least for debugging environments. If anyone has additional 
      information on this topic, please feel free to share it.
    

    这段EN文,大概的意思就是在目录文件IIS运行用户的权限,我觉得很麻烦,然后就试着不要自己手动添加临时文件夹,而采用系统自己的临时文件夹,使用System.IO.Path.GetTempFileName()可以创建一个临时文件,如:C:\Users\Jeff\AppData\Local\Temp\tmpEAFD.tmp,这是我的Win7下的临时文件,也行你的XP是:C:\Documents and Settings\UserName\Local Settings\Temp\tmpEAFD.tmp。而且你也可以使用File.Delete来删除该临时文件。
    修改后的代码为:

    C1XLBook book = ExcelDocument.Instance.ExpertExcel(dt);
    string fileName = Path.GetTempFileName();
    book.Save(fileName);
    book.Dispose();
    FileStream fs = null;
    
    try {
        fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        byte[] buffer = new byte[(int)fs.Length];
        fs.Read(buffer, 0, buffer.Length);
        
        Response.ContentEncoding = Encoding.GetEncoding("GB2312");
        Response.HeaderEncoding = Encoding.GetEncoding("GB2312");
        Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(Path.GetFileNameWithoutExtension(fileName)) + ".xls");
        Response.AppendHeader("Content-Length", buffer.Length.ToString());
        Response.ContentType = "application/octet-stream";
        Response.OutputStream.Write(buffer, 0, buffer.Length);
    } catch(Exception ex) {
        lblResult.Text = "生成Excel文件过程中,出现异常:" + ex.Source;
        lblResult.Visible = true;
    } finally {
        fs.Close();
        try {
        	File.Delete(fileName);
        } catch { }
    }
    
  • 定期自动更新Js文件

    by{ guangboo }, published {2009-10-12}, Tag { Javascript / asp.net / }

    js文件大部分是起到存储js库,js函数等的功能,很少用于存储数据;特别情况下,如使用AJAX技术的时候会将数据序列号成JSON对象,并以application/javascript的Content-type返回给客户端,功能客户端js库解析。此时的数据就好像是js文件。

    案例:

    某券商,部门A负责网站的代销基金数据更新,部门B负责维护网站(包括代销基金栏目);网站初期开了代销基金更新周期较短,故代销基金栏目做成html文件,如有更新就从部门A整理的数据中,修改该html文件;由于工作量较少,一直没有出现什么问题,现在代销基金列表需要“单位净值”,“累计净值”,“基金类型”等,由于“单位净值”,“累计净值”的更新周期一般为一天,故需读取数据库,然而开发后台又比较麻烦,希望将数据保存在js文件中,每天自动更新js文件就可以了。

    解决方案:

    1. 固定每天访问当天最新的js文件,js文件以日期命名,如“funds-2009-10-12.js”;

    2. 每天第一次访问的时候,当天的js文件是不存在的,需要创建,然后删除过时的js文件。

    3. 当天的其他对该js文件的请求都定位到当天的js文件上,如果该文件不存,视为该请求为当天第一个请求,处理参照2。

    示例代码:

    DateTime now = DateTime.Now;
    string rootDir = AppDomain.CurrentDomain.BaseDirectory;
    // 最新js文件
    string newJsFileName = "fundvalues-" + now.ToString("yyyy-MM-dd") + ".js";
    string jsFilePath = rootDir + newJsFileName;
    
    try {
        FileInfo file = new FileInfo(jsFilePath);
        // 不存在就重新获取今天最新的数据创建该js文件
        if(!file.Exists) {
           string result = "test"; // 存储数据库获取的数据
            StreamWriter writer = null;
            try {
                writer = file.CreateText();
                writer.Write(result);
            } catch(Exception ex) {
                // 处理异常
            } finally { writer.Close(); }
        }
        // 删除以前的老js文件
        // 忽略异常
        string[] oldJsFiles = Directory.GetFiles(rootDir, "fundvalues-*.js", SearchOption.TopDirectoryOnly);
        StringBuilder sbErr = new StringBuilder();
        if(oldJsFiles.Length > 0) {
            try {
                foreach(string path in oldJsFiles) {
                    if(path.ToLower() != jsFilePath.ToLower()) {
                        File.Delete(path);
                    }
                }
            } catch(Exception ex0) {
                // 忽略异常
            }
        }
    } catch(Exception ex) {
        // 处理异常
    }
    // 返回js文件
  • 如何使用Usercontorl控制页面

    by{ guangboo }, published {2009-09-27}, Tag { asp.net / }

    在ASP.NET当中,如何使用UserControl控制页面,访问肯定有很多中,这里基于asp.net的原理事件驱动来实现UserControl控制页面的目的。

    将各个功能模块做成UserControl是一个好习惯,但是在一个页面中一个控件更新后需要更新页面就比较麻烦一些,下面一个例子来说明如何通过事件驱动来实现该目的。

    简单介绍一下示例,该示例是一个Label,一个TextBox和一个Button,我们将Button做成在UserControl里,然后在Button的Click事件后,将页面中的Label更新;之后我们实现使用AJAX异步更新效果。

    1. 创建UC_Button.ascx,和页面Test.aspx,在页面中添加一个Label控件,并给UC_Button.ascx的Button控件添加Click处理函数,处理函数,处理函数内容暂时空着。

    UC_Button.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UC_Button.ascx.cs" Inherits="Web.UC_Button" %>
    <asp:Button runat="server" ID="btnUpdate" EnableViewState="false" 
        Text=" Update " onclick="btnUpdate_Click" />
    

    Test.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="test.aspx.cs" Inherits="Web.test" %>
    
    <!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 runat="server" ID="pageLabel" EnableViewState="false" />
        </form>
    </body>
    </html>
    

     在UC_Button.ascx中定义事件接口:

    public delegate void Command();

    UC_TextBox.ascx.cs和UC_Button.ascx.cs文件中分别添加:

    public const string CallbackKey = "CallbackKey";
    
    public event Command OnUpdated {
           add {
                  base.Events.AddHandler(CallbackKey, value);
           }
           remove {
                  base.Events.RemoveHandler(CallbackKey, value);
           }
    }
    
    protected virtual void UpdatedHandler() {
            Command handler = (Command)base.Events[CallbackKey];
            if(handler != null)
                handler();
    }
    

    并且在UC_Button的Button_Click时间中添加

    UpdatedHandler();

     将UC_Button.ascx控件放入Test.aspx页面中,并且给该控件添加OnUpdate处理函数。

    Test.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="test.aspx.cs" Inherits="Web.test" %>
    <%@ Register src="UC_Button.ascx" tagname="UC_Button" tagprefix="uc2" %>
    
    <!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 runat="server" ID="pageLabel" EnableViewState="false" />
            <uc2:UC_Button ID="UC_Button1" OnUpdated="BindData" runat="server" />
        </form>
    </body>
    </html>
    

    Test.aspx.cs

     public void BindData() {
            pageLabel.Text = DateTime.Now.ToString();
     }
    

    OK,运行测试,运行结果如图:

    UserContorl控制页面显示 

    实现AJAX异步更新:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="test.aspx.cs" Inherits="Web.test" %>
    
    <%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        Namespace="System.Web.UI" TagPrefix="asp" %>
    <%@ Register src="UC_Button.ascx" tagname="UC_Button" tagprefix="uc2" %>
    
    <!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:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <asp:Label runat="server" ID="pageLabel" EnableViewState="false" />
                    <uc2:UC_Button ID="UC_Button1" OnUpdated="BindData" runat="server" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </form>
    </body>
    </html>