惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
Arctic Wolf
T
Tenable Blog
T
Troy Hunt's Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy & Cybersecurity Law Blog
NISL@THU
NISL@THU
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Hacker News: Front Page
S
Secure Thoughts
AWS News Blog
AWS News Blog
L
LINUX DO - 最新话题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
M
MIT News - Artificial intelligence
T
Tor Project blog
S
Schneier on Security
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
美团技术团队
Google DeepMind News
Google DeepMind News
V
Visual Studio Blog
爱范儿
爱范儿
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
罗磊的独立博客
T
Threat Research - Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V
V2EX
C
CXSECURITY Database RSS Feed - CXSecurity.com
Stack Overflow Blog
Stack Overflow Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
D
Docker
J
Java Code Geeks
GbyAI
GbyAI
H
Heimdal Security Blog
The Hacker News
The Hacker News
MongoDB | Blog
MongoDB | Blog
V
Vulnerabilities – Threatpost
T
Tailwind CSS Blog
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
C
CERT Recently Published Vulnerability Notes
Y
Y Combinator Blog
Recorded Future
Recorded Future
Cisco Talos Blog
Cisco Talos Blog
T
Threatpost
The Register - Security
The Register - Security
Hacker News - Newest:
Hacker News - Newest: "LLM"

博客园 - jeff377

