需求:
在公司使用SharePoint的过程中,常常有这样的需求,要为一个Document Library或者List中的某一个Item进行打分,投票,根据投票高低来作决策。 比如优秀员工的评比,用户可以直接在页面上点击投票按钮,并得到当前的投票状态。
现状:
在CodePlex上找了一个现成的Content type(HighLight), 安装使用后,发现普通用户不太会使用,而且投票页面没有直接的结果显示,不能完全满足用户的需求,所以想自己开发一个custom field,让普通用户可以象添加一般的field一样添加,并且容易使用。
开发:
要实现投票的功能,需要开发2个相关联的custom field。一个负责记录当前所有已投票的用户的信息,这样防止用户多次点击后重复投票,我们称这个field为voting field。另一个field负责计数,显示当前总共的投票数,称为Voting Result field。可能有人要问,为什么不把这两个合而为一呢? 这里主要考虑到排序的问题,因为要记录用户投票信息的字段的type不能是text的,因为text类型有255的长度限制,只能是note型,而note型是不支持在view的页面中排序的。
下面详细介绍具体的开发
1 Voting Field
首先是字段类ItemVoting的代码


2 {
3 private static string [] CustomPropertyNames = new string [] { " FieldNameProperty " };
4
5 public ItemVoting(SPFieldCollection fields, string fieldName)
6 : base (fields, fieldName)
7 {
8 InitProperties();
9 this .ShowInNewForm = true ;
10 this .ShowInEditForm = true ;
11
12 }
13
14 public ItemVoting(SPFieldCollection fields, string typeName, string displayName)
15 : base (fields, typeName, displayName)
16 {
17 InitProperties();
18 this .ShowInNewForm = true ;
19 this .ShowInEditForm = true ;
20 }
21
22 #region Property storage and bug workarounds - do not edit
23
24 /// <summary>
25 /// Indicates that the field is being created rather than edited. This is necessary to
26 /// work around some bugs in field creation.
27 /// </summary>
28 public bool IsNew
29 {
30 get { return _IsNew; }
31 set { _IsNew = value; }
32 }
33 private bool _IsNew = false ;
34
35 /// <summary>
36 /// Backing fields for custom properties. Using a dictionary to make it easier to abstract
37 /// details of working around SharePoint bugs.
38 /// </summary>
39 private Dictionary < string , string > CustomProperties = new Dictionary < string , string > ();
40
41 /// <summary>
42 /// Static store to transfer custom properties between instances. This is needed to allow
43 /// correct saving of custom properties when a field is created - the custom property
44 /// implementation is not used by any out of box SharePoint features so is really buggy.
45 /// </summary>
46 private static Dictionary < string , string > CustomPropertiesForNewFields = new Dictionary < string , string > ();
47
48 /// <summary>
49 /// Initialise backing fields from base property store
50 /// </summary>
51 private void InitProperties()
52 {
53 foreach ( string propertyName in CustomPropertyNames)
54 {
55 CustomProperties[propertyName] = base .GetCustomProperty(propertyName) + "" ;
56 }
57 }
58
59 /// <summary>
60 /// Take properties from either the backing fields or the static store and
61 /// put them in the base property store
62 /// </summary>
63 private void SaveProperties()
64 {
65 foreach ( string propertyName in CustomPropertyNames)
66 {
67 base .SetCustomProperty(propertyName, GetCustomProperty(propertyName));
68 }
69 }
70
71 /// <summary>
72 /// Get an identifier for the field being added/edited that will be unique even if
73 /// another user is editing a property of the same name.
74 /// </summary>
75 /// <param name="propertyName"></param>
76 /// <returns></returns>
77 private string GetCacheKey( string propertyName)
78 {
79 return SPContext.Current.GetHashCode() + " _ " + (ParentList == null ? " SITE " : ParentList.ID.ToString()) + " _ " + propertyName;
80 }
81
82 /// <summary>
83 /// Replace the buggy base implementation of SetCustomProperty
84 /// </summary>
85 /// <param name="propertyName"></param>
86 /// <param name="propertyValue"></param>
87 new public void SetCustomProperty( string propertyName, object propertyValue)
88 {
89 if (IsNew)
90 {
91 // field is being added - need to put property in cache
92 CustomPropertiesForNewFields[GetCacheKey(propertyName)] = propertyValue + "" ;
93 }
94
95 CustomProperties[propertyName] = propertyValue + "" ;
96 }
97
98 /// <summary>
99 /// Replace the buggy base implementation of GetCustomProperty
100 /// </summary>
101 /// <param name="propertyName"></param>
102 /// <param name="propertyValue"></param>
103 new public object GetCustomProperty( string propertyName)
104 {
105 if ( ! IsNew && CustomPropertiesForNewFields.ContainsKey(GetCacheKey(propertyName)))
106 {
107 string s = CustomPropertiesForNewFields[GetCacheKey(propertyName)];
108 CustomPropertiesForNewFields.Remove(GetCacheKey(propertyName));
109 CustomProperties[propertyName] = s;
110 return s;
111 }
112 else
113 {
114 return CustomProperties[propertyName];
115 }
116 }
117
118 /// <summary>
119 /// Called when a field is created. Without this, update is not called and custom properties
120 /// are not saved.
121 /// </summary>
122 /// <param name="op"></param>
123 public override void OnAdded(SPAddFieldOptions op)
124 {
125 base .OnAdded(op);
126 Update();
127 }
128
129 #endregion
130
131
132 public override BaseFieldControl FieldRenderingControl
133 {
134 get
135 {
136 BaseFieldControl fieldControl = new ItemVotingControl( this );
137 fieldControl.FieldName = InternalName;
138 if (String.IsNullOrEmpty(fieldControl.ItemFieldValue as string ))
139 {
140 fieldControl.ItemFieldValue = " <Root><Item><CurrentFieldName Type=\ " String\ " >[ " + fieldControl.FieldName + " ]</CurrentFieldName><Account Type=\ " String\ " >ads\\mossadmin</Account></Item></Root> " ;
141 }
142 return fieldControl;
143 }
144 }
145
146 public override void Update()
147 {
148 SaveProperties();
149 base .Update();
150 }
151
152 public string FieldNameProperty
153 {
154 get { return this .GetCustomProperty( " HiddenFieldName " ) + "" ; }
155 set { this .SetCustomProperty( " HiddenFieldName " , value); }
156 }
157
158
159
160 }
为了能够在view页面得到当前字段的internal name(需要提供给投票处理页面voting.aspx),我们为字段赋的初始值中包含了当前field的internal name( fieldControl.ItemFieldValue = "<Root><Item><CurrentFieldName Type=\"String\">[" + fieldControl.FieldName + "]</CurrentFieldName><Account Type=\"String\">ads\\mossadmin</Account></Item></Root>";),为了在页面中,能够使得我们开发的2个自定义字段发生联系,我们在自定义字段的xml文件中为这个字段中添加了一个field property-- HiddenFieldName
接下来是呈现类代码:


{
private ItemVoting field;
private List < Voter > voterList;
private LiteralControl ltcNumber;
private bool isVoted = false ;
public ItemVotingControl(ItemVoting parentField)
{
this .field = parentField;
}
protected override void OnInit(EventArgs e)
{
base .OnInit(e);
if ( this .ListItemFieldValue != null )
{
this .voterList = SerializeUtility.Deserialize( this .ListItemFieldValue.ToString());
}
else
{
this .voterList = new List < Voter > ();
}
}
protected override void CreateChildControls()
{
ltcNumber = new LiteralControl();
this .Controls.Add(ltcNumber);
}
public override void UpdateFieldValueInItem()
{
}
protected override void RenderFieldForDisplay(HtmlTextWriter output)
{
int countValue = voterList.Count > 0 ? voterList.Count - 1 : voterList.Count;
output.Write(countValue.ToString());
}
protected override void Render(HtmlTextWriter output)
{
RenderFieldForDisplay(output);
}
}
呈现控件取出了序列化后的投票结果,并反序列化,然后显示. 注意这里的显示只是指在New.aspx,edit.aspx,display.aspx这三个页面的显示,当然用户要求直接在view的页面上投票,所以我们要提供字段在view页面的现示实现。于是我们修改了field_字段.xml文件中的<RenderPattern Name="DisplayPattern">节。
下面是完整的field_字段.xml 文件:


2 < FieldTypes >
3 < FieldType >
4 < Field Name ="TypeName" > ItemVoting </ Field >
5 < Field Name ="ParentType" > Note </ Field >
6 < Field Name ="TypeDisplayName" > Item Voting </ Field >
7 < Field Name ="TypeShortDescription" > Item Voting </ Field >
8 < Field Name ="UserCreatable" > TRUE </ Field >
9 < Field Name ="Sortable" > TRUE </ Field >
10 < Field Name ="AllowBaseTypeRendering" > TRUE </ Field >
11 < Field Name ="Filterable" > TRUE </ Field >
12 < Field Name ="FieldTypeClass" > AECSharePoint.Field.Voting.ItemVoting, AECSharePoint.Field.Voting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c8afc6fd77dac842 </ Field >
13 < PropertySchema >
14 < Fields >
15 < Field Hidden ="FALSE" Name ="HiddenFieldName"
16 DisplayName ="My Custom Property"
17 Type ="Text" >
18 </ Field >
19 </ Fields >
20 < Fields ></ Fields >
21 </ PropertySchema >
22 < RenderPattern Name ="DisplayPattern" >
23 < HTML >
24 <![CDATA[
25 <a id='linkVoting' href="javascript:window.location= SetLocation(' ]]> </ HTML >
26 < ListProperty Select ="Name" />
27 < HTML >
28 <![CDATA[ ',' ]]>
29 </ HTML >
30 < ListProperty Select ="Title" />
31 < HTML >
32 <![CDATA[ ',' ]]>
33 </ HTML >
34 < Column Name ="ID" />
35 < HTML >
36 <![CDATA[ ',' ]]>
37 </ HTML >
38 < ListProperty Select ="DefaultViewUrl" />
39 < HTML >
40 <![CDATA[ ',' ]]>
41 </ HTML >
42 < Column HTMLEncode ="TRUE" />
43 < HTML >
44 <![CDATA[ ');"><img onerror="SetLinkDisplayContent(' ]]>
45 </ HTML >
46 < Column HTMLEncode ="TRUE" />
47 < HTML >
48 <![CDATA[ ',this);" border=0 src="#"><div align="center" id="divCount" style="clear:left;display:inline;"></div></a> ]]>
49 </ HTML >
50 < HTML >
51 <![CDATA[
52 <script>
53 function SetLocation(listName,listTitle, itemID, listViewUrl, fieldValue)
54 {
55 var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
56 xmlDoc.loadXML(fieldValue);
57 var fieldNameNode = xmlDoc.documentElement.selectSingleNode("//Item/CurrentFieldName");
58 var fieldName = fieldNameNode.text;
59 var start = fieldName.indexOf("[");
60 var end = fieldName.indexOf("]");
61
62 fieldName = fieldName.substring(start+1, end);
63
64 var queryStr = "?listName=" + listName + "&itemID=" + itemID + "&fieldName=" + fieldName + "&Source=" + window.location;
65 var currentSiteURL = "http://" + document.location.host;
66
67 var endIndex = listViewUrl.indexOf("/Lists/");
68 if(endIndex == -1)
69 {
70 endIndex = listViewUrl.indexOf("/" + listTitle + "/")
71 }
72 var subUrl = listViewUrl.substring(0,endIndex);
73 subUrl += "/_layouts/AECSharePoint/Voting.aspx";
74 currentSiteURL += subUrl + queryStr;
75 return currentSiteURL;
76 }
77
78 function SetLinkDisplayContent(fieldValue,o)
79 {
80
81 o.src = "/_layouts/AECSharePoint/voting.gif";
82 }
83
84 </script>
85 ]]>
86 </ HTML >
87 </ RenderPattern >
88 </ FieldType >
89 </ FieldTypes >
90
91
在 <RenderPattern Name="DisplayPattern"> 中我们使得字段在view页面显示为一个图标,点击图标后跳转到我们开发的一个application page,同时为这个page传输必要的listid,itemid等信息,此页面处理此次投票,保存投票结果作为字段的值。
下面是我们放在Template/Layout/下的自定义application page,也就是上面xml中提到的voting.aspx 代码:


<% @ Page Language = " C# " Inherits = " Microsoft.SharePoint.WebControls.LayoutsPageBase " MasterPageFile = " ~/_layouts/application.master " %>
<% @ Import Namespace = " Microsoft.SharePoint " %>
<% @ Import Namespace = " Microsoft.SharePoint.WebControls " %>
<% @ Import Namespace = " System.Xml " %>
<% @ Import Namespace = " System.Reflection " %>
<% @ Import Namespace = " System.Collections.Generic " %>
<% @ Import Namespace = " System.Diagnostics " %>
< script runat = " server " >
public Dictionary < string , object > initialParam = null ;
private List < Voter > voterList;
private bool isVoted = false ;
class Voter
{
private string _currentFieldName;
private string _account;
public string CurrentFieldName
{
get
{
return this ._currentFieldName;
}
set
{
this ._currentFieldName = value;
}
}
public string Account
{
get
{
return this ._account;
}
set
{
this ._account = value;
}
}
}
#region // Serialize&deserialize the voting result
class SerializeUtility
{
public static string Serialize(List < Voter > GenericList)
{
XmlDocument result = new XmlDocument();
result.LoadXml( " <Root></Root> " );
foreach (Voter obj in GenericList)
{
XmlElement Item = result.CreateElement( " Item " );
PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.GetValue(obj, null ) != null )
{
XmlElement element = result.CreateElement(property.Name);
element.SetAttribute( " Type " , property.PropertyType.Name);
element.InnerText = property.GetValue(obj, null ).ToString();
Item.AppendChild(element);
}
}
result.DocumentElement.AppendChild(Item);
}
return result.InnerXml;
}
public static List < Voter > Deserialize( string XmlStr)
{
List < Voter > result = new List < Voter > ();
XmlDocument XmlDoc = new XmlDocument();
XmlDoc.LoadXml(XmlStr);
foreach (XmlNode ItemNode in XmlDoc.GetElementsByTagName( " Root " ).Item( 0 ).ChildNodes)
{
Voter item = Activator.CreateInstance < Voter > ();
PropertyInfo[] properties = typeof (Voter).GetProperties();
foreach (XmlNode propertyNode in ItemNode.ChildNodes)
{
string name = propertyNode.Name;
string type = propertyNode.Attributes[ " Type " ].Value;
string value = propertyNode.InnerXml;
foreach (PropertyInfo property in properties)
{
if (name == property.Name)
{
property.SetValue(item, Convert.ChangeType(value, property.PropertyType), null );
}
}
}
result.Add(item);
}
return result;
}
}
#endregion
class VotingBiz
{
internal Voter GetCurrentVoter( string fieldName, string account)
{
Voter currentVoter = new Voter();
currentVoter.Account = account;
currentVoter.CurrentFieldName = string .Empty;
return currentVoter;
}
internal void UpdateListFieldValueWithElevatedPrivileges(Guid listID, int itemID, Dictionary < string , object > dicFieldValue)
{
if (SPContext.Current == null )
return ;
SPSecurity.RunWithElevatedPrivileges( delegate ()
{
using (SPSite site = new SPSite(SPContext.Current.Site.ID))
{
using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
{
web.AllowUnsafeUpdates = true ;
SPList list = web.Lists[listID];
SPListItem item = list.GetItemById(itemID);
if (item == null )
{
return ;
}
else
{
foreach (KeyValuePair < string , object > singleItem in dicFieldValue)
{
item[singleItem.Key] = singleItem.Value;
}
item.SystemUpdate( false );
}
}
}
});
}
internal bool CheckIsItemVoted( object itemValue, string fieldName, string account)
{
VotingBiz voteMgnt = new VotingBiz();
List < Voter > voterList = null ;
Voter currentVoter = voteMgnt.GetCurrentVoter(fieldName, account);
if (itemValue == null )
{
return false ;
}
else
{
voterList = SerializeUtility.Deserialize(itemValue.ToString());
// if (itemValue.ToString().IndexOf(currentVoter.Account) != -1)
// {
// return true;
// }
// else
// {
// return false;
// }
if ( ! voterList.Exists( delegate (Voter v) { return v.Account == currentVoter.Account; }))
{
return false ;
}
else
{
return true ;
}
}
// if (!voterList.Exists(delegate(Voter v) { return v.Account == currentVoter.Account; }))
// {
// return false;
// }
// else
// {
// return true;
// }
}
}
protected override void OnLoad(EventArgs e)
{
InitialParamter();
}
protected void InitialParamter()
{
try
{
initialParam = new Dictionary < string , object > ();
initialParam.Add( " listName " , Request.QueryString[ " listName " ].ToString());
initialParam[ " itemID " ] = Request.QueryString[ " itemID " ].ToString();
initialParam[ " fieldName " ] = Request.QueryString[ " fieldName " ].ToString();
initialParam[ " fieldValue " ] = GetItemValue(initialParam[ " fieldName " ] as string , initialParam[ " listName " ] as string , Int32.Parse(initialParam[ " itemID " ] as string ));
}
catch (Exception ex)
{
EventLog.WriteEntry( " Voting Field " , SPContext.Current.Web.Title + " : " + ex.Message + " -- " + ex.StackTrace);
Response.Write( " Error happend, please contact site admin " );
}
}
protected object GetItemValue( string fieldName, string listName, int itemId)
{
using (SPSite site = new SPSite(SPContext.Current.Site.ID))
{
using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
{
Guid listId = new Guid(listName);
SPList list = web.Lists[listId];
SPListItem item = list.GetItemById(itemId);
return item[fieldName];
}
}
}
private string GetVotingResultFieldName(Guid listId)
{
if (SPContext.Current == null )
return null ;
string votingFieldName = String.Empty;
using (SPSite site = new SPSite(SPContext.Current.Site.ID))
{
using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
{
SPList list = web.Lists[listId];
foreach (SPField field in list.Fields)
{
if ( null != field.GetCustomProperty( " FlagFieldNameProperty " ))
{
votingFieldName = field.InternalName;
break ;
}
}
}
}
return votingFieldName;
}
protected void btnVote_Click( object sender, EventArgs e)
{
VotingBiz voteMgnt = new VotingBiz();
if ( null != initialParam)
{
if ( ! voteMgnt.CheckIsItemVoted(initialParam[ " fieldValue " ], initialParam[ " fieldName " ] as string , SPContext.Current.Web.CurrentUser.ID.ToString()))
{
Voter currentVoter = voteMgnt.GetCurrentVoter(initialParam[ " fieldName " ] as string , SPContext.Current.Web.CurrentUser.ID.ToString());
if (initialParam[ " fieldValue " ] != null )
{
this .voterList = SerializeUtility.Deserialize(initialParam[ " fieldValue " ] as string );
}
voterList.Add(currentVoter);
Guid listId = new Guid(initialParam[ " listName " ] as string );
int itemId = Int32.Parse(initialParam[ " itemID " ] as string );
string fieldName = initialParam[ " fieldName " ] as string ;
string votingResultFieldName = GetVotingResultFieldName(listId);
Dictionary < string , object > fieldNameValuePairs = new Dictionary < string , object > ();
fieldNameValuePairs[fieldName] = SerializeUtility.Serialize(voterList);
if ( ! String.IsNullOrEmpty(votingResultFieldName))
{
fieldNameValuePairs[votingResultFieldName] = voterList.Count - 1 ;
}
voteMgnt.UpdateListFieldValueWithElevatedPrivileges(listId, itemId, fieldNameValuePairs);
this .isVoted = true ;
}
}
ReturnToSourcePage();
}
private void ReturnToSourcePage()
{
string sourceUrl = Request.QueryString[ " Source " ].ToString();
Response.Redirect(sourceUrl);
}
protected void btnBack_Click( object sender, EventArgs e)
{
ReturnToSourcePage();
}
</ script >
< asp:Content ID = " Content1 " ContentPlaceHolderID = " PlaceHolderPageTitleInTitleArea " runat = " server " >
< h >
Vote Here
</ h >
</ asp:Content >
< asp:Content ID = " Content2 " ContentPlaceHolderID = " PlaceHolderMain " runat = " server " >
< div >
Are you sure to vote this item ? < br />
< b > Please note that your vote would be counted only once even if you vote again !</ b >
< br />
< br />
< asp:Button ID = " btnVote " runat = " server " Text = " Vote " OnClick = " btnVote_Click " class = " ms-ButtonHeightWidth " Width = " 80px " />
< asp:Button ID = " btnBack " runat = " server " Text = " Cancel " OnClick = " btnBack_Click " class = " ms-ButtonHeightWidth " Width = " 80px " />
</ div >
</ asp:Content >
下面介绍统计投票的字段voting result field
1.字段类代码


2 {
3 private static string [] CustomPropertyNames = new string [] { " MyCustomProperty " };
4
5 public ItemVotingResult(SPFieldCollection fields, string fieldName)
6 : base (fields, fieldName)
7 {
8 InitProperties();
9 }
10
11 public ItemVotingResult(SPFieldCollection fields, string typeName, string displayName)
12 : base (fields, typeName, displayName)
13 {
14 InitProperties();
15 }
16
17 #region Property storage and bug workarounds - do not edit
18
19 /// <summary>
20 /// Indicates that the field is being created rather than edited. This is necessary to
21 /// work around some bugs in field creation.
22 /// </summary>
23 public bool IsNew
24 {
25 get { return _IsNew; }
26 set { _IsNew = value; }
27 }
28 private bool _IsNew = false ;
29
30 /// <summary>
31 /// Backing fields for custom properties. Using a dictionary to make it easier to abstract
32 /// details of working around SharePoint bugs.
33 /// </summary>
34 private Dictionary < string , string > CustomProperties = new Dictionary < string , string > ();
35
36 /// <summary>
37 /// Static store to transfer custom properties between instances. This is needed to allow
38 /// correct saving of custom properties when a field is created - the custom property
39 /// implementation is not used by any out of box SharePoint features so is really buggy.
40 /// </summary>
41 private static Dictionary < string , string > CustomPropertiesForNewFields = new Dictionary < string , string > ();
42
43 /// <summary>
44 /// Initialise backing fields from base property store
45 /// </summary>
46 private void InitProperties()
47 {
48 foreach ( string propertyName in CustomPropertyNames)
49 {
50 CustomProperties[propertyName] = base .GetCustomProperty(propertyName) + "" ;
51 }
52 }
53
54 /// <summary>
55 /// Take properties from either the backing fields or the static store and
56 /// put them in the base property store
57 /// </summary>
58 private void SaveProperties()
59 {
60 foreach ( string propertyName in CustomPropertyNames)
61 {
62 base .SetCustomProperty(propertyName, GetCustomProperty(propertyName));
63 }
64 }
65
66 /// <summary>
67 /// Get an identifier for the field being added/edited that will be unique even if
68 /// another user is editing a property of the same name.
69 /// </summary>
70 /// <param name="propertyName"></param>
71 /// <returns></returns>
72 private string GetCacheKey( string propertyName)
73 {
74 return SPContext.Current.GetHashCode() + " _ " + (ParentList == null ? " SITE " : ParentList.ID.ToString()) + " _ " + propertyName;
75 }
76
77 /// <summary>
78 /// Replace the buggy base implementation of SetCustomProperty
79 /// </summary>
80 /// <param name="propertyName"></param>
81 /// <param name="propertyValue"></param>
82 new public void SetCustomProperty( string propertyName, object propertyValue)
83 {
84 if (IsNew)
85 {
86 // field is being added - need to put property in cache
87 CustomPropertiesForNewFields[GetCacheKey(propertyName)] = propertyValue + "" ;
88 }
89
90 CustomProperties[propertyName] = propertyValue + "" ;
91 }
92
93 /// <summary>
94 /// Replace the buggy base implementation of GetCustomProperty
95 /// </summary>
96 /// <param name="propertyName"></param>
97 /// <param name="propertyValue"></param>
98 new public object GetCustomProperty( string propertyName)
99 {
100 if ( ! IsNew && CustomPropertiesForNewFields.ContainsKey(GetCacheKey(propertyName)))
101 {
102 string s = CustomPropertiesForNewFields[GetCacheKey(propertyName)];
103 CustomPropertiesForNewFields.Remove(GetCacheKey(propertyName));
104 CustomProperties[propertyName] = s;
105 return s;
106 }
107 else
108 {
109 return CustomProperties[propertyName];
110 }
111 }
112
113 /// <summary>
114 /// Called when a field is created. Without this, update is not called and custom properties
115 /// are not saved.
116 /// </summary>
117 /// <param name="op"></param>
118 public override void OnAdded(SPAddFieldOptions op)
119 {
120 base .OnAdded(op);
121 Update();
122 }
123
124 #endregion
125
126
127 public override BaseFieldControl FieldRenderingControl
128 {
129 get
130 {
131 BaseFieldControl fieldControl = new ItemVotingResultControl( this );
132
133 fieldControl.FieldName = InternalName;
134
135 return fieldControl;
136 }
137 }
138
139 public override void Update()
140 {
141 SaveProperties();
142 base .Update();
143 }
144
145 public string MyCustomProperty
146 {
147 get { return this .GetCustomProperty( " FlagFieldNameProperty " ) + "" ; }
148 set { this .SetCustomProperty( " FlagFieldNameProperty " , value); }
149 }
150
151 }
为了能够在投票处理页面voting.aspx中找到此字段,并将结果保存到此字段,我们加了field property--FlagFieldNameProperty
2 字段显示类


{
private ItemVotingResult field;
private LiteralControl lcResult = null ;
public ItemVotingResultControl(ItemVotingResult parentField)
{
this .field = parentField;
}
protected override void OnInit(EventArgs e)
{
base .OnInit(e);
}
protected override void CreateChildControls()
{
base .CreateChildControls();
lcResult = new LiteralControl();
object currentResult = GetItemVotingResult();
if ( null != currentResult)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(currentResult.ToString());
lcResult.Text = (xmlDoc.DocumentElement.ChildNodes.Count - 1 ) + "" ;
}
else
{
lcResult.Text = " 0 " ;
}
}
public override void UpdateFieldValueInItem()
{
}
protected override void Render(HtmlTextWriter output)
{
lcResult.RenderControl(output);
}
}
3 field_VotingResult.xml


< FieldTypes >
< FieldType >
< Field Name = " TypeName " > ItemVotingResult </ Field >
< Field Name = " ParentType " > Text </ Field >
< Field Name = " TypeDisplayName " > Item Voting Result </ Field >
< Field Name = " TypeShortDescription " > Item Voting Result </ Field >
< Field Name = " UserCreatable " > TRUE </ Field >
< Field Name = " Sortable " > TRUE </ Field >
< Field Name = " AllowBaseTypeRendering " > TRUE </ Field >
< Field Name = " Filterable " > TRUE </ Field >
< Field Name = " FieldTypeClass " > AECSharePoint.Field.VotingResult.ItemVotingResult, AECSharePoint.Field.VotingResult, Version = 1.0 . 0.0 , Culture = neutral, PublicKeyToken = 09a5c45e643a0621 </ Field >
< PropertySchema >
< Fields >
< Field Hidden = " FALSE " Name = " FlagFieldNameProperty "
DisplayName = " My Custom Property "
Type = " Text " >
</ Field >
</ Fields >
< Fields ></ Fields >
</ PropertySchema >
< RenderPattern Name = " DisplayPattern " >
< Column />
</ RenderPattern >
</ FieldType >
</ FieldTypes >
这个字段主要用来现示投票结果,比较简单。
主要效果图:
投票页面:
点击vote后跳转到投票处理页面Voting.aspx
点击OK完成投票。注意同一用户投票只记录一次,这两个custom field同时支持List和Document Library。
代码和安装包如下:
安装包:/Files/tonnie/Deployfile.rar
欢迎分享和指正!