[ASP.NET]在GridView中的wuc(WebUserControl)透過裡面的按鈕事件取得所在Row的相關資料

[ASP.NET]在GridView中的wuc(WebUserControl)透過裡面的按鈕事件取得所在Row的相關資料

緣起

會有這篇是小喵撰寫類似討論區的功能,在討論與回應裡面,每一篇討論/回應上面會有一排工具按鈕,而按鈕會依照這篇討論/回應的【回應者、討論/回應、目前瀏覽者的身分(站長、版主、討論發起者、回應的作者、...】等。會決定這一排按鈕那些可以看到,那些看不到,然後相同的按鈕因為身分可能功能會有點差異。

一開始小喵用Property來傳值,那麼個按鈕可以依照傳入Property來設定他的顯示、隱藏等初始狀態,接著開始撰寫這些按鈕按下後,要進行的動作。

這時發現這些傳入的值在按鈕按下時,去取private的傳入值是無法取得的,原因也很簡單,Web是沒有狀態的(後來想到解決方法也很簡單,小喵也用這方法做了,請容後補充)

這時小喵開始想,有沒有可能按下按鈕的同時,去取得該wuc所在Row的一些Key值來做事情呢??於是開始先測試單純Page與wuc的時候是否可行,測試結果是OK的(這一篇:[ASP.NET]WebUserControl裡的Button觸發事件,透過Interface取得使用Page內的資料)。那麼在ListView / GridView呢??

 

範例說明

小喵這一篇範例,用北風資料庫的Region資料表來當作練習,用GridView顯示Region裡面的所有資料,然後安排一個Templete Field裡面放著小喵寫的WebUserControl(以下簡稱wuc),wuc裡面放兩個label與一個button,希望這個按鈕按下後,能夠觸發wuc的事件,然後在Page中去取得該Row的資料,再傳回wuc並放在兩個Label上

準備傳遞的物件類別

 

 

為了在wuc與page在運作時能夠減低耦合力(wuc的設計盡量獨立,不必去知道Page用了哪些控制項,而Page也不必知道wuc裡面有哪些控制項要處理得到的資料),因此小喵特別為了傳遞資料,寫了一個類別物件,放在App_Code中,到時候傳遞參數的時候,直接傳遞這個類別物件。這樣未來如果有必要在別的地方要用這個wuc必須多傳參數時(假設本來傳兩個,新的要多傳一個),可以只改wuc與這個類別物件,本來寫好的Page不須傳第三個參數的頁面不必改寫。

這個類別的內容如下:

Public Class MyRegionObj
    Private m_RegionID As Decimal
    Private m_RegionDescription As String

    Public Property RegionID() As Decimal
        Get
            Return m_RegionID
        End Get
        Set(ByVal value As Decimal)
            m_RegionID = value
        End Set
    End Property

    Public Property RegionDescription() As String
        Get
            Return m_RegionDescription
        End Get
        Set(ByVal value As String)
            m_RegionDescription = value
        End Set
    End Property
End Class

 

準備Interface

接著寫個Interface,這個用來定義未來這個wuc的事件介面

'宣告事件的Handler,注意第二個參數透過ByReference的方式
'透過ByReference可以傳遞回來處理
Public Delegate Sub Region_GetDataHandler(ByVal sender As Object, ByRef myRegion As MyRegionObj)

Public Interface IEvntMode001
    Event GetData As Region_GetDataHandler
End Interface

準備wuc

畫面上準備兩個Lable用來顯示接回來的資料,並且安排一個按鈕,到時候這個按鈕按下後,觸發使用這個wuc的頁面去取得資料。

wucEvent001.ascx

<hr />
Region:<asp:Label ID="lblRegion" runat="server" Text=""></asp:Label><br />
RegionDescription:<asp:Label ID="lblRegionDescription" runat="server" Text=""></asp:Label><br />
<asp:Button ID="btnGet" runat="server" Text="取得資料" />

wucEvent001.ascx.vb

Partial Class wucEvent001
    Inherits System.Web.UI.UserControl

    'Implement介面
    Implements IEvntMode001

    '宣告要傳遞的物件類別
    Private myRegion As MyRegionObj

    Public Event GetData(ByVal sender As Object, ByRef myRegion As MyRegionObj) Implements IEvntMode001.GetData

    Protected Sub btnGet_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnGet.Click
        '觸發事件,傳送wuc本身,以及myRegion這個物件類別
        '其中myRegion是透過ByReference的方式傳遞,到時候也透過這個把資料傳回
        RaiseEvent GetData(Me, myRegion)

        '如果運作後傳遞回來的物件類別有執行個體(不是Notiong)
        '就可以把物件類別中的資料放到Label
        If myRegion IsNot Nothing Then
            Me.lblRegion.Text = myRegion.RegionID.ToString
            Me.lblRegionDescription.Text = myRegion.RegionDescription
        End If
    End Sub
End Class

 

畫面安排

畫面中安排一個GridView與SqlDataSource,資料來源為北風資料庫裏面的Region資料表

在GridView安排一個Templete Fields,裡面放了wuc,為了方便取值,也順便放了兩個hidden來存放RegionID, RegionDescription,這樣到時候就可以直接從這兩個HiddenField取得資料

其中wuc裡面我們因為做了一個GetData的事件,所以在wuc會有個OnGetData的事件在,我們指向一個我們自己的Sub去運作

Default.aspx

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
    DataKeyNames="RegionID" DataSourceID="SqlDataSource1" EmptyDataText="沒有資料錄可顯示。">
    <Columns>
        <asp:BoundField DataField="RegionID" HeaderText="RegionID" ReadOnly="True"
            SortExpression="RegionID" />
        <asp:BoundField DataField="RegionDescription" HeaderText="RegionDescription"
            SortExpression="RegionDescription" />
        <asp:TemplateField>
            <ItemTemplate>
                <asp:HiddenField ID="hidRegionID" runat="server" Value='<%#Eval("RegionID") %>' />
                <asp:HiddenField ID="hidRegionDescription" runat="server" Value='<%#Eval("RegionDescription") %>' />
                <uc1:wucEvent001 ID="wucEvent0011" runat="server" OnGetData="GetData" />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
    ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString1 %>"
    ProviderName="<%$ ConnectionStrings:NorthwindConnectionString1.ProviderName %>"
    SelectCommand="SELECT [RegionID], [RegionDescription] FROM [Region]"
    >
</asp:SqlDataSource>

畫面CodeFile

Default.aspx.vb

'自訂的Sub,用來配合wuc的OnGetData事件
Protected Sub GetData(ByVal sender As Object, ByRef myRegion As MyRegionObj)
    '定義一個wuc1變數,是wucEvent001的類型,並且承接sender
    Dim wuc1 As wucEvent001 = CType(sender, wucEvent001)

    '透過wuc1的parent取得該wuc的Cell
    '再透過FindControl,去找同一層的HiddenFiled
    Dim hidRegionID As HiddenField = CType(wuc1.Parent.FindControl("hidRegionID"), HiddenField)
    Dim hidRegionDescription As HiddenField = CType(wuc1.Parent.FindControl("hidRegionDescription"), HiddenField)

    If Not (hidRegionID Is Nothing) Then
        '如果有取得HiddenField物件
        Dim RegionID As Decimal = CType(hidRegionID.Value, Decimal)
        Dim RegionDescription As String = hidRegionDescription.Value
        '如果傳入的是沒有執行個體(理論上應該是沒有)
        If myRegion Is Nothing Then
            '產生執行個體
            myRegion = New MyRegionObj
        End If
        '將資料設定入物件類別
        myRegion.RegionID = RegionID
        myRegion.RegionDescription = RegionDescription
    End If
End Sub

 

 

 

 

 

裡面沒有寫道怎麼傳回,不過還記得嗎,我們撰寫Call By Reference的方式傳遞參數,所以資料就是靠ByRef的參數myRegion這個物件類別帶回去給wuc。

末記

在緣起裡面提到,這個應用是因為在設計時遇到狀況而想到的一個方式,實際運用上可能還是透過Property屬性傳遞比較直觀,而上面有提到透過Property屬性遇到了無狀態的問題,造成按下按鈕後取不到值的窘境。事實上Property屬性問題解決的方式很簡單,只需要在wuc中放HiddenField物件,在Property的Get時,把資料放到HiddenField中,透過控制項有ViewState機制保留,就輕鬆地解決的這問題。

所以目前這個測試的功能小喵把它紀錄下來,未來如果有機會需要考量不同做法的時候,可以參考這一次的測試。

分享也分享給大家參考看看

^_^

 


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6) 
topcat
Blog:http://www.dotblogs.com.tw/topcat