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


26 comments:

  1. Hi,
    just wondering, this "background-image: url('/Style Library/Images/layout.gif');" would only work if the web is at the root, right?
    I have been trying to work out the best approach for branding: Style Library or Layouts folder, I can't see the Site Library being a practical option, am I missing something?

    Cheers, Vincent

    ReplyDelete
  2. Hey Vincent,

    Did you get the images for the above post.

    ReplyDelete
  3. Vince,

    I would definitely consider dropping the images in the layouts folder if this is a site collection that isn't at the root level. Using relative paths can get very tedious with CSS and site collections. You would also be adding another level of complexity with permissions management.

    Thomas

    ReplyDelete
  4. Hi Thomas,

    Great article. Good work. Do you happen to have a copy of the images?

    Thanks,

    Adam

    ReplyDelete
  5. I'll see if I can't post the images up for you Adam. Sorry for the late response, but I was out of the country last week.

    T

    ReplyDelete
  6. Where do I go and downloads the images?

    ReplyDelete
  7. Bhavin,

    I have added a link to the source code. The code includes the css, images, and the javascript.

    To get this code to work, your web part chrome type should be set to "Title Only".

    T

    ReplyDelete
  8. Hum for some reason I can't download the files. What to do? Great tuto by the way

    ReplyDelete
  9. Any chance you could post the images up again? The link is broken...Paul

    ReplyDelete
  10. Hi, thank you - nice Post! Could you upload the solution again?

    ReplyDelete
  11. source link zip file is missing one gif file - c3-mid-border.gif, please update the source file again.

    ReplyDelete
  12. Hello, the Source Code link seems to be broken again. Can you re-post? Great article!!

    ReplyDelete
  13. Going through this same thing right now and ran across your post... Using jQuery = genius! Thanks for taking the time to post this!

    ReplyDelete
  14. Hello! Great work, and it works nice, except for a QCWP... Is that the case for anyone else?

    Regards
    Roar

    ReplyDelete
  15. Try setting a fixed width. I've had a few problems with the borders if the width isn't fixed.

    Thomas

    ReplyDelete
  16. I cant get the borders to work correctly even on fixed width. They display fine on the top header, and the sides, but they extend all the way down the the bottom of the square that is the web part. So the bottom has rounded edges with a line making them square. I have tried hiding the bottom border but the lines still remain. I have also tried making the left and right borders transparent on your custom CSS to no avail. Any hints? I would like my borders to be gray and line up like yours.

    ReplyDelete
  17. The reason your borders aren't working is because you are using fixed width sprite image. It would be better to seperate the images if you have wide varying widths.

    ReplyDelete
    Replies
    1. The images and scripts I provided should allow for a fluid layout, but the content in the web part is most likely hiding the borders on the left and right. You may need to add CSS padding to the border to get it to display properly.

      Delete
  18. Hi, that's an cool idea! I just tested this and it worked as expected. Btw I added some stuff for the header bottomrow.

    /* Border bottom for all TDs in the table row */
    .ms-WPHeader TD{
    border-bottom:1px #e2e2e2 solid!important;
    }
    /* hover web part menu */
    .ms-WPHeaderTdMenu:hover{
    border-right:1px solid #ccc!important;
    border-left:1px solid #ccc!important;
    background-color:#f7f7f7!important;
    }

    / Christian

    ReplyDelete
    Replies
    1. I've actually been playing with a few JQuery libraries that can handle this branding much easier... The problem is with page load times. In IE, these libraries load extremely slow. The most useful library is the jQuery.backgroundCanvas library, but notice my disclaimer of slow load times in IE. Of course, if you can force every user to IE 9 or Firefox, you have the ability to plugin css 3 and HTML 5, which offer a wide array of rounded borders, gradients, and background canvas functionality. You do need to change one meta tag in the head of your page however, which is the

      <meta http-equiv="X-UA-Compatible" content="IE=8" />

      changed to:

      <meta http-equiv="X-UA-Compatible" content="IE=9" />

      Be careful that you test all functionality before rolling this change out to your production system though.

      Delete
  19. Hi Thomas! I have just blogged about 'Customize the UI of web parts in SharePoint 2010' http://chrisstahl.wordpress.com/2012/01/27/customize-the-ui-of-web-parts-in-sharepoint-2010/ describing some technics for the web part UI, and i have linked to this blog. I have tried the jQuery wrapper in a coupe of different ways and my conclustions is if you got an really fast SP environment it works ok in IE, otherwise it quite slow in an already slow nestled table structure. I dont recommend the IE9 force because this causes some quirky things in SharePoint, for example the people picker control, info path forms and web part drag and drops.

    Thanks for a good blog!

    ReplyDelete
  20. I liked your article, I will share your article to everyone!!



    ________________________________________________________________________
    WoW gold|Diablo 3 Gold|RS Gold|GW2 Gold

    ReplyDelete
  21. Where should i copy that Jscript? in Master page?

    ReplyDelete
  22. the right top image didn't appear when i am view user while it appears with full permission user.
    please help
    thanks

    ReplyDelete