[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機制保留,就輕鬆地解決的這問題。
所以目前這個測試的功能小喵把它紀錄下來,未來如果有機會需要考量不同做法的時候,可以參考這一次的測試。
分享也分享給大家參考看看
^_^
以下是簽名:
- 歡迎轉貼本站的文章,不過請在貼文主旨上加上【轉貼】,並在文章中附上本篇的超連結與站名【topcat姍舞之間的極度凝聚】,感恩大家的配合。
- 小喵大部分的文章會以小喵熟悉的語言VB.NET撰寫,如果您需要C#的Code,也許您可以試著用線上的工具進行轉換,這裡提供幾個參考
Microsoft MVP Visual Studio and Development Technologies (2005~2019/6) | topcat Blog:http://www.dotblogs.com.tw/topcat |