DevExpertise

Practical tips and tricks for all things .NET, SharePoint, Silverlight, InfoPath, and general application development.

Archive for February, 2009

Creating a Custom Vista Theme for SharePoint

Posted by DevExpert on 9th February 2009

There are plenty of resources for learning how to create themes for SharePoint.  A theme is a pre-packaged set of styles and images that make your SharePoint site look a little less…well…”SharePoint-y”.  Themes are limited in the respect that they don’t really modify the layout and positioning of your pages, but rather change the colors and background images applied to elements.  You can still get pretty fancy with just a theme, but recognize that it’s not enough to completely eliminate the default look and feel.

That said, let’s take a look at theme I put together for the purposes of this blog.  It is loosely based on Vista’s black and aqua stylings, and is aptly named “Vista Theme”.  Before I get into the details about how I approach theme development (since it’s probably not the way you’re used to), let’s take a look at some screenshots of what the finished theme looks like.  In addition, I’m feeling extra generous today and have provided my completed theme for your enjoyment :)

Home Page:
image

Calendar:
image

Reports Subsite:
image

Document Library:
image

Search Center:
image

Site Actions Menu:
image

Theme Development 
Ok…now that I’ve [hopefully] gotten your attention, let me discuss what it took to develop this theme.  As you probably know, themes are stored in the following SharePoint directory:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\THEMES\

In here you’ll find all of the themes that ship with SharePoint out of the box, including GRANITE, SIMPLE, JET, etc..  To create your own theme, the easiest way is to start with a theme you like, copy the folder, and rename it something unique.  In my case, I named it VISTA.  Once you have created your custom folder, it’s imperative you locate the .INF file inside your theme folder, and rename to the name of the folder.  Again, in my case, VISTA.INF.  Next, open it up and change every occurrence of the previous theme’s name, and update it with the name of yours:

image

The next part is a little unorthodox, and I originally saw this approach described by Heather Solomon, and it has proven extremely useful and efficient.  The idea is to separate the actual theme artifact files (CSS and images) from the theme definition.  The theme definition, which includes every file in the theme folder, is cached whenever the theme is applied to the site.  This means that the only way you can make changes and see them appear is by updating the files in the file system, setting the site’s theme to another theme other than your own, and finally applying your theme again.  Trust me, this is nowhere near ideal when it comes to CSS, as I guarantee you’ll have to try something, test, try something, test, and so on.  The other option is to use SharePoint Designer and modify the cached files, in which case you must remember to update the actual file system files before it gets cached again. The beauty of Heather’s approach is that a single CSS file is cached, and the only thing it contains is a link to a file and the images on the file system in another location which aren’t cached.  This will allow you to make changes directly to the files on the file system and immediately see your changes.

Here is the structure of my Visual Studio solution.  NOTE: The VISTA folder is collapsed due to space, but inside it are all of my images and style sheets:

image

The next steps are typical – create an entry in the SPTHEMES.XML file that identifies your theme, reset IIS or recycle the application pool, and apply your theme.  If all was successful, you should see the following in your site’s theme settings page:

image

One thing I would like to explicitly explain – how to create custom web part headers.  If you pay close attention to the above screenshots, you’ll notice all of my web parts look like this:
image

This is achieve relatively easily as long as you can create decent-looking header images with your editor of choice (mine is Photoshop).  Basically you need 3 images: a background, a left side, and a right side. Zoomed in, my custom images look like the following:

wp-header-bg.gif:
image

wp-header-right.gif
image

wp-header-left.gif
image

The CSS styles are fairly simple:

/* Web Part Header Styles */
h3.ms-WPTitle {
    background: url('wp-header-left.gif') top left no-repeat;
    font-size: 8pt;
    padding: 3px 0px 4px 6px; }    

