在焦点文本框和 DataGridView 单元格上使用虚拟键盘

Use a virtual Keyboard on focused Textboxes and DataGridView Cells

在我的表单中,我有各种文本框,我使用按钮创建的表单键盘写入这些文本框。我在 Form.Load 中有这段代码,它使用事件处理程序来确定哪个文本框具有焦点:

For Each control As Control In Me.Controls
    If control.GetType.Equals(GetType(TextBox)) Then
        Dim textBox As TextBox = control
        AddHandler textBox.Enter, Sub() FocussedTextbox = textBox
     End If
Next

然后我用这个在每个按钮上写一个特定的字符:

Private Sub btnQ_Click(sender As Object, e As EventArgs) Handles btnQ.Click
    If btnQ.Text = "Q" Then
        FocussedTextbox.Text += "Q"
    ElseIf btnQ.Text = "q" Then
        FocussedTextbox.Text += "q"
    End If
End Sub

到那时我还不错,一切都按预期进行。问题是我也有一个 DataGridView 我想写入但不能像在文本框上那样专注于选定的单元格。

我试过这个:

For Each control As Control In Me.Controls
    If control.GetType.Equals(GetType(TextBox)) Then
        Dim textBox As TextBox = control
        AddHandler textBox.Enter, Sub() FocussedTextbox = textBox
    ElseIf control.GetType.Equals(GetType(DataGridViewCell)) Then
        Dim DGVC As DataGridView = control
        AddHandler DGVC.CellBeginEdit, Sub() FocussedTextbox = DGVC
    End If
Next

但它只选择了我的最后一个文本框。

我将变量 FocussedTextbox 声明为 Control 所以它不是特定于文本框而是任何控件。

任何帮助将不胜感激。

要使用按钮将文本添加到当前 ActiveControl,这些按钮不得从 ActiveControl 窃取焦点(否则它们将成为 ActiveControl)。
这样,您还可以避免所有这些 FocusedTextbox = textBox 等并删除该代码。

您只需要没有设置 Selectable 属性的按钮。您可以使用派生自 Button 的自定义控件并删除 ControlStyles.Selectable in its constructor using the SetStyle 方法:

Public Class ButtonNoSel
    Inherits Button
    Public Sub New()
        SetStyle(ControlStyles.Selectable, False)
    End Sub
End Class

用这个按钮替换您的按钮(或者,如果您已经在使用自定义控件,则只需设置样式)。


要用此自定义控件替换现有按钮:

  • 向您的项目添加一个新的 class 对象,将其命名为 ButtonNoSel,将上面的所有代码复制到新的 class 中,以替换您在其中找到的两行代码。
  • 构建项目。您现在可以在工具箱中找到 ButtonNoSel 控件。用这个替换你的按钮。
  • 或者,打开 Form 的 Designer 文件并将 (CTRL+H) 所有与虚拟键盘相关的 System.Windows.Forms.Button() 替换为 ButtonNoSel
  • 删除现有的事件处理程序,不再需要这些。

在托管这些按钮(表单或您正在使用的任何其他内容)的 class 的构造函数中添加相同的 Click 事件处理程序。
然后,您可以删除所有这些事件处理程序,每个控件一个,您现在拥有;所有人只需要一个事件处理程序:

Public Sub New()
    InitializeComponent()

    For Each ctrl As Control In Me.Controls.OfType(Of ButtonNoSel)
        AddHandler ctrl.Click, AddressOf KeyBoardButtons_Click
    Next
End Sub

当然,您也不需要向任何其他控件添加事件处理程序,这就是所需的全部内容。

现在,您可以过滤希望键盘使用的控件类型,例如,TextBoxBase 控件(TextBox 和 RichTextBox)、DataGridView、NumericUpDown 等
或者仅过滤需要 特殊处理 的特殊情况(例如,MonthCalendar)。

要添加与按下的按钮对应的字符,可以使用SendKeys.Send():它将在当前插入点插入新字符,因此您不需要任何其他代码来存储和重置caret/cursor 设置控件的文本 属性 时出现的位置。

在这个例子中,我正在检查 ActiveControl 是否是一个 TextBoxBase 控件,然后只发送被点击的按钮包含的字符。
如果是DataGridView,先发送F2进入编辑模式,再发送char.
您也可以只发送一个字符(这样就不需要过滤器),但在这种情况下,您将替换而不是添加到该 Cell 的现有值。

Private Sub KeyBoardButtons_Click(sender As Object, e As EventArgs)
    Dim selectedButton = DirectCast(sender, Control)
    Dim keysToSend = String.Empty

    If TypeOf ActiveControl Is TextBoxBase Then
        keysToSend = selectedButton.Text
    ElseIf TypeOf ActiveControl Is DataGridView Then
        Dim ctrl = DirectCast(ActiveControl, DataGridView)
        If TypeOf ctrl.CurrentCell IsNot DataGridViewTextBoxCell Then Return
        SendKeys.Send("{F2}")
        keysToSend = selectedButton.Text
    Else
        ' Whatever else
    End If
    If Not String.IsNullOrEmpty(keysToSend) Then
        SendKeys.Send(keysToSend)
    End If
End Sub

► 请注意 {F2} 仅发送一次:当单元格进入编辑模式时,ActiveControlDataGridViewTextBoxEditingControl,因此一个 TextBox 控件,由 TextBoxBase 过滤器处理。

这是它的工作原理(仅使用此处发布的代码):