Set ComboBox cue banner in Windows XP

2019-06-14 13:35发布

I'm updating a VB.net project that needs to have cue banners added to text boxes and read-only comboboxes (DropDownStyle=DropDownList). The machine I'm developing on is Windows 7. I'm adding the cue text in a class that extends a combobox and adds a cue text property. This is how the cue text is added to the combobox:

 '"Me" refers to a combobox that has been extended to include a Cue Text property
 SendMessage(New HandleRef(Me, Me.Handle), CB_SETCUEBANNER, IntPtr.Zero, _cueText)

The above code is from this blog bost: http://www.aaronlerch.com/blog/page/7/, which is in C#; I translated it to VB. I tried other similar variations that I found elsewhere, all with the same result: it works great for text boxes and comboboxes when I run the program in Windows 7; it only works for text boxes in Windows XP.

I've read lots of comments on different forums about making sure visual styles are selected and disabling east Asia languages and complex scripts. I've done all that, but still haven't gotten it to work on XP.

Has anyone gotten cue banners for comboboxes to work on XP?

1条回答
我欲成王,谁敢阻挡
2楼-- · 2019-06-14 14:04

Using various blog and forum posts, I created a class that extends the ComboBox control and implements a CueText property that works on Windows 7 and XP. I found the most relevant pieces of information here:

  1. How to set cue text in XP: http://www.ageektrapped.com/blog/the-missing-net-1-cue-banners-in-windows-forms-em_setcuebanner-text-prompt/
  2. How to set cue text in Windows 7: http://www.aaronlerch.com/blog/2007/12/01/watermarked-edit-controls/

In a nutshell, Windows 7 and XP set the cue banner text slightly differently, so you need to check which operating system the program is running on and then handle the cue text appropriately. You need to use EM_SETCUEBANNER As Integer = &H1501 for XP and CB_SETCUEBANNER As UInteger = &H1703 for Windows 7. You also need to single out the text part of the combo box if the app is running on XP. You can see the details in the code below. To figure out which OS is running, see MS KB articles 304289 (VB) or 304283 (C#). (I would post the links, but I don't have enough reputation points to post more than two.)

One caveat is that this will not work on XP if the combo boxes are read only (DropDownStyle = DropDownList). Windows 7 seems to work fine either way. If your application needs to run on XP and you need the combo boxes to be read only but still display the cue text, here's what you can do:

  1. Create a combo box and use the default DropDownStyle, "DropDown"
  2. Handle the KeyPress event for the combo box(es) and in that method tell it that the event has been handled like this: e.Handled = True. This will prevent text from being typed.
  3. Create a blank ContextMenuStrip using the Toolbox in the design view, click on the combo box, view its properties, and set its ContextMenuStrip to the one you just created. This will cause nothing to happen when the user right clicks in the combo box so they can't paste text into it.

Here's the VB code for a class that inherits the ComboBox control and adds a CueText property that works for XP and 7. The only thing you'll have to do is figure out which OS is running:

Imports System.ComponentModel
Imports System.Runtime.InteropServices

Public Class CueComboBox
Inherits ComboBox

' Occurs when the CueText property value changes.
Public Event CueTextChanged As EventHandler

'Windows XP
Private Shared EM_SETCUEBANNER As Integer = &H1501
'Windows 7
Private Shared CB_SETCUEBANNER As UInteger = &H1703

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

<DllImport("user32.dll")> _
Private Shared Function GetComboBoxInfo(ByVal hwnd As IntPtr, ByRef pcbi As COMBOBOXINFO) As Boolean
End Function

<StructLayout(LayoutKind.Sequential)> _
Private Structure COMBOBOXINFO
    Public cbSize As Integer
    Public rcItem As RECT
    Public rcButton As RECT
    Public stateButton As IntPtr
    Public hwndCombo As IntPtr
    Public hwndItem As IntPtr
    Public hwndList As IntPtr
End Structure

<StructLayout(LayoutKind.Sequential)> _
Private Structure RECT
    Public left As Integer
    Public top As Integer
    Public right As Integer
    Public bottom As Integer
End Structure

Private Shared Function GetComboBoxInfo(ByVal control As Control) As COMBOBOXINFO
    Dim info As New COMBOBOXINFO()
    'a combobox is made up of three controls, a button, a list and textbox;
    'we want the textbox
    info.cbSize = Marshal.SizeOf(info)
    GetComboBoxInfo(control.Handle, info)
    Return info
End Function


Private _cueText As String = [String].Empty

' Gets or sets the text that will display as a cue to the user.
<Description("The text value to be displayed as a cue to the user.")> _
<Category("Appearance")> <DefaultValue("")> <Localizable(True)> _
Public Property CueText() As String
    Get
        Return _cueText
    End Get
    Set(ByVal value As String)
        If value Is Nothing Then
            value = [String].Empty
        End If

        If Not _cueText.Equals(value, StringComparison.CurrentCulture) Then
            _cueText = value
            UpdateCue()
            OnCueTextChanged(EventArgs.Empty)
        End If
    End Set
End Property

<EditorBrowsable(EditorBrowsableState.Advanced)> _
Protected Overridable Sub OnCueTextChanged(ByVal e As EventArgs)
    RaiseEvent CueTextChanged(Me, e)
End Sub


Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
    UpdateCue()
    MyBase.OnHandleCreated(e)
End Sub


Private Sub UpdateCue()
    ' If the handle isn't yet created, this will be called when it is created
    If Me.IsHandleCreated Then
        ' Windows XP sets the cue banner differently than Windows 7
        If Form1.OPERATING_SYSTEM = "Windows XP" Then
            Dim info As COMBOBOXINFO = GetComboBoxInfo(Me)
            SendMessage(info.hwndItem, EM_SETCUEBANNER, 0, _cueText)
        Else
            SendMessage(New HandleRef(Me, Me.Handle), CB_SETCUEBANNER, IntPtr.Zero, _cueText)
        End If
    End If
End Sub

End Class
查看更多
登录 后发表回答