緣起
在使用WebUserControl的時候,如果一個畫面有數個WebUserControl的話,他們彼此間要怎麼溝通呢,也許有人說,使用FindControl/Property啊!!的確,小喵以前是這樣處理的,不過小喵卻發現這會讓WebUserControl在程式撰寫上變得複雜,既然要寫成WebUserControl,就是要把這些東西抽出來獨立處理/重複使用。但是如果寫的過程讓他與別的東西關係太密切,那就失去了抽出獨立處理/重複使用的目的。
再一次偶然的機會與熱心的Allen大聊到了物件(物件小喵一直都還在學),Allen大十分熱心的提示小喵可以用Interface來解決這類的問題。後來小喵在Allen大的文章也找到相關的範例與影片教學。不過範例是C#的,小喵用VB.NET來說明一次。
兩種模式
這邊會介紹兩種方式,都是透過Interface來處理:
- 訂閱者模式:定義發行者(Publisher)與讀者(Reader)這兩種Interface處理
- 事件模式:定義一個Event來丟資料,然後由Reciver來承接事件丟出的資料並且處理
Allen大的範例與教學檔如下,有興趣的人或者是學C#的人可以到Allen大的網站上查看:
範例:MasterPage, Page, UserControl 如何互動, 傳值(事件模式)
教學影片:如何做到 User control 互動-觀察者模式(觀察者模式)
準備WebUserControl畫面
接著小喵分別描述一下使用這兩種方式的範例:兩個範例的動作是一樣的,只是使用不同的方式處理。範例中使用北風資料庫,分別有三個WebUserControl
- 選擇Employee的下拉式選單:wucGetEmployee.ascx
- 顯示某Employee的Orders:wucEmpOrder.ascx
- 顯示某筆Order的Detail資料:wucOdrDetails.ascx
這三個的畫面分別如下
wucGetEmployee.ascx
1 |
<asp:DropDownList ID="ddlEmployee" runat="server" AutoPostBack="True" |
2 |
DataSourceID="sdsEmployeeID" DataTextField="EmpName" |
3 |
DataValueField="EmployeeID"> |
5 |
<asp:SqlDataSource ID="sdsEmployeeID" runat="server" |
6 |
ConnectionString="<%$ ConnectionStrings:NWind %>" |
7 |
SelectCommand="SELECT EmployeeID, CONVERT (varchar(10), EmployeeID) + '-' + LastName AS EmpName FROM Employees ORDER BY EmployeeID"> |
--
wucEmpOrder.ascx
01 |
<asp:GridView ID="gvEmpOrder" SkinID="GV01" runat="server" AllowPaging="True" |
02 |
AutoGenerateColumns="False" DataKeyNames="OrderID" DataSourceID="sdsOrders"> |
04 |
<asp:CommandField ShowSelectButton="True" /> |
05 |
<asp:BoundField DataField="OrderID" HeaderText="OrderID" InsertVisible="False" |
06 |
ReadOnly="True" SortExpression="OrderID" /> |
07 |
<asp:BoundField DataField="CustomerID" HeaderText="CustomerID" |
08 |
SortExpression="CustomerID" /> |
09 |
<asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID" |
10 |
SortExpression="EmployeeID" /> |
11 |
<asp:BoundField DataField="OrderDate" HeaderText="OrderDate" |
12 |
SortExpression="OrderDate" /> |
13 |
<asp:BoundField DataField="Freight" HeaderText="Freight" |
14 |
SortExpression="Freight" /> |
15 |
<asp:BoundField DataField="ShipName" HeaderText="ShipName" |
16 |
SortExpression="ShipName" /> |
17 |
<asp:BoundField DataField="ShipCity" HeaderText="ShipCity" |
18 |
SortExpression="ShipCity" /> |
21 |
<asp:SqlDataSource ID="sdsOrders" runat="server" |
22 |
ConnectionString="<%$ ConnectionStrings:NWind %>" |
23 |
SelectCommand="SELECT [OrderID], [CustomerID], [EmployeeID], [OrderDate], [Freight], [ShipName], [ShipCity] FROM [Orders] WHERE ([EmployeeID] = @EmployeeID)"> |
25 |
<asp:Parameter Name="EmployeeID" Type="Int32" /> |
--
wucOdrDetails.ascx
01 |
<asp:GridView ID="GridView1" SkinID="GV02" runat="server" |
02 |
AutoGenerateColumns="False" DataKeyNames="OrderID,ProductID" |
03 |
DataSourceID="sdsOrderDetails"> |
05 |
<asp:BoundField DataField="OrderID" HeaderText="OrderID" ReadOnly="True" |
06 |
SortExpression="OrderID" /> |
07 |
<asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" |
08 |
SortExpression="ProductID" /> |
09 |
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" |
10 |
SortExpression="UnitPrice" /> |
11 |
<asp:BoundField DataField="Quantity" HeaderText="Quantity" |
12 |
SortExpression="Quantity" /> |
13 |
<asp:BoundField DataField="Discount" HeaderText="Discount" |
14 |
SortExpression="Discount" /> |
17 |
<asp:SqlDataSource ID="sdsOrderDetails" runat="server" |
18 |
ConnectionString="<%$ ConnectionStrings:NWind %>" |
19 |
SelectCommand="SELECT * FROM [Order Details] WHERE ([OrderID] = @OrderID)"> |
21 |
<asp:Parameter Name="OrderID" Type="Int32" /> |
兩種模式會用到的WebUserControl是一樣的,可以複製一份到另一個資料夾,以備等一下使用
接著就要開始來使用,兩種模是從這裡起會有不同的內容,以下分別說明
觀察者模式
首先先幫觀察者模式建立Interface,相關程式碼如下
01 |
Imports Microsoft.VisualBasic |
03 |
Public Interface IOrder '訂閱者 |
05 |
Sub ProcessData(ByVal Data As String) |
08 |
Public Interface IPublisher '發行者 |
10 |
Sub RegOrder(ByVal obj As IOrder) |
接著,一個一個的wuc來處理囉!!
首先是wucGetEmployee.ascx,這個wuc是個發行者。他選了某個Employee後,把EmployeeID發行出去,至於訂閱者收到Employee之後,要做什麼事情,這個跟目前的wucGetEmployee.ascx沒關係,那是別人的事情。
所以我們要在wucGetEmployee.ascx的CodeFile中Implements 這個Interface
1 |
Partial Class tOrderMode_wucGetEmployee |
2 |
Inherits System.Web.UI.UserControl |
此時會在畫面中自動多出一段程式碼,這是配合Implements IPublisher的原因
1 |
Public Sub RegOrder(ByVal obj As IOrder) Implements IPublisher.RegOrder |
接著安排一個本頁的全域變數m_Orders,用這個來記錄有哪些訂閱者,訂閱了這個wuc
1 |
Dim m_Orders As New List(Of IOrder) '建立訂閱者的集合變數 |
再來,我們在剛剛的Regorder中,加上要註冊訂閱者的程式碼
1 |
Public Sub RegOrder(ByVal obj As IOrder) Implements IPublisher.RegOrder |
2 |
m_Orders.Add(obj) '註冊訂閱者 |
另外,這個下拉式選單,結合資料庫後,希望多出一筆請選擇。最後,希望在DropDownList的SelectedIndexChanged時,可以通知每一個訂閱者目前的EmployeeID
01 |
Protected Sub ddlEmployee_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlEmployee.DataBound |
02 |
Me.ddlEmployee.Items.Insert(0, "--請選擇--") |
05 |
Protected Sub ddlEmployee_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)Handles ddlEmployee.SelectedIndexChanged |
06 |
If m_Orders.Count > 0 Then |
07 |
For Each odr In m_Orders |
08 |
odr.ProcessData(Me.ddlEmployee.SelectedValue) |
這樣就完成的這個發行者wucGetEmployee.ascx的部分。
接著,撰寫wucEmpOrder.ascx的部分,對於wucGetEmployee.ascx來說,他是訂閱者(觀察者)所以一開始的時候,先在後端CodeFile加入Implements IOrder,因為Implements了IOrder,畫面會順便帶出ProcessData的部分
3 |
Public Sub ProcessData(ByVal Data As String) Implements IOrder.ProcessData |
我們希望他訂閱到資料後,將得到的EmployeeID給畫面中的SqlDataSouce於是程式碼
1 |
Public Sub ProcessData(ByVal Data As String) Implements IOrder.ProcessData |
2 |
'將訂閱到的資料給SqlDataSouce的SelectParameters("EmployeeID") |
3 |
Me.sdsOrders.SelectParameters("EmployeeID").DefaultValue = Data |
事實上,wucEmpOrder.ascx他不只是個訂閱者,對OrderDetail來說他也是個發行者,希望選擇GridView裡面的Order之後,就顯是相對應的OrderDetail
過程與上面相似,就不贅述...詳細程式內容請參考本文最後面的範例檔。
最後把這兩個wuc放到畫面後,還需要處理一些些程式。
我們先把這三個wuc拉到畫面中,畫面如下
1 |
<uc1:wucGetEmployee ID="wucGetEmployee1" runat="server" /> |
2 |
<uc2:wucEmpOrder ID="wucEmpOrder1" runat="server" /> |
3 |
<uc3:wucOdrDetails ID="wucOdrDetails1" runat="server" /> |
註冊上去後,他們彼此的關係還沒有設定,因此我們在PageLoad事件中,直行發行者的RegOrder來註冊訂閱者
1 |
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load |
2 |
Me.wucGetEmployee1.RegOrder(Me.wucEmpOrder1) |
3 |
Me.wucEmpOrder1.RegOrder(Me.wucOdrDetails1) |
這樣在PageLoad的時候,wucGetEmployee1知道了wucEmpOrder1訂閱了他,wucEmpOrder1也知道了wucOdrDetails1訂閱了他。
事件模式
上面說明了觀察者模式後,接著說明事件模式。這兩個模是要做的事情是一樣的,不過裡面運作的概念上卻不太相同。這裡改以觸發事件的方式來處理。
一樣的先撰寫需要的Interface,首先建立一個類別,清除類別的程式碼,改成這樣
01 |
Imports Microsoft.VisualBasic |
04 |
Public Delegate Sub SetDataHandler(ByVal sender As Object, ByVal Data As String) |
06 |
Public Interface IEventMode |
08 |
Event SetData As SetDataHandler |
11 |
Public Interface IReciver |
12 |
'設定一個IReciver介面,裡面有UseData的Sub |
13 |
Sub UseData(ByVal Data As String) |
設定好後,接著一樣的,先從源頭的wucGetEmployee.ascx開始撰寫起
一樣的,先幫這個wuc的CodeFile安排Implements IEventMode此時會自動產生一個Event的語法
3 |
Public Event SetData(ByVal sender As Object, ByVal Data As String) Implements IEventMode.SetData |
接著,在DorpDownList的SelectedIndexChanged事件中,要去觸發這個事件,並且將目前的SelectedValue透過事件傳出去
1 |
Protected Sub ddlEmployee_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)Handles ddlEmployee.SelectedIndexChanged |
2 |
RaiseEvent SetData(Me.ddlEmployee, Me.ddlEmployee.SelectedValue) |
接著安排wucEmpOrder.ascx這個,對於這個而言,當wucGetEmployee.ascx觸發了事件後,他要透過Reciver的UseData把事件中的值給予SqlDataSouce,一樣的要先Implements IReciver介面
3 |
Public Sub UseData(ByVal Data As String) Implements IReciver.UseData |
4 |
Me.sdsOrders.SelectParameters("EmployeeID").DefaultValue = Data |
而對於此wuc他不只是Reciver也是EventMode因此也要Implements IEventMode介面,這部分與上面一樣,就不贅述了。
最後要把三個wuc放在畫面上,並且在aspx的CodeFile要處理一下,當三個wuc放到畫面後,畫面並不知道事件觸發應該要影響其他哪些wuc,因此要撰寫SetOrderData與SetOrderDetail這兩個Sub來通知Reciver開始使用事件發生的資料相關的程式碼如下(這部分與C#的程式碼差異度較大,如果是C#的使用者請參考Allen大的範例)
01 |
Protected Overrides Sub OnInit(ByVal e As System.EventArgs) |
03 |
AddHandler Me.wucGetEmployee1.SetData, AddressOf SetOrderData |
04 |
AddHandler Me.wucEmpOrder1.SetData1, AddressOf SetOrderDetailData |
08 |
Public Sub SetOrderData(ByVal sender As Object, ByVal Data As String) |
10 |
Me.wucEmpOrder1.UseData(Data) |
11 |
Me.wucOdrDetails1.UseData("") |
14 |
Public Sub SetOrderDetailData(ByVal sender As Object, ByVal Data As String) |
15 |
Me.wucOdrDetails1.UseData(Data) |
最後小喵將相關程式碼壓縮在此
請下載參考
http://vip2.blueshop.com.tw/topcat/DEMO/UC2UC/UC2UC.zip
u9oMDYHi奢侈品仿牌,保固說到做到,誠信經營,,四季經典精品來襲 時尚優雅 品味和思想上更為優雅簡約,詳詢訂購加賴ID:kk2023 感恩喔,貨到付款,黑貓宅配。圖片下面不回復留言喔。 p0qPulB4Z