h3.ms-WPTitle, h3.ms-WPTitle a, h3.ms-WPTitle a:link,
h3.ms-WPTitle a:hover, h3.ms-WPTitle a:visited{
    color:#181818;}    

.ms-WPHeader TD {
    border-bottom:1px #297c95 solid;
    background: url('wp-header-bg.gif') top left repeat-x;
    padding-right: 0 !important; }        

.ms-WPHeader .ms-HoverCellInActive, .ms-WPHeader .ms-HoverCellActiveDark {
    background: url('wp-header-right.gif')  no-repeat right 0px;
    margin-top:0px;
    margin-right:0px; }

H3.ms-standardheader SPAN {
    color: #181818;}

/* Web Part Header Hover Image Styles */
.ms-HoverCellInActive, .ms-SpLinkButtonInActive{
    padding-right:5px;
    padding-top:2px;
    margin-bottom:0px; }

.ms-HoverCellActiveDark{
    border:none;
    padding-right:4px;
    padding-top:2px; }

.ms-HoverCellActiveDark img{
    border:1px black solid;
    background: #5E5E5E;}     


The only drawback to including these styles in a theme is these are applied to each and every web part in your site. It may not be appropriate to style all of the web parts, but rather web parts on a single page.  If you only want web parts on a single page to be styled, then you could add the CSS classes to a Content Editor Web Part, or link to the style sheet on the page’s page layout.  The important thing to note is that all web parts on a single page will by styled the same.  There’s no easy way to apply a style to one web part and not the other.  For simplicity sake, I’ve just included these styles in my theme.

You can download this theme and install it via the following farm-scoped feature.  NOTE:  The feature receiver I’m using relies on classes from the .NET 3.5 Framework (you’ll see why in my next post), but this will fail if you try to install it on a farm that doesn’t have 3.5.  If you’re just interested in the source files and style sheets, change the .WSP extension to .CAB and extract the stuff you want.  As I mentioned, I will be explaining how to install a theme via a feature in my next post.  Stay tuned!

 

Tags: , , , , , ,
Posted in CSS, SharePoint, SharePoint UI, Visual Studio | 7 Comments »

Retrieving SharePoint List Data from Silverlight Without a Custom WCF or ASP.NET Web Service

Posted by DevExpert on 7th February 2009

I’ve done a little work in the recent months with Silverlight, specifically with leveraging Silverlight inside of SharePoint.  My first project involved a pulling in data from SharePoint via a WCF service, and while it worked, it took a long time to configure correctly and to add the necessary components to SharePoint in order for it to work.  Sahil Malik describes a method beginning here, and as you can see it’s definitely doable but not the simplest approach.

While creating a WCF will offer you the most power and flexibility when it comes to the data it can expose, as you can manipulate SharePoint data (or any data for that matter) any way you like.  In this post, I’m going to show you how to retrieve SharePoint list data in Silverlight the “old-fashioned” way – by using the out-of-the-box SharePoint web services, and executing an HttpWebRequest to get our data.

Because there are a ton of different concepts I could elaborate on related to Silverlight and SharePoint, such as creating a web part host, configuring SharePoint for Silverlight, etc., for the sake of this post I’m going to keep it focused on the data retrieval only.  In future posts, I’ll dive into my approach for hosting a Silverlight app in SharePoint.  Also, take a look at this post from Karine Bosch – she has a very similar approach and hers is a lot prettier than mine!

First, I defined the “Widgets” list I want to retrieve data from. In addition to the Title field, it contains a Description field and an Image field:image

Next, I created a new Silverlight application in Visual Studio, and defined a Widget class, which will serve as the representation of a Widget list item:

public class Widget {
    public string Title { get; set; }
    public string Description { get; set; }
    public string ImageUrl { get; set; }
}

I also added a few class-level variables in my Page.xaml.cs file, which I’ll use in my data methods:

private List<Widget> _widgets = null;
private Stream _body = null;
private string _responseString;

For the data retrieval, I’m going to be leveraging the out-of-the-box Lists.asmx web service to retrieve the list items, and build an HttpWebRequest object.  The BeginGetRequestStream method executes the request, and will asynchronously call the RequestCallback method when it finishes:

private void GetData() {
    try {
        Uri listUri = new Uri("http://server/_vti_bin/Lists.asmx", UriKind.Absolute);
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(listUri);

        request.Method = "POST";
        request.ContentType = "application/soap+xml; charset=utf-8";
        request.Headers["ClientType"] = "Silverlight";
        request.BeginGetRequestStream(new AsyncCallback(RequestCallback), request);
    }
    catch (Exception ex) {
        HandleException(ex);
    }
}

Here’s where the real work needs to be done.  In our callback method, we need to build a SOAP envelope that will be passed to the web service in order to get a valid response.  If you’re uncertain where to find that envelope or are unsure if how it should be formatted, simply browse to the web service itself and click the method that you will be executing:

image

Once the SOAP envelope format is determined, simply add in the listName, viewName (if necessary), query, viewFields, and queryOptions, and you have your envelope.  The next step is to get the response by asynchronously executing the BeginGetResponse method and passing in the ResponseCallback callback method:

private void RequestCallback(IAsyncResult asyncResult) {
    try {
        string envelope =
            @"<?xml version=""1.0"" encoding=""utf-8""?>
                <soap12:Envelope
                 xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
                 xmlns:xsd=""http://www.w3.org/2001/XMLSchema""
                 xmlns:soap12=""http://www.w3.org/2003/05/soap-envelope"">
                    <soap12:Body>
                        <GetListItems xmlns=""http://schemas.microsoft.com/sharepoint/soap/"">
                            <listName>Widgets</listName>
                            <query>
                                <Query xmlns="""">
                                    <OrderBy>
                                        <FieldRef Name=""Title"" />
                                    </OrderBy>
                                </Query>
                            </query>
                            <viewFields>
                                <ViewFields xmlns="""">
                                    <FieldRef Name=""Title"" />
                                    <FieldRef Name=""Description"" />
                                    <FieldRef Name=""Image"" />
                                </ViewFields>
                            </viewFields>
                            <queryOptions>
                                <QueryOptions xmlns="""">
                                    <IncludeMandatoryColumns>False</IncludeMandatoryColumns>
                                </QueryOptions>
                            </queryOptions>
                        </GetListItems>
                    </soap12:Body>
                </soap12:Envelope>";

        UTF8Encoding encoding = new UTF8Encoding();
        HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
        _body = request.EndGetRequestStream(asyncResult);
        envelope = string.Format(envelope, _listName);
        byte[] formBytes = encoding.GetBytes(envelope);

        _body.Write(formBytes, 0, formBytes.Length);
        _body.Close();

        request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
    }
    catch (WebException ex) {
        HandleException(ex);
    }
}

The ResponseCallback method is responsible for determining that the response is valid, and to get the data out of the response.  Once the data is retrieved from the response stream, the only step left is to process that data, and for that we call ProcessResponse.  We must use the BeginInvoke() method of the page’s Dispatcher object, because all of the data retrieval is done asynchronously on a different thread.  Since you can’t modify controls on a different thread than the one it was created on, we have to invoke it this way and let Silverlight handle the thread marshalling:

private void ResponseCallback(IAsyncResult asyncResult) {
    HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
    Stream content = response.GetResponseStream();

    if (request != null && response != null) {
        if (response.StatusCode == HttpStatusCode.OK) {
            _widgets = new List<Widget>();

            using (StreamReader reader = new StreamReader(content)) {
                _responseString = reader.ReadToEnd();
                reader.Close();
            }

            try {

                this.Dispatcher.BeginInvoke(ProcessResponse);

            }
            catch (WebException ex) {
                HandleException(ex);
            }
        }
    }
}

And finally, the ProcessResponse method that parses the response data and creates are Widgets collection, which is then set as the page’s DataContext:

private void ProcessResponse() {
    XDocument results = XDocument.Parse(_responseString);

    _widgets = (from item in results.Descendants(XName.Get("row", "#RowsetSchema"))
                select new Widget() {
                    Title = item.Attribute("ows_Title").Value,
                    Description = item.Attribute("ows_Description").Value
                    ImageUrl = FormatImageUrl(item.Attribute("ows_Image").Value),
                }).ToList();

    this.DataContext = _widgets;
}

private string FormatImageUrl(string value) {
    return value.Substring(0, value.IndexOf(','));
}

Now that we have our data, creating a snazzy Silverlight web part is only limited by your creativity and XAML-slinging skills.  Here’s a quick and dirty snippet to verify we’re actually getting data:

<UserControl x:Class="DevExpertise.Silverlight.App.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <StackPanel>
        <Grid x:Name="LayoutRoot" Background="White">
            <ListBox ItemsSource="{Binding}" VerticalAlignment="Stretch">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{Binding ImageUrl}" Height="40" />
                                <StackPanel>
                                    <TextBlock Text="{Binding Title}" FontWeight="Bold" />
                                    <TextBlock Text="{Binding Description}" />
                                </StackPanel>
                            </StackPanel>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </StackPanel>
</UserControl>

…and voila!  Silverlight in SharePoint consuming list data:

image

That’s all it takes to get data from SharePoint without using a custom web service or having to go through the hassle of building and deploying a WCF service.  In a future post, I’ll describe how to build a generic and reusable web part to host Silverlight applications.  Stay tuned!

Tags: , , , , ,
Posted in .NET, SharePoint, Silverlight, XAML | 9 Comments »

SharePoint List Template IDs and Site Template IDs

Posted by DevExpert on 6th February 2009

Whether you’re building a custom site definition or building SharePoint features, you will probably need to get the ID of either a List Template or a Site Template.  Many others have blogged these as well, but I wanted a consolidated list of both list and site template IDs for quick access.

List Template IDs:

Template ID List Description
1200 Administrator tasks list
104 Announcements list
303 Blog Categories list
302 Blog Comments list
301 Blog Posts list
105 Contacts list
120 Custom grid for a list
118 Custom Workflow Process
130 Data Connection library
110 Data sources
108 Discussion board
101 Document library
106 Events list
150 Gantt Tasks list
100 Generic list
1100 Issue tracking
103 Links list
114 List template gallery
116 Master pages gallery
201 Meeting Agenda list
202 Meeting Attendees list
204 Meeting Decisions list
207 Meeting Objectives list
200 Meeting Series list
210 Meeting text box
211 Meeting Things To Bring list
212 Meeting Workspace Pages list
117 No-Code Workflows
2002 Personal document library
109 Picture library
300 Portal Sites list
2003 Private document library
111 Site template gallery
102 Survey
107 Tasks list
112 User Information list
113 Web Part gallery
119 Wiki Page library
140 Workflow History
115 XML Form library

 

WSS Site Template IDs: 

Template ID Site Description
STS#0 Team Site
STS#1 Blank Site
STS#2 Document Workspace
MPS#0 Basic Meeting Workspace
MPS#1 Blank Meeting Workspace
MPS#2 Decision Meeting Workspace
MPS#3 Social Meeting Workspace
MPS#4 Multipage Meeting Workspace
CENTRALADMIN#0 Central Admin Site
WIKI#0 Wiki Site
BLOG#0 Blog

 

MOSS Site Template IDs:

Template ID Site Description
BDR#0 Document Center
OFFILE#1 Records Center
OSRV#0 Shared Services Administration Site
SPS#0 SharePoint Portal Server Site
SPSPERS#0 SharePoint Portal Server Personal Space
SPSMSITE#0 Personalization Site
SPSMSITE#0 Contents area Template
SPSTOPIC#0 Topic area template
SPSNEWS#0 News Site
CMSPUBLISHING#0 Publishing Site
BLANKINTERNET#0 Publishing Site
BLANKINTERNET#1 Press Releases Site
BLANKINTERNET#2 Publishing Site with Workflow
SPSNHOME#0 News Site
SPSSITES#0 Site Directory
SPSCOMMU#0 Community area template
SPSREPORTCENTER#0 Report Center
SPSPORTAL#0 Collaboration Portal
SRCHCEN#0 Search Center with Tabs
PROFILES#0 Profiles
BLANKINTERNETCONTAINER#0 Publishing Portal
SPSMSITEHOST#0 My Site Host
SRCHCENTERLITE#0 Search Center

Tags:
Posted in SharePoint | 1 Comment »

Why Isn’t InfoPath Retrieving All SharePoint List Items?

Posted by DevExpert on 5th February 2009

InfoPath and SharePoint make a great team when it comes to creating simple electronic forms.  It’s relatively simple to create a robust data-entry form, and is easy to submit an InfoPath form to a form library.  It’s even simple to pull in data that is stored in a SharePoint list.  However, what you may not realize is if a data connection is configured to retrieve SharePoint list items, it only retrieves the items from the first page of the default view.  What does that mean?  Let’s say you have a SharePoint list that contains 500 items, and your default view (the view that is loaded whenever you visit the list) is set to only show 100 items at a time (like it is by default).  When InfoPath executes its query to retrieve the list items, it will only retrieve 100 items.  More specifically, it will only retrieve the first 100 items.  So, if you happen to change the default view’s criteria or sort order, then InfoPath will be pulling in a different set of data! Don’t believe me? Let’s take a look…

To begin, I created a custom list in SharePoint named Widgets, and added 100 items to it.  Next, I created a new view named Sample View in the list, and set that as the list’s default view:
image

Next, I set the Item Limit to only 7 items per page:
image

After clicking OK and saving the view, whenever I access my list I’m presenting with the first 7 items in the list:
image

Now that I have a data source, I create my InfoPath form and set up a data connection to retrieve data from the Widgets list.  I added the results of the data source to a repeating table, and whenever I run my form, I am only presented with the first 7 items (told ya!):
image

Just to make sure this is the actual behavior and I’m not just hallucinating after a long day, I modified the view and changed the sort order to sort by Title in descending order, and changed the item limit to display 12 items at a time:
image image

When I open the form again and the data connection is executed, it retrieves the first 12 items after the new sort has been applied!:
image

One would think that there would be some type of configuration option in the data connection wizard that asks to retrieve all items, or the results from a specific view, but there isn’t.  If you do need to retrieve all list items, it’s imperative that your list’s default view displays them all, or consider a different data retrieval method such as the built-in or custom web services.

There’s even a KB article that says this behavior is by design: http://support.microsoft.com/kb/892954

Tags: ,
Posted in InfoPath, SharePoint | 6 Comments »

Deleting List Items in a SharePoint List

Posted by DevExpert on 4th February 2009

Occasionally you may encounter a need to delete list items from a SharePoint list.  This sounds like an easy enough task – just loop through the SPList’s Items collection and delete the item.  That would be the intuitive way, but then again this is the SharePoint Object Model.  Let’s take a look a bunch of ways you may try to accomplish this task, and a single way which actually works.

To run these tests, I created a simple custom list named Widgets that contains 10 items:

image 

Incorrect Method #1
The first approach that is probably taken is to loop through the list items with a For..Each loop, and call the Delete() method of each list item, like the following:

using (SPSite siteCollection = new SPSite("http://server")) {
    using (SPWeb site = siteCollection.OpenWeb()) {
        SPList list = site.Lists["Widgets"];

        foreach (SPListItem item in list.Items) {
            item.Delete();
        }
    }
}


However, whenever you run this you encounter the following error:

Collection was modified; enumeration operation may not execute.

To help explain this, it will help to first understand the IEnumerable interface, from with the SPListItemCollection ultimately implements. Unfortunately the MSDN documentation is a little scarce on modifying collections with this interface, but the IEnumerator interface contains a lot of good information that applies to IEnumerable as well. 

Basically, enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection.  An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection (such as deleting), the enumerator is irrecoverably invalidated.

Incorrect Method #2

Ok, learning from the mistake in method 1, it makes sense to eliminate the For..Each loop and use a traditional For loop. This method uses an index which increments at each loop, and we call the Delete(index) method of the list item which accepts an item index:

for (int i = 0; i < list.Items.Count; i++) {
    Console.WriteLine("Deleting {0}", list.Items[i].Title);
    list.Items.Delete(i);
}


To visualize the results, I’m outputting the item it’s deleting so we can see if everything is working as it should:

image

What the heck is happening here?  Why is it deleting every other item?  Well, every time you delete an item from the Items collection, the number of items in that collection decreases and thus the indexes no longer match up.

Incorrect Method #3

Hmm, OK…if it’s deleting every other item because the collection is adjusted when items are deleted from it, let’s just be safe and always try to delete the first item at index 0:

for (int i = 0; i < list.Items.Count; i++) {
    Console.WriteLine("Deleting {0}", list.Items[0].Title);
    list.Items.Delete(0);
}


The result is exactly like Method #2 – it deletes every other item:

image


Incorrect Method #4

For kicks and giggles, what if the code is adjusted to delete the item at index 0 inside a For..Each loop?:

foreach (SPListItem item in list.Items) {
    Console.WriteLine("Deleting {0}", list.Items[0].Title);
    list.Items.Delete(0);
}


Woah, that worked! As you can see, it deleted every item:

image

But wait… What if we need to delete an item only if a condition is met?  Let’s try to loop through the collection and only delete the item whose title is Sample Item #5:

foreach (SPListItem item in list.Items) {
    if (item.Title == "Sample Item 5") {
        Console.WriteLine("Deleting {0}", list.Items[0].Title);
        list.Items.Delete(0);
    }
}


The result is what you should expect after deleting the first item in the index – it deletes the first item, not the item that met the condition:image


Correct Method

Ok, finally here’s the correct method for deleting list items.  The trick is to use a decrementing For loop.  The For loop in the following example counts downward (i–) instead of upward (i++), because items are being deleted and the number of items decreases with each increment:

for (int i = list.Items.Count - 1; i >= 0; i--) {
    list.Items.Delete(i);
}


Which produces:

image

What about deleting an item based on a condition?:

for (int i = list.Items.Count - 1; i >= 0; i--) {
    if (list.Items[i].Title == "Sample Item 5") {
        Console.WriteLine("Deleting {0}", list.Items[i].Title);
        list.Items.Delete(i);
    }
}


It works!:

image

 

Hopefully you will always remember to delete list items (actually, the same goes for most other SharePoint collections too, such as SPFieldCollection, SPWebCollection, etc.) using a decrementing counter.  Happy coding!

Tags: , ,
Posted in .NET, Object Model, SharePoint | 8 Comments »