SharePoint: Programmatically Setting Lookup Fields

Although when you get a lookup field, it returns a value ID;#LookupFieldValue (e.g. 45;#test@example.com), when you set it, you just pass it the id:

// To Get
SPWeb web = new SPSite("http://url/to/web").OpenWeb();
SPList list = web.Lists["listname"];
string lookupFieldValue = list.Items[itemIndex]["lookupFieldDisplayName"].ToString();
...
// To Set
SPWeb web = new SPSite("http://url/to/web").OpenWeb();
SPListItem newItem = web.Lists["listname"].Items.Add();
newItem["lookupFieldDisplayName"] = idOfAnItemInTheLookupTable;
newItem.Update();

System.Reflection

I’ve been wondering how to do stuff in C# similar to $$i from my PHP days – i.e. dynamically calling properties on an object, when I do not know their names at compile time.

Example code

using System;
using System.Reflection;
using System.Windows.Forms;
namespace ReflectionExample
{
    public class ReflectionExample
    {
        public virtual void ShowObjectProperties(object exampleObject)
        {
            PropertyInfo[] properties = exampleObject.GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                object resultProperty = exampleObject.GetType().GetProperty(property.Name).GetValue(exampleObject, null);
                if (resultProperty != null)
                {
                    MessageBox.Show(string.Format("{0} has a value of: {1}", property.Name, resultProperty.ToString()));
                }
            }
        }
    }
}

There is obviously lots more to the reflection namespace than this – the above example is the first time I’ve really used it though.

SharePoint: Adding list items remotely

To add an item remotely to a SharePoint list, you need to use the _vti_bin/list.asmx web service. Note that this must be called at the url of the site where the list resides, e.g. http://myserver/sites/mainsite/subsite/_vti_bin/lists.asmx

Node that your project needs to have a web reference to the list.asmx webservice. I normally set the web reference url property to http://localhost/_vti_bin/lists.asmx and set the url behaviour property to dynamic. In the code example below, i have given this web reference the id of UpdateListItemsWS. Note that the code below assumes that System.Xml and Microsoft.SharePoint is referenced.

The code below outlines how to add items remotely to a list.

webServiceUrl = "http://localhost/sites/testsite";
userName = "testuser";
password = "testpassword";
domain = "testdomain";
title = "Hello World";
testField = "Testing 123";

// Prepare connection to webservice
UpdateListItemsWS.Lists updateListItemsWS = new UpdateListItemsWS.Lists();
updateListItemsWS.Url = string.Format("{0}/_vti_bin/lists.asmx", webServiceUrl);
updateListItemsWS.Credentials = new System.Net.NetworkCredential(userName, password, domain);
// Create an XmlDocument object and construct a Batch element and its attributes.

XmlDocument doc = new System.Xml.XmlDocument();
// The name of this node is irrelevant
Xml.XmlElement batchElement = doc.CreateElement("Batch");
batchElement.SetAttribute("OnError","Continue");

// Construct field value xml
StringBuilder newFeedbackItemXml = new StringBuilder();
newFeedbackItemXml.Append("");

// Have to use title in place of build number as title is the internal name
newFeedbackItemXml.Append("");
newFeedbackItemXml.Append(title);
newFeedbackItemXml.Append("");
newFeedbackItemXml.Append("");
newFeedbackItemXml.Append(testField);
newFeedbackItemXml.Append("");
newFeedbackItemXml.Append("");
batchElement.InnerXml = newFeedbackItemXml.ToString();

// Add item to list
XmlNode addItemResults = updateListItemsWS.UpdateListItems("Build Log", batchElement);

SharePoint: Adding choice fields programmatically

As I know I will forget this, and I couldn’t find any decent examples on the web to do this, the code below shows how to add a choice field programmatically to a SharePoint list (with populated choices). Note that the class will need to reference the Microsoft.SharePoint namespace.

SPWeb web = new SPSite("http://localhost/sites/demosite").OpenWeb();
web.Lists.Add("Test List", "Test list description", SPListTemplateType.GenericList);

web.Lists["Test List"].Fields.Add("ExampleChoiceField", SPFieldType.Choice, false);

SPFieldMultiChoice choice = (SPFieldMultiChoice)web.Lists["Test List"].Fields["ExampleChoiceField"];
choice.Choices.Add("hello");
choice.Choices.Add("world");
choice.Update();

Cruise Control.NET

I was introduced to cruise control.net the other day – and was very impressed by its power, as well ease of setup and use. Cruise control.NET is a build tool that polls a source repository (eg sourcesafe, cvs) for changes. If changes have been made, it performs specified operations (such as a Nant Build, calling of execuatables / batch files and emailing specified persons with the result of the build). It can also apply a label to a source tree.

