RSS 2.0 Feed
2004-10 Entries
摘要:上次的第二个问题是,为什么动态生成的DropDownList控件,在PostBack后,在Page_Load里其选择的项没有被设置。 拿TestDyn1.aspx为例,如果你在第一个(静态)下拉框里选择2,在第二个(动态)下拉框里选择3,然后按Click Me按钮,你的输出是这样的 [Page_Load]静态:1[Page_Load]动态:0[Button_Click]静态:1[Button_Click]动态:2 不管你选什么,第二项总是[Page_Load]动态:0 即,动态下拉框的选项在Page_Load没有被正确设置,但在Button的Click事件里被正确设置了。 大家知道,表单控件(TextBox, CheckBox, DropDownList, ListBox,....) 的输入值或被选状态与ViewState无关,而是在Load Postback Data阶段被设置的,因为它们都实现了IPostBackDataHandler接口。 上次说到动态控件被加入父控件的Controls集合时,会通过阶段“追赶(catch-up)”过程来赶上父控件当前的阶段,如果你仔细看一下前一个贴里leighsword和microhelper贴的Control的AddedControl方法,你将看到 control.InitRecursive(control1); ...control.LoadViewStateRecursive(obj1); ...control.LoadRecursive(); ... 并没有涉及Load Postback Data。那么这个阶段是什么时候被执行的呢?如果你参考Reflector(也可以参考上一个贴的2个回贴)里System.Web.UI.Page的ProcessRequestMain()方法,在去掉了那些Trace语句后是这样的: base.InitRecursive(null); if (this.IsPostBack){      this.LoadPageViewState();     //注意,这里是._requestValueCollection      this.ProcessPostData(this._requestValueCollection, true); //第二个参数表明是否是在Load前调用的}   base.LoadRecursive(); if (this.IsPostBack){ //注意,这里是._leftoverPostData,即,尚未被处理的PostData this.ProcessPostData(this._leftoverPostData, false);  this.RaiseChangedEvents();  this.RaisePostBackEvent(this._requestValueCollection); } base.PreRenderRecursiveInternal(); this.SavePageViewState(); base.RenderControl(this.CreateHtmlTextWriter(this.Response.Output)); ProcessPostData会根据Request.Form里每对名字/值,看是否有实现了IPostBackDataHandler接口的对应名字的控件,有的话,就会调用该控件的LoadPostData方法,譬如DropDownList的LoadPostData是这样的 bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection){      string[] textArray1 = postCollection.GetValues(postDataKey);      if (textArray1 != null)      {            int num1 = this.Items.FindByValueInternal(textArray1[0]);            if (this.SelectedIndex != num1)            {                  this.SelectedIndex = num1;                  return true;            }      }      return false;} 从上面可见,ProcessPostData在Load前被执行了一次,在Load后又会被执行一次。看上去有点怪,但这正是系统给你的方便,允许你在Load里动态生成控件,并让那些实现了IPostBackDataHandler接口的控件获取用户输入的值或选择的状态。 在我们当前的情形下,我们的动态控件是在Load里生成的,错过了第一次ProcessPostData,所以在Page_Load里其选项还没有被正确设置,但第二次ProcessPostData让其获取了用户输入的值或选择的状态,所以在Button的Click事件里被正确设置了。 这也意味着,如果我们的表单控件是在Load之后生成的,譬如你的控件是在PreRender事件里生成的, void Page_PreRender(Object sender, EventArgs e){   DropDownList ddlDynamic2 = new DropDownList();   ddlDynamic2.ID = "ddlDynamic2";    form1.Controls.Add(ddlDynamic2);    if (!IsPostBack)   {      for (int i=1; i <=3; i++)          ddlDynamic2.Items.Add(new ListItem(i.ToString(), i.ToString()));   }   else     Response.Write("[Page_Load]动态2:" + ddlDynamic2.SelectedIndex + "<BR>");} 那么尽管它可以恢复ViewState,但因为它错过了2次ProcessPostData机会,它不可能获取用户输入的值或选择的状态。同时这些控件也不会触发Changed Events 与 Postback Events。 当然,你尽可以使用Request.Form来获取用户输入的值或选择的状态,但这跟我们的讨论无关。 所以,如果你需要产生动态控件,而且需要获取用户设置的输入值或触发Changed Events 与 Postback Events事件的话,最好在Load阶段或之前生成。 关于ASP.NET里Page事件及其次序的细节,请参考MVP Paul Wilson的文章Page Events: Order and PostBack 也请参考 The......[阅读全文]

posted @ | Feedback (15) |

摘要: 你是否对软件开发已经了如指掌?如果是,那么就请别往下读了。 Coder(编码人员)与Developer(开发人员)的区别是什么?Coder是指那些熟练掌握了某门计算机语言的人。而Developer是指那些人,能拿起需求或概念来,组成一支团队,应用现代开发过程,做出合乎需求的设计,然后制造出来高质量的产品来。 Mike Gunderloy在他的新书《Coder To Developer -- Tools and Strategies for Delivering Your Software》里告诉我们,想成为一个合格的软件开发人员,应该掌握什么东西。 在该书的15章里,Mike Gunderloy以一个.NET程序开发为例,深入浅出地谈及了软件开发中的方方面面,从计划,组织,应用源码控制,防卫性编程,使用单元测试,熟练掌握和武装IDE,深入理解/重用代码,使用工具生成编码,缺陷追踪,记录程序行为,团队合作,产生文档,精通运行代码的生成过程,保护知识产权,到交付与产品的安装/部署,在每个方面都提出了非常可借鉴的看法和经验。 譬如,在第三章《Using Source Code Control Effectively(有效地使用源码控制)》里,他谈及源码控制里的三个层次,现有市面上的种种源码控制工具,如何据于需求(价格,开发风格,代码repository,网络友好性,与IDE的集成,所需高级指令,跨平台支持等等)做出选择。 在第五章《Pumping Up the IDE(如何武装IDE)》里,他谈及怎么用户化IDE,设置有用选项,使用VS.Net PowerToys,编写Macros,选择Add-Ins,怎么使用工具浏览和分析你的源码,更换编辑器等等。 在第九章《Logging Application Activities(记录程序行为)》里,他从.Net里现有的类(System.Diagnostics.Debug 和Trace)出发,谈到微软的Enterprise Instrumentation Framework以及The Logging Application Block,最后论及开放源代码的log4net。 在第十三章《Mastering the Build Process(精通Build过程)》里,他谈及自动Build 过程,以及该过程的简洁性和可靠性,分析比较了当前市面上高低档Build 工具,如何使用开发源代码的NAnt等等。 虽说自己开发软件也有不少年了,但惭愧的是,涉及的方面很有限。通过阅读这本书,了解到了自己与一个全面的软件开发人员间的距离,认识到在哪些方面还有待进一步提高。 比较欣赏书尾的几句话: If there's one thing that distinguishes good developers from the rest of the pack, it's  an attitude toward learning. Good developers just don't stop learning. There's always some new part of the software universe to explore, some new languages to learn, or some new tool to test-drive. As you hone your own developing skills, I urge you to make use of the resources of the Internet......[阅读全文]

posted @ | Feedback (24) |

摘要:要理解第一个问题,即,对于一个动态生成的DropDownList对象,为什么先添加ListItem,后调用父控件的Controls.Add,其状态并没有保存,其关键在于理解TrackViewState的调用以及动态控件加入父控件的Controls后的阶段“追赶”过程。 如果你在文档里查询Control的TrackViewState方法描述,其中说到,只有调用该方法后,view-state的变化才会存到服务器控件的StateBag对象里去,这样才会在下一次的PostBack后的LoadViewState中恢复到原来状态。 大家都知道每个控件一般都会经历如下几个阶段 (抄自《Developing Microsoft ASP.NET Server Controls and Components》 一书第九章) 1。Instantiate 2。Initialize 3。Begin Tracking View State 4。Load View State (postback only) 5。Load Postback Data (postback only) 6。Load 7。Raise Changed Events (postback only, optional)8。Raise Postback Events (postback only, optional)9。PreRender10。SaveViewState11。Render12。Unload13。Dispose 在页面里declared的服务器控件,譬如,例子中的ddlStatic,其TrackViewState方法是在Init阶段后面调用的,其后的变化将保存到StateBag里去,但其前的变化不会保存。如果你用Paul Wilson的ViewState Parser查看该例的ViewState,你是看不到其状态的。但假如在此之后,你改变其状态,那么这些状态也许就会保存到ViewState去(取决于该对象是否override了SaveViewState)。 那动态控件呢?其一开始是新建对象,处于原始状态,当它被加到父控件的Controls里时,父控件会根据其当前的control阶段来调用该子控件的一些方法,让子控件赶上父控件的control阶段 (这些方法可以从上个贴里leighsword和microhelper贴的Control的AddedControl方法里看到,在此就不重复了,而且也不用看那些方法)。为什么要这样呢?这应该跟整个页面的生命周期有关吧。 打个比方,不是很恰当,但凑合着吧,这好象是复制一个人后,让他快速经历婴儿,童年,少年,青年,。。。直至赶上被复制人目前的阶段为止。 但大概来讲,当我们在Page_Load里调用form1.Controls.Add()时,父控件form1处于Load阶段(上面的第六行),它就会调用下拉框的一些方法让它经过Init->Load状态,其中的一个结果是在Init后面调用了TrackViewState,DropDownList的父类ListControl, override了TrackViewState,在其中调用了Items(ListItemCollection类)对象的TrackViewState。其结果是,如果你在form1.Controls.Add()之后改变动态DropDownList控件的Items的话,那些ListItem就会被保存下来,因为ListItemCollection对象 override 了 SaveViewState() 。而在form1.Controls.Add()之前添加的ListItem则不会被保存下来。 其实解决TestDyn2.aspx中的问题有个现成的答案,即去除 if (!IsPostBack): DropDownList ddlDynamic = new DropDownList();ddlDynamic.ID = "ddlDynamic";for (int i=1; i <=3; i++)  ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));   form1.Controls.Add(ddlDynamic); 这样,跟ddlStatic一样,每次都生成ListItem对象 第二个问题,即为什么动态生成的DropDownList控件在PostBack后在Page_Load里其选择的项没有被设置,再供大家研究。...[阅读全文]

