19.5 连接Web Part
在处理Web Part时,有时需要以某种方式连接它们。“连接Web Part”是指必须把信息(对象)从页面上的一个Web Part传递给另一个Web Part。
例如,把某人在一个文本框中输入的文本值(如邮政编码或名字)传递给页面上的其他Web Part。另一个例子是用一个DropDownList控件指定了系统中所有可用的货币。如果终端用户从下拉列表中选择新的货币值,就会改变该页面上使用这个货币值的所有其他Web Part。需要以这种方式构建页面时,就可以使用这里定义的Web Part连接功能,或者使用ASP.NET 2.0的其他系统,例如,通过Profile类提供的新个性化功能。
在连接Web Part时,要注意这些Web Part彼此交互的特殊规则。首先,如果要连接两个Web Part,其中一个Web Part就必须是提供程序。这个提供程序Web Part是提供其他Web Part需要的信息的组件。需要该信息的Web Part是用户Web Part。Web Part提供程序可以为一个或多个用户Web Part提供信息,而用户Web Part只能连接一个提供程序Web Part,不能把一个用户Web Part连接到多个提供程序Web Part上。如图19-24所示。
在图19-24中,无论用户Web Part位于哪个Web Part区域,都可以将一个提供程序Web Part用于多个用户Web Part。
在使用提供程序Web Part和用户Web Part时,注意,无论Web Part是简单还是复杂,都必须将Web Part封装起来,使之变成定制的Web Part。为此,提供程序Web Part必须提供正确的项或属性,以正确的方式给用户Web Part使用传递过来的值。本章介绍一个连接Web Part的简单示例,尽管完成此任务需要许多步骤,但我们获得的好处更多。

图 19-24
19.5.1 建立提供程序Web Part
要建立可由页面上任意用户Web Part使用的提供程序Web Part,应先建立一个接口,以提供要从一个Web Part传递给另一个Web Part的数据项。
本例假定将一个文本框提供为Web Part,以便让终端用户输入一个值。然后,在页面上另一个Web Part区域的Web Part中,一个日历控件会通过它的Caption属性使用该值。
该接口如程序清单19-15所示。
程序清单19-15 建立一个接口,以提供用于传递给Web Part的属性
VB
Namespace Wrox.ConnectionManagement
Public Interface IStringForCalender
Property CalenderString() As String
End Interface
End Namespace
C#
namespace Wrox.ConnectionManagement
{
public interface IStringForCalender
{
string CalenderString(get; set;)
}
}
在这段代码中,接口IStringForCalender是相当简单的,它只有一个String属性CalenderString。这个接口将由下面的定制提供程序Web Part使用。
建立定制的提供程序Web Part的过程类似于本章前面的其他定制Web Part。唯一的区别是还要提供一些额外的信息,让ASP.NET知道该Web Part要提供什么数据值,以及该值如何提取。定制的提供程序Web Part如程序清单19-16所示。
程序清单19-16 建立定制的提供程序Web Part
VB
Imports Microsoft.VisualBasic
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Namespace Wrox.ConnectionManagement
Public Class TextBoxChanger
Inherits WebPart
Implements IStringForCalendar
Private myTextBox As TextBox
Private _calendarString As String = String.Empty
<Personalizable()> _
Public Property CalendarString()As String Implements _
Wrox.ConnectionManagement.IStringForCalendar.CalendarString
Get
Return _calendarString
End Get
Set(ByVal value As String)
_calendarString= value
End Set
End Property
<ConnectionProvider("Provider for String From TextBox", _
"TextBoxStringProvider")> _
Public Function TextBoxStringProvider() As IStringForCalendar
Return Me
End Function
Protected Overrides Sub CreateChildControls()
Controls.Clear()
myTextBox = New TextBox()
Me.Controls.Add(myTextBox)
Dim myButton As Button = New Button()
myButton.Text = "Change Calendar Caption"
AddHandler myButton.Click, AddressOf Me.myButton_Click
Me.Controls.Add(myButton)
End Sub
Private Sub myButton_Click(ByVal sender As Object, _
ByVal e As EventArgs)
If myTextBox.Text <> String.Empty Then
CalendarString = myTextBox.Text
myTextBox.Text = String.Empty
End If
End Sub
End Class
End Namespace
C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace Wrox.ConnectionManagement
{
public class TextBoxChanger : WebPart, IStringForCalendar
{
private TextBox myTextBox;
private string _calendarString = String.Empty;
[Personalizable()]
public string CalendarString
{
Get {return _calendarString;}
Set {_calendarString= value;}
}
[ConnectionProvider("Provider for String From TextBox",
"TextBoxStringProvider")]
public IStringForCalendar TextBoxStringProvider()
{
return this;
}
protected overrides void CreateChildControls()
{
Controls.Clear();
myTextBox = new TextBox();
this.Controls.Add(myTextBox);
Button myButton = new Button();
myButton.Text = "Change Calendar Caption";
myButton.Click += new EventHandler(this.myButton_Click);
this.Controls.Add(myButton);
}
private void myButton_Click(object sender, EventArgs e)
{
if (myTextBox.Text != String.Empty)
{
CalendarString = myTextBox.Text;
myTextBox.Text = String.Empty;
}
}
}
}
这个提供程序Web Part不仅继承了WebPart类,还实现了前面程序清单19-15中所创建的IStringForCalendar接口。这个Web Part利用了IStringForCalendar接口中的String属性,它用Personalizable()属性标记。程序清单19-16的重点是TextBoxStringProvider()方法。这方法返回一个IStringForCalendar类型,用ConnectionProvider属性进行了进一步的定义。这个属性可以给提供程序指定友好的名称和一个编程用的名称,编程用的名称可以在后面建立的定制用户Web Part中使用。
使用CreateChildControls()方法可以确定Web Part的设计布局。在本例中,Web Part仅包含一个文本框和一个按钮。除了确定控件在设计界面上的布局之外,还可以使用该方法指定控件的事件,例如将处理程序赋予按钮控件,就可以指定按钮单击事件。
最后在这个Web Part的按钮单击事件(myButton_Click)中,给IStringForCalender接口的属性指定一个值,现在已经给用户Web Part准备好了所有的一切。下面介绍第二个Web Part的构建。
19.5.2 建立用户Web Part
页面上有了一个提供程序Web Part后,就可以将它提供的对象用于页面上的任意多个用户Web Part。但本例仅使用提供程序Web Part中的TextBox控件的String值。用户Web Part提取这个String值,把它赋予Calendar控件的Caption属性。用户Web Part的构建过程如程序清单19-17所示。
程序清单19-17 建立用户Web Part
VB
Imports Microsoft.VisualBasic
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Namespace Wrox.ConnectionManagement
Public Class ModifyableCalendar
Inherits WebPart
Private _myProvider As IStringForCalendar
Private _stringTitle As String
Private myCalender As Calendar = New Calendar()
<ConnectionConsumer("Calender Title Consumer", _
"ConnectionTitleConsumer")> _
Public Sub RetrieveTitle(ByVal Provider As IStringForCalender)
_myProvider = Provider
End Sub
Protected Overrides Sub OnPreRender(ByVal e As EventArgs)
EnsureChildControls()
If Not (Me._myProvider Is Nothing) Then
_stringTitle = _myProvider.CalendarString.Trim()
myCalendar = _stringTitle
End If
End Sub
Protected Overrides Sub CreateChildControls()
Controls.Clear()
Me.Controls.Add(myCalendar)
End Sub
End Class
End Namespace
C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace Wrox.ConnectionManagement
{
public class ModifyableCalendar: WebPart
{
private IStringForCalendar _myProvider;
string _stringTitle;
Calendar myCalendar;
[ConnectionConsumer("Calendar Title Consumer", _"CalendarTitleConsumer")]
public void RetrieveTitle(IStringForCalendar Provider)
{
_myProvider = Provider;
}
protected override void OnPreRender(EventArgs e)
{
EnsureChildControls();
if (this._myProvider != null)
{
_stringTitle = _myProvider.CalendarString.Trim();
myCalendar.Caption = _stringTitle;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
myCalendar = new Calendar();
Me.Controls.Add(myCalendar);
}
}
}
这个新的用户Web Part叫作ModifyableCalendar,是一个继承了WebPart的类。它需要引用IStringForCalendar接口。IStringForCalender在同一个命名空间中,所以可以提供对这个接口的简单引用。
用户Web Part需要一个方法来连接同一个页面上的提供程序Web Part。在本例中,RetrieveTitle()方法用ConnectionConsumer属性构建,再声明该方法。与提供程序Web Part使用的ConnectionProvider属性一样,ConnectionConsumer属性可以为用户Web Part指定一个友好的名称和一个编程用的引用名称。
接着在Web Part的PreRender()方法中提取提供程序Web Part中的值,并赋予Calendar控件,再在CreateChildControls()方法中把Calendar控件放在页面上。
有了提供程序Web Part和用户Web Part后,下一步就是在ASP.NET页面上放置它们,建立把两个Web Part连接起来的机制,如下所示。
19.5.3 连接ASP.NET页面上的Web Part
在连接Web Part的过程中,需要一个提供程序Web Part和一个用户Web Part。这些项详见程序清单19-16和19-17。在处理Web Part的连接过程时,若只把这两项放在页面上,并不能给它们建立起连接。除了这一步外,还必须把Web Part关联起来。
Web Part的连接是通过本章开头介绍的WebPartManager控件实现的。用于这个例子的ASP.NET页面如程序清单19-18所示。
程序清单19-18 连接两个Web Part控件的ASP.NET页面
<%@ Page Language="VB" %>
<%@ Register Namespace="Wrox.ConnectionManagement"
TagPrefix="connectionControls" %>
<html xmlns="http://www.w3/org/1999/xhtml" >
<head runat="server" >
<title>Connecting Web Parts</title>
</head>
<body>
<form id="form1" runat="server" >
<div>
<asp:WebPartManager ID="WebPartManager1" runat="server">
<StaticConnections>
<asp: WebPartConnection ID="WebPartConnection1"
ConsumerID="ModifyableCalendar1"
ConsumerConnectionPointID="CalendarTitleConsumer"
ProviderID="TextBoxChanger1"
ProviderConnectionPointID="TextBoxStringProvider">
</asp: WebPartConnection>
</StaticConnections>
</asp:WebPartManager>
<table cellpadding="3">
<tr valign="top">
<td style="width: 100px">
<asp: WebPartZone ID="WebPartZone1" runat="server">
<ZoneTemplate>
<ConnectionControls:TextBoxChanger ID="TextBoxChanger1"
runat="server" Title="Provider Web Part" />
</ZoneTemplate>
</asp: WebPartZone>
</td>
<td style="width: 100px">
<asp: WebPartZone ID="WebPartZone2" runat="server">
<ZoneTemplate>
<ConnectionControls: ModifyableCalendar
ID=" ModifyableCalender1"
runat="server" Title="Consumer Web Part" />
</ZoneTemplate>
</asp: WebPartZone>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
利用Web Part的这个ASP.NET页面包含一个只有两个单元格的表。表中的每个单元格都包含一个WebPartZone控件,分别是WebPartZone1和WebPartZone2。
在连接Web Part之前,在ASP.NET页面上用@Register页面指令注册新的用户Web Part控件。这个指令指向命名空间Wrox.ConnectionManagement。这是接口和两个定制Web Part控件使用的命名空间。
每个定制Web Part都放在它自己的WebPartZone控件中。这两个Web Part控件用WebPartManager控件连接起来。
<asp:WebPartManager ID="WebPartManager1" runat="server">
<StaticConnections>
<asp: WebPartConnection ID="WebPartConnection1"
ConsumerID="ModifyableCalendar1"
ConsumerConnectionPointID="CalendarTitleConsumer"
ProviderID="TextBoxChanger1"
ProviderConnectionPointID="TextBoxStringProvider">
</asp: WebPartConnection>
</StaticConnections>
</asp:WebPartManager>
WebPartManager服务器控件在声明的<StaticConnections>段中嵌套了定义好的连接。该定义是用WebPartConnection服务器控件实现的。这个控件用4个重要的属性建立了需要的连接。第一组的两个属性处理用户设置的定义。其中ConsumerID属性(通过其ID属性)引用ASP.NET页面上的控件名,而ConsumerConnectionPointID引用在连接过程中用作用户的对象ID。这个对象就是RetrieveTitle()方法,如下所示:
<ConnectionConsumer("Calendar Title Consumer", "CalendarTitleConsumer")> _
Public Sub RetrieveTitle(ByVal Provider As IStringForCalendar)
_myProvider = Provider
End Sub
WebPartConnection需要的第二组属性处理提供程序Web Part。其中第一个属性是ProviderID,它引用ASP.NET页面上用作提供程序的控件名,第二个属性是Provider ConnectionPointID,它类似于ConsumerConnectionPointID,但前者引用在连接过程中用作提供程序的对象ID。
<ConnectionProvider("Provider for String From TextBox","TextBoxStringProvider")>_
Public Function TextBoxStringProvider() As IStringForCalendar
Return Me
End Function
运行这个页面,结果如图19-25所示。

图 19-25
如果在页面的提供程序Web Part上向文本框输入文本字符串,并单击这个控件中的按钮,Calendar控件就会把这个字符串值用作Caption属性值,如图19-26所示。

图 19-26
从这个例子可以看出,建立连接需要的步骤较多,但并不困难。在这个例子中,把一个简单的String对象从一个Web Part传递给另一个Web Part。还可以利用该过程传递更复杂的对象(甚至定制对象)或较大的项,如DataSet对象。
19.5.4 连接Web Part时处理Master页面的难点
在ASP.NET页面上连接Web Part时,有一个需要考虑的地方。在处理使用ASP.NET 2.0中新Master页面功能的ASP.NET页面时,这个过程会变得非常困难。
只能在ASP.NET页面上放置一个WebPartManager控件。但在处理Master页面时,将这个控件放在Master页面,要比把它放在使用该Master页面的内容页面上更有意义。如果采用这种方法,在Master页面上使用WebPartConnection控件就没有意义。因为很容易出现在多个内容页面上有ID相同的控件的情况。此时,在WebPartConnection控件中进行的引用对其他控件来说就没有意义。所以,需要使用ProxyWebPartManager控件。
假定一个Master页面上有WebPartManager控件。该WebPartManager控件可以相当简单,如下所示:
<asp:WebPartManager ID="WebPartManager1" runat="server">
</asp:WebPartManager>
.master页面上有这个WebPartManager控件后,就应确保这个控件管理使用该Master页面的所有内容页面上的Web Part。
接着,如果使用该Master页面的一个内容页面试图连接某个Web Part,就必须在该内容页面上放置一个ProxyWebPartManager控件。这个控件将定义该内容页面上的Web Part连接,如下所示:
<asp:ProxyWebPartManager ID="ProxyWebPartManager1" runat="server">
<StaticConnections>
<asp: WebPartConnection ID="WebPartConnection1"
ConsumerID="ModifyableCalendar1"
ConsumerConnectionPointID="CalendarTitleConsumer"
ProviderID="TextBoxChanger1"
ProviderConnectionPointID="TextBoxStringProvider">
</asp: WebPartConnection>
</StaticConnections>
</asp:ProxyWebPartManager>