Installation of cruise control is ridicuosly simple – you just run an installer, set up a config file (C:\Program Files\CruiseControl.NET\server\ccnet.config) and set the ccnet program / service running. The installer even sets up a web dashboard on your machine (provided you have iis installed) – usually at http://localhost/ccnet.

In my first experience with cruise control.net, I set cruise control to poll a sourcesafe database every minute. If it detected a change in the source code (ie via a checkin etc) it called a nant build file (nant was also installed on my build server), before labelling sourcesafe and emailing relevant persons the results of builds (this includes and error generated by the nant build).

Note that when cruise control calls nant, it passed through parameters such as the label, status etc so you can run nant operations using these paramaters. See http://confluence.public.thoughtworks.org/display/CCNET/NAnt+Task

Note that there is only one config file, but this can contain multiple projects. Also note that log files are stored at C:\Program Files\CruiseControl.NET\server\nameofproject\Artifacts\buildlogs

The only major thing I have struggled with, is trying to set the build number to increment from a certain value. Within the project state file (called nameofproject.state, located at C:\Program Files\CruiseControl.NET\server) – there is a node called LastSuccessfulIntegrationLabel. I altered this after stopping cruise control, but this did not seem to have any effect – the build number sequence still continued from where it previously was.

Example ccnet.config (so I remember!)

<cruisecontrol>
    <project  name="testproject">
        <webURL>http://localhost/ccnet/</webURL>
        <triggers>
            <intervalTrigger  seconds="60"  />
        </triggers>
        <modificationDelaySeconds>60</modificationDelaySeconds>
        <sourcecontrol  type="vss"  autoGetSource="true"  applyLabel="true">
            <executable>C:\Program  Files\Microsoft  Visual  Studio\VSS\win32\SS.EXE</executable>
            <project>$/testproject/</project>
            <username>username</username>
            <password>password</password>
            <ssdir>//sourcesafeserver/repository</ssdir>
            <workingDirectory>C:\code</workingDirectory>
            <culture>en-GB</culture>
            <cleanCopy>false</cleanCopy>
        </sourcecontrol>
        <tasks>
            <exec  executable="C:\dosomething.bat"  />
            <nant>
                <executable>C:\Program  Files\NANT-0.85-RC2-BIN\nant-0.85-rc2\bin\nant.exe</executable>
                <baseDirectory>C:\code</baseDirectory>
                <buildArgs>-D:Version="ABuildArg"</buildArgs>
                <buildFile>C:\path\to\nantbuildfile.xml</buildFile>
                <buildTimeoutSeconds>3600</buildTimeoutSeconds>
            </nant>
        </tasks>
        <publishers>
                <xmllogger/>
                <email  from="cc@example.com"  mailhost="127.0.0.1"  includeDetails="TRUE">
                    <users>
                        <user  name="developer"  group="developers"  address="rob@rnowik.com"/>
                    </users>
                    <groups>
                        <group  name="developers"  notification="always"/>
                    </groups>
                </email>
        </publishers>
    </project>
</cruisecontrol>

Opening and resealing SharePoint .stp files

