SPList.Update() doesn't update an SPList

Well it does, but when it feels like it, not straight away.

I have two event handlers in a list called 'Tasks'. The first event handler is on ItemAdding, the second on ItemAdded.

In ItemAdding I add a column to a separate list called 'Attributes'. In ItemAdded I reference this new column. I receive and exception because the new column in the Attributes list does not exists, yet.

I changed the code in ItemAdding so the next line after adding the column I call attributesList.Update() for force/commit the change to the Attributes list. However, this method does not do what it appears to.

SPList.Update() did not update the attribute list. After ItemUpdating had completed and we are into ItemUpdated there is still no new column. The column only gets created sometime after ItemUpdated has finished, or whenever SharePoint can get round to it.

Annoyingly when you reflect on SPList.Update it is obfuscated, so who knows what this method does? The only info on msdn states "Updates the database with changes that are made to the list.". Sure.

I learnt two things here:

  1. There is no way in SharePoint to force a commit now
  2. You cannot rely on things being done between ItemUpdating and ItemUpdated
Suppress compiler warnings from your code

You can hide compiler warnings by adding some key words to your class.

Ok, there are very few situations when you would want to do this. Some argue that it should never be done and no code should have warnings. However, if you are really sure then you can use the following.

In my example I have a class that overrides the Equals method but not the GetHashCode method. There is no way of returning a correct hash code in this situation and overriding it and calling the base would suggest the class takes responsibility for that being correct.

So the compiler throws up this warning:

warning CS0659: SPListItemChange' overrides Object.Equals(object o) but does not override Object.GetHashCode()

Fair enough.

In my class definition I can add the following:

#pragma warning disable 0659 //override Equals but not GetHashCode
public class SPListItemChange

{

A short compile later 0 warnings.

ICloneable

Recently I wanted to create a class that had the ability to clone itself. Here is a simple example class:

internal class CloneableObject
{
     private string message;

     internal CloneableObject(string message)
     {
         this.message = message;
     } 
}

upon implementing the ICloneable interface I noticed the interface specified the return type of object.

#region ICloneable Members

public object Clone()
{
     return new CloneableObject("A am a IClonable object");
}

#endregion

My initial opinion was that this wasn't that helpful. You are only ever going to want a CloneableObject returned from CloneableObject.Clone() and I would prefer to avoid casting for runtime type safety and performance.

I can appreciate why IClonable is like this, its not like you can implement your interface in a generic way, e.g.

public interface ICloneable
{
    <T> Clone();
}

So, I implemented both ways. One way for those consumers that know it is a ClonableObject and the other way for those who just care that its IClonable.

internal class CloneableObject : ICloneable
{
    private string message;

    internal string Message { get { return message; } }

    internal CloneableObject(string message)
    {
        this.message = message;
    }

    internal CloneableObject Clone()
    {
        return new CloneableObject("I am a CloneableObject");
    }

    #region ICloneable Members

    object ICloneable.Clone()
    {
        return new CloneableObject("I am ICloneable");
    }

