<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>ASP.NET 進階</title><link>http://blog.blueshop.com.tw/jeff377/category/2565.aspx</link><description>ASP.NET 進階</description><managingEditor>jeff</managingEditor><dc:language>zh-CHT</dc:language><generator>.Text Version 0.95.2004.101</generator><item><dc:creator>jeff</dc:creator><title>BasePage 撰寫 PageCommand 事件</title><link>http://blog.blueshop.com.tw/jeff377/archive/2008/08/29/56861.aspx</link><pubDate>Fri, 29 Aug 2008 20:46:00 GMT</pubDate><guid>http://blog.blueshop.com.tw/jeff377/archive/2008/08/29/56861.aspx</guid><wfw:comment>http://blog.blueshop.com.tw/jeff377/comments/56861.aspx</wfw:comment><comments>http://blog.blueshop.com.tw/jeff377/archive/2008/08/29/56861.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://blog.blueshop.com.tw/jeff377/comments/commentRss/56861.aspx</wfw:commentRss><trackback:ping>http://blog.blueshop.com.tw/jeff377/services/trackbacks/56861.aspx</trackback:ping><description>&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;前言&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;在「&lt;A href="http://www.dotblogs.com.tw/jeff377/archive/2008/08/01/4752.aspx"&gt;&lt;FONT color=#669966&gt;撰寫自用 Confirm 方法&lt;/FONT&gt;&lt;/A&gt;」一文中提到的「案例三：先執行伺服端程式再彈出詢問訊息，依用戶端回應再接續執行」，當用戶端回應詢問訊息後，會呼叫 __doPostBack() 函式來產生 PostBack 的動作，頁面再自行判斷 Me.Request.Form("__EVENTTARGET") 及 Me.Request.Form("__EVENTARGUMENT")&amp;nbsp; 來處理後序的作業。可是這樣處理 PostBack 的方式略顯麻煩，換個角度思考，若 Page 也能像 GridView 一樣具有類似 RowCommand 的事件，可以處理這類的 PostBack 那不是更方便了嗎？所以本文將在 BasePage 加入 PageCommand 事件，針對這個案例改用 PageCommand 來處理。&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;程式實作&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;我們將使用「&lt;A href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/25/4116.aspx"&gt;&lt;FONT color=#669966&gt;撰寫自用的 ScriptManager 來管理用戶端指令碼&lt;/FONT&gt;&lt;/A&gt;」文中提及的 TBBasePage 類別及 TBScriptManager 類別為基礎做擴展。首先在 TBBasePage 新增一個 PageCommand 事件，作法如下。&lt;/P&gt;
&lt;P&gt;1.定義 PageCommand 事件。 &lt;BR&gt;2.新增 EventTarget 唯讀屬性，傳回 Me.Request.Form("__EVENTTARGET")。 &lt;BR&gt;3.新增 EventArgument 唯讀屬性，傳回 Me.Request.Form("__EVENTARGUMENT")。 &lt;BR&gt;4.覆寫 OnLoad 方法，判斷 EventTarget 及 EventArgument 屬性值，來決定是否引發 PageCommand 事件。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;''' &amp;lt;summary&amp;gt;
''' 頁面基礎類別。
''' &amp;lt;/summary&amp;gt;
Public Class TBBasePage
    Inherits System.Web.UI.Page

#Region " PageCommand 事件 "

    ''' &amp;lt;summary&amp;gt;
    ''' DayCommand 事件引數。
    ''' &amp;lt;/summary&amp;gt;
    Public Class PageCommandEventArgs
        Inherits System.EventArgs
        Private FCommandName As String = String.Empty
        Private FCommandArgument As String = String.Empty

        ''' &amp;lt;summary&amp;gt;
        ''' 命令名稱。
        ''' &amp;lt;/summary&amp;gt;
        Public Property CommandName() As String
            Get
                Return FCommandName
            End Get
            Set(ByVal value As String)
                FCommandName = value
            End Set
        End Property

        ''' &amp;lt;summary&amp;gt;
        ''' 命令引數。
        ''' &amp;lt;/summary&amp;gt;
        Public Property CommandArgument() As String
            Get
                Return FCommandArgument
            End Get
            Set(ByVal value As String)
                FCommandArgument = value
            End Set
        End Property

    End Class

    ''' &amp;lt;summary&amp;gt;
    ''' 頁面命令事件。
    ''' &amp;lt;/summary&amp;gt;
    &amp;lt; _
    System.ComponentModel.Description("頁面命令事件。") _
    &amp;gt; _
    Public Event PageCommand(ByVal sender As Object, ByVal e As PageCommandEventArgs)

    ''' &amp;lt;summary&amp;gt;
    ''' 引發 PageCommand 事件。
    ''' &amp;lt;/summary&amp;gt;
    Protected Overridable Sub OnPageCommand(ByVal e As PageCommandEventArgs)
        RaiseEvent PageCommand(Me, e)
    End Sub

