Recently by Joe Ferner


I had a requirement to spell check some fields on a custom application page and I knew SharePoint had the ability because on the edit item page there is a "Spelling" button. Come to find out this is one of the easiest things to do and there is really no excuse to not include it on all of your application pages.

First step is to include the javascript needed to run the spell checking. You'll need two includes form.js and SpellCheckEntirePage.js.

<script type="text/javascript" language="javascript" 
  src="/_layouts/1033/form.js?rev=df60y6YolDjUVbi91%2BZw%2Fg%3D%3D"></script>
<script type="text/javascript" language="javascript"
  src="/_layouts/1033/SpellCheckEntirePage.js?rev=zYQ05cOj5Dk74UkTZzEIRw%3D%3D"></script>

When I saw SpellCheckEntirePage.js for the first time I had to laugh because my initial estimate of the task was 3-4 days, I ended up doing it in less than 4 hours.

Next step is to add the button to actually check the spelling.

<input type="button" value="Spell Check"
  onclick="javascript:SpellCheckEntirePage('<%= SPContext.Current.Web.Url %>/_vti_bin/SpellCheck.asmx', '<%= SPContext.Current.Web.Url %>/_layouts/SpellChecker.aspx');" />

Done!

Well almost. There were a couple of fields on the form which didn't make sense to spell check. But looking at the source of SpellCheckEntirePage.js you can quickly find the solution. Just add excludeFromSpellCheck="true" to the fields you don't want to check.

OK, now I'm done.

Well not quite yet. The "excludeFromSpellCheck" doesn't work on People pickers. But SharePoint has this problem too. If you edit a list item with a people picker and run the spell checker it will try to spell check people's login names which is never going to work. I went ahead and added a method to my master page which turns spell check off for people picker fields. It fixed the edit list item spell checking problem too :). I do have to warn you I suck at javascript so if anyone can send me a better way of doing this I would appreciate it.

function disableSpellCheckOnPeoplePickers() {
  var elements = document.body.getElementsByTagName("*");
  for (index = 0; index < elements.length; index++) {
    if (elements[index].tagName == "INPUT"
        && elements[index].parentNode
        && elements[index].parentNode.tagName == "SPAN") {
      var elem = elements[index];
      if (elem.parentNode.getAttribute("NoMatchesText") != "") {
        disableSpellCheckOnPeoplePickersAllChildren(elem.parentNode);
      }
    }
  }
}

function disableSpellCheckOnPeoplePickersAllChildren(elem) {
  try {
    elem.setAttribute("excludeFromSpellCheck", "true");
    for (var i = 0; i < elem.childNodes.length; i++) {
      disableSpellCheckOnPeoplePickersAllChildren(elem.childNodes[i]);
    }
  } catch (e) {
  }
}

I don't usually blog about subjective things like my likes and dislikes, and I usually like (some will say I am in love with) Microsoft, but TFS is just horrible.