    #endregion
}

At this point I should point out that I know it should be return new CloneableObject(this.message) as it should be creating a copy of the original not just making up the members, but for this example, it suits.

My next question was, which Clone() gets called when?

The obvious answer is object ICloneable.Clone() when the consumer treats it as a ICloneable object and CloneableObject Clone() when treated as a Cloneable object.

static void Main(string[] args)
        {
            CloneableObject first = new CloneableObject("original");
            ICloneable second = new CloneableObject("original");

            Console.WriteLine(first.Clone());
            Console.WriteLine(second.Clone());
            Console.ReadLine();
        }

image

Best of both worlds in my opinion. I guess the question to ask is why are you implementing ICloneable? Do you consumer classes care?

Sharepoint Lookup fields only support one data type

When you add a new column to a Sharepoint list you can decide for the new column to be a lookup field to a different list.

By default the lookup will show the title of the list item in the lookup list. When adding the column (either via the web or programmatically) you can lookup to a field other than the item's title. However,

Look up fields will only reference fields that are of type "Single Line of Text". You can in code set them to other field types, such as DateTime fields, but Sharepoint will ignore this and default to Title.

There is a bit of a hack around where by you fool Sharepoint into looking up to a different field type. You create the column as a single line of text, create a lookup to it then change the target field from a single line of text to the data type you require. I wouldn't advise this though, it is unsupported and it is best to treat the column you are adding as the data type it represents, also doing this programmatically is a real Kludge!

Synergy - A Software KVM

Ever had more than one machine on your desktop but wanted only one Keyboard and mouse to control them both? In the past I have used hardware KVM switches like the one below to physically plug the keyboard and mouse into both machines.

image

Recently I discovered a software KVM called Synergy. Synergy means you don't need a hardware KVM like the one above as it uses the network instead.

Here is my setup at the moment. I have a desktop PC with a Keyboard and mouse and screen plugged into it. I also have a laptop.

image

Both machines are plugged into the network.

Synergy can be installed on the PC as a KVM 'server', making its keyboard and mouse available on the network.

The Laptop runs the Synergy client application and connects to the PC over the network. Once the connection is establish I can use the mouse and keyboard as if it was a standard dual screen setup. I drag my mouse to the right and it appears on the laptop, I drag it left and its back to the PC.

 

There is an initial configuration of Synergy that can be a bit confusing. You essentially have to tell it where your screens in relation to each other and map the usable space. Below is how I've got mine setup.

image

In the screens you add the network names of the machines you want to use. The links here define a route right from the PC into Laptop territory and a route left to get you back to the PC.

Now I can seamlessly switch between the machines that are in front of me, they even share a clipboard so you can copy and paste from one to another. The only issues I've come across is that it locks you out from UAC elevated applications and it won't work on ctrl-alt-del login screens, for obvious reasons.

Just to see if I could I tried the following setup just for fun.

image

Sure enough you can move your mouse left and right between all these machines and use them, it even works with the PC that has two monitors.

Synergy is open source and released under the GNU Public License (GPL) and is available here.

Google Chrome first Beta

UPDATE: Yes running as admin allows you to set it as default.

 

There are a couple of odd things about Google's first beta release of its new web browser, Chrome.

Firstly, why does it install to C:\Users\<USERNAME>\AppData\Local\Google\Chrome\Application ?

Secondly it is not able to assert itself as the default browser in Vista.

If you go to tools > options in Chrome you see the following button to make Chrome your default browser.

image

This button did its job on my XP box but on my Vista box I received the following:

image

Fortunately, Microsoft shipped Vista with a built in tool for managing your default applications. However, Chrome does not appear in the list of programs and you cannot browse for it:

image

Next stop, 'Set file associations'. From here I was able to set windows to open .htm and .html files with Chrome:

image

However, when it came to setting a protocol such as http and https, there was again no option to browse for an application:

image

I also tried running Chrome as Admin and hitting the button and copying the app to program files, just in case windows would find it and place it in the programs list. No joy with this so far, but hey, things like this are what you expect with beta software. Knowing Google, it will stay in Beta for a few years just to abstain them from any responsibility.

Apart from this I am enjoying the browser more than I thought I would. Its far from innovative, but it is well made. Its really quick, I like that tabs run in separate processes and the search and find features are unoriginal but well done.

I will just wait for another release to solve the default issue, let me know if anyone finds a solution.

When is a Query not a Query?

The answer? In SharePoint when you use the word "Query".

I recently used a tool entitled "U2U CAML Query Builder and Execution tool". Don't take this as a recommendation of this tool, its rough round the edges but it does what it says on the tin.

You can use this tool to connect to your SharePoint site, drill in to your SPList and, using a pre-populated choice of fields in your list, construct a CAML query.

U2U

You can test your queries from here too.

I was using it to generate the query I needed to add to my code:

private static SPListItemCollection GetIncompleteTasks(SPList taskList)
        {
            SPQuery query = new SPQuery();
            query.Query = @"<Query><Where><IsNull><FieldRef Name='CompletionDate' /></IsNull></Where></Query>";
            return taskList.GetItems(query);
        }

However, there is a well hidden deceitful gotcha here. If you use the Query tag in your SPQuery.Query string it totally negates the rest of your query!. In this case it ignores the where clause and will always return the entire contents of the list.

This seems very misleading to me. If the CAML is malformed I would have anticipated an exception to be thrown so developers could know to remove the <Query> tag. The other alternative is to detect and ignore this tag, or worse case don't return any results. But to pretend to work and return all records!?

Resolution, remove <Query>:

private static SPListItemCollection GetIncompleteTasks(SPList taskList)
        {
            SPQuery query = new SPQuery();
            query.Query = @"<Where><IsNull><FieldRef Name='CompletionDate' /></IsNull></Where>";
            return taskList.GetItems(query);
        }
Another SharePoint lie

If you try and access the 'MySite' area of SharePoint to see your user profile and you receive the following error:

"The evaluation version of Microsoft Office SharePoint Server 2007 for this server has expired."

and you are not running an evaluation copy, SharePoint might be lying. That might actually be SharePoint's way of telling you that the MOSS account does not have access to the correct area of the registry.

To fix this issue give the groups "WSS_WPG" and "WSS_ADMIN_WPG" access to the following registry area:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12.0

Problem solved.

Installing Visual Studio 2005 SP1

SP1 for VS 2005 has the worse installer ever!

If you want to skip the slow bit at the beginning (I think its validating the signiture) then open the exe in 7 Zip and extract the msp to you local disk. Run that and it will go straight to the are your sure you're sure part.

 

NJ.

System.IO.Directory.Move doesn't support cross-volumes

Did you know System.IO.Directory.Move does not support moving from one drive to another!? i.e. C:\ to D:\

 

See:

Failed to move storage: System.IO.IOException: Source and destination path must have identical roots. Move will not work across volumes.

   at System.IO.Directory.Move(String sourceDirName, String destDirName)

 

 

Any particular reason? Would be kind of handy rather than write a recursive copy method then a delete.

 

NJ.