SharePoint的开发中,我们经常需要自己开发一些服务器控件,然后把它添加到MOSS的页面上。
SharePoint服务器控件的开发跟普通的服务器控件没有区别,只不过是在部署的时候需要进行一些特别的配置。
下面拿一个具体的例子来说明这个过程。
以下的示例是一个自定义的导航服务器控件(WebFolderNavigation),这个导航控件的第一级为所有显示在导航栏上的列表或文档库链接,列表或文档库链接的子级为相应的文件夹。效果图如下:
Step1:新建一个 Server Control项目,名称为CodeArt.SharePoint.WebControls。(或普通的类库项目,添加System.Web的引用。)
Step2:因为WebFolderNavigation控件需要访问SharePoint的对象模型,所有添加对Microsoft.SharePoint.dll的引用。
Step3:WebFolderNavigation控件的代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;
using System.Web.UI;
namespace CodeArt.SharePoint.WebControls
{
/// <summary>
/// 当前站点文件夹导航
/// </summary>
public class WebFolderNavigation : Menu
{
const String SORT_FIELD_NAME = "排序号";
const String SHOW_SUB_FIELD_NAME = "显示子栏目";
private bool _EnableSort = false;
public bool EnableSort
{
get { return _EnableSort; }
set { _EnableSort = value; }
}
private int _MaxFolderDepth = 2;
public int MaxFolderDepth
{
get { return _MaxFolderDepth; }
set { _MaxFolderDepth = value; }
}
protected override void CreateChildControls()
{
if ( this.Items.Count > 0)
return;
base.CreateChildControls();
SPWeb web = SPContext.Current.Web;
string webUrl = web.ServerRelativeUrl;
if (!webUrl.EndsWith("/"))
webUrl += "/";
Dictionary<string, SPList> allVisibleList = new Dictionary<string, SPList>();
foreach (SPList list in web.Lists)
{
if (list.OnQuickLaunch)
allVisibleList.Add(list.DefaultViewUrl.ToLower() , list);
}
List<SPNavigationNode> navLinks = new List<SPNavigationNode>();
foreach (SPNavigationNode levelOneNode in web.Navigation.QuickLaunch)
{
if (levelOneNode.IsVisible)
{
foreach (SPNavigationNode levelTwoNode in levelOneNode.Children)
{
if (levelTwoNode.IsVisible)
{
navLinks.Add(levelTwoNode);
}
}
}
}
foreach (SPNavigationNode node in navLinks)
{
if (allVisibleList.ContainsKey(node.Url.ToLower()))
{
SPList list = allVisibleList[node.Url.ToLower()];
MenuItem rootItem = new MenuItem();
rootItem.Text = list.Title;
rootItem.ToolTip = list.Description ;
rootItem.NavigateUrl = list.DefaultViewUrl;
this.Items.Add(rootItem);
string showSubFieldName = getShowSubFieldName(list);
if (EnableSort)
{
string sortFieldName = getSortFieldName(list);
if (sortFieldName == "")
CreateItem(list.RootFolder, rootItem.ChildItems, webUrl, 1, showSubFieldName);
else
CreateItem(list.RootFolder, rootItem.ChildItems, webUrl, sortFieldName, 1, showSubFieldName);
}
else
{
CreateItem(list.RootFolder, rootItem.ChildItems, webUrl, 1, showSubFieldName);
}
}
else
{
CreateItemByNavigationNode(this.Items, node);
}
}
}
void CreateItemByNavigationNode(MenuItemCollection items, SPNavigationNode node)
{
MenuItem item = new MenuItem();
item.Text = node.Title ;
item.NavigateUrl = node.Url ;
items.Add(item);
if (node.Children.Count == 0) return;
foreach (SPNavigationNode n in node.Children )
{
CreateItemByNavigationNode(item.ChildItems, n);
}
}
string getSortFieldName(SPList list)
{
if (list.Fields.ContainsField(SORT_FIELD_NAME))
{
SPField f = list.Fields.GetField(SORT_FIELD_NAME);
return f.InternalName;
}
return "";
}
string getShowSubFieldName(SPList list)
{
if (list.Fields.ContainsField( SHOW_SUB_FIELD_NAME ))
{
SPField f = list.Fields.GetField(SHOW_SUB_FIELD_NAME);
return f.InternalName;
}
return "";
}
bool ShowSubFolder(SPFolder folder, string showSubFieldName)
{
SPListItem folderItem = folder.Item;
if (folderItem == null) return true;
if ( folderItem.Fields.ContainsField(showSubFieldName))
{
object value = folder.Item[showSubFieldName];
if( value == null )
return false ;
else
return value.ToString().ToLower() == "true";
}
else
{
return true;
}
}
void CreateItem(SPFolder folder, MenuItemCollection items, string webUrl, int depth, string showSubFieldName)
{
if (!ShowSubFolder(folder, showSubFieldName)) return;
if (folder.SubFolders.Count == 0) return;
foreach (SPFolder f in folder.SubFolders)
{
if (f.Item==null) continue; //说明是隐藏文件夹
MenuItem item = new MenuItem();
item.Text = f.Name;
item.NavigateUrl = f.ServerRelativeUrl;
items.Add(item);
if (depth < this.MaxFolderDepth )
{
CreateItem(f, item.ChildItems, webUrl, depth + 1, showSubFieldName);
}
}
}
void CreateItem(SPFolder folder, MenuItemCollection items, string webUrl, string sortField, int depth, string showSubFieldName)
{
if (!ShowSubFolder(folder, showSubFieldName)) return;
if (folder.SubFolders.Count == 0) return;
IList<SPFolder> folders = SPUtil.GetSortedFolders(folder.SubFolders , sortField );
foreach (SPFolder f in folders)
{
if (f.Item==null) continue;
MenuItem item = new MenuItem();
item.Text = f.Name;
item.NavigateUrl = f.ServerRelativeUrl;
items.Add(item);
if (depth < MaxFolderDepth)
CreateItem(f, item.ChildItems, webUrl, sortField, depth + 1, showSubFieldName);
}
}
}
}
代码中用到的SPUtil类如下:
SPUtil
///<summary>
///
///</summary>
publicclassSPUtil
{
staticpublicboolIsHiddenFolder(SPFolderf)
{
returnf.Item==null;
//returnf.Properties.Count<20;
}
staticpublicstringGetWebUrl(SPWebweb)
{
stringwebUrl=web.Url;
if(!webUrl.EndsWith("/"))
webUrl+="/";
returnwebUrl;
}
publicstaticStringDictionaryGetFieldDictionary(SPListlist)
{
StringDictionaryfields=newStringDictionary();
foreach(SPFieldfinlist.Fields)
{
if(fields.ContainsKey(f.Title))continue;
fields.Add(f.Title,f.InternalName);
}
returnfields;
}
staticpublicint[]GetSelectedItemIDs(SPListlist)
{
returnGetSelectedItemIDs(list.ID.ToString());
}
staticpublicint[]GetSelectedItemIDs(stringlistId)
{
if(System.Web.HttpContext.Current==null)
thrownewSPException("contextisnull.");
stringids=System.Web.HttpContext.Current.Request["spItemSelectionCheckBox_"+listId];
if(ids==null||ids=="")
returnnull;
String[]arr=ids.Split(',');
int[]intIDs=newint[arr.Length];
for(inti=0;i<arr.Length;i++)
{
intIDs[i]=Convert.ToInt32(arr[i]);
}
returnintIDs;
}
///<summary>
///修改视图中的查询条件部分
///</summary>
///<paramname="doc"></param>
///<paramname="query"></param>
staticpublicvoidChangeSchemaXmlQuery(XmlDocumentdoc,stringquery)
{
if(String.IsNullOrEmpty(query))return;
stringinnerQuery=GetInnerQuery(query);
if(innerQuery=="")return;
XmlNodequeryNode=doc.DocumentElement.SelectSingleNode("Query");
//queryNode.InnerXml=qxml;
XmlNodewhereNode=queryNode.SelectSingleNode("Where");
if(whereNode!=null)
queryNode.RemoveChild(whereNode);
XmlNodenewWhereNode=doc.CreateElement("Where");
newWhereNode.InnerXml=innerQuery;
queryNode.AppendChild(newWhereNode);
XmlNodeViewEmptyNode=doc.DocumentElement.SelectSingleNode("ViewEmpty");
ViewEmptyNode.InnerXml="<HTML><![CDATA[<fontcolor='red'><b>未找到符合查询条件的记录。</b></font>]]></HTML>";
}
///<summary>
///设置视图xml中的查询条件
///</summary>
///<paramname="doc"></param>
///<paramname="query1">条件1</param>
///<paramname="query2">条件2</param>
staticpublicvoidChangeSchemaXmlQuery(XmlDocumentdoc,stringquery1,stringquery2)
{
stringquery="";
stringinnerQuery=GetInnerQuery(query1);
stringinnerQuery2=GetInnerQuery(query2);
if(innerQuery==""&&innerQuery2=="")return;
if(innerQuery!=""&&innerQuery2!="")
query="<And>"+innerQuery+innerQuery2+"</And>";
else
query=innerQuery+innerQuery2;
XmlNodequeryNode=doc.DocumentElement.SelectSingleNode("Query");
//queryNode.InnerXml=qxml;
XmlNodewhereNode=queryNode.SelectSingleNode("Where");
if(whereNode!=null)
queryNode.RemoveChild(whereNode);
XmlNodenewWhereNode=doc.CreateElement("Where");
newWhereNode.InnerXml=query;
queryNode.AppendChild(newWhereNode);
XmlNodeViewEmptyNode=doc.DocumentElement.SelectSingleNode("ViewEmpty");
ViewEmptyNode.InnerXml="<HTML><![CDATA[<fontcolor='red'><b>未找到符合查询条件的记录。</b></font>]]></HTML>";
}
staticstringGetInnerQuery(stringq)
{
XmlDocumentdoc=newXmlDocument();
doc.LoadXml("<Query>"+q+"</Query>");
XmlNodewhereNode=doc.DocumentElement.SelectSingleNode("Where");
if(whereNode!=null)
returnwhereNode.InnerXml;
else
return"";
}
///<summary>
///获取ViewXml中的排序结
///</summary>
///<paramname="viewXml"></param>
///<returns></returns>
publicstaticstringGetOrderBySection(stringviewXml)
{
XmlDocumentdoc=newXmlDocument();
doc.LoadXml(viewXml);
XmlNodeListnodes=doc.DocumentElement.GetElementsByTagName("OrderBy");
if(nodes==null||nodes.Count==0)
return"";
else
returnnodes[0].OuterXml;
}
///<summary>
///指定ID的试图是否存在
///</summary>
///<paramname="views"></param>
///<paramname="viewId"></param>
///<returns></returns>
staticpublicboolViewExist(SPViewCollectionviews,GuidviewId)
{
foreach(SPViewviewinviews)
{
if(view.ID==viewId)
returntrue;
}
returnfalse;
}
///<summary>
///获取按名称排序后的子文件夹列表
///</summary>
///<paramname="folders"></param>
///<returns></returns>
staticpublicIList<SPFolder>GetSortedFolders(SPFolderCollectionfolders)
{
List<SPFolder>sortedFolders=newList<SPFolder>();
foreach(SPFolderfinfolders)
{
if(f.Item!=null)//为非系统文件夹
sortedFolders.Add(f);
}
sortedFolders.Sort(CompareFolder);
returnsortedFolders;
}
staticintCompareFolder(SPFolderf1,SPFolderf2)
{
pareTo(f2.Name);
}
///<summary>
///获取按某个字段排序后的子文件夹
///</summary>
///<paramname="folders"></param>
///<paramname="sortFieldName"></param>
///<returns></returns>
staticpublicIList<SPFolder>GetSortedFolders(SPFolderCollectionfolders,stringsortFieldName)
{
List<SPFolder>sortedFolders=newList<SPFolder>();
foreach(SPFolderfinfolders)
{
if(f.Item!=null)//为非系统文件夹
sortedFolders.Add(f);
}
SPFolderComparerc=newSPFolderComparer(sortFieldName);
sortedFolders.Sort(c);
returnsortedFolders;
}
classSPFolderComparer:IComparer<SPFolder>
{
privatestring_sortFieldName;
publicSPFolderComparer(stringsortFieldName)
{
_sortFieldName=sortFieldName;
}
#regionIComparer<SPFolder>成员
publicintCompare(SPFolderx,SPFoldery)
{
//if(IsHiddenFolder(x)||IsHiddenFolder(y))return0;
if(!x.Item.Fields.ContainsField(_sortFieldName)||!y.Item.Fields.ContainsField(_sortFieldName))
return0;
stringsX=""+x.Item[_sortFieldName];
if(sX=="")
sX="0";
stringsY=""+y.Item[_sortFieldName];
if(sY=="")
sY="0";
returnConvert.ToInt32(sX).CompareTo(Convert.ToInt32(sY));
}
#endregion
}
}
WebFolderNavigation 直接继承于Menu,用递规遍历站点的所有列表和子文件夹来创建Menu的子菜单项。考虑到了对列表和文件夹的排序支持,
列表的排序通过站点的导航数据进行,文件夹的排序通过给文件夹添加一个"排序号"的栏来实现(给文件夹添加栏参考:WSS 扩展文件夹的属性--如何给文件夹添加扩展字段 )。
Step4:SharePoint上的服务器控件可以部署到站点下的bin目录,也可以部署到GAC中,经验之谈,建议部署到GAC中,那么首先需要给项目进行签名。
Step5:把dll部署到GAC。
可以直接把debug目录下的dll拖入C:\WINDOWS\assembly目录,也可以写个命令行脚本来自动化操作.开发的过程中要不断的把dll放入GAC,然后重启IIS应用程序池,
所以,最好还是采用脚本来操作:在项目根目录下创建一个文本文件,改名为deployGAC.cmd , 添加以下内容:
echo Adding assemblies to the GAC...
"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\CodeArt.SharePoint.WebControls.dll
iisapp /a "SharePoint - 81" /r
iisapp是重启应用程序池的命令,"SharePoint – 81为应用程序池的名称,请修改为自己开发环境的应用程序池名称。
Step6:利用reflector找到的dll的全名:CodeArt.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9a525f21aa237e5b
Step7:配置SafeControl。
SharePoint站点页面中使用的所有服务器控件必须在应用程序的web.config中进行配置(这样做是为了安全原因,防止普通用户通过站点上传一个dll来运行)。
打开应用程序的web.config文件(一般为C:\Inetpub\wwwroot\wss\VirtualDirectories\XXX\web.cofig),在<SafeControls>节点下添加如下配置:
<SafeControl Assembly=" CodeArt.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9a525f21aa237e5b" Namespace=" CodeArt.SharePoint.WebControls " TypeName="*" Safe="True" />
Step8:WebFolderNavigation应该添加进站点的母板页里。用SPD打开站点,打开要修改的母板页。首先添加dll的Register指令。在<html标签上方添加:
<%@ Register Assembly=" CodeArt.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9a525f21aa237e5b" Namespace=" CodeArt.SharePoint.WebControls " TagPrefix="codeart" %>
Step9:我们修改的是导航控件,所有,需要把WebFolderNavigation放入<Sharepoint:SPNavigationManager这个服务器控件内部:
<Sharepoint:SPNavigationManager
id="QuickLaunchNavigationManager"
runat="server"
QuickLaunchControlId="SiteNavigation1"
ContainedControl="QuickLaunch"
EnableViewState="false">
< codeart: WebFolderNavigation ID="WebFolderNavigation1" runat="server" />
</Sharepoint:SPNavigationManager>
Step10:保存并发布母板页。
Game over.
注意:CodeArt.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9a525f21aa237e5b中的PublicKeyToken值请用自己dll的值。