Link to home
Start Free TrialLog in
Avatar of omroepbrabant
omroepbrabantFlag for Netherlands

asked on

How to direct a keystroke to different control

Hello,

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!
Avatar of topdog770
topdog770
Flag of United States of America image

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()
        {
            InitializeComponent();
        }
 
        protected override bool ProcessDialogKey(Keys keyData)
        {
            // add your custom logic here 
            return base.ProcessDialogKey(keyData);
        }
    }
}

Open in new window

Avatar of omegaomega
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.

Groetjes,
Randy

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
        Application.AddMessageFilter(mMyFilter)
 
    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 http://www.pinvoke.net/default.aspx/user32/GetComboBoxInfo.html
    ' vvvvv=====vvvvv=====vvvvv=====vvvvv=====vvvvv  
    Private Enum ComboBoxButtonState
        STATE_SYSTEM_NONE = 0
        STATE_SYSTEM_INVISIBLE = &H8000
        STATE_SYSTEM_PRESSED = &H8
    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
                            mcbxTarget.Focus()
                            ' 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
        Get
            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

Avatar of omroepbrabant

ASKER

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...

Anyone?
ASKER CERTIFIED SOLUTION
Avatar of topdog770
topdog770
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hello, omroepbrabant,

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

        Me.ComboBox1.Text = Me.ComboBox1.Text & "K"
        Me.ComboBox1.Focus()

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?

Cheers,
Randy
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!