DCSIMG
David Birin's blog
WPF Auto-scroll ListBox

The WPF’s ListBox is a great control with infinite ways to design the items template, but I found that is lacks one important feature: AutoScroll.
I had to use a control that shows messages which are received in runtime, so I had to add this functionality because the user needs to see the newest messages, I also gave the ability to stop the AutoScroll using a DependencyProperty and using this ability I wrote a trigger that stops the scrolling when the mouse is over the ListBox (imagine trying to scroll to read old messages when new message arrive and the control “jumps” to the new one, not a great UX…)

 

The first step was to write a control which inherits from ListBox and adds the AutoScroll capabilities, the catch is to hook into the items CollectionChanged event:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Collections.Specialized;
 
namespace SampleAutoScrollListBox.Helpers
{
    class AutoScrollListBox : ListBox
    {
 
        public bool AutoScroll
        {
            get { return (bool)GetValue(AutoScrollProperty); }
            set { SetValue(AutoScrollProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for AutoScoll.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoScrollProperty =
            DependencyProperty.Register("AutoScroll", typeof(bool), typeof(AutoScrollListBox), new UIPropertyMetadata(default(bool), OnAutoScrollChanged));
 
        public static void OnAutoScrollChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
        {
            AutoScrollListBox thisLb = (AutoScrollListBox)s;
 
            // Add the event handler in case that the property is set to true
            if ((bool)e.NewValue == true && (bool)e.OldValue == false)
            {
                var ic = thisLb.Items as INotifyCollectionChanged;
                if (ic == null)
                {
                    return;
                }
                ic.CollectionChanged += new NotifyCollectionChangedEventHandler(thisLb.ic_CollectionChanged);
            }
            // Remove the event handel in case the property is set to false
            if ((bool)e.NewValue == false && (bool)e.OldValue == true)
            {
                var ic = thisLb.Items as INotifyCollectionChanged;
                if (ic == null)
                {
                    return;
                }
                ic.CollectionChanged -= new NotifyCollectionChangedEventHandler(thisLb.ic_CollectionChanged);
            }
        }
 
        void ic_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var ic = sender as ItemCollection;
            if (ic != null)
            {
                //Scroll into the last item
                if (ic.Count > 1)
                {
                    this.ScrollIntoView(ic[ic.Count - 1]);
                }
            }
        }
    }
}

So all I had to do now is to add the control to the page and write a style which disables the AutoScroll when the mouse is over the control:

<Window x:Class="SampleAutoScrollListBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Helpers="clr-namespace:SampleAutoScrollListBox.Helpers"
        xmlns:local="clr-namespace:SampleAutoScrollListBox"
        Title="Sample WPF AutoScrollListBox" Height="150" Width="250">
    <Window.Resources>
        <local:SampleDataSource x:Key="listboxDataSource"/>
        
        <Style x:Key="autoScrollLBStyle" TargetType="Helpers:AutoScrollListBox">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="AutoScroll" Value="false"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="false">
                    <Setter Property="AutoScroll" Value="true"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
        <Grid>
        <Helpers:AutoScrollListBox Style="{StaticResource autoScrollLBStyle}"  ItemsSource="{Binding Source={StaticResource listboxDataSource}, Path=Messages}"/>
    </Grid>
</Window>

I wrote a sample data source for the control which adds a message on every second (just for demo purposes):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.Collections.ObjectModel;
 
namespace SampleAutoScrollListBox
{
    class SampleDataSource
    {
 
        ObservableCollection<string> _messages;
 
        DispatcherTimer dt;
 
        public SampleDataSource()
        {
            _messages = new ObservableCollection<string>();
            dt = new DispatcherTimer();
            dt.Interval = new TimeSpan(0,0,1);
            dt.Tick += new EventHandler(dt_Tick);
            dt.Start();
        }
 
        void dt_Tick(object sender, EventArgs e)
        {
            _messages.Add(String.Format("New message received at {0}",DateTime.Now.ToLongTimeString()));
        }
 
        public ObservableCollection<string> Messages
        {
            get
            {
                return _messages;
            }
        }
    }
}

The final result is:

Messages added on runtime (AutoScroll): Mouse over (no AutoScroll):
image image

The sample project can be downloaded from here (tested in .NET 3.5 and 4)

Why can’t we be friends (Assemblies)?

This great song asks “why can’t we be friends?” well after working 5 years in .NET I found out that assemblies can be friends…

So what are friend assemblies, well it's pretty simple, it’s a way of giving another assembly access to the internal members on an assembly, and yes, it reminds me of C++ friend classes, but in C++ a friend has access to private members and in .NET only to internal members.

I created a new assembly in C# and I want other assemblies to access it internal members.

namespace Friendly
{
    internal class InternalClass
    {
        internal string internalString = "Howdy friend";
    }
}

Now all I need to do is to explicitly note the friends in the AssembleyInfo.cs file

[assembly: InternalsVisibleTo("SomeAssembly")]

Of course that in real life cases you would use a full assembly evidence with SN.

Now if you are working in C# just add a reference from “SomeAssembly” (the consumer) to the first assembly (the provider - above) and you can access the internal members.

static void Main(string[] args)
{
    Friendly.InternalClass ic = new Friendly.InternalClass();
    Console.WriteLine(ic.internalString);
}

But if you are working in C++/CLI (the language which replaced Managed C++) consuming a friend is a bit different, first of all don’t add a reference to the first assembly Visual studio, you need to include it manually in the following way, first of all add the path of the dll in the project setting under: VC++ Directories –> Reference Directories, now you need to add a special #using statement as seen bellow:

// FreindlyCLI.cpp : main project file.
 
#include "stdafx.h"
 
using namespace System;
#using "Friendly.dll" as_friend
 
int main(array<System::String ^> ^args)
{
    Friendly::InternalClass^ ic = gcnew Friendly::InternalClass();
    Console::WriteLine(ic->internalString);
    return 0;
}
C# anticelebrities – Part 1 – The ?? operator

I decided to start a new series of posts on the anticelebrities operators / keywords of C#,  being anticelebrity for a keyword / operator means that it’s not widely used by programmers. Some may say that main purpose of the members in the anticelebrity club is to be used in tricky interview questions, I hope that I will give them a push towards celebritness by explaining how to use them, at the worst case you will have some geek trivia questions for your next coffee break.

 

The ?? operator is a fairly simple one, it used as a shorten if condition when assigning a value into variables,  checking if the object being assigned into the variable points to a NULL reference, giving it a different value in case it is NULL, or the intended value if it isn’t NULL.

The following example will make things clearer:

static void Main(string[] args)
{
    string sampleText = null;
    string outputText;
 
    outputText = sampleText ?? "sampleText used to be null";
    /*
     * This is eqvivalent to one of the following:
     * 1:  if (outputText == null) 
        {
            outputText = "sampleText used to be null"; 
        }
        else 
        {
        outputText = sampleText
        }             
     * 2: outputText = sampleText == null ? "sampleText used to be null" : sampleText;            
    */
 
    Console.WriteLine(outputText);
 
    sampleText = "sampleText now has a value";
 
    outputText = sampleText ?? "sampleText used to be null";
 
    Console.WriteLine(outputText);
 
    Console.Read();
   
}

 

The output will be:


output

 

More anticelebrities coming soon… stay tuned.

Different behaviors between select to variable in SQL

I encountered different behaviors between the two syntaxes of select to variable in SQL.

The first type of select has the following syntax:

SELECT @Variable = C FROM T

and the second syntax:

SET @Variable = (SELECT C FROM T)

When the query result is a single value the behavior of both is the same (as expected the query result will be placed into the variable).

Things become more difficult when the query returns more than one result, or zero results as can be seen in the following examples:

For the first syntax:

--ZERO RESULTS
DECLARE @answer int
 
SET @answer = 42
 
SELECT @answer = object_id FROM sys.objects WHERE 1=0
 
IF (@answer = 42) 
BEGIN
    PRINT 'Answer to the Ultimate Question of Life, the Universe, and Everything is: 42'
END
ELSE
BEGIN
    IF (@answer IS NULL) 
        PRINT 'NULL'
    ELSE PRINT CONVERT(char(20), @answer)
END 
 
--MUTLIPLE RESULTS
 
SET @answer = 42
 
SELECT @answer = object_id FROM sys.objects 
 
IF (@answer = 42) 
BEGIN
    PRINT 'Answer to the Ultimate Question of Life, the Universe, and Everything is: 42'
END
ELSE
BEGIN
    IF (@answer IS NULL) 
        PRINT 'NULL'
    ELSE PRINT CONVERT(char(20), @answer)
END 

Will print:

Answer to the Ultimate Question of Life, the Universe, and Everything is: 42 

2147251896

Which means that if the query will return zero results , the variable will keep its old value (and not NULL as we expect). In case of multiple results the value that will be placed into the variable is the value which returned last from the query (2147251896 in my case)

For the second syntax:

--ZERO RESULTS
DECLARE @answer int
 
SET @answer = 42
SET @answer = (SELECT  object_id FROM sys.objects WHERE 1=0)
 
IF (@answer = 42) 
BEGIN
    PRINT 'Answer to the Ultimate Question of Life, the Universe, and Everything is: 42'
END
ELSE
BEGIN
    IF (@answer IS NULL) 
        PRINT 'NULL'
    ELSE PRINT CONVERT(char(20), @answer)
END 
 
--MUTLIPLE RESULTS
 
SET @answer = 42
 
SET @answer = (SELECT  object_id FROM sys.objects)
 
IF (@answer = 42) 
BEGIN
    PRINT 'Answer to the Ultimate Question of Life, the Universe, and Everything is: 42'
END
ELSE
BEGIN
    IF (@answer IS NULL) 
        PRINT 'NULL'
    ELSE PRINT CONVERT(char(20), @answer)
END 

Will print:

NULL
Msg 512, Level 16, State 1, Line 22
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Answer to the Ultimate Question of Life, the Universe, and Everything is: 42

As you can see in case of zero results NULL will be placed into the variable, and in case of multiple results an error message will appear (but the script continues to run) and the variable will keep the value it had before the query.

 

These behaviors should be taken in consideration when you write scripts, I used the first syntax in one of my scripts and because I wasn’t aware to the above, it caused me to debug until midnight…

Why do ASP.NET sessions not shared between multiple IE processes

It was always one of the axioms in the back of my mind that when you open a window in Internet Explorer using Ctrl+N it has the same session in ASP.NET apps like it’s parent window, but if you run a new process of Internet Explorer it has a different session.

Well… today I had to check why does it happen.

The first step is to get familiar with two different kinds of cookies: the first is a cookie with an expiration date (AKA persistent cookie) this cookie will be saved to the disk and will be available for use until the expiration date. The second type is a cookie without an expiration date (AKA per session cookie) this cookie will be saved in the RAM and therefore deleted when the browser is closed.

Now understating the ASP.NET session issue is pretty simple: the HTTP Module which is responsible on session (SessionStateModule) creates a cookie named ASP.NET_SessionId, the cookie is created without an expiration date (per session cookie), because different processes of IE don’t share the same RAM the new process is not aware of the cookie, and the HTTP module issues a new session ID.

 

Few notes:

  • ASP.NET session can work without cookies (by setting the key cookieless="true" in the sessionstate section of the Web.Config) in such case the session ID will be part of the URL.
  • This behavior occurs in IE 6 and IE 7, IE 8 has a different process model where each tab is a process and  there is shared memory between the processes (to hold the per session cookies and other stuff) – this behavior is similar to Google Chrome behavior.
Embedded resources in XSL files

If you ever worked with embedded resources you probably know the required steps for embedding resources:

  • Setting the build action to “Embedded Resource” in the file properties.
  • Adding a web resource attribute with the file (best practice is to add this line in the AssemblyInfo.cs file) the first parameter is the default project namespace + the filename and the second parameter is the mime-type:
[assembly: WebResource("Namespace.Filename.gif", "image/gif")]

The WebResource attribute has a third parameter PerformSubstitution we will use this parameter if the file we use as an embedded resource (i.e. JavaScript, CSS, etc.) contain links to other embedded resources.
Here is an example of a URL to an embedded resources in a CSS file which is also an embedded resource:

.Classname 
{ 
background-image:url('<% =WebResource("Namespace.Image.jpg") %>'); 
background-repeat:no-repeat; 
background-position:left; height:25px; 
} 

In runtime this is rendered to:

.Classname
{
    background-image: url('WebResource.axd?d=BAzWlMC4u9l74AvfGT40aSGC_Uhb-Uv5LVuAnHIqvgzwohb4KNp3sNEplurqJHjiqPl73PP2CdE4QL5GUaYRCA2&t=633818747586323927');
    background-repeat:no-repeat;
    background-position:left;    
    height:25px;    
}

note that the URL was replaced by a WebResource.axd with two parameters: d is build from the following parameters “asseblyname|resourcename“ encoded using the Page.EncryptString and the t parameter is the timestamp of the assembly build (used for tracking changes).

So for everything look fine, the problem occurs when you try to use the
<%=WebResource(“…”)%> tag in an XML file (for example XSL) because the URL generated contains ampersand (&) the XML file is invalid for reading.
(note the &t in the example above).

I thought there was a possibility to encode this string, but after digging using a reflector I found out that there is a function named: System.Web.Handlers.AssemblyResourceLoader.GetWebResourceUrlInternal  this function looks (using RegEx) for the text <%=WebResource(“…”)%>  and makes the replacement using the function: System.Web.Handlers.AssemblyResourceLoader.FormatWebResourceUrl(String, String, Int64, Boolean), the last parameter determines whether to encode the string or not, the main problem is that the HTTP handler which handles the web resources requests (System.Web.Handlers.AssemblyResourceLoader.System.Web.IHttpHandler.ProcessRequest) is calling the function with the desired parameter equals to false…

So I had to find a workaround because I had to load my XSL in JavaScript for performing the transform in client side, and when I used the load method of Msxml2.FreeThreadedDOMDocument it ended in an empty document because of the & problem. Here is my solution:

xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
xslDoc.async = false;
//Download the XSL using XMLHttpRequest (the js file is also embedded resource//which has the property PerformSubstitution=True in the assembly: WebResource//attribute)
var xhr_object = new XMLHttpRequest;
xhr_object.open("GET", '<% =WebResource("NameSpace.StyleSheet.xsl") %>', false);
xhr_object.send(null);
//Create a temp string to include the XSL
var tempXsl = xhr_object.responseText;
//Perform the subtituion of the illegal &t in the WebResource.axd link
tempXsl = replaceAll(tempXsl , '&t', '&amp;t')
//Load the valid XML
xslDoc.loadXML(xsl);

Ugly but working….

David

Entity Framework in a SOA environment

I received a mission to explore entity framework as a DAL for a new project, this project is intended to be N-Tier SOA application.

Here are some of my findings:

  • The partial classes which are created by entity framework already contain DataContract and DataMember attributes and are “ready to use” in WCF.
  • Entity framework has a change tracking mechanism, the main problem with this mechanism is that once the object was serialized (before transferred in a service) it gets the state “detached” and from there on it doesn’t track changes until reattached to the graph (read more here).

The second issue is a major disadvantage in comparison to typed datasets, which has the ability to track changes in any tier (using DiffGrams).

There are few community projects which try to overcome this issue by developing their own type of DiffGram for entities (some of them can be found here and here), But after testing these project (and using SQL server profiler) we found out that there are 3 major issues that we couldn’t find a solution for all of them together in any of the community projects, the issues are:

  1. Performances (we saw that one solution made 17 select statements to the DB before updating a single entity).
  2. Updating entities which are connected by foreign keys to another entity (not supported by some solutions and problematic in others).
  3. We don’t want to create a 3 service functions for each Entity in example: UpdateCustomer(CustomerEntity c) , AddCustomer(CustomerEntity c) and DeleteCustomer(CustomerEntity c). We just want one single function:
    CommitChanges(EntityObject e). We found that implementing such a function could be a very complex task.

But not everything is so bad, it appears that the ADO.NET team at Microsoft is aware of this issue, and in EF4 (VS2010) there will be a solution for these problems called  “self tracking entities” (as you can read here and here).

To sum things up, my personal opinion is that until the “self tracking entities” mechanism will be implemented, this technology is still not mature for large scale SOA projects and requires many tweaks and infrastructure work.

David

MOSS Excel Services – Trusted Data Connection “/” makes the difference

Here is a problem that can be solved in seconds or you can spend few hours trying to solve it and bang your head against the screen (like I did…) when you find how easy is the solution.

I had to configure an Excel service, the Excel file included data connection reference to an MSAS cube. After adding the Excel file, setting the trusted file location, the trusted data location and the security of the data connection, I still got the following error message:
 es-error

As I said before I did a lot of digging to find out what’s the problem, I repeated the steps above with extra care, and still nothing it took me 2 more rounds of rechecking before I found out that the error was just a little “/”  (slash) that was missing in the Trusted Data Connection:

Not Working:

dcl-no

Working:

dcl-yes

After that the Excel worked fine and I could drill-down in the cube.

David

SharePoint – Events are duplicated when using templates

I encountered a strange behavior in SharePoint, this phenomenon happens when you create an event handler and attach it to a list template (for example, a document library) using a web-site level feature. If you save the list as template, the event handler reference is saved inside the list template (STP file) and when you create an instance using the template it can cause the following problems:

  1. The event handler will be activated automatically on a web site where the feature is not enabled.
  2. If the event feature is enabled in the web site the event will pop twice (or more if a list with duplicated events will be saved as template…)

Now for proving this point I will attach some code samples which demonstrate the above, and a solution to the problem.

I created a sample Event Handler, which adds the text “From event handler [THE CURRENT TIME] to the Title field:

public class SampleEvent : SPItemEventReceiver
{
    public override void ItemAdded(SPItemEventProperties properties)
    {
        try
        {
            this.DisableEventFiring();
 
            //Get the context item
            using (SPWeb contextWeb = properties.OpenWeb())
            {
                SPList contextList = contextWeb.Lists[properties.ListId];
                SPListItem contextItem = contextList.GetItemById(properties.ListItemId);
                //Sample update for testing
                contextItem["Title"] += "From event handler " + DateTime.Now.ToShortTimeString();
                contextItem.SystemUpdate(false);
            }
        }
        finally
        {
            this.EnableEventFiring();
        }
    }
 
}

I registered it using the following feature:

Feature.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Feature Id="227EEA0A-D42F-4ab7-84DF-E31406A98AFD" 
Title="Sample event handler feature"
Description="Used for testing eventviewer"
Version="1.0.0.0"
Scope="Web"
Hidden="False"
AlwaysForceInstall="TRUE"
ImageUrl=""          
xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementManifest Location="event.xml" />
    </ElementManifests>
</Feature>

event.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <Receivers ListTemplateId="101">
      <Receiver>
          <Name>SampleEvent</Name>
          <Type>ItemAdded</Type>
          <SequenceNumber>1000</SequenceNumber>
          <Assembly>SampleEvent, Version=1.0.0.0, Culture=neutral, PublicKeyToken=392ad9e36e9f08a3</Assembly>
          <Class>SampleEvent.SampleEvent</Class>
          <Data></Data>
      </Receiver>       
</Receivers>
</Elements>

I activated the feature (web-site level):

image

and uploaded a document to a document library in a site, as seen in the next screenshot the event did it’s job:

image

Now, I saved the document library as a template and created a new document library from this template, as seen in the screenshot bellow after adding an item to the new list, you can see that the event occurred twice:

image

And for those of you who are sceptic the following screenshot form SharePoint Manager 2007 will prove that I am not bluffing you…

image

Now that we are aware of the problem, how do we fix it?

So I wrote a new function named RemoveDuplicateEvents to remove the duplicate events:

/// <summary>
/// This function remove duplicate events that were created when
/// saving a list with an event receiver as a template and creating
/// instances from the template
/// </summary>
/// <param name="list">The list to check for duplicate events</param>
/// <param name="className">The class which contains the receiver function</param>
/// <param name="eventType">The event type (if more than one event reciver in class)</param>
/// <returns>True if duplicate events were found, False otherwise</returns>
private bool RemoveDuplicateEvents(SPList list, string className, SPEventReceiverType eventType)
{
    //List of pointers to the duplicate events
    List<int> evnetReciverPointers = new List<int>();
    bool rc = false;
    try
    {
        SPSecurity.RunWithElevatedPrivileges(delegate
        {
            if (list.EventReceivers.Count < 2)
            {
                rc = false;
                return;
            }
            //Find events from the same type and classs
            for (int i = 0; i < list.EventReceivers.Count; i++)
            {
                if ((list.EventReceivers[i].Class == className) && (list.EventReceivers[i].Type == eventType))
                {
                    evnetReciverPointers.Add(i);
                }
            }
            //We delete one at a time
            if (evnetReciverPointers.Count > 1)
            {
                list.EventReceivers[evnetReciverPointers[1]].Delete();
                list.Update();
                rc = true;
            }
        });
 
    }
    catch (Exception ex)
    {
        //Do some error handling
    }
    return rc;
}

If we want to call this function from the event receiver we need to be aware of two things:

