一、开发环境
(1)Windows 2003 Server(2)Windows SharePoint Service 3.0(或Microsoft Office SharePoint Server 2007)
(3)Visual Studio 2005
(5)SQL Server 2005
(6)Windows SharePoint Services 3.0 Tools: Visual Studio 2005 Extensions
(7)Windows SharePoint Services SDK 3.0
二、WSS 2.0和WSS 3.0下Web Part开发的不同
众所周知,Windows SharePoint Service 3.0(以下简称WSS 3.0)是基于ASP.NET 2.0进行了重写,因此与WSS 2.0有很多地方存在一些不同。对于Web Part的开发,也是如此。目前网络上的很多资料介绍的Web Part开发,还是基于WSS 2.0的,而非WSS 3.0风格的。Web Part是首先在WSS 2.0作为仪表板(Dashboard)的替代技术引入的,后来ASP.NET 2.0中也引入了一个新版本的Web Part Framework,该框架可以脱离SharePoint的环境运行。WSS 3.0的Web Part框架也基于该ASP.NET Web Part Framework进行了完全的重构。不过为了兼容性(主要是考虑到以前的解决方案的升级),原WSS 2.0的Web Part框架也得以保留,但对于以后的开发而言,都应该基于WSS 3.0的ASP.NET框架进行。
例如,基于WSS 2.0的Web Part都是从 Microsoft.SharePoint.WebPartPages.WebPart类派生的,而WSS 3.0的Web Part应该从标准的ASP.NET类System.Web.UI.WebControls.WebParts.WebPart继承。下表是基于 ASP.NET Web Part Framework中一些新的属性和类型,以及为了兼容性而保留的对应WSS 2.0模型中的属性和类型,具体的示例将在下面的章节中出现。
ASP.NET Web Parts | SharePoint Backward Compatibility |
---|---|
WebBrowsableAttribute | BrowsableAttribute |
WebDisplayName | FriendlyName |
WebDescription | Description |
Personalizable | WebPartStorage |
PersonalizationScope | Storage |
EditorPart | ToolPart |
EditorPartCollection | ToolPart[] |
CreateEditorParts() | GetToolParts() |
RenderContents() | RenderWebPart() |
SetPersonalizationDirty() | SaveProperties |
三、SharePoint Web Part开发基础
Web Part是一种特殊类型的Web Control(Web控件),它被部署于Web Part Zone控件内。虽然基于ASP.NET的Web Part Framework可以脱离WSS环境运行,但是WSS 3.0中的Web Part Framework提供了一些附加的功能,包括:动态SharePoint站点模型、模板框架、安全框架以及使用WSS Web Part Gallery的Web Part管理。以下二图就是Web Part开发中涉及的基本类,虽然SharePoint针对Web Part Zone和Web PartManager类有特定的实现,但是我们仍将使用ASP.NET Web Part类作为我们开发Web Part应用程序的基类。WSS 3.0中使用的Web Part Manager类为SPWebPartManager,它是在页面Web Part Zone对象和SharePoint内容数据库之间的桥梁。当在页面中添加一个Web Part时,实际上是在内容数据库中加入了一个Web Part的序列化实例。对于一个最简单的Web Part而言,只需从Web Part 基类System.Web.UI.WebControls.WebParts.WebPart.派生一个具体类,然后重写RenderContents方 法来以HTML方式呈现该Web Part,就像在ASP.NET中开发Web控件一样。一个Web Part的生命周期如下表所示:
方法/事件 | 描述 |
---|---|
OnInit | 处理控件的初始化 |
OnLoad | 处理控件的装载 |
CreateChildControls | 创建子控件 |
EnsureChildControls | 确保CreateChildControls方法被调用,使用该方法来确保在访问控件的数据之前控件已经存在。 |
OnPreRender | 处理在呈现可控件之前必须完成的任务,例如数据装载。异步页面任务应该从该方法中被启动。 |
Page.PreRenderComplete | 在所有的控件都完成其OnPreRender 方法并且页面已经完成了异步任务的执行之后,将会触发该事件。 |
Render | 呈现整个控件,包括外部标签。 |
RenderContents | 仅仅呈现控件在外部标签和样式属性内的内容。 |
四、Web Part属性
除了类似输出”Hello World“这样内容的最简单Web Part之外,一般实际的Web Part都是可动态配置的,这是通过Web Part属性来实现的。在WSS 2.0中,一个Web Part属性是这样子的:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
当然,这不是我们推荐的用法,而应该采用如下新的语法:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
上述两个声明是完全等价的,大家可以参考第二节中的对应表来一一比较。注意到上述属性中WebBrowsable的属性为false,如果该属性为 true的话,就是使用默认的属性编辑界面,也就是一个文本输入框,如果想定制该属性的输入界面,则应该将WebBrowsable属性置为false, 然后提供定制属性界面。
在WSS 2.0中,自定义属性配置界面是通过重写Web Part的GetToolParts方法实现的,例如:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
因为这是WSS 2.0中已经过时的用法,所以在此不再详述,我们重点关注一下WSS 3.0中的对应的实现。在WSS 3.0(ASP.NET 2.0)中,自定义属性配置界面是通过重写Web Part的CreateEditorParts方法实现的:
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
要实现一个EditorPart,最简单的是重写三个方法:
(1)CreateChildControls:负责创建EditorPart的子控件
(2)ApplyChanges:保存配置到Web Part的属性
(3)SyncChanges:从Web Part属性初始化EditorPart
以下是一个具体的WebPart的代码:
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using System.ComponentModel;
using System.Collections.Generic;
namespace KingFactoryTagValueWebPart2
{
[Guid( " 3fd90ae1-9ff9-4b5e-8262-ac2649be9ebe " )]
public class KingFactoryTagValueWebPart2:System.Web.UI.WebControls.WebParts.WebPart
{
private string dataSourceName;
private string rtTagName;
private string refreshInterval;
private string webUrl;
/// <summary>
/// 构造函数。
/// </summary>
public KingFactoryTagValueWebPart2()
{
this .ExportMode = WebPartExportMode.All;
this .refreshInterval = " 5000 " ;
}
/// <summary>
/// 输出错误信息。
/// </summary>
/// <paramname="writer"></param>
/// <paramname="errorInfo"></param>
private void RenderError(HtmlTextWriterwriter, string errorInfo)
{
string content = " <span><tableclass="ms-WPBody"style="padding:0px;width:100%;"> " +
" < tr><tdvalign="top"style="padding-left:4px;padding-right:4px;"> <imgsrc="/_layouts/1033/images/error.gif"alt="错误信息"/>< /td> " +
" <tdwidth="100%"style="padding-left:4px;padding-right:4px;"> " +
errorInfo +
" </td></tr></table></span> " ;
writer.Write(content);
}
/// <summary>
/// 输出结果
/// </summary>
/// <paramname="writer"></param>
protected override void Render(HtmlTextWriterwriter)
{
// 检查网址
if ( this .webUrl == null || this .webUrl == string .Empty)
{
RenderError(writer, " 错误:请选择网站 " );
return ;
}
// 检查数据源
if ( this .dataSourceName == null || this .dataSourceName == string .Empty)
{
RenderError(writer, " 错误:请选择数据源 " );
return ;
}
// 检查变量
if ( this .rtTagName == null || this .rtTagName == string .Empty)
{
RenderError(writer, " 错误:请选择变量 " );
return ;
}
// 刷新间隔
if ( this .refreshInterval == null || this .refreshInterval == string .Empty)
this .refreshInterval = " 5000 " ;
// 生成输出
SPWebweb = SPContext.Current.Web;
string content = string .Format( " <iframesrc="{0}?UserName={1}&DataSourceName={2}&TagName={3}&RefreshInterval={4}"width="100%"height="100%"></iframe> " ,
this .webUrl,
SPEncode.UrlEncode(web.CurrentUser.LoginName),
SPEncode.UrlEncode( this .dataSourceName),
SPEncode.UrlEncode( this .rtTagName),
this .RefreshInterval);
writer.Write(content);
}
/// <summary>
/// 获得编辑界面。
/// </summary>
/// <returns></returns>
public override EditorPartCollectionCreateEditorParts()
{
EditorPartCollectionbaseParts = base .CreateEditorParts();
List < EditorPart > editorParts = new List < EditorPart > ( 1 );
EditorPartpart = new KingFactoryTagValueEditorPart2();
part.ID = this .ID + " _tagValueEditor " ;
part.Title = "KingFactory变量选择";
editorParts.Add(part);
return new EditorPartCollection(baseParts,editorParts);
}
[WebDescription( " WebURL " ),
Category( " Miscellaneous " ),
DefaultValue( "" ),
Personalizable(PersonalizationScope.Shared),
WebDisplayName( " WebURL " ),
WebBrowsable( false )]
public string WebURL
{
get { return this .webUrl;}
set { this .webUrl = value;}
}
[WebDescription( " DataSourceName " ),
Category( " Miscellaneous " ),
DefaultValue( "" ),
Personalizable(PersonalizationScope.Shared),
WebDisplayName( " DataSourceName " ),
WebBrowsable( false )]
public string DataSourceName
{
get { return this .dataSourceName;}
set { this .dataSourceName = value;}
}
[WebDescription( " TagName " ),
Category( " Miscellaneous " ),
DefaultValue( "" ),
Personalizable(PersonalizationScope.Shared),
WebDisplayName( " TagName " ),
WebBrowsable( false )]
public string RtTagName
{
get { return this .rtTagName;}
set { this .rtTagName = value;}
}
[WebDescription( " RefreshInterval " ),
Category( " Miscellaneous " ),
DefaultValue( "" ),
Personalizable(PersonalizationScope.Shared),
WebDisplayName( " RefreshInterval " ),
WebBrowsable( false )]
public string RefreshInterval
{
get { return this .refreshInterval;}
set { this .refreshInterval = value;}
}
}
}
以下为对应的EditorPart的代码:
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
namespace KingFactoryTagValueWebPart2
{
public class KingFactoryTagValueEditorPart2:EditorPart
{
protected LabelLabel1;
protected LabelLabel2;
protected LabelLabel3;
protected LabelLabel4;
protected LabelLabel5;
protected TextBoxrefreshInterval;
protected TextBoxselectedTagName;
protected TreeViewTagNameView;
protected DropDownListwebSiteList;
protected DropDownListdataSourceList;
/// <summary>
/// 创建子控件。
/// </summary>
protected override void CreateChildControls()
{
base .CreateChildControls();
this .Label1 = new Label();
this .Label1.Text = " 选择网站: " ;
this .Label1.Width = 80 ;
this .Label2 = new Label();
this .Label2.Text = " 选择数据源: " ;
this .Label3 = new Label();
this .Label3.Text = " 刷新间隔: " ;
this .Label4 = new Label();
this .Label4.Text = " 选择变量: " ;
this .refreshInterval = new TextBox();
this .selectedTagName = new TextBox();
this .TagNameView = new TreeView();
this .webSiteList = new DropDownList();
this .dataSourceList = new DropDownList();
this .dataSourceList.SelectedIndex = - 1 ;
this .webSiteList.SelectedIndex = - 1 ;
this .webSiteList.AutoPostBack = true ;
this .webSiteList.SelectedIndexChanged += new EventHandler(webSiteList_SelectedIndexChanged);
this .dataSourceList.AutoPostBack = true ;
this .dataSourceList.SelectedIndexChanged += new EventHandler(dataSourceList_SelectedIndexChanged);
this .TagNameView.ImageSet = TreeViewImageSet.XPFileExplorer;
this