Saturday, June 26, 2010

Adding a custom control to the SharePoint 2010 Team Site Wiki Page Template ( wkpstd.aspx ) programmatically

Topic

This is maybe my last post about the Team Site Wiki Page customization with the aim of exceeding its limitations. This time we are going to customize the unique template ( wkpstd.aspx) by replacing the control that renders the editable content by a custom one.
In this post example, we will take advantage of the use of this custom control to render not editable additional contents, for instance, a header and a footer that could be the same for a specific Wiki Page Library. Of course, we are not going to really customize the template, but we are going to use a delegate control to replace the control at runtime.

Here are the screen shots of the result:

We can see a non editable Header and Footer on the Team Site Wiki Page

In edit mode these additional contents are not rendered, but we can add new content as usual

And of course, after having saved the page, the new content is rendered wtih the non editable Header and Footer.

Steps required  

If you look at the Team Site Wiki Page template, wkpstd.aspx, you can see the control that is rendering the editable content/

<SharePoint:EmbeddedFormField ID="WikiField" FieldName="WikiField" ControlMode="Display" runat="server" />

We are first going to create a custom EmbeddedFormField control

Then, we are going to use a delegate control to replace the native EmbeddedFormField control by the custom one in order to have the control on the wiki page content rendering.

1 - Creating a custom EmbeddedFormField control

Here is the screen shot of the Visual Studio solution:

and the custom control source code:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Linq;

using System.Text;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using Microsoft.SharePoint;

using Microsoft.SharePoint.WebControls;

using Microsoft.SharePoint.Security;

using System.Security.Permissions;

using System.Diagnostics;

 

namespace CustomControls

{

    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]

    public class EmbeddedFormFieldCustom : FormField

    {

        public EmbeddedFormFieldCustom()

        {

            Debug.WriteLine("EmbeddedFormFieldCustom: " + "constructor...");

        }

 

        private string _header = "";

        private string _footer = "";

 

        public string Footer

        {

            get { return _footer; }

            set { _footer = value; }

        }

 

        public string Header

        {

            get { return _header; }

            set { _header = value; }

        }

 

        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]

        public string Content

        {

            get

            {

                SPFieldMultiLineText field = base.Field as SPFieldMultiLineText;

                if (field != null)

                {

                    Debug.WriteLine("EmbeddedFormFieldCustom: " + "SPFieldMultiLineText ok");

                    return field.GetFieldValueAsHtml(this.ItemFieldValue, base.ListItem);

                }

 

                Debug.WriteLine("EmbeddedFormFieldCustom: " + "SPFieldMultiLineText non ok");

                return base.Field.GetFieldValueAsHtml(this.ItemFieldValue);

            }

            set

            {

                Debug.WriteLine("EmbeddedFormFieldCustom: " + "set Content");

                this.ItemFieldValue = value;

            }

        }

 

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

        protected override void Render(HtmlTextWriter output)

        {

            Debug.WriteLine("EmbeddedFormFieldCustom: " + "rendering..");

            output.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);

            output.Write(_header);

            output.RenderBeginTag(HtmlTextWriterTag.Div);

            base.Render(output);

            output.Write(_footer);

            output.RenderEndTag();

        }

    }

}

 

 

2 - Replacing the standard control by the custom one by using a delegate control

 Here is the delegate control source code:

<%@ Control Language="C#" ClassName="WikiPageCustomLink" %>

<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"

    Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Assembly Name="CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0f4e5a441a5ac20c" %>

<%@ Import Namespace="Microsoft.SharePoint" %>

<%@ Import Namespace="System.Diagnostics" %>

 

    <!--delegate control begin -->

 