  1. If the problematic list was saved as a template, and we create a list instance from this template there will be 3 event handlers (and so on… save as template… instance… 4 event receivers… and so on…)
  2. Let’s say that we have a list with duplicate ItemAdded event, if in the first call to the ItemAdded function even if we remove the duplication, all the duplicate events will pop for the item who triggered the event(the next one will work fine).

that’s why in the function above I delete one duplication at a time. In order to overcome these problems your event receiver code should look like this:

public override void ItemAdded(SPItemEventProperties properties)
{
    try
    {
        this.DisableEventFiring();
 
        //Get the context item
        using (SPWeb contextWeb = properties.OpenWeb())
        {
            //Remove duplicate events
            if (RemoveDuplicateEvents(contextWeb.Lists[properties.ListId], this.GetType().ToString(), SPEventReceiverType.ItemAdded))
            {
                //because we want the code bellow the run only once
                return;
            }
            SPList contextList = contextWeb.Lists[properties.ListId];
            SPListItem contextItem = contextList.GetItemById(properties.ListItemId);
            //Sample update for testing
            contextItem["Title"] += "From event handler " + DateTime.Now.ToShortTimeString();
            contextItem.SystemUpdate(false);
        }
    }
    finally
    {
        this.EnableEventFiring();
    }
}

Stay tuned for more SharePoint adventures :-)

David Birin

SharePoint – Add a SPField to all the content types in a SPList

I found out (another) strange behavior of SharePoint, I tried to add a field (a column) to a list which contain more than one content type using the following code (the new field name is “New Field”) :

using (SPSite currentSite = new SPSite("http://moss"))
{
    using (SPWeb currentWeb = currentSite.OpenWeb())
    {
        SPList currentList = currentWeb.Lists["DocLib"];
        SPField newField = currentList.Fields.CreateNewField(SPFieldType.Text.ToString(), "New Field");
        currentList.Fields.Add(newField);
        currentList.Update();
    }
}

I found out that the field was added only to the default content type as shown in the following screenshot:

image

This was pretty annoying because when a field is added using the GUI it’s added to all the content types. I had to do some digging using Reflector (old helpful friend), and I found out that this behavior is controlled by a Enum named SPAddFieldOptions, which can be sent as a parameter to the AddFieldAsXML function as seen in the code sample bellow (the new field name is “New Field2”):

using (SPSite currentSite = new SPSite("http://moss"))
{
    using (SPWeb currentWeb = currentSite.OpenWeb())
    {
        SPList currentList = currentWeb.Lists["DocLib"];
        SPField newField = currentList.Fields.CreateNewField(SPFieldType.Text.ToString(), "New Field2");
        currentList.Fields.AddFieldAsXml(newField.SchemaXml, true, SPAddFieldOptions.AddToAllContentTypes);
        currentList.Update();
    }
}

Which brings us to the requested result:

image

Note: This will not work on content types that will be added to the list later on (at least this behavior is consistent with the GUI)
MOSS – Open search results for editing (Office 2003/7)

I visited a customer which wanted the option to directly open a document from the search results for editing (in default when you click a document from the search results it opens as read-only). I found this blog article which describes how to do it by embedding some JavaScript into the search results XSL, I did exactly as described and it didn’t work, I realized that this happens because this customer uses Office 2003. I had to do some digging using the IE developer toolbar (soon to be an integral part of IE8) on the “Edit with Microsoft Word” context menu item, which brought me to the conclusion to use other JavaScript functions to open files as described bellow:

<div class="srch-Description">
    <a href="{$url}" onclick="return editDocumentWithProgID2('{$url}','','SharePoint.OpenDocuments')">Edit Document</a>
</div>

To use this, edit you results XSL, insert this code before the srch-Description span (in the “main body template” area – this span appears few more times in the XSL).

After adding it your search result should look like this:
 image

This code was tested and working both with Office 2003 and Office 2007.

I did some more editing on the XSL in order to display the “Edit Document” link only for office documents.

The version which supports 2003 documents (.doc, .xls, .ppt)

<xsl:if test="substring($url,string-length(normalize-space($url))-3,4) = '.doc'
or substring($url,string-length(normalize-space($url))-3,4) = '.xls'
or substring($url,string-length(normalize-space($url))-3,4) = '.ppt'">
 
<div class="srch-Description">
        <a href="{$url}" onclick="return editDocumentWithProgID2('{$url}','','SharePoint.OpenDocuments')">Edit Document</a>
    </div>
</xsl:if>

The version which supports 2003 & 2007 documents (.doc, .xls, .ppt, .docx, .xlsx, .pptx)

<xsl:if test="substring($url,string-length(normalize-space($url))-3,4) = '.doc'
or substring($url,string-length(normalize-space($url))-3,4) = '.xls'
or substring($url,string-length(normalize-space($url))-3,4) = '.ppt'
or substring($url,string-length(normalize-space($url))-4,5) = '.docx'
or substring($url,string-length(normalize-space($url))-4,5) = '.pptx'
or substring($url,string-length(normalize-space($url))-4,5) = '.xlsx'">
 
<div class="srch-Description">
        <a href="{$url}" onclick="return editDocumentWithProgID2('{$url}','','SharePoint.OpenDocuments')">Edit Document</a>
    </div>
</xsl:if>

David Birin

SharePoint - Internal field names in Hebrew

A field (sometimes called a column) in SharePoint - represented in the object model by the SPField class, has two main properties which are used to identify it in code, the Title property and the InternalName property, the reason for the duplication is that the Title property can be changed from the GUI or translated into other languages, therefore if we want to write code which can be used without recompiling after every field name change in the GUI or recompiling for different languages we will use the InternalName .
For example:

  1. If you want to access the field which holds the last modification date of a list item you will use the InternalName property which has the value Modified and not on the Title which has the value Modified in English and השתנה in Hebrew.
  2. SPListItemCollection.GetDataTable() will return a DataTable with internal names as columns.

After understanding why it’s important to used internal names now let’s talk about the surprises, when you create a field in a list of a document library using the GUI, the name that was typed in the creation will be used as an internal name after some sort of encoding f.e. space character is encoded to _x0020_ Hebrew characters are also encoded if I create a field named שלום עולם the InternalName will be the following monster:
_x05e9__x05dc__x05d5__x05dd__x0020__x05e2__x05d5__x05dc__x05dd_

I wrote a function to do the transform between the encoded version and the decoded version.

The Encode function:

/// <summary>
/// This function converts strings in Hebrew (it can be used for other
/// languages too) to the hexadecimal presentation that is used by SharePoint
/// for internal field names
/// </summary>
/// <param name="inputString">The string to convert</param>
/// <returns>The converted string</returns>
public static string ConvertHebrewToUnicodeHex(string inputString)
{
    StringBuilder outputString = new StringBuilder();
    //convert the string to char array and manipulate the chars
    char[] charArray = inputString.ToCharArray();
    foreach (char c in charArray)
    {
        int charIntRepresentation = c;
        outputString.Append("_x" + String.Format("{0:x4}", (uint)System.Convert.ToUInt32(charIntRepresentation.ToString())) + "_");
 
    }
    return outputString.ToString();
}

The Decode function:

/// <summary>
///This function converts strings in the unicode hexadecimal presentation 
/// for Hebrew that is used by SharePoint to a string in Hebrew
/// </summary>
/// <param name="inputString">The string to convert</param>
/// <returns>The converted string</returns>
public static string ConvertUnicoeHexToString(string inputString)
{
    StringBuilder outputString = new StringBuilder();
    //Each char is represented in the following format
    // _x????_ (7 chars long) where ???? is number in hex 
    for (int i = 0; i < inputString.Length; i += 7)
    {
        string hexValue = inputString.Substring(i, 7).Substring(2, 4);
        char charCode = (char)UInt32.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
        outputString.Append(charCode);
 
    }
 
    return outputString.ToString();
}

Have fun…

David Birin

How to create an application in IIS6 using C#

Before the IIS7 era, we had no API for directly manipulating IIS, but we still needed to perform some actions like creating a virtual directory or an application (mostly used in installations).

Creating an application using the IIS GUI is done in the following way:

IIS-CreateApp

 

Creating it in code is a more complex task which is dived into 3 parts:

1. Finding the website in the IIS which we want to create the application underneath:

/// <summary>
/// This function looks for the path of a web site in IIS
/// </summary>
/// <param name="siteName">The name of the website we are looking for</param>
/// <returns>The requested path or String.Empty if not found</returns>
public string GetAdsiPathForSite(string siteName)
{
    try
    {
        DirectoryEntry entry = new DirectoryEntry("IIS://LocalHost/W3SVC");
        // Go over all the nodes in the iis
        foreach (DirectoryEntry site in entry.Children)
        {
            //Check if the node is a website (it can be ftp and more...)
            if (site.SchemaClassName == "IIsWebServer")
            {
                // site.Properties["ServerComment"].Value stores the website name
                object tempObj = site.Properties["ServerComment"].Value;
                string serverComments = "";
                if (tempObj != null)
                {
                    serverComments = tempObj.ToString();
                }
                //Compare the name of the website to the name we got as parameters
                if (serverComments == siteName)
                {
                  
                    return site.Path;
                }
 
            }
        }
    }
    catch (Exception ex)
    {
       //Do some exception handling
    }
 
    return string.Empty;
}

2. Creating a virtual directory (if not exists), you can’t create an application on a folder which is not virtual directory:

/// <summary>
/// This function creates an virtual directory on an IIS site
/// </summary>
/// <param name="parentPath">The path to the directory (without the directory name)</param>
/// <param name="name">The name of the directory</param>
public void CreateWebDirectory(string parentPath, string name)
{
    try
    {
        if (EntryExists(parentPath + "/" + name))
        {
            //Virtual dirctory already exists on IIS, there is no need to create one
            return;
        }
        using (DirectoryEntry entry = new DirectoryEntry(parentPath))
        {
            //Invoking the "Create" method which creates the virtual directory
            DirectoryEntry newEntry = (DirectoryEntry)entry.Invoke("Create", new
            object[2] { "IIsWebDirectory", name });
            newEntry.CommitChanges();
        }
      
    }
    catch (Exception ex)
    {
        //Do some exception handling
    }
}
 
/// <summary>
/// This fuction check if a virtual direcory exists in IIS
/// </summary>
/// <param name="path">The path to the virtual directory</param>
/// <returns>True if exists False otherwise</returns>
public bool EntryExists(string path)
{
    try
    {
        using (DirectoryEntry entry = new DirectoryEntry(path))
        {
            return entry.Guid != Guid.Empty;
        }
    }
    catch
    {
        return false;
    }
}

3. Create the application:

/// <summary>
/// This function creates an application from virtual directory
/// </summary>
/// <param name="path">The path to the virtual directory</param>
/// <param name="name">The name of the new application</param>
public void CreateApplication(string path, string name)
{
    try
    {
        using (DirectoryEntry entry = new DirectoryEntry(path))
        {
            //Invoking the "AppCreate2" method which creates the application
            entry.Invoke("AppCreate2", new object[1] { 3 });
            entry.Properties["AppFriendlyName"].Value = name;
            entry.CommitChanges();
        }   
    }
    catch (Exception ex)
    {
        //Do some exception handling;
    }
}

It took some time to resolve the above because it’s undocumented, I hope it will help you.

David Birin

How to get the context item in Workflow activity (SharePoint)

Although it seems a fairly simple and commonly used task to get the context item (the item that the workflow is currently working on), it took me and Dor Rotman some digging to find the correct way to do it.

The first step is to add 3 properties to your activity, these properties have fixed names, I guess that in runtime the activity is loaded using reflection and the context variables are placed into these properties. To simply add a property in a workflow activity, you can use the code snippet wdp in visual studio 2005 (if you don’t have this snippet download the latest version of WF extensions for VS here)

wdp

The name of the properties need to be added are:

