本系列第二部分揭示 ViewState 在伺服器端的樹狀儲存結構(Pair 遞迴結構),以及 Control 類別如何遞迴載入、儲存、追蹤整個控件樹的狀態。
Pair 只有兩個欄位 First、Second,透過遞迴構成樹狀結構:
First:儲存當前控件的 ViewState(ArrayList 格式)。Second:儲存子控件集合(ArrayList,交替存放 ID/索引與子控件的 Pair)。根節點為 Page 的 savedState(Pair),完整保存整頁控件樹的層級關係與狀態資料。
定義於 Control:protected virtual StateBag ViewState { get; }。所有伺服器控件、Page 繼承此屬性。控件屬性(如 Button.Text)實際讀寫 ViewState["Text"],透過 StateBag 實現跨 PostBack 記憶。
決定子控件載入方式:true 依 ID 查找(延遲建立子控件時需用),false 依索引(預設,效能較好)。
控制該控件及其子樹是否參與 ViewState 機制。
Page.LoadAllState 呼叫入口。遞迴流程:
EnableViewState,呼叫 LoadViewState(savedState.First) 載入自身狀態。LoadViewStateByID 分派子控件狀態:
LoadChildViewStateByID(savedState.Second),遍歷 ID 陣列,FindControl(id).LoadViewStateRecursive(childPair)。LoadChildViewStateByIndex(savedState.Second),遍歷索引陣列,Controls[index].LoadViewStateRecursive(childPair)。Page.SaveAllState 呼叫入口。遞迴流程:
SaveViewState() 取得自身狀態 x(ArrayList)。SaveViewStateRecursive() 取得 z(ArrayList,格式:ID/索引 + 子 Pair)。new Pair(x, z)。在 Control.InitRecursive 中遞迴呼叫,進而呼叫 ViewState.TrackViewState() 設定 marked=true,啟動狀態追蹤。
Page.LoadAllState └─ Page.LoadPageStateFromPersistenceMedium → HiddenFieldPageStatePersister.Load → ObjectStateFormatter.Deserialize → Pair (savedState) └─ Control.LoadViewStateRecursive(savedState) → 遞迴載入全樹 ViewState Page.SaveAllState └─ Control.SaveViewStateRecursive() → 遞迴收集全樹 ViewState → Pair (savedState) └─ Page.SavePageStateToPersistenceMedium → HiddenFieldPageStatePersister.Save → ObjectStateFormatter.Serialize → Base64 字串 └─ Render 階段寫入 __VIEWSTATE 隱藏欄位
Pair 遞迴結構完美映射控件樹層級,Control 類別透過 LoadViewStateRecursive/SaveViewStateRecursive 實現整樹狀態的序列化與反序列化。理解此流程有助於診斷 ViewState 異常、實作自訂控件狀態管理、優化序列化效能。