<script runat="server">   

    Control ctrlTofind = null;

    CustomControls.EmbeddedFormFieldCustom myCustomEmbeddedFormField = null;

 

    protected void FindControl(Control aControl, string ID)

    {

        if (aControl.HasControls())

        {

            foreach (Control aControl2 in aControl.Controls)

            {

                if (aControl2.ID == ID)

                {

                    ctrlTofind = aControl2;

                }

                else

                {

                    FindControl(aControl2, ID);

                }

            }

        }

        else

        {

            if (aControl.ID == ID)

            {

                ctrlTofind = aControl;

            }

        }

    }

 

    protected override void OnInit(EventArgs e)

    {

        if (!IsPostBack)

        {

            Debug.WriteLine("onInit");

            if (Page.TemplateControl.ToString().Contains("ASP.WKPSTD_ASPX"))

            {

                try

                {

                    int ctrlNumber = this.Page.Controls.Count;

                    foreach (Control aControl in this.Page.Controls)

                    {

                        FindControl(aControl, "WikiField");

                    }

 

                    myCustomEmbeddedFormField = new CustomControls.EmbeddedFormFieldCustom();

 

                    myCustomEmbeddedFormField.Header = "<span style='color:red;font-size:12pt' >This is my Custom header, not editable</span>";

                    myCustomEmbeddedFormField.Footer = "<span style='color:red;font-size:12pt' >This is my Custom footer, not editable</span>";

 

                    Control updatePannelContentTemplateContainer = ctrlTofind.Parent;

 

                    UpdatePanel myUpdatepannel = (UpdatePanel)updatePannelContentTemplateContainer.Parent;

 

                    int index = 0;

                    for (int i = 0; i < updatePannelContentTemplateContainer.Controls.Count; i++)

                    {

                        if (updatePannelContentTemplateContainer.Controls[i].ID == "WikiField")

                        {

                            index = i;

                        }

                    }

                    updatePannelContentTemplateContainer.Controls.Remove(ctrlTofind);

                    updatePannelContentTemplateContainer.Controls.AddAt(index, myCustomEmbeddedFormField);

                    myCustomEmbeddedFormField.FieldName = "WikiField";

                    myCustomEmbeddedFormField.ID = "WikiField";

                    myCustomEmbeddedFormField.ControlMode = Microsoft.SharePoint.WebControls.SPControlMode.Display;

                }

                catch (Exception ex)

                {

                    Response.Write("<br>error : <br>" + ex.Message);

                    Response.Write("<br>error : <br>" + ex.StackTrace);

                }

            }

        }

        base.OnInit(e);

    }

 

</script>

    <!--delegate control end -->

 

 The secret is to load the custom control only if it is not a post back, since the embedded control itself seems to be used only for displaying the content.

 Conclusion

Of course this trick could be used to add an additional custom control, a web part zone, etc... to no more be limited by the unique wkpstd.aspx template features. I let you explore the possibilities...

This is maybe my last post on the Team Site Wiki Page customization.

  1. We have already explored the customizations linked to the CSS, and the way to add a delegate control in these posts:

    Managing the Styles and the Markup Styles Menus for the Wiki Pages of a SharePoint 2010 Team Site
    Themable CSS Registration and Rendering for SharePoint 2010

  2. We have explained how to provision custom content in a Wiki Page in this one:

    Provisioning a custom Wiki Page within a SharePoint 2010 Team Site

  3. And this is the general post on the SharePoint 2010 Team Site Wiki Pages Branding and Customizations that also shows how to debug the Ribbon

    Customizing and branding the SharePoint 2010 wiki pages

I think I can now keep exploring the SharePoint 2010 wiki cuztomizations, but in the SharePoint 2010 Server version and try to use a custom SPFieldMultiLineText.

4 comments:

Unknown said...

Hey mate -
How do you get your delegate control in? - ie - which delegate control place holder do you register it against? additionalpagehead?

Unknown said...

PS -
Awesome idea there - thinking out of the box at its best!

Marc Charmois said...

Hi Chris,

thank you !!!

I think, yes, I used AdditionalPageHead as in my previous post :

http://mosshowto.blogspot.com/2009/12/sharepoint-2010-wiki-styles.html


<Control Id="AdditionalPageHead" Sequence="90" ControlSrc="~/_ControlTemplates/WikiPageCustomCSSLink.ascx" />

Steve said...

Does anyone else have this solution cause your entire page to be uneditable? I am the administrator and can go into edit mode, but cannot actually edit anything.