經過上一篇文章
WSS頁面定製系列(2)---定製單個列表的表單頁面您應該瞭解到瞭如何定製列表那些查看,新增,修改的頁面。可是隻限於頁面佈局。
若是須要修改保存邏輯應該怎麼作呢?
這個需求仍是很常見的,好比,保存以前作一些校驗,保存以後重定向到某個頁面。
系統模板裏面負責保存操做的是以下的控件:

<SharePoint:SaveButton runat="server"/>
這個控件位於Microsoft.SharePoint.WebControls名稱控件。咱們用reflector找到關鍵代碼:

OnBubbleEvent

[SharePointPermission(SecurityAction.Demand, ObjectModel=true)]

protected override bool OnBubbleEvent(object source, EventArgs e)

{

SPListItem listItem;

string redirectUrl;

bool flag = false;

if (e is CommandEventArgs)

{

CommandEventArgs args = (CommandEventArgs) e;

if (!(args.CommandName == "SaveItem"))

{

return flag;

}

listItem = base.ItemContext.ListItem;

if ((listItem != null) && (base.ItemContext.ContentType != null))

{

try

{

listItem["ContentType"] = base.ItemContext.ContentType.Name;

listItem.SetExtraInfo("ContentTypeId", base.ItemContext.ContentType.Id.ToString(), "");

}

catch (ArgumentException)

{

}

}

}

else

{

return flag;

}

this.Page.Validate();

if (this.Page.IsValid)

{

bool flag2 = false;

EventHandler onSaveHandler = base.ItemContext.FormContext.OnSaveHandler;

if (onSaveHandler == null)

{

if (base.List.BaseTemplate != SPListTemplateType.Survey)

{

flag2 = this.SaveItem();

}

else if (base.ItemContext.FormContext.NextFieldName != null)

{

if (base.ControlMode != SPControlMode.New)

{

flag2 = this.SaveItem();

}

else

{

listItem.Checkout();

flag2 = true;

}

}

else

{

listItem.Checkin();

flag2 = true;

}

}

else

{

onSaveHandler(this, EventArgs.Empty);

flag2 = true;

}

flag = true;

if (!flag2)

{

return flag;

}

redirectUrl = base.RedirectUrl;

if (((base.ItemContext.List.BaseTemplate == SPListTemplateType.Events) || ((base.ItemContext.ContentType != null) && base.ItemContext.ContentType.Id.IsChildOf(SPBuiltInContentTypeId.Event))) && base.ItemContext.FormContext.WantRedirectForMWS)

{

if ((base.ItemContext.FormContext.FormMode == SPControlMode.New) && base.ItemContext.FormContext.NeedIDForNewMWS)

{

redirectUrl = redirectUrl + "&Item=" + base.ItemContext.ListItem.ID.ToString(CultureInfo.InvariantCulture);

}

SPUtility.Redirect(redirectUrl, SPRedirectFlags.Default, this.Context);

return flag;

}

}

else

{

return true;

}

if (base.ItemContext.List.BaseTemplate == SPListTemplateType.WebPageLibrary)

{

redirectUrl = ((SPListItem) base.Item).File.ServerRelativeUrl;

}

SPUtility.Redirect(redirectUrl, SPRedirectFlags.UseSource, this.Context);

return flag;

}

SaveItem

public static bool SaveItem(SPContext itemContext, bool uploadMode, string checkInComment)

{

if (itemContext == null)

{

throw new ArgumentNullException("itemContext");

}

if (checkInComment == null)

{

throw new ArgumentNullException("checkInComment");

}

ActionBeforeSaveItem(itemContext);

if ((itemContext.FormContext != null) && (itemContext.FormContext.FormMode == SPControlMode.New))

{

SPFolder rootFolder = null;

string rootFolderUrl = itemContext.RootFolderUrl;

if ((!string.IsNullOrEmpty(rootFolderUrl) && (itemContext.List != null)) && (itemContext.List.ParentWeb != null))

{

rootFolder = itemContext.List.ParentWeb.GetFolder(rootFolderUrl);

}

if (rootFolder == null)

{

rootFolder = itemContext.List.RootFolder;

}

if (itemContext.FormContext.IsFolder)

{

return CreateFolder(rootFolder, itemContext);

}

}

else if (!uploadMode)

{

if (itemContext.Item == null)

{

throw new InvalidOperationException();

}

try

{

itemContext.Item.Update();

goto Label_01C8;

}

catch (SPException exception)

{

SPList list = itemContext.List;

if ((exception.ErrorCode == -2130575305) && (list != null))

{

while (list.BaseTemplate != SPListTemplateType.WebPageLibrary)

{

if (list.BaseType == SPBaseType.DocumentLibrary)

{

goto Label_00B8;

}

break;

}

throw new SPException(SPResource.GetString("ListVersionMismatch", new object[0]));

}

Label_00B8:

throw;

}

}

else

{

SPListItem item = itemContext.Item as SPListItem;

if (!SPUtility.IsCheckedOut(item))

{

item.UpdateOverwriteVersion();

}

else

{

item.Update();

item.File.CheckIn(checkInComment);

}

goto Label_01C8;

}

if (itemContext.List.BaseType == SPBaseType.DocumentLibrary)

{

throw new InvalidOperationException();

}

if (itemContext.Item == null)

{

throw new InvalidOperationException();

}

itemContext.Item.Update();

Label_01C8:

HandleNonFatalError(itemContext);

return true;

}
SaveButton 控件也是採用模板實現的,它的模板以下 :