Here is a list of why I don't like it, in no particular order:

  • CodePlex -- First there was the SVN bridge (yes, someone hated TFS enough to make a product to make it look like something else) then Microsoft just caved and supported SVN directly. Not really a problem with TFS, it's just an indication that others feel the same way as I do.
  • Size -- I guess Microsoft doesn't really like it either since they don't even ship it with Visual Studios, if you do need it, its a separate download and a big one at that -- 200MB+. Subversion, Tortoise SVN, and Ankh on the other hand combined are less than 15MB. How they filled up 200MB I have no idea, maybe they installed a client that doesn't suck somewhere I don't know about.
  • Read-only Files -- Every file on your system is read-only. Why does everything need to be read-only. If I'm in another editor and I want to make a change to a file, I need to switch over to VS and check out a file for edit. This is rediculous.
  • Identical Files -- I just want to see what changed. Why does TFS insist upon showing me all the files even if they are identical. Call me crazy but I like to see what files I actually changed when I check in. Someone told me there is some command line tool to see this but that's just silly.
  • Code reviews/update -- On my current project I'm the project lead and I like to review the junior developers code when I update my code. Yeah, I can't do that, or I haven't found the button yet. Nor can I find the button to view a particular revision without finding a file that was part of that change and viewing it's history.
  • Deleted files -- Team Explorer doesn't show them by default. This took me a while to figure this one out. You need to go into VS options then find TFS and then click show deleted files. Why is this not on by default and why isn't there a button to toggle this setting in Team Explorer.
  • Project file modification -- We have some people working inside a VPN and some out, the TFS server name is different depending on where you sit. Since the solution file stores the connection string to TFS it's constantly getting checked in.
  • Everything needs to be in the solution -- If you have a bunch of support files or non-.NET files, all of them need to be added to the solution or they don't get checked in or out. VS doesn't help you with this either because you can't have a solution folder map to a directory.
  • Integrating with non-.NET developers -- If you have a mixed development environment (Java, Ruby, etc) like we do. You need to run TFS and something else because all the non-.NET developers can't use TFS especially if they are on a Mac. Usually the something else is much better anyway so you might as well use that instead.
  • Working offline/Speed -- If you ever travel relax and take a nap because you won't be developing. Everything you do talks to the server and that in turn makes it slow. Open up a file and start making changes, to the server you go. Move a file, to the server you go. Diff a change you made, to the server you go.
  • Workspaces -- Who ever came up with the concept of workspaces at Microsoft should be ashamed of him/herself. They are just a stupid idea. If you want two copies of the same project checked out (I do it with SVN if I'm working on a quick bug fix and a big feature at the same time) good luck because I can't figure it out.
  • Server isn't free -- 'Nuff said.

I'm sure there are other problems but this is all I could think of while writing this.

Sure there are some nice things about TFS, but honestly the cons far outweigh the pros. So if you have to decide which SCM to use and you run across this blog I think it's pretty obvious that I don't recommend it. I love SVN and as GIT (tools) mature I'm sure I will switch to that.


I had a problem with creating and using tasks in a SharePoint state machine workflow so I wanted to capture it in a blog so that others wouldn't need to. There isn't a lot of magic just a few gotchas which I'll point out.

Let me start by showing an overview of the workflow we are trying to create.
workflow_overview.jpg
This is about as simple as it gets. Start -> Manager Approve -> Done.

If you look at "ManagerReview" you see we have three things.

  • ManagerReviewInit - This activity is responsible for creating the task
    Gotcha #1 - If we create the task here we encapsulate the ability that later in the workflow we can transition back to the ManagerReview state and it will know how to create a new task.
  • OnManagerReviewTaskCreatedEvent - This activity is going to initialize the task
  • OnManagerReviewTaskChangedEvent - This activity is going to perform the logic of when the task is completed

ManagerReviewInit

manager_review_init.jpg
No surprises here. Unless you take a look at the CorrelationToken on the create task.
create_manager_review_task_props.jpg
Gotcha #2 - The OwnerActivityName needs to be scoped at the "State" level (in our case "ManagerReview"). This is important because later if you add a state that transitions back to "ManagerReview" you will get an exception stating that the correlation token was already initialized. Narrowing the scope will invalidate the correlation token when you leave the state.

OnManagerReviewTaskCreated

manager_review_on_create.jpg
Gotcha #3 - At first I assumed that you needed to have a "set state" at the end of every event to loop back onto itself. Well you don't, there is an implied loop back. In fact if you do, the state initialization routine will be called causing a task to be created which will basically create an infinite recursion.

OnManagerReviewTaskChanged

manager_review_on_change.jpg
If we take a closer look at the conditional you'll see I'm using a property I created, TaskComplete.
complete_rule.jpg
Gotcha #4 - Microsoft doesn't expose the task status and since most users don't update percent complete when completing tasks you have to attach to the changed event and expose it to the workflow.
public bool TaskComplete { get; set; }

private void OnManagerReviewTaskChanged_Invoked(object sender, ExternalDataEventArgs e) {
  TaskComplete = AfterTaskProperties.ExtendedProperties[SPBuiltInFieldId.TaskStatus].ToString() == "Completed";
}


I know what you are thinking, another article on how to change the toolbar on a SharePoint ListViewWebPart. Well I have to say, I have tried every method documented on the web and none of them work consistantly for me. I thought I was doing something wrong until I read all the comments and I was but one of many having similar results. Most of the articles go down the path of grabbing the view XML from the ListViewWebPart and modifing the toolbar type attribute which I have found to only exist about 25% of the time.

My method takes a different approach. I started my journey by looking at the ListViewWebPart code directly in Reflector, because it obviously needs to know how to do this because that's where the user changes it from the web site. From there I looked at "ListViewWebPart.GetToolParts" method which is the method SharePoint calls to get additional web part setting controls from the web part. In the case of the ListViewWebPart it returned a ListViewToolPart. The ListViewToolPart had a method called "ApplyChanges", this looks promising. Damn, obfuscated. No worry I can decipher IL for the most part. Bingo, there was the call I was looking for "ApplyViewToListWebPart". It took all the parameters I would expect, a reference to the ListViewWebPart and a toolbar type.

Here is the resulting code to wrap all the reflection nasty bits...

public static class ListViewWebPartHelper {
  public static void SetToolbarType(
    this ListViewWebPart lvwp,
    SPLimitedWebPartManager limitedWebPartManager,
    ListViewWebPartToolbarType type) {
#pragma warning disable 618
    string storageKey = lvwp.StorageKey.ToString("B").ToUpper();
    string listId = lvwp.ListName;
    string viewId = lvwp.ViewGuid;
    uint toolbarType = (uint)type;

    // get the SPWebPartManager from SPLimitedWebPartManager
    PropertyInfo webPartManagerProp = typeof(SPLimitedWebPartManager).GetProperty(
      "WebPartManager",
      BindingFlags.NonPublic | BindingFlags.Instance);
    SPWebPartManager webPartManager = (SPWebPartManager)webPartManagerProp.GetValue(limitedWebPartManager, null);

    // get the SPWebPartConnection from SPWebPartManager
    PropertyInfo spWebPartsProp = typeof(SPWebPartManager).GetProperty(
      "SPWebParts",
      BindingFlags.NonPublic | BindingFlags.Instance);
    SPWebPartCollection webParts = (SPWebPartCollection)spWebPartsProp.GetValue(webPartManager, null);

    // Call the ApplyViewToListWebPart method on the SPWebPartConnection
    // internal void ApplyViewToListWebPart(
    //    string storageKey,
    //    string listID,
    //    string viewID,
    //    uint toolbarType,
    //    out uint flags)
    MethodInfo applyViewToListWebPart = typeof(SPWebPartCollection).GetMethod(
      "ApplyViewToListWebPart",
      BindingFlags.NonPublic | BindingFlags.Instance,
      null,
      new[]{
        typeof(string),
        typeof(string),
        typeof(string),
        typeof(uint),
        typeof(uint).MakeByRefType()
      },
      null);
    object[] parameters = new object[5];
    parameters[0] = storageKey;
    parameters[1] = listId;
    parameters[2] = viewId;
    parameters[3] = toolbarType;
    applyViewToListWebPart.Invoke(webParts, parameters);
#pragma warning restore 618
  }
}

public enum ListViewWebPartToolbarType {
  Full = 0,
  Summary = 1,
  NoToolbar = 2
}

And here is how to use the code...

SPFile homePage = _ctx.Site.Files["default.aspx"];
SPLimitedWebPartManager limitedWebPartManager = homePage.GetLimitedWebPartManager(PersonalizationScope.Shared);
ListViewWebPart webPart = (ListViewWebPart)limitedWebPartManager.WebParts[0];
webPart.SetToolbarType(limitedWebPartManager, ListViewWebPartToolbarType.NoToolbar);
limitedWebPartManager.SaveChanges(webPart);

And here is my rant to Microsoft that each and everyone of these articles about this issue must include... Really, Microsoft did you not think that people would want to do this. You have had 2 service packs come out for SharePoint and you couldn't have fixed this yet. Please fix this in SharePoint 2010.


I needed this yesterday and was amazed at how many forums and blogs I needed to visit before I found the answer. So I decided to blog about it myself so that I wouldn't forget about where to find the answer again.

If you have a class like this and need to call "PrivateMethod"

public class PrivateMethodClass {
  private int PrivateMethod(int arg1, ref int arg2, out int arg3) {
    arg2 = arg2 + 10;
    arg3 = 999;
    return arg1;
  }
}

This is the code you need to call it

PrivateMethodClass target = new PrivateMethodClass();
MethodInfo methodInfo = typeof(PrivateMethodClass).GetMethod(
  "PrivateMethod",
  BindingFlags.NonPublic | BindingFlags.Instance,
  null,
  new[] {
    typeof(int), 
    typeof(int).MakeByRefType(), 
    typeof(int).MakeByRefType()
  },
  null);
object[] parameters = new object[3];
parameters[0] = 1;
parameters[1] = 2;
parameters[2] = 3;
int result = (int)methodInfo.Invoke(target, parameters);
Console.WriteLine("parameter[0]: " + parameters[0]);
Console.WriteLine("parameter[1]: " + parameters[1]);
Console.WriteLine("parameter[2]: " + parameters[2]);
Console.WriteLine("result: " + result);

There are two things that are different from a typical reflection call. The first is the call to "MakeByRefType" which as the name suggests makes a reference type from a type. This is required for a "ref" or a "out" parameter. The second is the creation of the parameters array before invoking the method.