多连接的消费者 Web 部件





0/5 (0投票)
允许 ASP.NET、WSS 3.0 和 MOSS 2007 中的消费者 Web 部件拥有多个提供程序。
引言
您是否曾开发过一对提供程序/消费者 Web 部件,允许单个消费者 Web 部件连接到多个提供程序 Web 部件,结果却发现 ASP.NET / WSS 3.0 / MOSS 2007 只允许一对一的连接?如果是这样,那么您来对地方了。
背景
几周前,我正在为我忙碌的一个 SharePoint 项目开发一些新的 Web 部件,却遇到了瓶颈。需求是:当列表项中的一个链接被点击时,将在列表右侧显示一些帮助/信息文本。需求还规定,一个门户页面上可以有多个列表,并且将使用相同的帮助/信息文本 Web 部件。我以为这会很容易,因为我知道 ConnectedConsumerAttribute
类有一个名为 AllowMultipleConnection
的属性,我只需要将其值设置为 true
;毕竟,WSS 2.0/MOSS 2003 已经支持此功能。我错了,所以在我查阅了所有论坛/博客/维基等信息后,我得出结论:许多人正经历和我一样的痛苦。不幸的是,我一直未能找到解决该问题的方法,这让我走到了这一步。要么是 ASP.NET Web 部件的实现设计有缺陷,要么是存在一个需要修复的重大缺陷。我认为我已经找到了解决该问题的一个潜在方案(尽管我在此过程中想出了几个其他方案,这是最巧妙的)。
使用代码
我直接开始吧。这不是一个使用 VS 2008 开发代码的循序渐进的教程;我将假设您已经知道这些。我将使用代码来描述解决方案的概念。我的开发环境如下:
- Vista Enterprise
- VSTS 2008
- WSS 3.0 (在 Vista 上使用 Bamboo Solutions 安装)
- VS Extensions for SharePoint SVCS 1.2
- + 许多其他内容(对于本文,以上内容是必需的)
注意: 如果您正在使用 Vista,请记住以提升的权限启动 VS,并且不要忘记在项目调试属性中更改 WSS URL。
我做的第一件事是创建一个名为 ConnectedConsumerInterfaceAttribute
的新自定义属性类,我用它来标记消费者 Web 部件上的相关方法。此方法将由提供程序 Web 部件调用。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class ConnectConsumerInterfaceAttribute : Attribute
{
private Type _interfaceType;
public ConnectConsumerInterfaceAttribute(Type interfaceType)
{
_interfaceType = interfaceType;
}
public Type InterfaceType
{
get
{
return _interfaceType;
}
}
}
接下来,我创建了 ASP.NET WebPart
类的子类并创建了一个 ProviderWebPart
类。所有需要连接到多连接消费者的提供程序都必须继承自这个新类。我添加了一个名为 ConnectedConsumerInvoke
的新的 protected virtual
方法,其签名如下:
protected virtual void InvokeConnectedConsumer<TInterfaceType>(TInterfaceType graph)
此方法拥有调用消费者方法所需的所有魔法,它基于接口类型以及它是否被 ConnectedConsumerInterfaceAttribute
属性标记。该方法使用 WebPartManager
对象检索所有当前的 Web 部件连接。然后,它遍历每个连接,直到找到属于当前提供程序 Web 部件(即 this
)的连接。一旦识别出连接提供程序是同一个对象,就将连接消费者添加到列表中。一旦消费者 Web 部件列表填充完毕,代码就会遍历每个消费者 Web 部件,并使用反射检索该 Web 部件的所有方法。然后,它会检查该方法是否被 ConnectedConsumerInterfaceAttribute
属性标记。如果是,则将 TInterfaceType
类型与 ConnectedConsumerInterfaceAttribute.InterfaceType
进行比较,如果相同,则调用检索到的方法,并将 graph
对象作为参数传递。在附加的代码示例中,我创建了一个名为 SPSHelper
的辅助类,它完成了上述部分工作。
public class ProviderWebPart : WebPart
{
public ProviderWebPart() : base()
{
}
protected virtual void InvokeConnectedConsumer<TInterfaceType>(TInterfaceType graph)
{
// Retrieve all the consumer web parts using the SPSHelper class...
WebPart[] consumerWebParts = SPSHelper.GetConnectedConsumerWebParts(
this.WebPartManager, this, typeof(TInterfaceType));
// Process all the consumer web parts...
foreach (WebPart consumerWebPart in consumerWebParts)
{
// Retrieve all the methods of the consumer web part...
MethodInfo[] methods = consumerWebParts.GetType().GetMethods(
BindingFlags.Public | BindingFlags.Instance);
// Loop through each method and check if any
// are marked with ConnectConsumerInterfaceAttribute...
foreach (MethodInfo method in methods)
{
// Retrieve all the custom attributes of the method...
object[] attributes = method.GetCustomAttributes(
typeof(ConnectConsumerInterfaceAttribute), false);
// Loop through until we find a valid ConnectConsumerInterfaceAttribute
// - there should only be 1...
foreach (ConnectConsumerInterfaceAttribute attribute in attributes)
{
// Check if the interface types are the same...
if (typeof(TInterfaceType) == attribute.InterfaceType)
{
// Now invoke the method pass in the data...
method.Invoke(consumerWebPart, new object[] { graph });
// Now need to loop through the rest...
break;
}
}
}
}
}
}
然后,我开发了两个 Web 部件,名为 InputWebPart
(提供程序)和 DisplayWebPart
(消费者)。
public class InputWebPart : ProviderWebPart
{
private TextBox _txtInputText;
private ITextData _textData;
public InputWebPart()
{
}
protected ITextData TextData
{
get
{
if (_textData == null)
{
_textData = new TextDataProvider();
}
return _textData;
}
set
{
_textData = value;
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
// Add a new text box...
_txtInputText = new TextBox();
this.Controls.Add(_txtInputText);
// Add a new button...
Button btnSend = new Button();
btnSend.Text = "Send";
// Wire up the click event...
btnSend.Click += new EventHandler(btnSend_Click);
this.Controls.Add(btnSend);
}
private void btnSend_Click(object sender, EventArgs e)
{
// this is required to take care of the normal SharePoint behaviour...
this.TextData.Text = _txtInputText.Text;
// this is where all the magic happens...
this.InvokeConnectedConsumer<ITextData>(this.TextData);
}
// This is required for SharePoint to allow provider to connect to consumers...
[ConnectionProvider("Text Data")]
public ITextData SetTextData()
{
return _textData;
}
}
public class DisplayWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
private ITextData _textData;
public DisplayWebPart()
{
}
protected ITextData TextData
{
get
{
return _textData;
}
set
{
_textData = value;
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
if (this.TextData != null)
{
Label lblDisplayText = new Label();
lblDisplayText.Text = this.TextData.Text;
this.Controls.Add(lblDisplayText);
}
}
// Required for SharePoint to allow multiple connections...
[ConnectionConsumer("Text Data Consumer", AllowsMultipleConnections = true)]
// Required for the provider to invoke multiple connected consumers...
[ConnectConsumerInterface(typeof(ITextData))]
public void GetTextData(ITextData textData)
{
this.TextData = textData;
this.EnsureChildControls();
}
}
这占了示例代码的大部分。一旦您将示例加载到 VS 2008 中,它就会更容易理解。要将解决方案部署到 WSS 3.0,请右键单击 VS Solution Explorer 中的解决方案文件,然后在上下文菜单中单击 Deploy 选项。
我希望这将使您将来开发 Web 部件的生活更加轻松。