#End Region

    ''' &amp;lt;summary&amp;gt;
    ''' 觸發 PostBack 的控制項 UniqueID。
    ''' &amp;lt;/summary&amp;gt;
    Public ReadOnly Property EventTarget() As String
        Get
            Return Me.Request.Form("__EVENTTARGET")
        End Get
    End Property

    ''' &amp;lt;summary&amp;gt;
    ''' 觸發 PostBack 動作事件引數 。
    ''' &amp;lt;/summary&amp;gt;
    Public ReadOnly Property EventArgument() As String
        Get
            Return Me.Request.Form("__EVENTARGUMENT")
        End Get
    End Property

    ''' &amp;lt;summary&amp;gt;
    ''' 判斷二字串是否相同(不區分大小寫)。
    ''' &amp;lt;/summary&amp;gt;
    ''' &amp;lt;param name="S1"&amp;gt;第一個字串。&amp;lt;/param&amp;gt;
    ''' &amp;lt;param name="S2"&amp;gt;第二個字串。&amp;lt;/param&amp;gt;
    Public Shared Function SameText(ByVal S1 As String, ByVal S2 As String) As Boolean
        If S1 Is Nothing Then
            Return S2 Is Nothing
        End If
        Return S1.Equals(S2, StringComparison.CurrentCultureIgnoreCase)
    End Function

    ''' &amp;lt;summary&amp;gt;
    ''' 覆寫。引發 Load 事件。
    ''' &amp;lt;/summary&amp;gt;
    Protected Overrides Sub OnLoad(ByVal e As EventArgs)
        Dim oArgument() As String
        Dim oEventArgs As PageCommandEventArgs

        MyBase.OnLoad(e)

        If SameText(Me.EventTarget, Me.UniqueID) Then
            '引發 PageCommand 事件
            oArgument = Split(Me.EventArgument, "$")
            If oArgument.Length = 3 Then
                If SameText(oArgument(0), "PageCommand") Then
                    oEventArgs = New PageCommandEventArgs()
                    oEventArgs.CommandName = oArgument(1)
                    oEventArgs.CommandArgument = oArgument(2)
                    Me.OnPageCommand(oEventArgs)
                End If
            End If
        End If
    End Sub

End Class&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;然後在 TBScriptManager 類別新增一個 GetPageCommandEventReference 方法，取得引發 PageCommand 事件的用戶端指令碼。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;''' &amp;lt;summary&amp;gt;
''' 用戶端指令碼管理。
''' &amp;lt;/summary&amp;gt;
Public Class TBScriptManager

    ''' &amp;lt;summary&amp;gt;
    ''' 取得引發 PageCommand 事件的用戶端指令碼。
    ''' &amp;lt;/summary&amp;gt;
    ''' &amp;lt;param name="CommandName"&amp;gt;命令名稱。&amp;lt;/param&amp;gt;
    ''' &amp;lt;param name="CommandArgument"&amp;gt;命令引數。&amp;lt;/param&amp;gt;
    Public Function GetPageCommandEventReference(ByVal CommandName As String, ByVal CommandArgument As String) As String
        Dim sArgument As String

        sArgument = String.Format("{0}${1}${2}", "PageCommand", CommandName, CommandArgument)
        Return Me.GetPostBackEventReference(FPage, sArgument)
    End Function

