4

Is there a better way to have a keydown handler for many controls on a busy form? (Using the form handler alone doesn't work in some cases.)

Private Sub rText_KeyDown(ByVal Sender As Object, ByVal e As KeyEventArgs) Handles rText0.KeyDown, txTaxon.KeyDown, txCommon.KeyDown, _
  txConfidence.KeyDown, txDate.KeyDown, txDateAdded.KeyDown, txFileName.KeyDown, txGPS.KeyDown, txRating.KeyDown, _
  txConfidence.KeyDown, txQuality.KeyDown, txRemarks.KeyDown, txKeyWords.KeyDown, txOriginalPath.KeyDown, txDateAdded.KeyDown, _
  txLink.KeyDown, chkLink.KeyDown, rview.KeyDown, cmdNext.KeyDown, tvTaxon.KeyDown, Me.KeyDown, _
  cmdTaxon.KeyDown
Call globalkey(e)
End Sub
xpda
  • 15,585
  • 8
  • 51
  • 82
  • possible duplicate of [Best way to implement keyboard shortcuts in winforms?](http://stackoverflow.com/questions/400113/best-way-to-implement-keyboard-shortcuts-in-winforms) – Hans Passant Oct 01 '12 at 07:03
  • @HansPassant I don't think this is really a duplicate of that question, although it may be that he is trying to implement a global hotkey in a clumsy way. Hard to tell without more context. – J... Oct 01 '12 at 11:08
  • @J... I'm not trying to implement a global shortcut. I just used a bad name "globalkey" for the called sub. – xpda Oct 01 '12 at 16:41
  • @HansPassant The answer to that question definitely applies, but the two questions are not duplicates -- someone searching for one wouldn't be likely to find the other. – xpda Oct 01 '12 at 16:44
  • Well, how is anybody to guess that from this question? If you want a better way then at least document what "globalkey" actually does. – Hans Passant Oct 01 '12 at 16:48
  • A funny note: one risk to the method above is that you could accidentally put in the same control twice, in which case it will execute the handler twice when a key is pressed with focus on that control. Nobody noticed (in particular, me, until now) that txConfidence.KeyDown is in there twice. – xpda Oct 09 '12 at 00:59

2 Answers2

2

I am a C# programmer, and hence wont be able to provide a working solutions (as in vb.net code).

I think that you can get this working by doing following things.

  1. Write up a method which AddHandlers for your respective controls, if and only if, they have specific value set to a property (details in step #2 below)
  2. All controls, to which you want a common handler to be attached, set their Tag property to a unique value, for example : "KeyDown". I would recommend use of Enum though in this case, instead of string
  3. Now, in your Form constructor, after InitializeComponent call, call the method you wrote in #1 above. This will attach handlers to all desired controls.
  4. If you have multiple forms having this requirement, you can add this functionality to a main form, which acts like a parent for all others (inheritance)

Hope I am clear enough and this helps.

AYK
  • 3,312
  • 1
  • 17
  • 30
  • So you are setting the Tag of all the controls in the form designer? Then why not just connect the events in the form designer event tab, and avoid all the special code? – MarkJ Oct 01 '12 at 12:26
  • He wants it for some of the selected controls and he might decide in future to add 10 more controls. So instead of adding handlers in UI, we can mark those controls with tag property and they would automatically be applied. – AYK Oct 01 '12 at 12:55
  • 1
    @MarkJ If it were C# it wouldn't make a difference. VB, however, insists on extending the handler's declaration each time you do that, `Handles ...` etc. If you have a lot of components sharing a handler it makes for a big messy line of code. I think what the OP is after is a way to get rid of that huge string of component names trailing `Handles`. – J... Oct 01 '12 at 16:27
2

I like AYK's answer. You might use a function like this :

Public Shared Function GetAllControlsRecurs(ByVal list As List(Of Control), _
  ByVal parent  As Control, ByVal ctrlType As System.Type) As List(Of Control)
    If Parent Is Nothing Then Return list
    If Parent.GetType Is ctrlType Then
        list.Add(Parent)
    End If
    For Each child As Control In Parent.Controls
        GetAllControlsRecurs(list, child, ctrlType)
    Next
    Return list
End Function

I find this is a handy function to get all controls (including control within controls) of a given type in some parent control. By tagging your controls as AYK has suggested (ie: set Tag property in designer) you can do a run through all the controls above and programmatically add handlers (probably in the constructor).

Dim textboxList As New List(Of Control)

For Each ctl As TextBox In GetAllControlsRecurs(textboxList, Me, GetType(TextBox))
    If ctl.Tag = MyTags.rTextKD then 
        AddHandler ctl.KeyDown, AddressOf rText_KeyDown
    End If
Next

Where you might define MyTags as an enum with a list of common handlers you want to implement. Here rTextKD would be a member of the enum (i've not defined here in the answer). The nice thing about this approach is that it is extensible - if you add a new control and tag it then this code will pick it up and hook up the handler without needing to be changed.

While the above is an answer to your direct question, if you are trying to make a global hotkey, however, this is not the way to do it. The link Hans provided in comment is probably where you want to go.

J...
  • 30,968
  • 6
  • 66
  • 143