posted @ | Feedback (15) |

摘要:在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,PostBack时需要重新生成或装载(LoadControl),而且需要深入了解其状态的变化过程。 有个同事另谋高就,要离开我们工作的地方了。我给她出了一道出门考题,同时也叫其他手下一起参加。这题目是这样的: 下面两页差别很小,就是一句语句的前后次序有所不同,但PostBack后显示效果有所不同,请解释为什么显示效果不同,并且解释正确显示的那页(你知道是哪页,对么?)中Response.Write的输出结果 TestDyn1.aspx: <html><body> <form id="form1" runat="server">  <asp:Button id="btn" runat="server" Text="Click Me" OnClick="Button_Click" /> <br/>  静态: <asp:DropDownList id="ddlStatic" runat="server">  <asp:ListItem Text="1" Value="1" />  <asp:ListItem Text="2" Value="2" />  <asp:ListItem Text="3" Value="3" />       </asp:DropDownList> <br/>  动态: </form></body></html><script language="C#" runat="server">void Page_Load(Object sender, EventArgs e){   DropDownList ddlDynamic = new DropDownList();   ddlDynamic.ID = "ddlDynamic";    form1.Controls.Add(ddlDynamic);    if (!IsPostBack)   { for (int i=1; i <=3; i++)  ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));   }        if (IsPostBack)   {    Response.Write("[Page_Load]静态:" + ddlStatic.SelectedIndex + "<BR>"); Response.Write("[Page_Load]动态:" + ddlDynamic.SelectedIndex + "<BR>");   }} void Button_Click(Object sender, EventArgs e){ DropDownList ddlDynamic = (DropDownList)form1.FindControl("ddlDynamic");      Response.Write("[Button_Click]静态:" + ddlStatic.SelectedIndex + "<BR>"); Response.Write("[Button_Click]动态:" + ddlDynamic.SelectedIndex + "<BR>");}</script> TestDyn2.aspx: <html><body> <form id="form1" runat="server">  <asp:Button id="btn" runat="server" Text="Click Me" OnClick="Button_Click" /> <br/>  静态: <asp:DropDownList......[阅读全文]