使用 Sandcastle Help File Builder 制作 VS.NET 的 HELP 文件 [ASP.NET 控件实作 Day31] TBContextMenu 控件三种不同模式的 Click 动作 [ASP.NET 控件实作 Day30] 整合 jQuery ContextMenu plugin 的右键选单控件 [ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题 [ASP.NET 控件实作 Day28] 图形验证码控件 [ASP.NET 控件实作 Day27] 控件依 FormView CurrentMode 自行设定状态 [ASP.NET 控件实作 Day26] 让你的 GridView 与众不同 [ASP.NET 控件实作 Day25] 自订 GridView 字段 - 日期字段 [ASP.NET 控件实作 Day24] TBDropDownField 的 Items 属性的数据系结 [ASP.NET 控件实作 Day23] 自订 GridVie 字段类别 - 实作 TBDropDownField 字段类别 [ASP.NET 控件实作 Day22] 让 DropDownList 不再因项目清单不存在而造成错误 [ASP.NET 控件实作 Day21] 实作控件智能卷标 [ASP.NET 控件实作 Day20] 侦错设计阶段的程序代码 [ASP.NET 控件实作 Day19] 控件设计阶段的外观 [ASP.NET 控件实作 Day18] 修改集合属性编辑器 [ASP.NET 控件实作 Day17] 集合属性包含不同型别的成员 [ASP.NET 控件实作 Day16] 继承 WebControl 实作 Toolbar 控件 [ASP.NET 控件实作 Day14] 继承 CompositeControl 实作 Toolbar 控件 [ASP.NET 控件实作 Day13] Flash 控件
[ASP.NET 控件实作 Day15] 复合控件隐藏的问题
jeff377 · 2008-10-16 · via 博客园 - jeff377

上一篇我们使用复合控件(继承 CompositeControl)的方式来实作 TBToolbar 控件,本文将针对复合控件做一些测试,说明在使用复合控件要注意的一些问题。

程序代码下载:ASP.NET Server Control - Day15.rar

一、复合控件建立子控件的时机

还记得我们之前介绍复合控件时有谈到 CompositeControl 类别会确保我们存取子控件时,它的子控件一定会事先建立;也就是当我们使用 Controls 属性去存取子控件时,一定会执行 CreateChildControls 方法,以确保子控件事先被建立。我们看一下 CompositeControl 类别的 Controls 属性的写法就可以了解其中的原由,在存取 CompositeControl.Controls 属性时,它会先执行 Control.EnsureChildControls 方法;而 EnsureChildControls 方法会去判断子控件是否已建立,若未建立会去执行 CreateChildControls 方法,这也就是为什么 CompositeControl 有辨法确保子控件事先被建立的原因。

CompositeControl.Controls 属性如下

Public Overrides ReadOnly Property Controls As ControlCollection
    Get
        Me.EnsureChildControls
        Return MyBase.Controls
    End Get
End Property

Control.EnsureChildControls 方法如下

Protected Overridable Sub EnsureChildControls()
    If (Not Me.ChildControlsCreated AndAlso Not Me.flags.Item(&H100)) Then
        Me.flags.Set(&H100)
        Try 
            Me.ResolveAdapter
            If (Not Me._adapter Is Nothing) Then
                Me._adapter.CreateChildControls
            Else
                Me.CreateChildControls
            End If
            Me.ChildControlsCreated = True
        Finally
            Me.flags.Clear(&H100)
        End Try
    End If
End Sub

二、复合控件隐藏的问题

我们以上篇的 TBToolbar 控件为例,撰写一些测试案例来说明复合控件的问题。在撰写测试案例之前,我们先修改一下 TBToolbar 控件,覆写 LoadViewState 及 SaveViewState 方法,将 Items 属性储存于 ViewState 中以维持状态。

        ''' <summary>
        ''' 由 ViewState 還原控制項的狀態。
        ''' </summary>
        ''' <param name="savedState">要還原的控制項狀態。</param>
        Protected Overrides Sub LoadViewState(ByVal savedState As Object)
            If Not (savedState Is Nothing) Then
                ' Load State from the array of objects that was saved at ;
                ' SavedViewState.
                Dim myState As Object() = CType(savedState, Object())
 
                If Not (myState(0) Is Nothing) Then
                    MyBase.LoadViewState(myState(0))
                End If
 
                If Not (myState(1) Is Nothing) Then
                    FItems = CType(myState(1), TBToolbarItemCollection)
                End If
            End If
        End Sub
 
        ''' <summary>
        ''' 控制項的狀態儲存至 ViewState。
        ''' </summary>
        ''' <returns>含有控制項之目前檢視狀態的物件。</returns>
        Protected Overrides Function SaveViewState() As Object
            Dim baseState As Object = MyBase.SaveViewState()
            Dim myState(1) As Object
            myState(0) = baseState
            myState(1) = Me.Items
            Return myState
        End Function

在测试页面上放置「测试一」、「测试二」、「PostBack」三个按钮,这三个按钮的动作如下。

「测试一」按钮:在工具列直接新增一个按钮。
「测试二」按钮:先使用 FindControl 取得工具列的按钮,然后在在工具列再新增一个按钮。
「PostBack」按钮:单纯执行 PostBack,不撰写程序代码。

image

三个按钮的程序代码如下所示。

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim oItem As TBToolbarItem
 
        '加入新按鈕
        oItem = New TBToolbarItem()
        oItem.Text = "新按鈕"
        oItem.Key = "NewButton"
        TBToolbar1.Items.Add(oItem)
        Me.Response.Write("「測試一」按鈕")
    End Sub
 
    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim oItem As TBToolbarItem
        Dim oButton As Button
 
        '先執行 FindControl 去取得 ID="Add" 的按鈕
        oButton = TBToolbar1.FindControl("Add")
 
        '再加入新按鈕
        oItem = New TBToolbarItem()
        oItem.Text = "新按鈕"
        oItem.Key = "NewButton"
        TBToolbar1.Items.Add(oItem)
        Me.Response.Write("「測試二」按鈕")
    End Sub
 
    Protected Sub Button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button3.Click
        '單純 PostBack,無程式碼
        Me.Response.Write("「PostBack」按鈕")
    End Sub

案例一:执行「测试一」按钮,在工具列直接新增一个按钮。

当按下「测试一」按钮时,工具列可以正常加入我们新增的按钮。

image

案例二:执行「测试二」按钮,先使用 FindControl 取得工具列的按钮,然后在在工具列再新增一个按钮。

重新执行程序,当按下「测试二」按钮时,你会发现奇怪的现象,工具列竟然没有加入我们新增的按钮?

image

此时再按下「PostBack」按钮,工具列才会出现我们刚刚加入的按钮。

image

为什么会发生这种怪现象呢?其实原因很简单,因为 FindControl 时会去存取 Controls 属性,而这时子控件已经被建立了;而之前再用 Items 属性加入新按钮,它已经不会在重建子控件,导致第一时间没有加入新按钮。不过 Items 属性会被存在 ViewState 中,所以当执行「PostBack」按钮时,就会出现我们刚刚新增的按钮。

三、解决方式

要解决上述「测试二」的问题,只要覆写 TBToolbar 控件的 Render 方法,在 Render 前执行 RecreateChildControls 方法,强制重建子控件。

        ''' <summary>
        ''' 覆寫 Render 方法。
        ''' </summary>
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Me.RecreateChildControls()
            MyBase.Render(writer)
        End Sub

再一次执行「测试二」的动作,就会发现执行结果就会正常了。

image

四、结语

在复合控件的 Render 前执行 RecreateChildControls 方法可以强制重建子控件,可是这样又会引发另一个问题,那就是当直接存取子控件去修改子控件的属性后,一旦在 Render 又重建子控件,那之前设定子控件状态又被全部重建了,所以需特别注意有这样的情形。另外复合控件有可能重复执行建立子控制的动作,在执行效能上也比较不佳。

备注:本文同步发布于「第一届iT邦帮忙铁人赛」,如果你觉得这篇文章对您有帮助,记得连上去推鉴此文增加人气 ^^
http://ithelp.ithome.com.tw/question/10012425