KB-Controls.AddAt可能破壞ViewState

前些時候,為了解決MatserPage下元件的ClientID會被加註Prefix的問題,我寫了一段彈性化找尋ClientID的Javascript Function取代document.getElementById(),並且為了確保WebControl在產生HTML的同時就可以插入Javascript呼叫它,我利用Page.Form.Control.AddAt(0, Literal)的技巧讓它插隊顯示在最前方。

今天同事回報,這種插隊法會讓下拉選單的選項在PostBack後掉光光,我懷疑是ViewState解析順序被破壞導致,於是寫了以下的Code驗證。以下的寫法,只要呼叫了Page.Form.Controls.AddAt(0, ...), 在按下Button1後,下拉選項就會消失。

<%@ Page Language="C#" AutoEventWireup="true" %>
<html>
<head runat="server">
    <title>ViewState Is Missing</title>
</head>
<body>
    <form id="form1" runat="server">
    <script type="text/C#" runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                DropDownList1.Items.Add("Item1");
                DropDownList1.Items.Add("Item2");
                DropDownList1.Items.Add("Item3");
            }
            Literal ltr = new Literal();
            ltr.Text =
                "<script type=\"text/javascript\">function blah() { }</"
                + "script>";
            Page.Form.Controls.AddAt(0, ltr);
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
 
        }     
    </script>
    <div>
        <asp:DropDownList ID="DropDownList1" runat="server">
        </asp:DropDownList>
        <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
    </div>
    </form>
</body>
</html>

微軟有篇文章提到這一點:

When adding a dynamic control c to some parent control p based on some condition (that is, when not loading them on each and every page visit), you need to make sure that you add c to the end of p's Controls collection.

所以囉! 用Controls.AddAt真的挺危險的在Page_Load中用Controls.AddAt真的挺危險的,但我又這麼在意要把<script>擺到最前面,怎麼辦?

改成Page.Header.Controls.Add(...)吧! 搞定收工!

Update @ 2007-01-04
網友大估找到更好的解法,將Control.AddAt()移至Page_Init()事件就可以了,這個測試結果也解釋了Control.AddAt攪亂ViewState的理由:
依Control Execution Lifecyvle中Event的順序,Load被夾在Load View State與Save State之間,因此在Load加入Contorl,會發生Save State時Control存在,下次PostBack Load State時卻Control卻還沒生出來的情況,因此造成了View State錯亂。嚴格來說,新增Control放在Init事件,會比放在Load中好。謝謝大估提供的建議!

Update 2008-01-19
強化版搜尋範圍擴及UserControl,說明在此

歡迎推文分享:
Published 03 January 2008 04:20 PM 由 Jeffrey
Filed under: ,
Views: 11,509



意見

# 大估 said on 03 January, 2008 06:26 PM

Dear 黑暗大大:

  昨天在套用你介紹的「解決MatserPage下元件的ClientID」方法時,就發現套用後,當網頁有postback時,menu或是tree的資料都會全部清空,今天上來就發現您的修正檔,真是感謝啦~

 不過,大估提出另一個解法,也就是將您原本的程式(Page.Form.Controls.AddAt),放在MasterPage的page_Init中,就ok了,menu及tree在postback後,也不會消失了,蒙古的馬照跑,舞照跳…

# 大估 said on 03 January, 2008 06:50 PM

Sorry補上程式

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

     MasterPageHelper.RegisterMPGet(Me)

  End Sub

以上

# Jeffrey said on 03 January, 2008 11:28 PM

To 大估,

這解法很優! 也算是結實地跟大家上了一堂Control Execution Lifecycle的Event順序課程,Initialize在Load view state與Save state之外,而Load則被夾在Load state與Save state之間,在Load時才加物件,Save時存在的東西,Load時還沒生出來,就錯亂了。Good Job!!

msdn2.microsoft.com/.../aa719775(VS.71).aspx

# Jet Tseng said on 26 March, 2014 02:59 AM

這問題 搞了我一早上 最後測試出AddAt 造成的 Google 看到你的文章 你的經驗豐富 真是令我崇拜

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<January 2008>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication