SharePoint Web Part Basics

All web parts are based on the same basic ingredients. When I started developing on my first web parts I did some extra work to find out the best practices. This post is meant to work as a simple reference and to answer some of the questions I had when I first started.

What web parts events should I use?

There are many different opinions on this. This is mine:

  • Never put code in the constructor as it might be called even if the object is later never used
  • In the OnInit initialize any controllers etc. you might have
  • In the CreateChildControls method create your controls, but don’t populate them. Note that it is a common practice to define all the controls and the wepart layout in a separate control class to keep the web part clean.
  • In the OnPreRender load the data into the controls and setup any async task you might need (they will be executed next)

How to register JavaScript?

There are many ways to include JavaScript on a page, the right one depends on your need. To include a script to to be executed directly after page load use the RegisterStartupScript combined with the ExecuteOrDelayUntilScriptLoaded (SOD) or _spBodyOnLoadFunctionNames.push functions:

public static class SharePointScriptHelper
{
public static string ExecuteOrDelayUntilScriptLoaded(Control control, string script, string after)
{
string script = string.Format("ExecuteOrDelayUntilScriptLoaded(function(){{{0}}}, '{1}');", script, after);
ScriptManager.RegisterStartupScript(control.Page, typeof(Page), "Script_" + control.ID, script, true);
}
}
SharePointScriptHelper.ExecuteOrDelayUntilScriptLoaded(this, "alert('Hello world!');", "SP.js");

The first mentioned takes care of the correct load order of scripts.

To include e.g. a library that is not meant to be directly executed use e.g. the RegisterClientScriptInclude method:

ScriptManager.RegisterClientScriptInclude(this.Page, typeof(Page), "MyScriptNamespace", ScriptFileUrl);

The point in using the ScriptManager is to avoid having the same script included multiple times, hence the key parameter in e.g. RegisterclientScriptInclude. The class offers a lot of functions that are worth taking a closer look at.

Note that is is a good practice to wrap you scripts in a “class”/namespace to avoid e.g. function name collisions with other scripts.

To easily include the web part id in your inline scripts you can use the ReplaceTokens function of the web part class to do this. It will simply replace tokens like e.g. _WPID_ with the correct property values.

How to include CSS on a page?

CssRegistration css = new CssRegistration();
css.After = "corev4.css";
css.Name = Settings.StyleSheetFileUrl;
this.Controls.Add(css);

I have not yet had a chance to test SharePoint 2013 to see if it works to refer to the corev4.css also in that version.

Where to define configuration parameters?

The web part definition file (yourwebpart.webpart) is definitely the only correct place. This way they the properties are fully customizable by the site owners when necessary.

Never store passwords in any file as they belong in the secure store service. You can prevent property values from being exportable for a simple way of hiding them.

How should I define my layout?

The most flexible and powerful way is to use XSLT for the rendering. You can implement it your self or inherit the DataFormWebPart class.

Where should I put the CSS, image and other files?

Put them in the appropriate SharePoint mapped folders. Always create a sub folder for you web part and give it a unique name e.g. based on the namespace of the main class.

How to do logging and debug?

Use the inbuilt framework. The most elegant solution would be to write your own service class deriving from the SPDiagnosticsServiceBase but note that it will need to be registered at deployment time.

public static class SPDiagnosticsServiceExtension
{
public static void WriteTrace(this SPDiagnosticsService service, string message, string categoryName, TraceSeverity severity)
{
SPDiagnosticsCategory category = new SPDiagnosticsCategory(categoryName, TraceSeverity.Unexpected, EventSeverity.Information);
service.WriteTrace(0, category, severity, message);
}
}

Usage

SPDiagnosticsService.Local.WriteTrace(ex.ToString(), "Web Parts", TraceSeverity.Unexpected);

I strongly recommend enabling the Developer dashboard and using the SPMonitoredScope to help optimize the performance of your web part.

That’s all for now, more to to come later.

Don’t go (Entity Framework) code first

Code first (Entity Framework) allows developers to forget about SQL, deploy schemas and have everything under source control. As long as you don’t think about the future all is just fine and dandy. But the life cycle of a solution and especially it’s database is only starting when it is first developed. The new Fluent Migrations framework and SQL Server Data Tools have got me thinking about what in the end would be the tooling that will retain its value also later in the life cycle.

Using code first getting started is easy and the initial deployment is close to zero effort. Unfortunately this is where the sunshine ends and the dark clouds appear. As time goes buy, the production database gets full with masses of business data and other systems start relying on all the views and schema. Some monday morning your boss asks you to to add a couple of columns, remove some depricated ones and maybe do some renaming while your at it. To be able to test your changes you of course start by copying a part of the production data to to the test server. Now then fun starts! You first edit your (code first) entities classes, then fire up SQL Server Management Studio to edit the database and finally give the changes a thorough testing. To finish off you prepare SQL scripts to get the production database updated. As this is all manual work, you find your self thinking that life could be easier – and so it can!

Imagine coding your schema first with Fluent Migrations, then letting the standard Entity Framework to generate your entities based on the database. Now, no mather what changes come you can always do them first by code, then let Visual Studio update the entities and finally deploy you changes without leaving your IDE. All the promises of code first, without code first. Oh happy days!

Dilemma of code first

Working with legacy web services and WCF

It is quite common that WCF has problems working with old and none-.NET web services. Usually the old “web reference” (ASMX) tehcnology works better in this kind of situtations, but this one time I was determined to solve the challange using WCF.

After having Visual Studio generate me the client classes I did a unit test to see if I could call the web service successfully. It turned out that the call succeeded and the function returned a response. Unfortunately the response object only contained the result code and descrption, but the data property was null.

Usually in this kind of situations I first turn to Wireshark or some similiar network traffic packer analyzer to see what’s actually gets sent and returned. This this time I had to come up with an alternative way as the web service only allowed me to use a secure HTTPS address so all the traffic was encrypted. As the calls returned a valid object with part of the expected data I knew the authentication was working and there was nothing wrong with the message headers. This meant it was enough for me to see the message content and writing this simple message inspector worked as the solution.

    public class SoapMessageInspector : IClientMessageInspector, IEndpointBehavior
    {
        public string LastRequest { get; private set; }
        public string LastResponse { get; private set; }

        #region IClientMessageInspector Members

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            LastResponse = reply.ToString();
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
        {
            LastRequest = request.ToString();
            return null;
        }

        #endregion

        #region IEndpointBehavior Members

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(this);
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
        public void Validate(ServiceEndpoint endpoint) { }

        #endregion
    }

Usage

            inspector = new SoapMessageInspector();
            factory.Endpoint.Behaviors.Add(inspector);

The message inspector revealed to me that the call was sent ok and also the data returned by the server was fine. It was the WCF framework that failed to properly deserialize the response.

The property for the result data was called Any in the response class, so I took a look at the WSDL provided by the server.

<s:complexType name="response">
<s:sequence>
<s:element type="s0:statusType" name="status" maxOccurs="1" minOccurs="1"/>
<s:any maxOccurs="unbounded" minOccurs="0" processContents="skip" namespace="targetNamespace"/> </s:sequence>
</s:complexType>

The any WSDL element leaves the structure of the content undefined. WCF translates this to a Any property of type XmlElment. The reason why WCF could not process the response correctly was pobably caused by this and the minOccurs value.

After trying to edit some of the attributes of the generated classes without success, I decided to take over the parsing of the response using a custom response formater.

        public class MyResponseBehaviour : IOperationBehavior
        {
            public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
            {
                clientOperation.Formatter = new MyResponseFormatter(clientOperation.Formatter);
            }

            public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }
            public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { }
            public void Validate(OperationDescription operationDescription) { }

        }

        public class MyResponseFormatter : IClientMessageFormatter
        {
            private const string XmlNameSpace = "http://www.eroom.com/eRoomXML/2003/700";

            private IClientMessageFormatter _InnerFormatter;
            public eRoomResponseFormatter(IClientMessageFormatter innerFormatter)
            {
                _InnerFormatter = innerFormatter;
            }

            #region IClientMessageFormatter Members

            public object DeserializeReply( System.ServiceModel.Channels.Message message, object[] parameters )
            {
                XPathDocument document = new XPathDocument(message.GetReaderAtBodyContents());
                XPathNavigator navigator = document.CreateNavigator();

                XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
                manager.AddNamespace("er", XmlNameSpace);

                if (navigator.MoveToFollowing("response", XmlNameSpace))
                {
                    ExecuteXMLCommandResponse commandResponse = new ExecuteXMLCommandResponse();
                    // and some XPath calls...
                    return commandResponse;
                }
                else
                {
                    throw new NotSupportedException("Failed to parse response");
                }
            }

            public System.ServiceModel.Channels.Message SerializeRequest( System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters )
            {
                return _InnerFormatter.SerializeRequest( messageVersion, parameters );
            }

            #endregion
    }

As the web service only provided one function returning a fairly simple response object writing the formater only required a couple of lines of code to parse
the response data using XPath. As soon as I had replaced the default formatter with my own, things started working perfectly.

            factory.Endpoint.Contract.Operations.Find("ExecuteXMLCommand").Behaviors.Add(new MyResponseBehaviour());

I found out later that the message inspector I had written earlier also provided me with a way to throw exceptions with meaningful messages as the server always included a error descrption in the SOAP error envelope that WCF did not reveal.

Alternate Data Streams

NTFS has for a long time supported the concept of alternate data streams. The idea is  that you can store data under a file, not by inserting or appending into it but more like tagging it with data. Even if the feature is not widely know, it is used by Microsoft in many places e.g.  for storing the cached thumb nail images under each thumbs.db file and for marking downloaded files as blocked.

Not all shell commands support the feature, but this example should give you the idea:

echo "hello world" > test.txt
echo "hello you" > test.txt:hidden.txt
notepad test.txt:hidden.txt
dir *.txt

The first line just creates the parent for the alternate data stream created by the second command (<file or directory>:<stream name>). Note that the last dir command only shows the parent file.

Note that if you move a file,  the alternative data streams will only follow as long as the destination device also uses NTFS.

You can work with alternate data streams in C#, but only by using the Windows API as none of the standard .NET components support it directly. I made the following class to easily access the file handle (open/create) for creating and modifying an alternative data stream:

public static class AlternateDataStreams
{
    private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
    private const uint GENERIC_ALL = 0x10000000;
    private const uint FILE_SHARE_READ = 0x00000001;
    private const uint OPEN_ALWAYS = 4;
    public static SafeFileHandle GetHandle(string path, string name)
    {
        if (string.IsNullOrEmpty(path))
            throw new ArgumentException("Invalid path", "path");
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("Invalid name", "name");
        string streamPath = path + ":" + name;
        SafeFileHandle handle = CreateFile(streamPath,
        GENERIC_ALL, FILE_SHARE_READ, IntPtr.Zero, OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
        if (handle.IsInvalid)
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        return handle;
    }
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
    uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
    uint dwFlagsAndAttributes, IntPtr hTemplateFile);
}

The handle can be passed to a FileStream for reading/writing.

I’m planning to use this for marking files in a way enabling my application to detect changes even if the file is left fully accessible to the users. I plan to achieve this by storing the file path+file hash data as a encrypted alternative data stream.

WPF: Cannot set Name attribute value errror

When writing a custom user control you might at some point be tempted to use it as an item container. If you then try to name the nested control you will get the following error:

Cannot set Name attribute value 'myControl' on element 'SomeControl'. 'SomeControl' is under the scope of element 'ContentPanel', which already had a name registered when it was defined in another scope.

This is unfortunately a common problem caused solely by the XAML parser.

The easiest solution to overcome this is by constructing the control yourself. I did this by defining my XAML in a resource dictionary and then writing the lines required to load it at runtime. The only downside in this approach is that no designer preview is available.

Here are the 3 easy steps to write a simple container with a title on top:

1. Add a new class (ContentPanel.cs) and a Resource Dictionary (ContentPanel.xaml) to your WPF project.

2. Then write your XAML into the dictionary defining the style for your control:


<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyNamespace">
    <Style TargetType="{x:Type local:ContentPanel}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ContentPanel}">
                    <Grid Background="Transparent">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="18"/>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" Background="#FF6C79A2" 
CornerRadius="4 4 0 0" Padding="5 3 5 3">
                            <TextBlock VerticalAlignment="Center" Foreground="White" 
Text="{Binding Path=Title, RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type local:ContentPanel}}}"/>
                        </Border>
                        <ContentPresenter Grid.Row="1"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3. Finally edit the class so that it inherits from the UserControl class and add a initialize method that will be called by the constructor to load the XAML from the resource dictionary:

    public class ContentPanel: UserControl
    {
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(ContentPanel), 
new UIPropertyMetadata(""));
        public ContentPanel()
        {
            Initialize();
        }
        private void Initialize()
        {
            ResourceDictionary resources = new ResourceDictionary();
            resources.Source = new Uri("/MyAssemblyName;component/ContentPanel.xaml", 
UriKind.RelativeOrAbsolute);
            this.Resources = resources;
        }
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }
    }

Supporting WPF Binging Without the WindowsBase Library

WPF Bindings provide a powerful way to link your data with the user interface. But what can you do to keep your data classes independent from the WPF framework?

There are a couple of ways to support WPF binding. For none-UI classes the best option is to implement the INotifyPropertyChanged and INotifyCollectionChanged interfaces. Implementing them explicitly keeps the interface of your data classes clean and all the WPF functionality available only for those classes that support it.

private event PropertyChangedEventHandler PropertyChanged;


protected void OnPropertyChanged(string name)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
    add { PropertyChanged += value; }
    remove { PropertyChanged -= value; }
}

Unfortunately in .NET 3.5 both the interfaces are a part of the WindowsBase WPF library, so it is not possible to support bindings without referencing to it. This also means all the assemblies that need to take advantage of your data classes need to reference the library.

It made me very happy to read in Jamie Rodriguez’s blog that in .NET 4.0 the notify interfaces have been type forwarded to System.dll! I could also confirm this using Visual Studio 2010 Beta 2. Hurray!

Ps. According to the Data Binding Performance article on MSDN, the notify interfaces provide a slightly better performance compared to using XXXChanged events.

Faster XML Serialization

A lot of run-time compilation is involved when the XmlSerializer is used. For better performance it is recommended to run sgen.exe to generate a serialization assembly to speed up the XML serialization. In a way, the idea behind SGen is the same as for NGen.

There is a Generate Serialization Assembly drop-down in the project settings in Visual Studio, but this covers only available Web service proxies. To have Visual Studio run SGen automatically for other types, you need to manually add the following lines to your project file:

<Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"  Outputs="$(OutputPath)$(_SGenDllName)">         
    <SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)"  References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="$(SGenToolPath)">
        <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
    </SGen>
</Target>