Using Windows Vista Command Links in Managed Code

In my first post on Command Links I talked about the challenge of developing for something when you have no documentation.  In this second post I will discuss how we can use Command Links from managed code - specifically Windows Forms using C#.

I take NO credit for the code snippets. They were expertly crafted by my US colleague Catherine Heller (catch Catherine talking about VSTO). However given the complete lack of docs, I felt I should do my little bit to get this info more widely known.

Quick RecapWe know that Command Links are no more than Win32 button. We send messages to the button to make it look like a Command Link and to set the note text etc.

  • New button style (BS_COMMANDLINK)
  • New button messages (BCM_SETNOTE, BCM_GETNOTE)
  • Shield (BCM_SETSHIELD)

Windows Forms has a Button control - and the Windows Forms Button Control is underneath the covers a Win32 Button. Looking promising.

It should therefore be possible to get a Windows Forms Button to display as a Command Link with a few messages.

The SolutionWe need to create a new button control (hmmmm.... lets call it CommandLink) which derives from a standard Button:

   public class CommandLink : System.Windows.Forms.Button

   {

   }

In the constructor we need to force the control to use the System style and avoid owner draw:

   this.FlatStyle = FlatStyle.System;

Next need to attach the style to set of parameters that Windows Forms sends though to Win32:

     protected override CreateParams CreateParams

   {

   get

   {

      CreateParams cp = base.CreateParams;

      cp.Style |= BS_COMMANDLINK;

      return (cp);

   }

}

 

 To set the control’s note text, send it the BCM_SETNOTE window message

void SetNote(string NoteText)

{

   SendMessage(new HandleRef(this, this.Handle), BCM_SETNOTE,

      IntPtr.Zero, NoteText);

}

 

 To set the control’s shield icon, send it the BCM_SETSHIELD window message

 

void SetShieldIcon(bool Show)

{

   SendMessage(new HandleRef(this, this.Handle),

     BCM_SETSHIELD, IntPtr.Zero, new IntPtr (Show ? 1 : 0));

}

 

 And you are pretty much done (Ok - there is a lot more to do around making it into a reusable control - but its now a shiny Command Link).

 

You will need to use interop services to make the above work. Minimally: 

using System.Runtime.InteropServices;

     

const int BS_COMMANDLINK = 0x0000000E;

const uint BCM_SETNOTE = 0x00001609;

const uint BCM_SETSHIELD = 0x0000160C;

 

// Override used to send the BCM_SETNOTE/BCM_SETSHIELD message

[DllImport("user32.dll", CharSet = CharSet.Unicode)]

static extern IntPtr SendMessage(HandleRef hWnd, UInt32 Msg,IntPtr wParam, string lParam);

 

A big thanks Catherine.