posted @ | Feedback (42) |

摘要:这是在CSDN论坛上的一个问题,感觉也许对其他人也会有点用处,所以贴出来 JScript是建立在COM之上的,设置变量/调用函数是通过IDispatch来实现的。在.NET里,调用IDispatch里的方法是通过反射来实现的,即,通过System.Type.InvokeMember 。该方法调用 IDispatch::GetIDsOfNames 以及IDispatch::Invoke来调用COM Automation 对象里的方法和属性。参考 Binding for Office automation servers with Visual C# .NET 假如有下列HTML, <script language="javascript">var var1 = 'yes';function testx(obj){  alert(obj);  alert(var1);}</script><input type=button value="click me" onclick="testx('hello');"> 在C#里,假设axWebBrowser1是你的WebBrowser控件对象,你已经装载了上述HTML,你可以这么做, mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)axWebBrowser1.Document;mshtml.IHTMLWindow2 win = (mshtml.IHTMLWindow2)doc.parentWindow; //读变量值object o = win.GetType().InvokeMember ("var1", BindingFlags.GetProperty, null, win, new Object [] {}); //写变量值win.GetType().InvokeMember ("var1", BindingFlags.SetProperty, null, win, new Object [] {"新的值"}); //调用方法win.GetType().InvokeMember("testx",BindingFlags.InvokeMethod,null,win,new object[]{1}); 比较麻烦,对么?幸运的是,我们不用这么麻烦,因为IHTMLWindow2里有个现成方法,execScript,我们可以这么做: win.execScript("var1 = 'abc';","javascript");win.execScript("testx(12)","javascript");...[阅读全文]

