none
[SOLVED] Alternative to SendKeys in VB.Net 2010

    Question

  • I came across of an API that will provide an alternative to SendKeys. And here it is...

    Public Class Form1
        Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
                         (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
                         (ByVal hWnd As IntPtr, ByVal hWndChildAfterA As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
        Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
                         (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
        Const WM_SETTEXT As Integer = &HC
    
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            Dim destination As IntPtr = FindWindow(Nothing, "Untitled - Notepad")
            Dim destControl As IntPtr = FindWindowEx(destination, IntPtr.Zero, "Edit", Nothing)
            SendMessage(destControl, WM_SETTEXT, IntPtr.Zero, "Hello" & vbTab & "GoodBye" & vbCrLf)
    
        End Sub
    
    End Class

    Public Class Form1
    
        Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
                     (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
                     (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
        Const WM_KEYDOWN As Integer = &H100
        Const VK_TAB As Integer = &H9
    
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    
            Dim destination As IntPtr = FindWindow(Nothing, "TestForm")
            SendMessage(destination, WM_KEYDOWN, VK_TAB, 0)
    
        End Sub
    
    End Class

    I use the second code snippet on my need to send a TabKey then UpKey on a DataGridView object. The problem with DataGridview is that after you've entered a data, the cursor goes to the next row--same column. What I want to do is to move the cursor on the same row--next column.

    But my problem is it does not work but if I use the SendKeys equivalent, it works on DataGridView.

    I prefer to use the SendMessage approach compared to SendKeys because of what I read from the internet that SendKeys has some issues.

    Appreciate any help on SendMessage to work on DataGridView. Btw, the DataGridView is not bound to any database or source, the user has to input a data manually.

    Thanks for the help.


    • Edited by rrb011270 Tuesday, July 09, 2013 11:44 PM
    Tuesday, July 09, 2013 1:28 PM

Answers

  • Hi KevinInstructor,

    Your suggested code snippet helps but I made some modification in the snippet to change the currentcell to the next column. If the currentcell is at the last column, it change the currentcell to the next row on the first column.

    Below is the code snippet with changes on selection.

    Private Sub DataGridView1_SelectionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGridView1.SelectionChanged
            If EditRow >= 0 Then
                Dim NewRow As Integer = EditRow
                Dim NewCol As Integer
    
                NewCol = DataGridView1.CurrentCell.ColumnIndex + 1
                If NewCol >= 2 Then
                    NewCol = 0
                    NewRow += 1
                End If
    
                DataGridView1.CurrentCell = DataGridView1.Rows(NewRow).Cells(NewCol)
                EditRow = -1
            End If
    End Sub

    I did not change the DataGridView1_EditingControlShowing() subroutine but I remove the DataGridView1_KeyDown() subroutine since I don't need it.

    Thanks for the usual support.

    • Marked as answer by rrb011270 Tuesday, July 09, 2013 11:43 PM
    Tuesday, July 09, 2013 11:43 PM
  • Hello,

    Somehow in my first reply I missed that you are working with a DataGridView. Perhaps the following would work for you.

    Requires a DataGridView

    Public Class Form1
        Private EditRow As Integer = -1
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            DataGridView1.ColumnCount = 3
            DataGridView1.Columns(0).Name = "First Name"
            DataGridView1.Columns(0).DataPropertyName = "Object"
            DataGridView1.Columns(1).Name = "Last Name"
            DataGridView1.Columns(2).Name = "Occupation"
            DataGridView1.Rows.Add(New Object() {"Rod", "Stephens", "Nerd"})
            DataGridView1.Rows.Add(New Object() {"Sergio", "Aragones", "Cartoonist"})
            DataGridView1.Rows.Add(New Object() {"Eoin", "Colfer", "Author"})
            DataGridView1.Rows.Add(New Object() {"Terry", "Pratchett", "?"})
        End Sub
        Private Sub DataGridView1_EditingControlShowing(
            ByVal sender As Object,
            ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) _
        Handles DataGridView1.EditingControlShowing
            EditRow = DataGridView1.CurrentRow.Index
        End Sub
        Private Sub DataGridView1_SelectionChanged(
            ByVal sender As Object,
            ByVal e As System.EventArgs) _
        Handles DataGridView1.SelectionChanged
            If EditRow >= 0 Then
                Dim new_row As Integer = EditRow
                EditRow = -1
                DataGridView1.CurrentCell = DataGridView1.Rows(new_row).Cells(DataGridView1.CurrentCell.ColumnIndex)
            End If
        End Sub
        Private Sub DataGridView1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown
            If e.KeyCode = Keys.Return Then
                If DataGridView1.CurrentRow.Index < DataGridView1.RowCount - 1 AndAlso DataGridView1.CurrentCell.ColumnIndex = DataGridView1.ColumnCount - 1 Then
                    DataGridView1.CurrentCell = DataGridView1(0, DataGridView1.CurrentRow.Index + 1)
                Else
                    Dim cur_cell As DataGridViewCell = DataGridView1.CurrentCell
                    Dim col As Integer = cur_cell.ColumnIndex
                    col = (col + 1) Mod DataGridView1.Columns.Count
                    cur_cell = DataGridView1.CurrentRow.Cells(col)
                    DataGridView1.CurrentCell = cur_cell
                End If
                e.Handled = True
            End If
        End Sub
    End Class


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem.

    • Marked as answer by rrb011270 Tuesday, July 09, 2013 11:42 PM
    Tuesday, July 09, 2013 5:41 PM
    Moderator

All replies

  • Also take note of the .StandardTab property.

    Please call me Frank :)

    Tuesday, July 09, 2013 2:01 PM
  • Hello,

    Here is an alternative

    Calling form

    Private Sub cmdChildForm_Click(sender As Object, e As EventArgs) Handles cmdChildForm.Click
        Dim f As New frmIncSearch()
        Try
            f.Owner = Me
            f.ShowDialog()
        Finally
            f.Dispose()
        End Try
    End Sub

    In frmIncSearch form

    First one targets a specific control while the second simply follows the form's tab order.

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        CType(Me.Owner, MainForm).ActiveControl = CType(Me.Owner, MainForm).cmdCloseForm
    End Sub

    Or which follows the tab order

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        CType(Me.Owner, MainForm).SelectNextControl(CType(Me.Owner, MainForm).ActiveControl, True, True, True, True)
    End Sub


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem.

    Tuesday, July 09, 2013 2:18 PM
    Moderator
  • If you are trying to do this in a button click event, then what is to prevent you from just setting the selected cell explicitly based on the current selected cell? I don't see what the need for sendmessage or sendkeys is, unless you are trying to do it by capturing a specific keyboard event to override, like the user pressing a specific key like enter

            If DGV.CurrentCell IsNot Nothing Then
                If DGV.CurrentCell.ColumnIndex = (DGV.ColumnCount - 1) Then
                    'LAST COLUMN, DECIDE TO GO TO NEXT ROW OR TAKE OTHER ACTION
                Else
                    'SELECT CELL NEXT TO (RIGHT OF) CURRENT SELECTION
                    DGV.CurrentCell = DGV.Rows(DGV.CurrentCell.RowIndex).Cells(DGV.CurrentCell.ColumnIndex + 1)
                End If
            End If


    Matt Kleinwaks - MSMVP MSDN Forums Moderator - www.zerosandtheone.com


    Tuesday, July 09, 2013 3:07 PM
    Moderator
  • Hi,

     Is the reason for using the FindWindow and SendMessage API`s because your datagridview is on another form than the button? If it is on the same form then you can just make sure the datagridview has focus and use sendkeys like this

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            DataGridView1.Focus()
            SendKeys.Send("{TAB}")
        End Sub

    However, if it is on the same form and you still want to use the API`s then you can do that like this

    Public Class Form1
        Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
        Const WM_KEYDOWN As Integer = &H100
        Const VK_TAB As Integer = &H9
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            SendMessage(DataGridView1.Handle, WM_KEYDOWN, VK_TAB, 0) 'Send the message to the datagridview`s handle
        End Sub
    End Class
    

    And if the datagridview is on another form you could do something like this. You will need to use FindWindowEx to find the child window (datagridview) of the main form and send the message to the child window handle. The following code will only work if the datagridview is the only child or the first child on the main form window. Otherwise you will need to use FindWindowEx to loop threw the children of the main form window to locate the handle of the datagridview. That will need to be done by some trial and error because FindWindowEx does not recognize the datagridview class or at least i have not been able to get it to recognize it.

    Public Class Form1
        Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd As IntPtr, ByVal hWndChildAfterA As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
        Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
        Const WM_KEYDOWN As Integer = &H100
        Const VK_TAB As Integer = &H9
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim hWnd As IntPtr = FindWindow(Nothing, "Form2") 'Get the handle to the main window
            Dim dgvWnd As IntPtr = FindWindowEx(hWnd, dgvWnd, vbNullString, vbNullString) 'Get the datagridview handle
            SendMessage(dgvWnd, WM_KEYDOWN, VK_TAB, 0) 'Send message to datagridview handle
        End Sub
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Form2.Show()
        End Sub
    End Class


    • Edited by IronRazerz Tuesday, July 09, 2013 4:07 PM edit
    Tuesday, July 09, 2013 4:05 PM
  • Hello,

    Somehow in my first reply I missed that you are working with a DataGridView. Perhaps the following would work for you.

    Requires a DataGridView

    Public Class Form1
        Private EditRow As Integer = -1
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            DataGridView1.ColumnCount = 3
            DataGridView1.Columns(0).Name = "First Name"
            DataGridView1.Columns(0).DataPropertyName = "Object"
            DataGridView1.Columns(1).Name = "Last Name"
            DataGridView1.Columns(2).Name = "Occupation"
            DataGridView1.Rows.Add(New Object() {"Rod", "Stephens", "Nerd"})
            DataGridView1.Rows.Add(New Object() {"Sergio", "Aragones", "Cartoonist"})
            DataGridView1.Rows.Add(New Object() {"Eoin", "Colfer", "Author"})
            DataGridView1.Rows.Add(New Object() {"Terry", "Pratchett", "?"})
        End Sub
        Private Sub DataGridView1_EditingControlShowing(
            ByVal sender As Object,
            ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) _
        Handles DataGridView1.EditingControlShowing
            EditRow = DataGridView1.CurrentRow.Index
        End Sub
        Private Sub DataGridView1_SelectionChanged(
            ByVal sender As Object,
            ByVal e As System.EventArgs) _
        Handles DataGridView1.SelectionChanged
            If EditRow >= 0 Then
                Dim new_row As Integer = EditRow
                EditRow = -1
                DataGridView1.CurrentCell = DataGridView1.Rows(new_row).Cells(DataGridView1.CurrentCell.ColumnIndex)
            End If
        End Sub
        Private Sub DataGridView1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown
            If e.KeyCode = Keys.Return Then
                If DataGridView1.CurrentRow.Index < DataGridView1.RowCount - 1 AndAlso DataGridView1.CurrentCell.ColumnIndex = DataGridView1.ColumnCount - 1 Then
                    DataGridView1.CurrentCell = DataGridView1(0, DataGridView1.CurrentRow.Index + 1)
                Else
                    Dim cur_cell As DataGridViewCell = DataGridView1.CurrentCell
                    Dim col As Integer = cur_cell.ColumnIndex
                    col = (col + 1) Mod DataGridView1.Columns.Count
                    cur_cell = DataGridView1.CurrentRow.Cells(col)
                    DataGridView1.CurrentCell = cur_cell
                End If
                e.Handled = True
            End If
        End Sub
    End Class


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem.

    • Marked as answer by rrb011270 Tuesday, July 09, 2013 11:42 PM
    Tuesday, July 09, 2013 5:41 PM
    Moderator
  • Hi KevinInstructor,

    Your suggested code snippet helps but I made some modification in the snippet to change the currentcell to the next column. If the currentcell is at the last column, it change the currentcell to the next row on the first column.

    Below is the code snippet with changes on selection.

    Private Sub DataGridView1_SelectionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGridView1.SelectionChanged
            If EditRow >= 0 Then
                Dim NewRow As Integer = EditRow
                Dim NewCol As Integer
    
                NewCol = DataGridView1.CurrentCell.ColumnIndex + 1
                If NewCol >= 2 Then
                    NewCol = 0
                    NewRow += 1
                End If
    
                DataGridView1.CurrentCell = DataGridView1.Rows(NewRow).Cells(NewCol)
                EditRow = -1
            End If
    End Sub

    I did not change the DataGridView1_EditingControlShowing() subroutine but I remove the DataGridView1_KeyDown() subroutine since I don't need it.

    Thanks for the usual support.

    • Marked as answer by rrb011270 Tuesday, July 09, 2013 11:43 PM
    Tuesday, July 09, 2013 11:43 PM
  • Your welcome and thanks for sharing your modifications.

    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem.

    Wednesday, July 10, 2013 12:05 AM
    Moderator