End Class&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;PageCommand 事件完成了，我們把「案例三：先執行伺服端程式再彈出詢問訊息，依用戶端回應再接續執行」使用 PageCommand 來改寫，它的程式流程如下。&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;FONT color=#800000&gt;請假單儲存(伺服端) -&amp;gt; 彈出詢問訊息(用戶端) -&amp;gt; 取得用戶端回應決定是否送審(伺服端)&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;第一個階段處理「請假單儲存(伺服端) -&amp;gt; 彈出詢問訊息(用戶端) 」，在下面的程式碼中，訊問訊息的回應結果無論是「確定」或「取消」都會引發 PageCommand 事件，CommandName 都是 "SaveConfirm"，差別是 CommandArgument 的值不同。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;    Protected Sub btnSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSave.Click
        Dim sTrueScript As String
        Dim sFalseScript As String

        '執行請假單儲存的程式碼

        '儲存完成彈出詢問訊息
        sTrueScript = Me.BeeScript.GetPageCommandEventReference("SaveConfirm", "True")
        sFalseScript = Me.BeeScript.GetPageCommandEventReference("SaveConfirm", "False")
        Me.BeeScript.Confirm("假單要送審嗎?", sTrueScript, sFalseScript)
    End Sub&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;第二階段在 PageCommand 事件處理用戶端詢問訊息的回應「取得用戶端回應決定是否送審(伺服端)」，在 PageCommand 事件中，我們只需判斷 e.CommandName 及 e.CommandArgument 來決定接續的執行動作即可。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;    Protected Sub Page_PageCommand(ByVal sender As Object, ByVal e As Bee.Web.TBBasePage.PageCommandEventArgs) Handles Me.PageCommand
        If String.Equals(e.CommandName, "SaveConfirm", StringComparison.CurrentCultureIgnoreCase) Then
            If String.Equals(e.CommandArgument, "True", StringComparison.CurrentCultureIgnoreCase) Then
                '按了確定要執行的程式碼
            Else
                '按了取消要執行的程式碼
            End If
        End If
    End Sub&lt;/TEXTAREA&gt;&lt;/P&gt;&lt;img src ="http://blog.blueshop.com.tw/jeff377/aggbug/56861.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>jeff</dc:creator><title>十幾行程式碼搞定 Master-Detail GridView(內含子 GridView)</title><link>http://blog.blueshop.com.tw/jeff377/archive/2008/06/21/55400.aspx</link><pubDate>Sat, 21 Jun 2008 12:32:00 GMT</pubDate><guid>http://blog.blueshop.com.tw/jeff377/archive/2008/06/21/55400.aspx</guid><wfw:comment>http://blog.blueshop.com.tw/jeff377/comments/55400.aspx</wfw:comment><comments>http://blog.blueshop.com.tw/jeff377/archive/2008/06/21/55400.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://blog.blueshop.com.tw/jeff377/comments/commentRss/55400.aspx</wfw:commentRss><trackback:ping>http://blog.blueshop.com.tw/jeff377/services/trackbacks/55400.aspx</trackback:ping><description>&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;摘要&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;在網路上看到很多 GridView 內含子 GridView 的範例，雖然方法不同不過程式碼都有點小複雜，想說難道沒有更簡單易懂的方法嗎？ &lt;BR&gt;在此文章中將用十幾行的程式碼，就教你快速學會並搞定這種 Master-Detail GridView 的需求。&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;程式碼實作&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;我們以 Northwind 資料庫為例，首先在頁面上放置二組 GridView+SqlDataSoruce，分別繫結至 [Orders] 、[Order Details] 這二個資料表。&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/MasterDetailGridViewGridView_1252/image_thumb.png"&gt;&lt;/P&gt;
&lt;P&gt;我們先來看一下它的程式碼及執行結果，後續再做進一步的詳細說明。 &lt;BR&gt;我們只要在 GridView 的 RowDataBound 事件撰寫下面十幾行的程式碼就完成了，不要懷疑這已經是全部的程式碼。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;    Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
        If (e.Row.RowState And DataControlRowState.Selected) = DataControlRowState.Selected Then
            Dim oRow As New GridViewRow(0, -1, DataControlRowType.DataRow, e.Row.RowType)
            GridView2.Visible = True
            GridView2.DataBind()
            Dim sHTML As String = Bee.Web.WebFunc.ControlToHTML(GridView2)
            GridView2.Visible = False
            Dim oCell As New TableCell
            oCell.Text = sHTML
            oCell.ColumnSpan = e.Row.Cells.Count
            oRow.Cells.Add(oCell)
            e.Row.Parent.Controls.AddAt(e.Row.RowIndex + 2, oRow)
        End If
    End Sub&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;執行程式，選取 GridView 的某一資料列，就會展開其子 GridView 。執行換頁動作，一樣可以正常展開子 GridView。&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/MasterDetailGridViewGridView_1252/image_thumb_2.png"&gt;&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/MasterDetailGridViewGridView_1252/image_thumb_3.png"&gt;&lt;/P&gt;