  • __Context
  • __ListId
  • __ListItem

After adding these properties your code should look like this: (note: change YourActivityClass to the name of your assembly)

   1: public static DependencyProperty __ContextProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(YourActivityClass));
   2:  
   3: [Description("Context")]
   4: [Category("Context")]
   5: [Browsable(true)]
   6: [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
   7: public WorkflowContext __Context
   8: {
   9:     get
  10:     {
  11:         return ((WorkflowContext)(base.GetValue(YourActivityClass.__ContextProperty)));
  12:     }
  13:     set
  14:     {
  15:         base.SetValue(YourActivityClass.__ContextProperty, value);
  16:     }
  17: }
  18:  
  19: public static DependencyProperty __ListIdProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__ListId", typeof(string), typeof(YourActivityClass));
  20:  
  21: [Description("List Id")]
  22: [Category("List Id")]
  23: [Browsable(true)]
  24: [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  25: public string __ListId
  26: {
  27:     get
  28:     {
  29:         return ((string)(base.GetValue(YourActivityClass.__ListIdProperty)));
  30:     }
  31:     set
  32:     {
  33:         base.SetValue(YourActivityClass.__ListIdProperty, value);
  34:     }
  35: }
  36:  
  37: public static DependencyProperty __ListItemProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__ListItem", typeof(int), typeof(YourActivityClass));
  38:  
  39: [Description("List Item")]
  40: [Category("List Item")]
  41: [Browsable(true)]
  42: [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  43: public int __ListItem
  44: {
  45:     get
  46:     {
  47:         return ((int)(base.GetValue(YourActivityClass.__ListItemProperty)));
  48:     }
  49:     set
  50:     {
  51:         base.SetValue(YourActivityClass.__ListItemProperty, value);
  52:     }
  53: }

And add the following to the parameters section of the actions file that you will create for you activity (located in: C:\program files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\[LOCALE]\Workflow )

   1: <Parameters>
   2:     <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In"/>
   3:     <Parameter Name="__ListId" Type="System.String, mscorlib, mscorlib" Direction="In" />
   4:     <Parameter Name="__ListItem" Type="System.Int32, mscorlib, mscorlib" Direction="In" />
   5: </Parameters>

When you will open this activity in SharePoint Designer you won’t see these parameters as they are identified as system parameters.

Finally, the code to get the item in the Execute function is:

   1: SPWeb contextWeb = __Context.Web;
   2: SPList contextList = contextWeb.Lists[new Guid(__ListId)];
   3: SPListItem contextItem = contextList.GetItemById(__ListItem);
   4: SPFile contextFile = contextItem.File;
dlroW olleH (no, there isn’t a problem with the encoding)

It is an unwritten law that the first post in a blog must have the title “Hello World”, I can’t break this law or I will be banned from the programmers / bloggers / geeks guild, I decided to give this post the title “dlroW olleH” as I hope that this blog will give you a different point of view in the flow of information that we are surrounded with (and no, you won’t need a mirror to view my future posts).

About myself:
Currently working in Omnisys as a software development team leader.
Main points of interest:

  • .NET and C# (The real hardcore…)
  • ASP.NET
  • WSS / MOSS
  • Project server
  • Extreme troubleshooting

 

“Do, or do not. There is no 'try.'"
Jedi Master Yoda

If you understood this quote as a detailed design for your next project (as I did), you are welcome to fasten your seatbelt as we start our journey.

I will be more than happy to receive comments to my posts.