WebPart Skins

First Apologies, I pulled the code from the previous articles as I wanted to ensure that it at least compiled, so I spent some time creating a full working version which I’m publishing here.

So. Web Part Skins.  This is different to just altering the CSS it combines having a base class for your custom webparts that modifies the render mechanism.  Luckily there are two handy methods that MS provide in the API that give you the HTML that surrounds the Web Part (GetDesignTimeHeader, GetDesignTimeFooter), from that you can regex it till your hearts content.

You could extend this further by using the techniques described in http://blog.spsclerics.com/archive/2005/07/11/5421.aspx A rounded Corner header for webPart.

One drawback of this is that it is only for your own custom web parts as you need to inherit from WebPartBase (or just code it in directly).  It is possible to replace the MS Content Editor in Sharepoint by editing the DWP files in the Area Template Folders and point it at a new one you create yourself.  I have done this on a Portal (I also removed the HTML edit button in mine so no-one goes putting Jscript directly in the editor part, you could also run a HTML validator routine to ensure no nasty tags go in your portal).  Because the content editor part is now mine and not MS I can apply this skin all over the place, and the Content part is the most widely used. *N.B. Editing dwp in templates - This falls under not supported so be warned.

You can’t do it for any of the list parts which is a shame unless you hard code it in the CSS for all parts (like the example from spsclerics).

Here is the code, in this version I used a SNK file (I uploaded it) I used a resx file for resources, the WebPartBase project properties have my namespace in them so go take a look (as the resx relates to this namespace here). I GAC my WebPartBase. 

You should use your own SNK file not mine (its just a dummy one not really mine)
The CSS should be copied into the Styles folder and the Portal should reference it by
altering the site settings (or hack them into ows.cs)

dummy_snk.zip (.7 KB)
InheritingPart.zip (4.63 KB)
bjam.zip (.63 KB)
WebPartBase1.zip (9.77 KB)

WebPartVersion extension of WebPartBase

Whilst developing web parts our team came across some interesting phenomenon, mainly that if you version your web parts using standard .Net assemblyVersion then upgrades work fine as Sharepoint would automatically upgrade the internally held dwp files for every occurrence of the web part you may have in Sharepoint.

 

This is fine moving forward, a little unnerving perhaps but fine.  What we did discover is that if you had to roll-back that version then too late you can’t as in an uninstall scenario the dwp’s are not updated.

 

We wondered why you would need to update dwp’s in Sharepoint at all if you GAC things and use version redirection or policy files.  But this is the way it works.

 

A colleague researched this more thoroughly than I’m describing here (Dan Woolstencroft) but after a lot of searching and an email conversation with Tariq, we came to the conclusion that we should not version web parts. We leave the version at Version 1.0.0.0.

 

This leaves you with a dilemma what is the version number of your part.  I came up with a way to do this, it’s not perfect as only those who can view the tool pane for that web part can see the version number, but however the only people who need to know are those who can see the tool pane.

 

I created a ToolPart to display an attribute of WebPartVersion, Here is the code, it has a few parts to it and is part of my web part base  so the code listed is for inclusion in the WebPartBase Project but you should be able to adapt it easily if you don’t want it like this.

 

The code was pulled from here and placed in a later post with more functionality and tested it can be found in this post

http://blog.binaryjam.com/PermaLink,guid,fa61431f-ecb8-4696-91dc-62a9c0454715.aspx

 

 

Web Part Base

If you are developing web parts then you probably find you are doing the same thing over and over, how many times have you coded the GetToolParts routine ? How many times have you coded it differently ?  Many times and hardly any retrospectively.  How many common routines do you have in your web parts relating solely to webparts.

 

If the answer is lots you need a new spangly web part base class.

 

Consider writing a base class for you web parts.  I have done this and it does a variety of tasks.  Initially it only did the GetToolParts routine and a new AddToolPart Routine (I needed someway to add custom tool parts when I used them).

 

As time went by the class grew.  It now implements a common way of web part versioning (I will discuss this later) it also has routines to determine SPS areas so in your web part you need just type this.ContextIsSPS or from the webpart you can get the current area by calling this.CurrentSPSArea.

 

I later added routines to ensure webparts are only placed in Areas, not on Team Sites, or web parts that are only allowed on the Home page not anywhere else, this forces content managers to place real content in their areas instead of duplicating functionality to fill the space.  All of this controlled by values in the dwp.

 

Here is the some initial code, you need to make the call on whether you cut and paste into projects or like me make a library and have it installed.

 

The next couple of posts build on this an recover some of my previous posts only hopefully in a more concise manner.

 

Some code stolen from (thanks)

    http://www.paraesthesia.com/blog/comments.php?id=460_0_1_0_C

 

Code:-

using System;
using System.Collections;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Portal;
using Microsoft.SharePoint.Portal.SiteData;
using Microsoft.SharePoint.Portal.Topology;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Utilities;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
namespace BinaryJam.Sharepoint.WebPartPages
{

    /// <summary>
    /// Replacement base class for Microsoft.SharePoint.WebPartPages.WebPart.
    /// </summary>
    ///
    [ToolboxData(“<{0}:ServiceStatusPart runat=server></{0}:ServiceStatusPart>”),
    XmlRoot(Namespace=“http://BinaryJam/SharePoint.WebPartPages.WebPartBase”)]
    public abstract class WebPartBase: Microsoft.SharePoint.WebPartPages.WebPart
    {

        private Queue toolPartQueue=new Queue();

        /// <summary>
        /// Constructor.
        /// </summary>
        protected WebPartBase () : base()
        {
        }

        #region Do the ToolPartThing
        /// <summary>
        /// AddToolParts is to allow inherited WebParts to add ToolParts as GetToolParts is sealed.
        ///
        /// You should add toolparts in the constructor of the inherited web part.
        /// </summary>
        /// <param name=”customToolPart”></param>
        protected void AddToolParts(ToolPart newToolPart)
        {

            //Check for muppets
            if ( newToolPart is WebPartToolPart )
            {
                throw new ToolPartException(“A WebPartToolPart is already added”);
            }
            if ( newToolPart is CustomPropertyToolPart )
            {
                throw new ToolPartException(“A CustomPropertyToolPart is already added”);
            }

            toolPartQueue.Enqueue(newToolPart);
        }

        public sealed override ToolPart[] GetToolParts()
        {
            int i=0;
            ToolPart[] toolPartsArray = new ToolPart[2 + this.toolPartQueue.Count];
            WebPartToolPart webPartToolPart = new WebPartToolPart();
            CustomPropertyToolPart customPropertyToolPart = new CustomPropertyToolPart();

            toolPartsArray[i++] = customPropertyToolPart;
            toolPartsArray[i++] = webPartToolPart;

            while (this.toolPartQueue.Count>0)
            {
                try
                {
                    ToolPart nextToolPart=(ToolPart)this.toolPartQueue.Dequeue();
                    toolPartsArray[i++] = nextToolPart;
                }
                catch{}
            }

            return toolPartsArray;

        }
        #endregion

        #region SPS Methods
        ///Thanks to http://www.paraesthesia.com/blog/comments.php?id=460_0_1_0_C
        /// <summary>
        /// Returns a Boolean indicating whether we’re running in SharePoint Portal Server
        /// or not.
        /// </summary>
        /// <returns>True if we’re running in SPS; false otherwise.</returns>
        public bool IsContextSPS()
        {
            Assembly assemblyInstance = null;
            try
            {
                // Try to bind to the Microsoft.SharePoint.Portal assembly.
                // If it isn’t there, we’re not in SPS.
                assemblyInstance = System.Reflection.Assembly.LoadWithPartialName(“Microsoft.Sharepoint.Portal”);
                if(assemblyInstance != null)
                {
                    // We’ve successfully bound to the assembly, so now let’s try to determine
                    // the current PortalContext.
                    System.Type oType = assemblyInstance.GetType(“Microsoft.SharePoint.Portal.PortalContext”);
                    PropertyInfo oInfo = oType.GetProperty(“Current”);
                    object result = oInfo.GetValue(null, null);
                    if (result == null)
                    {
                        // SPS Installation, but WSS context
                        return false;
                    }
                    else
                    {
                        // Running in SPS context
                        return true;
                    }
                }
                else
                {
                    // We couldn’t bind to the Portal assembly, so we’re not on an SPS box
                    return false;
                }
            }
            catch
            {
                //Something went very wrong so catch the error else the webpart will die
                //If there is something really wrong then the whole of Sharepoint will die
                //so I am not worrying about this specific catch
                return false;
            }
            finally
            {
                assemblyInstance = null;
            }
        }

        /// <summary>
        /// Return the current SPS Area, if this WSS though it will return null.
        /// </summary>
        public Area CurrentSPSArea
        {
            get
            {
                if (IsContextSPS())
                {
                    Microsoft.SharePoint.Portal.WebControls.PageInfo pageInfo = (Microsoft.SharePoint.Portal.WebControls.PageInfo)Context.Items[“SPS_PageInfo”];
                    Guid currentAreaGuid = pageInfo.CategoryId;
                    Area currentArea = AreaManager.GetArea(PortalContext.Current, currentAreaGuid);

                    return currentArea;
                }
                else
                {
                    return null;
                }
            }
        }
        #endregion

    }//End of Class

    /// <summary>
    /// ToolPart exception class, if someone tries to add the standard ToolParts again, this is thrown.
    /// </summary>
    [Serializable()]
    public class ToolPartException : System.Exception
    {
        public ToolPartException()
        {
        }

        public ToolPartException(string message): base(message)
        {
        }
        public ToolPartException(string message, Exception innerException): base (message, innerException)
        {
        }
        protected ToolPartException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }//End of class

}//End of Namespace

Full version of this code can be found in a later post with more functionality

http://blog.binaryjam.com/PermaLink,guid,fa61431f-ecb8-4696-91dc-62a9c0454715.aspx