Q:

How do I create command link buttons (with multiple lines of text) in VB.NET?

I know how to use the default buttons item, but is there any way to achieve the style of multiline buttons (or maybe rather, "clickable text"?) like shown below?

The situation is that I have an interface for the user to select what kind of file he wishes to establish, and there has to be a brief description under the larger, main line of text.
I'm only planning to run this on Windows 7, so I don't need to worry about backwards compatibility with older versions of Windows

A:

The button shown in the screenshot is actually one used throughout the Aero UI. It's a custom style of button called a "command link", and it can be easily applied to a standard Button control.
Unfortunately, the WinForms libraries don't expose this functionality via a simple property, but that's easily fixable with a bit of P/Invoke.
The style you're looking for is called BS_COMMANDLINK. According to the documentation, this style:

Creates a command link button that behaves like a BS_PUSHBUTTON style button, but the command link button has a green arrow on the left pointing to the button text. A caption for the button text can be set by sending the BCM_SETNOTE message to the button.

Here's a little custom button control class that extends the standard WinForms Button control, and implements the "command link" style as a property you can configure in the designer or through code.
A couple of things to note about the code:

The FlatStyle property must always be set to FlatStyle.System, which forces the use of the standard Windows API Button control, rather than one drawn by WinForms code. This is required for the BS_COMMANDLINK style to work (because it's only supported by the native controls), and it produces a better looking button control (with throbbing effects, etc.) anyway. To force this, I've overridden the FlatStyle property and set a default value.
The CommandLink property is how you toggle on and off the "command link" style. It's off by default, giving you a standard button control, so you can replace all of the button controls in your application with this one, if you want, just for convenience. When you turn the property on (set it to True), then you get a fancy, multiline command link button.
The command link button's caption is the same caption as is displayed on a standard button. However, caption button also support a "description" on the second line. This is configurable through another property, called CommandLinkNote, after the WinAPI message, BCM_SETNOTE. When you have the button configured as a standard button control (CommandLink = False), the value of this property is ignored.

Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Runtime.InteropServices

Public Class ButtonEx : Inherits Button

    Private _commandLink As Boolean
    Private _commandLinkNote As String

    Public Sub New() : MyBase.New()
        'Set default property values on the base class to avoid the Obsolete warning
        MyBase.FlatStyle = FlatStyle.System
    End Sub

    <Category("Appearance")> _
    <DefaultValue(False)> _
    <Description("Specifies this button should use the command link style. " & _
                 "(Only applies under Windows Vista and later.)")> _
    Public Property CommandLink As Boolean
        Get
            Return _commandLink
        End Get
        Set(ByVal value As Boolean)
            If _commandLink <> value Then
                _commandLink = value
                Me.UpdateCommandLink()
            End If
        End Set
    End Property

    <Category("Appearance")> _
    <DefaultValue("")> _
    <Description("Sets the description text for a command link button. " & _
                 "(Only applies under Windows Vista and later.)")> _
    Public Property CommandLinkNote As String
        Get
            Return _commandLinkNote
        End Get
        Set(value As String)
            If _commandLinkNote <> value Then
                _commandLinkNote = value
                Me.UpdateCommandLink()
            End If
        End Set
    End Property

    <Browsable(False)> <EditorBrowsable(EditorBrowsableState.Never)> _
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    <Obsolete("This property is not supported on the ButtonEx control.")> _
    <DefaultValue(GetType(FlatStyle), "System")> _
    Public Shadows Property FlatStyle As FlatStyle
        'Set the default flat style to "System", and hide this property because
        'none of the custom properties will work without it set to "System"
        Get
            Return MyBase.FlatStyle
        End Get
        Set(ByVal value As FlatStyle)
            MyBase.FlatStyle = value
        End Set
    End Property

#Region "P/Invoke Stuff"
    Private Const BS_COMMANDLINK As Integer = &HE
    Private Const BCM_SETNOTE As Integer = &H1609

    <DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=False)> _
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, _
                                        <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As IntPtr
    End Function

    Private Sub UpdateCommandLink()
        Me.RecreateHandle()
        SendMessage(Me.Handle, BCM_SETNOTE, IntPtr.Zero, _commandLinkNote)
    End Sub

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams

            If Me.CommandLink Then
                cp.Style = cp.Style Or BS_COMMANDLINK
            End If

            Return cp
        End Get
    End Property
#End Region

End Class

