10.2.4 预定义的接口
没有必要创建自己的接口,因为Web Part架构带有四个预定义接口:IWebPartField、IWebPartRow、IWebPartTable和IWebPartParameters。接口提供的机制允许使用程序和提供程序查找彼此的信息,并根据该信息调整它们的操作。从接口的名称可知,IWebPartField用于传送一个值,IWebPartRow用于传送一组数据,IWebPartTable用于传送多行数据,IWebPartParameters接口支持其他三个接口的功能,并为使用程序提供了一种机制,向提供程序表明它需要什么类型的数据。
IWebPartField、IWebPartRow和IWebPartTable接口非常类似,只需介绍如何使用其中一个接口即可。这里使用IWebPartField接口来说明。
1. IWebPartField接口概述
IWebPartField接口包含一个方法和一个属性。
● 该方法只有一个参数,允许提供程序调用使用程序中的某个例程。
● 属性是只读的,返回描述该属性的一个对象。
接口的这两个成员一起工作,来为使用程序提供数据和该数据的描述。这个过程描述起来很复杂,所以首先概述其中的步骤,先从使用程序一方开始。
(1) 提供数据的Web Part调用使用程序中的连接点方法,给使用程序传送提供程序使用的接口引用。
(2) 使用程序调用接口上的GetFieldValue方法,传送使用程序中某个例程的引用(这个例程必须是一个子例程,它接收一个Object类型的参数)。提供程序使用这个例程给使用程序传送数据。
(3) 在提供程序中,执行GetFieldValue方法,并给它传送使用程序中已有的例程的引用。
(4) 提供程序调用使用程序中的例程,并传送一个类型的Object参数。此时,提供程序在给使用程序提供数据。
(5) 执行使用程序中的例程,接收提供程序传送过来的数据。
这个过程的灵活性要比上述描述的更大。例如,提供程序接收到使用程序中例程的引用,就可以随时调用该例程,只要不在GetFieldValue方法中即可。
2. IWebPartField提供程序
首先看看提供程序的代码。如果不了解该代码的作用—— 使用IWebPartField接口的使用程序如何使用其例程—— 这段代码就没有什么意义。其中一些代码类似于前面的代码。例如,提供程序必须包含一个例程,它返回提供程序的接口执行代码的引用。
在使用IWebPartField接口时,使用程序调用IWebPartField接口的GetFieldValue方法,将启动通信过程。不同的接口有不同的启动方法。使用程序给GetFieldValue方法传送一个引用,该引用指向使用程序中由提供程序调用的例程(例程的引用称为委托)。接着,提供程序就调用使用程序中的例程,把数据传送回使用程序。开始编写提供程序的代码时,可以创建一个方法,给它传送使用程序中的委托。提供程序可以通过委托调用使用程序的例程,把信息传送给使用程序。
下面的Visual Basic 2005示例演示了执行IWebPartField接口的提供程序的代码。示例代码中的FieldValue方法执行IWebPartField接口中的GetFieldValue方法,这个FieldValue例程将由使用程序调用,把一个委托传送给提供程序。FieldValue方法必须带一个参数,它是使用程序中的委托。在这个例子中,参数叫作fld。
在使用程序调用FieldValue方法,传送一个委托后,提供程序就可以调用使用程序中该委托fld引用的例程。要调用由委托引用的例程,可以使用委托的Invoke方法,传送该例程需要的参数。所以,在这个例子的FieldValue方法中,代码调用参数fld的Invoke方法。使用程序的例程(下一节建立)需要一个参数,所以这段代码给Invoke方法提供了一个参数。在这个例子中,是把提供程序上的一个属性传送给使用程序中的方法。
Implements WebControls.WebParts.IWebPartField
Sub FieldValue(ByVal fld As WebControls.WebParts.FieldCallback) _
Implements WebControls.WebParts.IWebPartField.GetFieldValue
fld.Invoke(Me.BookData)
End Sub
ReadOnly Property BookData() As String
Get
Return strBookTitle
End Get
End Property
C#的对应代码如下所示。
void WebControls.WebParts.IWebPartField.FieldValue(
WebControls.WebParts.FieldCallback fld)
{
fld.Invoke(this.BookData);
}
string BookData
{
get
{
return strBookTitle;
}
}
当然,Invoke方法可以非常简单,只需给它传送一个变量或一个字面量,来替代提供程序上的属性。传送属性的原因是要支持IWebPartField接口的另一部分:Schema属性。Schema属性为使用程序提供了程序返回的所有信息。Schema属性必须返回一个PropertyDescriptor,这个对象可以包含属性的所有信息,如果GetFieldValue方法关联到一个属性上,提供程序上的Schema属性就可以使用.NET Framework内置的工具关联到同一个属性上。接着,使用程序就可以使用IWebPartField接口的Schema属性,获得使用程序要提取的数据信息。
在下面的代码中,DataDescription例程执行IWebPartField接口中的Schema属性,创建了一个PropertyDescriptorCollection,它包含提供程序的所有属性,接着返回BookData属性的PropertyDescriptor。
ReadOnly Property DataDescription() As ComponentModel.PropertyDescriptor _
Implements WebControls.WebParts.IWebPartField.Schema
Get
Dim pdc As PropertyDescriptorCollection
pdc = TypeDescriptor.GetProperties(Me)
Return pdc("BookData")
End Get
End Property
C#的对应代码如下所示。
ComponentModel.PropertyDescriptor.DataDescription
WebControls.WebParts.IWebPartField.Schema {
get
{
PropertyDescriptorCollection pdc;
pdc = TypeDescriptor.GetProperties(this);
return pdc["BookData"];
}
}
使用程序现在可以使用Schema属性提取属性描述器,描述传送给使用程序的数据,如后面所述。
提示:
根据属性给使用程序传送PropertyDescriptorCollection的一种替代方式是,创建一个专用对象,来处理所传送的数据。接着,就可以根据这个对象创建PropertyDescriptorCollection,把它传送给提供程序。
在介绍使用程序的代码之前,为了完整起见(并复习一些熟悉的内容),下面给出了Visual Basic 2005中提供程序的连接点例程。
<WebControls.WebParts.WebParts.ConnectionProvider("Provides
IWebPartField")> _
Public Function IWebPartFieldProvider() As
WebControls.WebParts.IWebPartField
Return Me
End Function
C#的对应代码如下所示。
[WebControls.WebParts.WebParts.ConnectionProvider("Provides
IWebPartField")]
public WebControls.WebParts.IWebPartField IWebPartFieldProvider()
{
return this;
}
3. IWebPartField 使用程序
使用程序中的代码必须完成两个任务。
● 必须包含一个由提供程序调用的例程。
● 必须给提供程序传送该例程的一个引用。
在下面的Visual Basic 2005示例中,提供程序调用的例程叫作ReceiveData。IWebPartField Consumer方法调用接口的GetFieldValue方法(该接口传送给IWebPartFieldConsumer方法),给提供程序传送一个指向ReceiveData例程的委托。在Visual Basic 2005中,只要使用AddressOf运算符,就创建了一个委托;在C#中,传送例程的名称,就会为该例程创建一个委托。
提示:
回顾一下提供程序一方发生的情况:在上一节的Visual Basic 2005示例提供程序代码中,GetFieldValue方法是在FieldValue方法中执行的,该例程接受使用程序中的例程委托,并调用该例程(这里是ReceiveData例程),把数据传送回使用程序。
代码中IWebPartFieldConsumer方法的后面是要由提供程序调用的ReceiveData例程。上一个例子中的提供程序调用传送给它的委托时,就会把参数中的数据传送给使用程序的例程。这个例子说明,这个例程带一个参数:ReceiveData子例程的BookTitle参数。ReceiveData代码使用接口的Schema属性,提取PropertyDescriptor对象,获得使用程序返回的数据描述。在这个例子中,Schema属性返回的PropertyDescriptor用于检查从提供程序返回的数据是否为一个字符串。
Dim ifld As WebControls.WebParts.IWebPartField
<WebControls.WebParts.WebParts.ConnectionConsumer("IWebPartField
Consumer")> _
Public Sub IWebPartFieldConsumer(ByVal fld As
WebControls.WebParts.IWebPartField)
ifld = fld
ifld.GetFieldValue(AddressOf ReceiveData)
End Sub
Sub ReceiveData(BookTitle As Object)
Dim pd As PropertyDescriptor
pd = ifld.Schema
If pd.PropertyType.Name = "String" Then
strTitle = BookTitle.ToString()
End If
End Sub
C#的对应代码如下所示。
WebControls.WebParts.IWebPartField ifld;
[WebControls.WebParts.ConnectionConsumer("IWebPartField Consumer")]
public void IWebPartFieldConsumer(UI.WebControls.WebParts.
IWebPartField fld)
{
ifld = fld;
fld.GetFieldValue(new WebControls.WebParts.FieldCallback(ReceiveData));
}
public void ReceiveData(object BookTitle)
{
PropertyDescriptor pd;
pd = ifld.Schema();
if(pd.PropertyType.Name == "String")
{
strTitle = BookTitle.ToString();
}
}
4. IWebPartTable和IWebPartRow
IWebPartTable和IWebPartRow接口类似于IWebPartField接口,使用方式也类似,主要区别如下所示。
● IWebPartTable和IWebPartRow接口的Schema属性返回一个属性描述器的集合(Property DescriptorCollection),它支持将各种数据项传送给使用程序的例程。
● 在IWebPartRow接口中,与GetFieldValue方法对应的是GetRowData方法。
● 在IWebPartTable接口中,与GetFieldValue方法对应的是GetTableData方法。
● 在IWebPartTable接口中,必须给要在提供程序中调用的例程传送一个集合,而不是一个对象。
5. IWebPartParameters接口
IWebPartParameters接口使用与IWebPartField、IWebPartTable和IWebPartRow类似的结构,但有四个区别,所以需要单独讨论。
● 与GetFieldValue例程对应的是GetParametersData
● 必须给使用程序中由提供程序调用的例程传送名称-值对的Dictionary实例
● 提供程序中的属性必须返回PropertyDescriptors集合,而不是单个PropertyDescriptor
● 该接口包含第三个方法SetConsumerSchema,使用程序必须先调用它
SetConsumerSchema方法使IWebPartParameters接口与其他三个预定义接口大不相同。SetConsumerSchema方法用于让使用程序给提供程序传送一个PropertyDescriptorCollection,指定使用程序要接受的信息类型。如前所述,在IWebPartField接口中,Schema属性为使用程序提供了一种查找提供程序返回的数据信息的方法。SetConsumerSchema方法反其道而行之——它让使用程序告诉提供程序它希望获得什么数据。
提示:
在本节中,Dictionary对象只是一种速记符号,.NET Framework中并没有Dictionary对象,而是有许多执行了IDictionary接口的对象,例如SortedList或StateBag。这些对象都可以在IWebPartParameters接口中与使用程序的例程一起使用。Dictionary对象虽然较短,但足以表示“执行IDictionary接口的对象”。
必须给SetConsumerSchema方法传送一个PropertyDescriptorCollection,列出要接受的项。在提供程序中,GetParametersData方法的代码应使用SetConsumerSchema传送的信息,建立要传送回使用程序的Dictionary对象。例如,提供程序可以循环通过SetConsumerMethod提供的PropertyDescriptorCollection,确定Dictionary对象中要返回的属性名及其数据类型。
在编写使用程序对象的代码时,几乎总是应在调用GetParametersData方法之前调用SetConsumerSchema方法。要利用IWebPartParameters接口,提供程序需要访问从使用程序传送来的PropertyDescriptorCollection对象,之后提供程序再调用使用程序中的例程。例如,提供程序使用PropertyDescriptorCollection中的信息,确定提供程序是否拥有使用程序需要的数据。提供程序一般在其GetParametersData方法中调用使用程序的例程。所以,要让提供程序利用使用程序中的数据,使用程序就应调用提供程序的SetConsumerSchema方法,传送PropertyDescriptorCollection,之后再调用提供程序的GetParametersData方法,如果没有遵循这个顺序,提供程序在建立要返回给使用程序的Dictionary集合时,在GetParametersData方法中就没有可供分析的PropertyDescriptorCollection。
Schema属性还有一个作用。SetConsumerParameters方法允许使用程序指定它需要的数据,而Schema属性允许提供程序用它可以提供的一个数据集合来响应。一般情况下,在Schema属性中,应检查使用程序发送的PropertyDescriptors集合,返回提供程序可以提供的属性集合PropertyDescriptorCollection。
在下面使用程序WebPart的Visual Basic 2005示例中,使用程序调用SetConsumerSchema方法,传送一个PropertyDescriptorCollection,来描述使用程序上的所有属性。把该信息传送给提供程序后,代码就通过Schema属性进行一个非常简单的检查,确定所请求的属性个数是否匹配要返回的属性个数。如果匹配,使用程序就调用GetParametersData方法,传送提供程序要使用的例程引用(这个例子称为ReceiveData)。ReceiveData例程由提供程序调用时,会循环传送给它的Dictionary对象,提取Dictionary中的BookTitle和AuthorName数据。这个示例代码包含BookTitle和AuthorName的定义,以说明传送给SetConsumerSchema的PropertyDescriptorCollection包含什么内容。
Public Class SampleConsumer _
Inherits System.Web.UI.WebControls.WebParts.WebPart
<WebControls.WebParts.ConnectionConsumer("IWebPartParameters Consumer")>_
Public Sub IWebPartParametersConsumer( _
ByVal prm As WebControls.WebParts.IWebPartParameters)
Dim pdc As PropertyDescriptorCollection
Dim iprm As WebControls.WebParts.IWebPartParameters
iprm = prm
pdc = TypeDescriptor.GetProperties(Me)
iprm.SetConsumerSchema(pdc)
iprm.GetParametersData(AddressOf ReceiveData)
End Sub
Sub ReceiveData(ByVal BookInfo As IDictionary)
Dim entry As DictionaryEntry
For Each entry In BookInfo.Keys
Select Case entry.Key.ToString
Case "BookTitle"
Me.BookTitle = entry.Value.ToString
Case "BookAuthor"
Me.BookAuthor = entry.Value.ToString
End Select
Next
End Sub
Property BookTitle() As String
...application logic
End Property
Property BookAuthor() As String
...application logic
End Property
C#的对应代码如下所示。
public class SampleConsumer : System.Web.UI.WebControls.WebParts.WebPart,
System.Web.UI.WebControls.WebParts.IWebPartParameters
{
[WebControls.WebParts.ConnectionConsumer("IParameters Consumer")]
public void IParametersConsumer(WebControls.WebParts.IWebPartParameters prm)
{
PropertyDescriptorCollection pd;
WebControls.WebParts.IWebPartParameters iprm;
iprm = prm;
pd = TypeDescriptor.GetProperties(this);
iprm.SetConsumerSchema(pd);
iprm.GetParametersData(ReceiveData);
}
public void ReceiveData(IDictionary BookInfo)
{
foreach(DictionaryEntry entry in BookInfo.Keys)
{
switch(entry.Key.ToString)
{
case "BookTitle":
this.BookTitle = entry.Value.ToString;
break;
case "BookAuthor":
this.BookAuthor = entry.Value.ToString;
};
}
}
public string BookTitle
{
...application logic
}
public string BookAuthor
{
...application logic
}
下面的Visual Basic 2005代码示例是执行IWebPartParameters接口的提供程序。代码在一个模块级变量pcSchema中保存了给SetConsumerSchema传送的PropertyDescriptorCollection的引用(这里SetConsumerData由一个方法sc执行)。Schema属性中的代码循环在SetConsumerSchema方法中传送的PropertyDescriptorCollection,检查提供程序是否有相应的属性。如果有,就把PropertyDescriptor添加到一个PropertyDescriptors数组中,该数组最终要转换为PropertyDescriptorCollection,返回给使用程序。在GetParametersData例程(在这个例子中由gpd例程执行它)中,属性集合用于确定添加到Dictionary对象的数据,Dictionary对象通过Invoke方法传送给使用程序的例程。下面的几个对象执行了IDictionary接口—— 这段代码使用StateBag对象。
Dim pcSchema As PropertyDescriptorCollection
Sub sc(ByVal schema As PropertyDescriptorCollection) _
Implements WebControls.WebParts.IWebPartParameters.SetConsumerSchema
pcSchema = schema
End Sub
Sub gpd(ByVal prm As WebControls.WebParts.ParametersCallback) _
Implements WebControls.WebParts.IParameters.GetParametersData
Dim entry As DictionaryEntry
Dim dict As New StateBag
Dim prop As PropertyDescriptor
For Each prop In pcSchema
Select Case prop.Name
Case "BookTitle"
dict.Add("BookTitle", Me.BookTitle)
Case "BookAuthor"
dict.Add("BookAuthor", Me.BooktAuthor)
End Select
Next
prm.Invoke(dict)
End Sub
C#的对应代码如下所示。
PropertyDescriptorCollection pcSchema;
void WebControls.WebParts.IWebPartParameters.SetConsumerSchema
(PropertyDescriptorCollection schema)
{
pcSchema = schema;
}
void WebControls.WebParts.IParameters.GetParametersData
(WebControls.WebParts.ParametersCallback prm)
{
DictionaryEntry entry;
StateBag dict = new StateBag();
foreach(PropertyDescriptor prop in pcSchema)
{
switch(prop.Name)
{
case "BookTitle":
dict.Add("BookTitle", this.BooktTitle);
break;
case "BookAuthor":
dict.Add("BookAuthor", this.BooktAuthor);
};
}
prm.Invoke(dict);
}






