1

So, basically, I'm programming this virtual keyboard, and I have to write code individually for each key, which are Buttons.
This is ridiculously tedious and I was wondering if there was maybe one single statement that could find the text of the key (Button) pressed.

My current code is like this:

textbox.TextBox1.Text = textbox.TextBox1.Text + zh.Text

zh is the name of a Button, for context.

Now, I have to copy and paste this string of code over and over in each Button's Click event.
I would think that there is a way to have the code grab the text of whatever Button I press, then send that text to the TextBox.

If this is indeed possible, would anyone know how to do it?

Jimi
  • 29,621
  • 8
  • 43
  • 61
w60
  • 49
  • 7

1 Answers1

3

Method 1:
Using the Designer, assign a single Click event to all your Buttons, then use the sender object, casting it to Button or Control:

Private Sub MyKeys_Click(sender As Object, e As EventArgs) Handles MyKeysA.Click, MyKeysB.Click, (...)
    TextBox1.AppendText(CType(sender, Button).Text)
End Sub

But the final event handler will have a lot of Buttons references attached to it. Not a beauty.

Method 2:
Create an event handler in code and assign it to all of your Buttons, using a classic delegate, with
AddHandler [Event], AddressOf [HandlerMethodName]

Assume that your Buttons have a common partial name, here "btnKey".
You could also use the Tag property and assign it a specific value(s) for your Keys Buttons.
You would then write in Where(): b.Tag.ToString().Contains("[Some Common Identifier]").

Note that the Tag property value is of type Object, so Contains() is just a generic example. It could evaluate to an Integer type or anything else.

Note 1: To assign a common identifier to all the Keys Buttons, you can use the Form Designer: select all the Buttons and use the Properties Window to change the Tag property of all the selected Buttons.

  • Here, assuming all Controls are child of the Form. Specify the actual Parent if's a different Container

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    For Each btn As Button In Me.Controls.OfType(Of Button).Where(Function(b) b.Name.Contains("btnKey"))
        AddHandler btn.Click, AddressOf Me.MyKeys_Click
    Next
End Sub

Private Sub MyKeys_Click(sender As Object, e As EventArgs)
    TextBox1.AppendText(DirectCast(sender, Control).Text)
End Sub

Note 2:
As Andrew Morton suggested in the comments, here the cast is performed using the DirectCast() operator. Since sender is a Button and also Button derives from Control, you can use the light-weight DirectCast() to see sender as a Button or as a Control (since Button derives from Control and the Text property is inherited from Control) and access its Text property.

From the Docs:
[DirectCast()] (...) it can provide somewhat better performance than CType when converting to and from data type Object.

I'm leaving CType() in the first example as a visual aide.

Difference between DirectCast() and CType() in VB.NET

Method 3:
Create an event handler in code and assign it to all of your Buttons using a Lambda as a method delegate:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    For Each btn As Button In Me.Controls.OfType(Of Button).Where(Function(b) b.Name.Contains("btnKey"))
        AddHandler btn.Click, Sub() TextBox1.AppendText(btn.Text)
    Next
End Sub
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • 1
    You might as well use DirectCast instead of CType because you already know that they are buttons. Further reading: [Difference between DirectCast() and CType() in VB.NET](https://stackoverflow.com/a/3056582/1115360). – Andrew Morton Nov 10 '18 at 17:33
  • @Andrew Morton Hmm, yes, it's worth mentioning, since it'll work casting to both Button and Control here. I'll link the link :) – Jimi Nov 10 '18 at 17:40
  • 1
    @Jimi What is textbox in `textbox.TextBox1` used in all three methods? – Mary Nov 10 '18 at 23:45
  • @Mary I'm not sure. You should ask the OP, it's in the question's code snippet. Maybe, it's just the Form container Name (is it a text editor Form?), or another container control. I just followed the *existing pattern*. But this is probably not a great idea, in a more generic use case scenario (also because, apparently, generates the question *what's that for?*). I'm removing it. – Jimi Nov 11 '18 at 03:00
  • @Jimi Thanks, I was pasting it into my code bank and was wondering. – Mary Nov 11 '18 at 03:25
  • Your approaches using `For Each btn As Button In Me.Controls.OfType(Of Button)`, are ASSUMING that all of the Buttons are directly contained by the Form itself. They won't be found if they are in a different container, like a Panel. Obviously, you could change `Me` to the name of the Panel, like `Panel1`, but what if the controls are spread out between multiple containers? Then you'd either need to use a RECURSIVE search, or possibly instead use the `Controls.Find()` method to look for them "by name", specifying a recursive search. – Idle_Mind Jan 12 '23 at 20:25
  • @JImi, sorry about that. I occasionally don't notice the dates on posts that show up in my feed. I'll try to pay closer attention! – Idle_Mind Jan 12 '23 at 22:34
  • @Idle_Mind No problem, your observation is nonetheless correct. I'll add a note – Jimi Jan 12 '23 at 23:00