<SharePoint:RenderingTemplate ID="SaveButton" runat="server">

<Template>

<TABLE cellpadding=0 cellspacing=0 ;<TR><TD align="<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,multipages_direction_right_align_value%>' EncodeMethod='HtmlEncode'/>" width=100% nowrap>

<asp:Button UseSubmitBehavior="false" ID=diidIOSaveItem CommandName="SaveItem" Text="<%$Resources:wss,tb_save%>" class="ms-ButtonHeightWidth" accesskey="<%$Resources:wss,tb_save_AK%>" target="_self" runat="server"/>

</TD> </TR> </TABLE>

</Template>

</SharePoint:RenderingTemplate>
這裏有一點有趣的東西,SaveButton利用了asp.net裏面的事件冒泡機制,即子控件產生事件(就是那個CommandName="SaveItem"
的Button),但並不處理,而是把事件「冒上去」,由父控件進行攔截處理(OnBubbleEvent方法)。
(既然是這樣,咱們用一個圖片按鈕來取代那個diidIOSaveItem應該也是能夠的,只要ID和CommandName不變。)
言歸正傳---------------我第一次看到SaveButton控件的核心代碼的時候,感到有點暈,太複雜了~ 並且,裏面的不少內部方法都是私有的!
怎麼改?
一種方法:繼承SaveButton,重載OnBubbleEvent方法。對私有的方法,咱們所有采用反射來調用。
這種方法實際上是可行的,可是好像有一點「醜陋」。
第二種方法:咱們是否是能夠簡化它的代碼?咱們重寫的按鈕只是用在有限的地方,不須要考慮的那麼全面。
當我悶頭探索第二種方法的時候,想到了第三種方法--我不要管它原來的代碼,只接調用ListItem的Add方法不行嗎?只要取到當前的全部字段,遍歷便可。
取列表的全部字段,能夠調用基類的 this.ItemContext.FormContext.FieldControlCollection類得到。
下面是實現代碼:

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.Security;

using System.Web.UI;

using Microsoft.SharePoint.WebControls;

using System.Security.Permissions;

using Microsoft.SharePoint.Utilities

namespace CodeArt.SharePoint

{

/// <summary>

/// 列表表單保存按鈕,保存後返回本頁面,

/// </summary>

public class FormSaveButton : SaveButton

{

//return true stop event bubble

//return false cotinue

protected override bool OnBubbleEvent(object source, EventArgs e)

{

this.Page.Validate();

if (!this.Page.IsValid)

{

return true ;

}
//some valid code

try

{

SaveData();

return true ;

}

catch (Exception ex)

{

throw new SPException(ex.Message,ex);

}

Page.Response.Redirect("/");

}

void SaveData()

{

SPListItem listItem;

if (this.ControlMode == SPControlMode.New)

listItem = this.List.Items.Add();

else

listItem = this.ListItem;

foreach (BaseFieldControl f in this.ItemContext.FormContext.FieldControlCollection)

{

try

{
//some valid code here --if(f.FieldName="XX") do something...

if (!f.Field.ReadOnlyField)

listItem[f.FieldName] = f.Value;

}

catch (ArgumentException) { }

}

listItem.Update();

}

}

}
這個FormSaveButton 的保存先後的行爲能夠由咱們任意控制了。把它編譯成dll,而後嵌入RenderingTemplate便可。
附:
SaveButton,FormField這類控件能夠稱爲「表單控件」,它們實現對列表表單的操做,或者是呈現一個字段,後者是顯示一個保存按鈕,或者是來迭代生成頁面。
它們的繼承關係以下:
SaveButton 》 FormComponent 》 TemplateBasedControl》SPControl》 Control
FormField 》 BaseFieldControl 》 FieldMetadata》FormComponent
》 TemplateBasedControl 》SPControl》 Control
全部的「表單控件」都繼承於
TemplateBasedControl,均可以經過修改模板或重載替換已有控件來控制它的內容。