Showing the Keyboard Navigation Indicators

Windows 2000 introduced a setting for hiding keyboard accelerator keys. If this setting is enabled, the shortcuts are not displayed in underlined font style.

There is no general managed way in .NET Framework 1.1 to show accelerator keys underlined automatically. To do that, a WM_CHANGEUISTATE message with appropriate data must be sent to the window that should show its accelerator keys. One of the parameters passed with the message needs the MAKELONG macro to combine two 16-bit integers to a 32-bit integer. In our sample, we use a trick with a structure to provide an easy and managed way to replace the MAKELONG, LOWORD, and HIWORD macros:

Imports System.Runtime.InteropServices

Public Class WordConverter
    <StructLayout(LayoutKind.Explicit)> _
    Private Structure DWord
        <FieldOffset(0)> Public LongValue As Integer
        <FieldOffset(0)> Public LoWord As Short
        <FieldOffset(2)> Public HiWord As Short
    End Structure

    Private Shared m_DWord As DWord

    Public Shared Function MakeLong( _
        ByVal LoWord As Short, _
        ByVal HiWord As Short _
    ) As Integer
        m_DWord.LoWord = LoWord
        m_DWord.HiWord = HiWord
        Return m_DWord.LongValue
    End Function

    Public Shared Function MakeLong( _
        ByVal LoWord As Integer, _
        ByVal HiWord As Integer _
    ) As Integer
        Return MakeLong(CShort(LoWord), CShort(HiWord))
    End Function

    Public Shared Function LoWord(ByVal LongValue As Integer) As Short
        m_DWord.LongValue = LongValue
        Return m_DWord.LoWord
    End Function

    Public Shared Function HiWord(ByVal LongValue As Integer) As Short
        m_DWord.LongValue = LongValue
        Return m_DWord.HiWord
    End Function
End Class

The procedure MakeAcceleratorsVisible takes a controls and makes its accelerator keys visible:

Private Declare Auto Function SendMessage Lib "user32.dll" ( _
    ByVal hWnd As IntPtr, _
    ByVal wMsg As Int32, _
    ByVal wParam As IntPtr, _
    ByVal lParam As IntPtr _
) As IntPtr

Public Const WM_CHANGEUISTATE As Int32 = &H127

Public Const UIS_CLEAR As Int32 = 2

Public Const UISF_HIDEACCEL As Int16 = &H2

Public Sub MakeAcceleratorsVisible(ByVal Control As Control)
    SendMessage( _
        Control.Handle, _
        WM_CHANGEUISTATE, _
        New IntPtr(WordConverter.MakeLong(UIS_CLEAR, UISF_HIDEACCEL)), _
        IntPtr.Zero _
    )
End Sub

If accelerators should be made visible within a Windows Forms control class, the code below can be used instead of MakeAcceleratorsVisible:

MyBase.WndProc( _
    Message.Create( _
        Me.Handle, _
        WM_CHANGEUISTATE, _
        New IntPtr(WordConverter.MakeLong(UIS_CLEAR, UISF_HIDEACCEL)), _
        IntPtr.Zero _
    ) _
)