Just so i remember – you can look at and modify the contents of a SharePoint .stp file by saving it and then changing the extension to .cab. You can then open, view and extract the contents of this cab file (by double clicking). The stp configuration will be specified in a manifest.xml file within that cab file (note that there may also be some other files containing content etc.

Once extracted and modified, you can make it back into an stp file by using visual studio to create a cab file project (file -> new -> project -> setup and deployment projects -> cab project). Add the relevant files and build the projects. Once complete simply change the extension of the .cab file to .stp.

C# Inheritance Summary

I thought id summarise some key facilitators of inheritance in C# to provide a quick reference of when and where to use these. Im going back to the core language features here a little, just to consolidate the ideas. I confess that i don’t use a lot of these features as much as i probably should – the underlying intention of this post is to remind me to use them a bit more!

Polymorphism
Polymorphism is a term thats used frequently in OO programming and is linked to inheritance. Polymorphism basically means that different entities (ie clases) can have the same properties / methods implemented in different ways (eg by implementing overrides on a base class / interface / abstract class).

In reality it means that different classes can be called as if there they were the same (eg so you can set up a method that is the base class type – it will be able to take either class inheriting from that derived type). For instance, you can pass and object deriving from a base class to a method that accepts the base class as the parameter. You can then call methods (of the base class) within that method, even if they are overridden in a deriving class.

Note that if the deriving classes has a method that overrides the base class method, the overriding method will be used (by a method that accepts operates on the base class and calls that method). However if the deriving class has a new method that hides a bass class method, the base class method will be used.
Click here to download a VS 2005 example project illustrating these points

Abstract Classes

  • Cannot be directly instantiated
  • They can contain a mixture of normal and abstract functions.
  • Abstract functions cannot contain implementation
  • Abstract classes are different from interfaces as they can contain code, member variables etc – interfaces only expose properties
  • Class can only derive from one class (abstract or not)
  • Of course, you can have just a normal c# class acting as a base class, however this can be directly instantiated and any non-virtual methods cannot be overridden.

Interfaces

  • Cannot be directly instantiated
  • Classes can derive from multiple interfaces
  • Do not contain fields – only define properties / indexers
  • You cannot specify access keywords (everything is implicitly public)
  • You cannot declare constructors / destructors (as you cannot create an instance of an interface)
  • You cannot nest enum, classes etc in an interface
  • Interface can inherit from other interfaces (but not classes as they contain implementation).

Virtual Methods

  • Specifies that a method may be overridden – a virtual method is just the first implementation (in the base class) of the method
  • Different to abstract methods, as abstract methods contain no implementation.
  • You cannot have private virtual methods

New Keyword

  • Note that the new keyword can be used to hide a virtual method, but as it implies that the two functions are for different purposes, but have the same signature, it cannot be used with the override keyword.
  • As stated above, if the deriving classes has a method that overrides the base class method, the overriding method will be used (by a method that accepts operates on the base class and calls that method). However if the deriving class has a new method that hides a bass class method, the base class method will be used.
  • Note that the method in the base class is still hidden by the new method and cannot be accessed when the deriving class is called.
  • Note that the new keyword is also used to instantiate a new instance of a class – this usage is unrelated.

Override Keyword

  • The override method is used complimentary to the virtual method. When your deriving from a base class with a virtual method, you can override the virtual method by using the override keyword.
  • AS virtual methods cannot be private and an override to that virtual methods must have an identical signature, override methods cannot be private.
  • Override methods are implicitly virtual – they can thus be overridden in classes subsequently deriving from them.

Sealed Classes

  • Sealed classes cannot be inherited – they can only be called.
  • Cannot contain virtual methods (as these can never be overridden)

Sealed Methods

  • A sealed method cannot be overridden by a deriving class.
  • These are only used when you have an method in a deriving class overridden a virtual method in the base class, but you dont want the override method in the deriving class to ever be overridden by subsequent inheriting classes.
  • All classes can contain sealed methods (that cannot be overridden or hidden by the new keyword).

Static methods

  • Static methods on class can be called with an object instance of that class being instantiated.

Static fields

  • Static data remains fixed across different instances of a classes – in is not reinitialised when a different object is instantiated.
  • A static field is shared between all objects.
  • Also known as a shared field.

Calling base class constructors
In a deriving class, if you don’t explicitly call the bass class constructor in a derived construct, the compiler will automatically call the base class constructor passing no parameters (the base class constructor is always called when a deriving class is instantiated silently by the compiler. If you need parameters to be passed to the base class constructor, you must explicitly call the bass class constructor using the base keyword.

eg. public DerivingClassContructor(string someVariable) : base(someVariable){ }

Other Notes
Also note that a class can inherit from multiple interface, but only one class. A class can inherit from another class and also implement interfaces simultaneously.

Also note that protected access means that a inheriting classes can see that member, but anything directly calling that class cannot.

Reading a config file from a test project

Reading a config file from a test project. Simply put the config file in the bin folder (the config file must be named the file dllname.dll.config, where dllname is the name of the test dll). To access the config entries in the code, use System.Configuration.ConfigurationSettings.AppSettings[“keyname”].ToString(); or equivilant.

Note that Visual Studio 2005 will generate a warning that System.Configuration.ConfigurationSettings.AppSettings is obsolete and has been replaced by System.Configuration.ConfigurationManager.AppSettings, however configuration manager does not appear to be available in the system.configuration namespace in a test project. I do not understand why this is yet.

Converting share point display name to internal name

Note to self: to convert from a SharePoint display name to the internal name, you must escape space characters with _x0020_ and truncate to 32 characters (if more). Note I am not sure if there are any other characters other than space that you need to escape.

string mappedName = displayName.Replace(" ", "_x0020_");
if (mappedName.Length > 32)
{
mappedName = mappedName.Substring(0, 32);
}

Thanks to Nick Grieff for this tip. Note that _x0020_ corresponds to the hexadecimal ascii code for the space character (escaping other characters also use hex ascii codes) – see http://www.lookuptables.com/.

Edit: Using System.Xml.XmlConvert.Encode

Sharepoint seems to convert display names to internal names by escaping using System.Xml.XmlConvert.Encode(myFieldNameString). E.g.

string internalName = XmlConvert.EncodeName(displayName);