How to direct a keystroke to different control

omroepbrabant used Ask the Experts™

I'm writing a Windows Form app in VS 2008 C#.
It has some buttons and a combobox. One of the buttons need to have the default focus.

All buttons have their own hotkey, but all other keys should invoke focus to the combobox and get typed there. I now did this with a keypreview on the form and mark hotkeys as handled.

How do I set the focus to the combobox and send a key there, without using Sendkey?

Thanx for any help!
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
You can create a custom form class, deriving from Form, and override the ProcessDialogKey method
using System.Windows.Forms;
namespace ConsoleApplication1
    public partial class DerivedForm : Form
        public DerivedForm()
        protected override bool ProcessDialogKey(Keys keyData)
            // add your custom logic here 
            return base.ProcessDialogKey(keyData);

Open in new window

Hello, omroepbrabant,

I'm not entirely sure what it is you are trying to do, but it seems like you would have a rather non-standard user interface, which I generally try to avoid.

But having said that, perhaps you can use  a message filter for this purpose.  There is an example in the snippet.  (Sorry, this is in VB (because that's what I do) but I'm sure you can convert it to C#.)

In the example I have created a very simple form with some Buttons and a ComboBox on it.  In the Form's Load event I add a message filter.  Both the Form's non designer code and the message filter are shown in the snippet.

I think this seems to do more or less what you are describing.  The main problem I see, is that the ComboBox loses/resets its SelectionStart and SelectionLength properties when it loses focus.  This means that any time you change focus away from the ComboBox (i.e. Click a button) and then start typing again the original contents of the ComboBox are lost.  I haven't been able to find a way around this yet.  (I tried saving these properties in the ComboBox's Leave event, which is OK if you leave with the Tab key, but it seems that if you leave with a mouse click the properties are already reset by the time the Leave event (or any other related event) is processed.)

Anyway, perhaps it will give you some idea.


Public Class Form1
    Private miptComboBox As IntPtr
    Private mMyFilter As MyMsgFilter
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        mMyFilter = New MyMsgFilter
        mMyFilter.TargetComboBox = Me.ComboBox1
    End Sub
End Class
Imports System.Runtime.InteropServices
Public Class MyMsgFilter
    Implements IMessageFilter
    Private Const WM_KEYFIRST As Integer = &H100
    Private Const WM_KEYLAST As Integer = &H109
    Private Const Key_HTab As Integer = 9   ' Horizontal Tab
    Private Structure Rect
        Dim Left As Integer
        Dim Top As Integer
        Dim Right As Integer
        Dim Bottom As Integer
    End Structure
    ' =============================================
    ' From
    ' vvvvv=====vvvvv=====vvvvv=====vvvvv=====vvvvv  
    Private Enum ComboBoxButtonState
    End Enum
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure COMBOBOXINFO
        Public cbSize As Int32
        Public rcItem As Rect
        Public rcButton As Rect
        Public buttonState As ComboBoxButtonState
        Public hwndCombo As IntPtr
        Public hwndEdit As IntPtr
        Public hwndList As IntPtr
    End Structure
    ' ^^^^^=====^^^^^=====^^^^^=====^^^^^=====^^^^^
    ' =============================================
    Private Declare Function GetComboBoxInfo Lib "user32" _
            (ByVal hWnd As IntPtr, ByRef pcbi As COMBOBOXINFO) As Boolean
    Private Declare Function GetLastError Lib "kernel32" () As Integer
    Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
            (ByVal hwnd As IntPtr, ByVal wMsg As Integer, _
             ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
    Private mcbiTarget As COMBOBOXINFO
    Private mcbxTarget As ComboBox
    Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
        Select Case m.Msg
            Case WM_KEYFIRST To WM_KEYLAST
                ' Special processing for keyboard messages.
                ' Skip processing if the target ComboBox is not
                ' set, or if the message is from the ComboBox.
                If (mcbxTarget IsNot Nothing And _
                    Not (m.HWnd = mcbiTarget.hwndCombo Or _
                         m.HWnd = mcbiTarget.hwndEdit Or _
                         m.HWnd = mcbiTarget.hwndList)) Then
                    Debug.WriteLine("LParam = " & m.LParam.ToString & ", WParam = " & m.WParam.ToString)
                    Select Case CInt(m.WParam)
                        Case Key_HTab
                            ' Skip processing for navigation keys.
                        Case Else
                            ' Send an equivalent message to the ComboBox.
                            If (Not PostMessage(mcbiTarget.hwndCombo, m.Msg, m.WParam, m.LParam)) Then
                                Dim intErrorCode As Integer = GetLastError()
                                Throw New Exception("Error # " & intErrorCode & " (" & Hex(intErrorCode) & ") from PostMessage.")
                            End If
                            Return True
                    End Select
                End If
        End Select
        Return False
    End Function
    Public Property TargetComboBox() As ComboBox
            Return mcbxTarget
        End Get
        Set(ByVal cbxTarget As ComboBox)
            mcbxTarget = cbxTarget
            mcbiTarget.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(mcbiTarget)
            If (Not GetComboBoxInfo(mcbxTarget.Handle, mcbiTarget)) Then
                Dim intErrorCode As Integer = GetLastError()
                Throw New Exception("Error # " & intErrorCode & " (" & Hex(intErrorCode) & ") from GetComboBoxInfo.")
            End If
        End Set
    End Property
End Class

Open in new window


Thanks a lot for your effords topdog and omegaomega...
but I might have been too vague about what I try to do:

The problem is that I cannot send a key to a combobox that doesn't have the focus.

In my program a button has the focus, but as soon as you type a letter, this letter should by typed in the combobox.
I CAN filter the key so it doesn't end at the focussed button,
I CAN set the focus on the combobox when the key is pressed,
I CAN append the letter to the Text property of the comboBox, but it turns out to be selected and the cursor is not at the end of the 'typed' text. So the second letter typed replaces the first.

The only way I have managed this is with Sendkey, but I think there should be a better way...

OWASP Proactive Controls

Learn the most important control and control categories that every architect and developer should include in their projects.

Ok, so the only thing it sounds like you may be missing is setting the combobox text like this:
// add letter to end of combo box
this.comboBox1.SelectedText += "a";
Hello, omroepbrabant,

So, I'm now imagining that you are doing something like:

        Me.ComboBox1.Text = Me.ComboBox1.Text & "K"

where "K" would be the letter you are appending.  Can't you just add the line:

        Me.ComboBox1.SelectionStart = Me.ComboBox1.Text.Length

to solve the problem?



I've tried it before with comboBox1.Text += "x", but that didn't put the cursor at the end. I didn't know SelectedText, so this was the missing link!
Glad it helped!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial