Friday, June 11, 2010

Branding web parts in SharePoint 2010

Month 1.5 of the development phase of our new SharePoint 2010 deployment, and I finally get to spend some time on the UI. Based on our design, each web part with a chrome would need to wrapped with rounded corners and some gradient images for the header and the footer. You might say that's impossible, but I've got news.


Here is the initial design of our branded web parts, taken directly from the wireframes our designers created:






As you can see, everything looks pretty, but in SharePoint 2003 this would have been nearly impossible. In 2007, I had a similar request, but it only involved branding the web part headers, which was easily achievable using CSS and images. In 2010, you also have the same control using CSS, but adding a border and footer required a bit more than CSS could do. This is where jQuery comes in.


Intitially, the wep part header is easy to find and brand. If you open view source on a web part page and search for "ms-WPHeader" you'll see the web part header table row. This row contains 5 columns, and using the first-child and last-child selectors would allow you to easily add rounded edges to the corners, while filling the remaining columns with the backround of the header. You could also modify font size, weight, and style using css this way. Great! But what about the footer and content border?


Before I begin, I have to throw a shout out to jQuery. Without it, none of this would be possible without significant hours of development in C# to write wrappers for all of your web parts.


I'll start with the header because CSS and cross-browser techniques can be very tedious to say the least. I wanted to also make sure that the branding was turned off while the user was in edit mode, to ensure that edit mode wasn't bogged down.First things first though, make sure you go out and download the jQuery library here: http://jquery.com/. I like to install jQuery in the SharePoint Hive C:\Program Files\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\Layouts\jquery\jquery-1.4.2.min.js. From here, you can reference the library on your Master Page like so:


<script type="text/javascript" src="/_layouts/jQuery/jquery-1.4.2.min.js">
</script>



Now that jQuery is installed and registered on the page, the fun begins. I begin with this call:


$(document).ready(function () { 
});


This allows me to bind event handlers, and call additional jQuery functions once the page has loaded.


Next I find the web part header:


$("tr.ms-WPHeader").each(function(){
});


This part is important. Applying branding to the children of a web part header requires that you traverse each header individually.


Next I traverse the children ("td") tags in the header, and apply an additional css class to handle the header branding. This allows me to keep the stock css intact, and to maintain a much smaller file for branding and master page customizations. It also ensures that during the edit process, the stock web part classes will be applied.


$(this).children("td").each(function(index) {
if(index == 0) {
$(this).addClass("c3-wp-toplt");
} else if(index >= 1 && index <= 3){
$(this).addClass("c3-wp-top");
} else {
$(this).addClass("c3-wp-toprt");
}
});


I'm sure I could have done this without looping, but the index of the children elements never changes, and I was in a hurry!


Now that the header is branded, you will notice that when you render the page, the web part context menu column will be off by a pixel. To fix this, you have to remove the border on the column like so:


$(".ms-WPHeaderTdMenu").css('border', '0').css('padding', 0);


And now it's time to move on to the web part content. You will notice in the page source that the content class is "ms-wpContentDivSpace". Now that you have the class, it's easy to find these div's and brand them accordingly:


$("div.ms-wpContentDivSpace").each( function () {
$(this).wrap('<div class="c3-wp-midlt" />');
$(this).wrap('<div class="c3-wp-midrt" />');
$(this).after('<div class="c3-wp-bot"><img src="/Style Library/Images/intuit/c3-wp-botlt.gif" class="c3-wp-botlt" /> <img src="/Style Library/Images/intuit/c3-wp-botrt.gif" class="c3-wp-botrt" /></div>');
});


As you can see, I've wrapped the content div with an lt and rt border using jQuery .wrap(). I then dropped in the footer using the jQuery .after() function and some simple html.


But how do you ensure that branding is turned off in edit mode? A simple if statement:


if($('#MSOLayout_InDesignMode').val() || $('#MSOSPWebPartManager_DisplayModeName').val() == "Edit") {   


} else {

}


The final code looks like this:


$(document).ready(function () {
if($('#MSOLayout_InDesignMode').val() || $('#MSOSPWebPartManager_DisplayModeName').val() == "Edit") {
// I have some addtional jQuery here that I will cover in a later post
} else {
// Brand the web parts
$("tr.ms-WPHeader").each(function(){
$(this).children("td").each(function(index) {
if(index == 0) {
$(this).addClass("c3-wp-toplt");
} else if(index >= 1 && index <= 3){
$(this).addClass("c3-wp-top");
} else {
$(this).addClass("c3-wp-toprt");
}
});
});

// fix the border issue in the web part menu
$(".ms-WPHeaderTdMenu").css('border', '0').css('padding', 0);

$("div.ms-wpContentDivSpace").each( function () {
$(this).wrap('<div class="c3-wp-midlt" />');
$(this).wrap('<div class="c3-wp-midrt" />');
$(this).after('<div class="c3-wp-bot"><img src="/Style Library/Images/intuit/c3-wp-botlt.gif" class="c3-wp-botlt" /> <img src="/Style Library/Images/intuit/c3-wp-botrt.gif" class="c3-wp-botrt" /></div>');
});
}
});


And the CSS looks like this:


.c3-wp-toplt {
    max-width: 15px;
    background-image: url('/Style Library/Images/layout.gif');
    background-repeat: no-repeat;
    background-position: left top; }


.c3-wp-toprt {
    min-width: 20px;
    background-image: url('/Style Library/Images/layout.gif');
    background-repeat: no-repeat;
    background-position: -930px 0; }


.c3-wp-top {
    background-image: url('/Style Library/Images/layout.gif');
    background-repeat: repeat-x;
    background-position: -20px 0; }


.c3-wp-bot {
    float: left;
    background-image: url('/Style Library/Images/layout.gif');
    background-repeat: repeat-x;
    background-position: -20px -82px;
    width: 100%;
    min-height: 20px; }


.c3-wp-botlt {
    float: left; }


.c3-wp-botrt {
    float: right; }


.c3-wp-midlt, .c3-wp-midrt {
    background: url('/Style Library/Images/c3-mid-border.gif') repeat-y; }


.c3-wp-midrt {
    background-position: right; }
    
If it isn't clear in the CSS, I'm using sprites for most of my borders and branding, which is why you will see odd background-position tags. You can slice the images up if needed, but it was just as easy for us to use sprites to achieve the same functionality. I did slice the bottom image up to account for a single div to store the left and right corner images, while cascading the backround gradient from the sprite.


Here is an example of the branded web part in SharePoint:




Source Code