posted @ | Feedback (18) |

摘要:最近在做的项目,老是有个神秘的cookie在不该出现的时候出现,后来经过一番检查,发现问题出现在下列语句  If Not Response.Cookies("SomeCookieName") Is Nothing Then     ' do somthing here End If 发现,假如一开始没有“SomeCookieName”这个cookie,Response.Cookies会自动加上一个,验证了一下, <% Response.Write(Response.Cookies.Count & "<BR>")  for each i as string in Response.Cookies     Response.Write(Response.Cookies(i).Name & "<BR>") next  if not Response.COokies("hello") is nothing then  end if  Response.Write(Response.Cookies.Count & "<BR>") '多了一个  for each i as string in Response.Cookies     Response.Write(Response.Cookies(i).Name & "<BR>") next%> 在Reflector里发现, System.Web.HttpCookieCollection 类的 public HttpCookie Get(string name){      HttpCookie cookie1 = (HttpCookie) base.BaseGet(name);      if ((cookie1 == null) && (this._response != null))      {            cookie1 = new HttpCookie(name);            this.AddCookie(cookie1, true);            this._response.OnCookieAdd(cookie1);      }      return cookie1;}  确实是如此, 但不是很理解为什么。当然,也许有人要问,在Response.Cookies里检查某Cookie存在是否恰当?看到编码后,我们发现确实是不太恰当,估计当初是为了赶时间才用了这么一个hack. 在Development Server上,在Staging Server上一切测试都觉得满意后,部署到Live Server上,居然出了问题。反复测试的结果是,在Mozilla FireFox上一切正常,但在IE上有时工作有时不工作。最后用Mozilla FireFox 的Cookie Culler工具发现,有一堆未知的Cookie存在,有时会把我们的Cookie给冲掉,后来才知道他们用了一个第三方公司的ISAPI DLL,用来tracking 用户情况的。这东西大量使用Cookie,少时会产生5个Cookie,多时会产生20个Cookie!大家都知道,在IE里,Cookie是有限制的。 一周之内居然被Cookies烧了2次,唉!...[阅读全文]

posted @ | Feedback (4) |