经过上一篇文章
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,均可以经过修改模板或重载替换已有控件来控制它的内容。