&lt;P&gt;接下來說明這個範例的一些細節，Master GridView 繫結 [Orders] 資料表，設定 DataKeyNames="OrderID"，當選取某筆資料時，GridView 的 SelectedValue 就是該筆資料的 OrderID 欄位值。另外設定 GridView 的 EnableViewState="False"，這個設定主要是讓 GridView 選取時都會重新做 DataBind 的動作。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=xml name=code rows=6 cols=50&gt;        &amp;lt;asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="4"
            DataKeyNames="OrderID" DataSourceID="SqlDataSource1" EmptyDataText="沒有資料錄可顯示。"
            ForeColor="#333333" GridLines="None" AllowPaging="True" EnableViewState="False"&amp;gt;&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;再來就是 Detail GridView 繫結 [Order Details] 資料表，一樣設定 GridView 的 EnableViewState="False"，且設定 Visible="False"，也就是初始狀態 Detail GridView 是隱藏的。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=xml name=code rows=6 cols=50&gt;        &amp;lt;asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False" BackColor="LightGoldenrodYellow"
            BorderColor="Tan" BorderWidth="1px" CellPadding="2" DataKeyNames="OrderID,ProductID"
            DataSourceID="SqlDataSource2" EmptyDataText="沒有資料錄可顯示。" ForeColor="Black" GridLines="None" Visible="False" EnableViewState="False"&amp;gt;&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;Detail GridView 所繫結的 SqlDataSource 控制項，將其 SelectParameters 的 @OrderID 參數關連至 Master GridView 的 SelectedValue。&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/MasterDetailGridViewGridView_1252/image_thumb_4.png"&gt;&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=xml name=code rows=6 cols=50&gt;            &amp;lt;SelectParameters&amp;gt;
                &amp;lt;asp:ControlParameter ControlID="GridView1" Name="OrderID" PropertyName="SelectedValue"
                    Type="Int32" /&amp;gt;
            &amp;lt;/SelectParameters&amp;gt;&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;再來還有一個重要步驟，就是在 aspx 程式碼中，要設定 Page 的 EnableEventValidation="false"，因為我們動態將 Detail GridView Render 出來插入 Master GridView 中，整個控制項階層都被異動，這樣會造成事件驗證失敗。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=xml name=code rows=6 cols=50&gt;&amp;lt;%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default5.aspx.vb" Inherits="Default5" EnableEventValidation="false" %&amp;gt;&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;另外在 Master GridView 的 RowDataBound 事件中，有一個 ControlToHTML 方法是取得 Detail GridView 的 HTML 碼，這個方法可以參考筆者另一篇「&lt;A href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/16/4006.aspx"&gt;&lt;FONT color=#669966&gt;使用 BasePage 來解決 GridView 執行 RenderControl 產生的錯誤&lt;/FONT&gt;&lt;/A&gt;」文章有詳細說明。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;Dim sHTML As String = Bee.Web.WebFunc.ControlToHTML(GridView2)&lt;/TEXTAREA&gt;&lt;/P&gt;&lt;img src ="http://blog.blueshop.com.tw/jeff377/aggbug/55400.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>jeff</dc:creator><title>利用 BasePage 實作表單權限控管</title><link>http://blog.blueshop.com.tw/jeff377/archive/2008/06/19/55346.aspx</link><pubDate>Thu, 19 Jun 2008 20:48:00 GMT</pubDate><guid>http://blog.blueshop.com.tw/jeff377/archive/2008/06/19/55346.aspx</guid><wfw:comment>http://blog.blueshop.com.tw/jeff377/comments/55346.aspx</wfw:comment><comments>http://blog.blueshop.com.tw/jeff377/archive/2008/06/19/55346.aspx#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://blog.blueshop.com.tw/jeff377/comments/commentRss/55346.aspx</wfw:commentRss><trackback:ping>http://blog.blueshop.com.tw/jeff377/services/trackbacks/55346.aspx</trackback:ping><description>&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;摘要&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;若表單(頁面)具有「執行、新增、修改、刪除」等操作權限控管，可以使用列舉來描述使用者在該表單的權限，詳細的作法可以參考下面的「&lt;A href="http://www.dotblogs.com.tw/chhuang/archive/2008/04/26/3514.aspx"&gt;&lt;FONT color=#669966&gt;Enum 的設計與應用 - 簡易權限設計&lt;/FONT&gt;&lt;/A&gt;」這篇文章。在此我們將利用這種列舉的方式來描述表單權限，並由 BasePage 來處理表單權限的控管。&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;程式說明及實作&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;首先定義 EFormActions 列舉，來描述表單操作權限。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;    ''' &amp;lt;summary&amp;gt;
    ''' 表單操作權限列舉。
    ''' &amp;lt;/summary&amp;gt;
    &amp;lt;Flags()&amp;gt; _
    Public Enum EFormActions
        ''' &amp;lt;summary&amp;gt;
        ''' 無。
        ''' &amp;lt;/summary&amp;gt;
        None = 0
        ''' &amp;lt;summary&amp;gt;
        ''' 執行。
        ''' &amp;lt;/summary&amp;gt;
        Execute = 1
        ''' &amp;lt;summary&amp;gt;
        ''' 新增。
        ''' &amp;lt;/summary&amp;gt;
        Add = 2
        ''' &amp;lt;summary&amp;gt;
        ''' 修改。
        ''' &amp;lt;/summary&amp;gt;
        Update = 4
        ''' &amp;lt;summary&amp;gt;
        ''' 刪除。
        ''' &amp;lt;/summary&amp;gt;
        Delete = 8
        ''' &amp;lt;summary&amp;gt;
        ''' 全部。
        ''' &amp;lt;/summary&amp;gt;
        All = Execute Or Add Or Update Or Delete
    End Enum&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;新增一個 TBActionPage 類別，繼承至 System.Web.UI.Page 或自訂的底層 BaseForm，做為權限控管的 BaseForm。首先新增一個 UserActions 屬性，來描述使用者的表單操作權限，在 OnInitComplete 方法中，第一次載入此表單時取得使用者的表單權限，即設定 UserActions 屬性，此屬性會儲存至 ViewState 中，使下次頁面 PostBack 時無需再重新取得表單權限。另外新增 CheckUserActions 方法，供檢查使用者是否具有指定的表單操作權限。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;Public Class TBActionPage
    Inherits System.Web.UI.Page
    ''' &amp;lt;summary&amp;gt;
    ''' 使用者權限。
    ''' &amp;lt;/summary&amp;gt;
    Private FUserActions As EFormActions = EFormActions.None

#Region " ViewState 狀態管理　"

    ''' &amp;lt;summary&amp;gt;
    ''' 由 ViewState 還原控制項的狀態。
    ''' &amp;lt;/summary&amp;gt;
    ''' &amp;lt;param name="savedState"&amp;gt;要還原的控制項狀態。&amp;lt;/param&amp;gt;
    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
                FUserActions = CType(myState(1), EFormActions)
            End If
        End If
    End Sub

    ''' &amp;lt;summary&amp;gt;
    ''' 控制項的狀態儲存至 ViewState。
    ''' &amp;lt;/summary&amp;gt;
    ''' &amp;lt;returns&amp;gt;含有控制項之目前檢視狀態的物件。&amp;lt;/returns&amp;gt;
    Protected Overrides Function SaveViewState() As Object
        Dim baseState As Object = MyBase.SaveViewState()
        Dim myState(1) As Object
        myState(0) = baseState
        myState(1) = FUserActions
        Return myState
    End Function

#End Region

    ''' &amp;lt;summary&amp;gt;
    ''' 覆寫。引發 InitComplete 事件。
    ''' &amp;lt;/summary&amp;gt;
    Protected Overrides Sub OnInitComplete(ByVal e As System.EventArgs)
        If Not Me.IsPostBack Then
            '取得使用者的表單權限
            '測試用：設定使用者具「執行/新增/修改」權限，實際應呼叫中間層取得使用者的此表單權限
            Me.UserActions = EFormActions.Execute Or EFormActions.Add Or EFormActions.Update

            '第一次載入頁面時檢查執行權限
            If Me.UserActions = EFormActions.None Then
                Throw New Exception("沒有此表單執行權限")
            End If
        End If

        MyBase.OnInitComplete(e)
    End Sub

    ''' &amp;lt;summary&amp;gt;
    ''' 使用者的表單操作權限。
    ''' &amp;lt;/summary&amp;gt;
    Public Property UserActions() As EFormActions
        Get
            Return FUserActions
        End Get
        Set(ByVal value As EFormActions)
            FUserActions = value
        End Set
    End Property

    ''' &amp;lt;summary&amp;gt;
    ''' 檢查使用者是否具有指定的表單操作權限。
    ''' &amp;lt;/summary&amp;gt;
    ''' &amp;lt;param name="Actions"&amp;gt;表單操作權限&amp;lt;/param&amp;gt;
    Public Function CheckUserActions(ByVal Actions As EFormActions) As Boolean
        If (Me.UserActions And Actions) = Actions Then
            Return True
        Else
            Return False
        End If
    End Function

End Class&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;測試程式&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;為測試使用，在上述的 TBActionPage 的 OnInitComplete 方法，直接設定具有「執行/新增/修改」權限；正確的作法應該是呼叫中間層傳入該表單的識別碼來取得使用者在該表單具有那些權限，並在此設定 UserActions 屬性值。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;Me.UserActions = EFormActions.Execute Or EFormActions.Add Or EFormActions.Update&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;新增一個測試頁面，繼承至 TBActionPage，當按下按鈕時利用 CheckUserActions 使用者具那些權限並輸出。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;Imports Bee.Web.WebCommon

Partial Class _Default
    Inherits Bee.Web.TBActionPage

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Response.Write("執行 -&amp;gt; " &amp;amp; IIf(Me.CheckUserActions(EFormActions.Execute), "Y", "N"))
        Me.Response.Write("&amp;lt; br/ &amp;gt;")
        Me.Response.Write("新增 -&amp;gt; " &amp;amp; IIf(Me.CheckUserActions(EFormActions.Add), "Y", "N"))
        Me.Response.Write("&amp;lt; br/ &amp;gt;")
        Me.Response.Write("修改 -&amp;gt; " &amp;amp; IIf(Me.CheckUserActions(EFormActions.Update), "Y", "N"))
        Me.Response.Write("&amp;lt; br/ &amp;gt;")
        Me.Response.Write("刪除 -&amp;gt; " &amp;amp; IIf(Me.CheckUserActions(EFormActions.Delete), "Y", "N"))
    End Sub
End Class&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;執行結果如下&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/BasePage_13E30/image_thumb.png"&gt;&lt;/P&gt;&lt;img src ="http://blog.blueshop.com.tw/jeff377/aggbug/55346.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>jeff</dc:creator><title>ObjectDataSoruce 繫結 BusinessObject 控制項</title><link>http://blog.blueshop.com.tw/jeff377/archive/2008/06/19/55345.aspx</link><pubDate>Thu, 19 Jun 2008 20:43:00 GMT</pubDate><guid>http://blog.blueshop.com.tw/jeff377/archive/2008/06/19/55345.aspx</guid><wfw:comment>http://blog.blueshop.com.tw/jeff377/comments/55345.aspx</wfw:comment><comments>http://blog.blueshop.com.tw/jeff377/archive/2008/06/19/55345.aspx#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://blog.blueshop.com.tw/jeff377/comments/commentRss/55345.aspx</wfw:commentRss><trackback:ping>http://blog.blueshop.com.tw/jeff377/services/trackbacks/55345.aspx</trackback:ping><description>&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;摘要&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;在 ASP.NET 中，ObjectDataSoruce 控制項是實現三層式的重要關鍵，我們可以透過 ObjectDataSoruce 控制項使用的自訂中間層商務物件。不過一般找到的範例都是直接繫結中間層商務物件，雖然範例通常寫得相當符合物件導向，可是在實際運用上有下列幾個問題。&lt;/P&gt;
&lt;P&gt;問題一：維護性不佳&lt;/P&gt;
&lt;P&gt;例如 Employee 商務物件的 Update 方法，可能有下列二種寫法&lt;/P&gt;
&lt;P&gt;[寫法一] Update 方法中，每個欄位皆為引數&lt;/P&gt;
&lt;P&gt;Public Function UpdateEmployee(EmployeeID As Integer, LastName As String, FirstName As String, _ &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Address As String, City As String, Region As String, _ &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PostalCode As String) As Integer&lt;/P&gt;
&lt;P&gt;參考：&lt;A title=http://msdn.microsoft.com/zh-tw/library/ms178538(VS.80).aspx href="http://msdn.microsoft.com/zh-tw/library/ms178538(VS.80).aspx"&gt;&lt;FONT color=#669966&gt;http://msdn.microsoft.com/zh-tw/library/ms178538(VS.80).aspx&lt;/FONT&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#669966&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;[寫法二] Update 方法使用強型別，每個欄位對應到類別的屬性&lt;/P&gt;
&lt;P&gt;Public Function UpdateEmployee(employee As NorthwindEmployee) As Integer&lt;/P&gt;
&lt;P&gt;參考：&lt;A title=http://msdn.microsoft.com/zh-tw/library/ms227562(VS.80).aspx href="http://msdn.microsoft.com/zh-tw/library/ms227562(VS.80).aspx"&gt;&lt;FONT color=#669966&gt;http://msdn.microsoft.com/zh-tw/library/ms227562(VS.80).aspx&lt;/FONT&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#669966&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;可是實際上真能這樣用嗎？以[方法一]為例，若異動欄位非常多，Update 方法的引數不就多到嚇死人；[方法二]使用強型別可以解決欄位多的問題，不過欄位增減時，都需要同時維護 NorthwindEmployee 類別不是很麻煩嗎？當你的系統非常龐大時，例如有上千個表單，當這些表單若需同時新增一個欄位時，那就需要同時更新上千個對應的類別，可能會搞到你瘋掉。&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;問題二：批次異動問題&lt;/P&gt;
&lt;P&gt;當 GridView 繫結 ObjectDataSource 在編輯一筆記錄時儲存時，有時我們並不希望該筆資料馬上異動回資料庫，也就是執行 ObjectDataSoruce 的 Update 方法，但資料不要真正寫回資料庫；而是所有編輯的資料先被暫存，最後執行一個方法(例如 Save)，才將這些多筆資料的異動同時寫回資料。而 ObjectDataSource 在處理這部分的問題，也很難完成。&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;問題三：Mater-Detail 問題&lt;/P&gt;
&lt;P&gt;有些表單具 Mater-Detail 的關係(如訂單)，可是 ObjectDataSource 很難描述這種關係，因為 ObjectDataSource 的角色相當於 DataTable，而 Mater-Detal 是多個 DataTable 的集合，相當於 DataSet。也就是說需要有多個 ObjectDataSource 才能描述 Master-Detal 的關係，而這些 ObjectDataSource 的異動不能馬上寫回資料庫，表單的編輯動作都需要被暫存，當按下儲存時才能將整個 DataSet 的異動同時寫回資料庫。&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff size=3&gt;&lt;STRONG&gt;TBBusinessObject 及 TBObjectDataSource 控制項&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;針對以上的問題，在此使用另一種方式來處理，讓 ObjectDataSource 繫結 BusinessObject 控制項，而 BusinessObject 控制項提供 DataSet 屬性，來暫存 ObjectDataSource 產生的異動。而呼叫真正的中間層商務物件時，再將 BusinessObject 控制項的 DataSet 屬性傳入，由中間層商務物件去異動資料庫。&lt;/P&gt;
&lt;P&gt;以下範例以 GridView 繫結 ObjectDataSource，當 GridView 進行修改、刪除的動作時，會透過 ObjectDataSource 來呼叫 BusinessObject 控制項來進行異動，而這此異動並不會被寫回資料庫，而是異動 BusinessObject 控制項的 DataSet 屬性。&lt;/P&gt;
&lt;P&gt;一般的作法為&lt;/P&gt;
&lt;P&gt;GridView --&amp;gt; ObjectDataSoruce --&amp;gt; 中間層 BusinessObject&lt;/P&gt;
&lt;P&gt;而這邊的作法為&lt;/P&gt;
&lt;P&gt;GridView --&amp;gt; ObjectDataSoruce --&amp;gt; BusinessObject 控制項 --&amp;gt; 中間層 BusinessObject&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/ObjectDataSoruceBusinessObject_A575/image_2.png"&gt;&lt;/P&gt;
&lt;P&gt;在頁面上放置 TBBusinessObject 控制項及 TBObjectDataSource 控制項，在 TBObjectDataSource 控制項設定 BusinessObjectID="TBBusinessObject1" 繫結至 TBBusinessObject 控制項。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=xml name=code rows=6 cols=50&gt;            &amp;lt;bee:TBBusinessObject ID="TBBusinessObject1" runat="server"&amp;gt;
            &amp;lt;/bee:TBBusinessObject&amp;gt;
            &amp;lt;bee:TBObjectDataSource ID="TBObjectDataSource1" runat="server" 
                OldValuesParameterFormatString="original_{0}" RowIndex="-1"
                TypeName="Bee.Web.WebControls.TBBusinessObject"
                BusinessObjectID="TBBusinessObject1"
                TableName="Employee"&amp;gt;
            &amp;lt;/bee:TBObjectDataSource&amp;gt;&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;當 Page Load 時產生的一個 Employee 的 DataTable，並將 DataTable 加入 TBBusinessObject.DataSet.Tables。在 GridView 執行 Update 及 Delete 時，要去設定 TBObjectDataSource 的 RowIndex，使用可以異動到對應的 DataRow。&lt;/P&gt;
&lt;P&gt;&lt;TEXTAREA class=vb name=code rows=6 cols=50&gt;Partial Class _Default
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not Me.IsPostBack Then
            Dim oTable As Data.DataTable
            Dim oColumn As Data.DataColumn
            Dim oRow As Data.DataRow

            oTable = New Data.DataTable("Employee")

            oColumn = New Data.DataColumn("ID", GetType(String))
            oTable.Columns.Add(oColumn)
            oColumn = New Data.DataColumn("Name", GetType(String))
            oTable.Columns.Add(oColumn)
            oColumn = New Data.DataColumn("Tel", GetType(String))
            oTable.Columns.Add(oColumn)

            oRow = oTable.NewRow()
            oRow("ID") = "001"
            oRow("Name") = "張三"
            oRow("Tel") = "02-11111111"
            oTable.Rows.Add(oRow)

            oRow = oTable.NewRow()
            oRow("ID") = "002"
            oRow("Name") = "李四"
            oRow("Tel") = "02-22222222"
            oTable.Rows.Add(oRow)

            oRow = oTable.NewRow()
            oRow("ID") = "003"
            oRow("Name") = "王五"
            oRow("Tel") = "02-33333333"
            oTable.Rows.Add(oRow)

            TBBusinessObject1.DataSet.Tables.Add(oTable)
            GridView1.DataBind()
        End If
    End Sub

    Protected Sub GridView1_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView1.RowCommand
        Select Case e.CommandName
            Case "Update"
                TBObjectDataSource1.RowIndex = CInt(e.CommandArgument)
            Case "Delete"
                TBObjectDataSource1.RowIndex = CInt(e.CommandArgument)
        End Select
    End Sub
End Class&lt;/TEXTAREA&gt;&lt;/P&gt;
&lt;P&gt;執行程式，GridView 會透過 TBObjectDataSource 的來取得 TBBusinessObject 中的 Employee DataTable 並顯示在 GridView 中。&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/ObjectDataSoruceBusinessObject_A575/image_6.png"&gt;&lt;/P&gt;按下編輯鈕，針對資料列進行編輯
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/ObjectDataSoruceBusinessObject_A575/image_thumb_3.png"&gt;&lt;/P&gt;
&lt;P&gt;按下更新鈕，異動就會被寫回 TBBusinessObject 中的 Employee DataTable。&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/ObjectDataSoruceBusinessObject_A575/image_thumb_4.png"&gt;&lt;/P&gt;
&lt;P&gt;當按下刪除鈕時，TBBusinessObject 中的 Employee DataTable 的該筆 DataRow 也會被刪除。&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://files.dotblogs.com.tw/jeff377/0806/ObjectDataSoruceBusinessObject_A575/image_thumb_5.png"&gt;&lt;/P&gt;
&lt;P&gt;範例程式下程：&lt;A href="http://files.dotblogs.com.tw/jeff377/0806/20086613351659.rar"&gt;&lt;FONT color=#669966&gt;BusinessObjectControl.rar &lt;/FONT&gt;&lt;/A&gt;&lt;/P&gt;&lt;img src ="http://blog.blueshop.com.tw/jeff377/aggbug/55345.aspx" width = "1" height = "1" /&gt;</description></item></channel></rss>