(Newbie VB.NET question)
Here's the specific code behind my simple winforms that I don't fully understand:
Private Sub okButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles okButton.Click
'do something
End Sub
Private Sub MainForm_Enter(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Enter
okButton_Click(Me, e) '<=== argumanets must be wrong
End Sub
What I'm trying to achieve:
If the user hits Enter when they have the winforms active then I'd like the Click event handler of the okButton to fire.
Obviously from the above my understanding of the arguments I need to supply to the event called okButton_Click is lacking; what are the correct arguments and why?
I think you might use the AcceptButton property of the form. Just set it to the desired button and it should do the trick.
Note that there is also a CancelButton property.
Answering your event-question:
The sender argument marks the sender of the event. Mostly, this is the Me instance of the class. In my opinion, Me seems to be absolutely correct.
The e argument contains the EventArgs of the specific event. If you're not using this argument in your function body, the content of this variable doesn't matter. You could use Nothing or just route the EventArgs (that's what you've done).
Refering to your comment:
EventArgs is a base class for event-specific data. For example, if you're subscribing to a mouse event, ewill be a MouseEventArgs. The MouseEventArg class offers you the mouse buttons that have been pressed and the coordinates of the pointer when the event was fired.
In your case, the events only have EventArgs which provide only basic information about the event. There does not seem to be special information about it.
Note: If you want to combine multiple events into one callback, you can make e of type EventArgs because every e should inherit from EventArgs following the Microsoft guidelines. Therefore, you can combine a Button-Click with a Mouse-Move into one callback because the signature of the delegates match.
A nicer way than just passing Nothing to the target Sub, is to combine two callbacks into one. You can do this in VB.NET using multiple Handles like this:
Private Sub SomeSub(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles okButton.Click, MyBase.Enter
'this is getting called on a okButton.Click and MyBase.Enter
End Sub
(Scroll to the right to see the Handles)
Note that you don't need a second Sub which calls the first one. Everything is in one Sub.
Try this instead:
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
If (keyData And Keys.KeyCode) = Keys.Enter Then
okButton.PerformClick()
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
You might need to exclude some controls in the check if you want to use enter key with other controls, ie:
If (keyData And Keys.KeyCode) = Keys.Enter AndAlso Not Textbox1.Focused Then
Related
I can't find anything about this anywhere:
I have a form with a DataGridView and a few Buttons. When a row of the datagridview is selected, and I click a button (on the form), dgv.RowLeave triggers before anything else. It triggers even before Click or MouseClick.
That kind of makes sense, but the problem is that the sender of RowLeave(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) is the DataGridView, not the button. So it doesn't seem to be possible to know at that point what button was clicked on, because sender and e both refer to the DataGridView, not the Form nor the Buttons.
The Click event is triggered, but only after RowLeave was processed.
So is there any way to know where the user clicked, before RowLeave does other things (in my case, resulting in the Button.Click to be never handled), or then from within RowLeave?
Class MainForm
' The form contains a DataGridView and btnQuit (and other buttons)
Private Sub dgv_RowLeave(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles dgv.RowLeave
ProgrammaticallyDoRowValidation(dgv.CurrentRow.Index) ' This does validation and more.
' But if btnQuit is clicked, I need to know here, or before RowLeave is
' triggered and NOT do this row validation.
' ...
End Sub
Private Sub Form_Click(sender As Object, e As EventArgs) Handles MyBase.Click
Dim frm As Form
frm = CType(sender, Form)
' Translated from Sach's comment below. Code never reaches this event
'(RowLeave prevents it).
End Sub
Private Sub Quit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnQuit.MouseClick
QuitPgm() ' Contains some more stuff.
' Also never executed, because RowLeave is handled first.
End Sub
End Class
I'm using WinForms here, but the idea is the same.
So Events are attached to Controls. A Button is a Control, so is a DataGridView. Then in the Code Behind you have Event Handlers which are essentially methods tied to Control Events.
So when you attach a Click event to a button, behind-the-scenes, VB.NET creates an Event Handler like so:
private void button1_Click(object sender, EventArgs e)
{
}
Now the sender is an object, but it's actually the DataGrid that is passed there. So contrary to your statement So it doesn't seem to be possible to know at that point what button was clicked on you CAN know if a button was clicked on. If it was, and if you have an event handler attached, it will get called. For example, this will show a MessageBox with the button text:
private void button1_Click(object sender, EventArgs e)
{
var btn = (Button)sender;
MessageBox.Show(btn.Text);
}
So if you want to know if the Form was clicked, attach a Click event handler:
private void Form1_Click(object sender, EventArgs e)
{
var frm = (Form)sender;
MessageBox.Show(frm.Text);
}
I'm not sure how you prevent the button click in your RowLeave event, but I think you should use RowValidating event to validate the DataGridView. Let's say we have a DataGridView with only 1 column and a Button. The validation is the column value must not higher than 100. If we put the validation in the RowValidating event, the validation is triggered after the Leave event but before Validated event. If the validation fails, the subsequence events are not fired.
Public Class Form1
Function ProgrammaticallyDoRowValidation(i As Integer) As Boolean
If Convert.ToInt32(dgv(0, i).Value) > 100 Then
Return False
Else
Return True
End If
End Function
Private Sub dgv_RowValidating(sender As Object, e As DataGridViewCellCancelEventArgs) Handles dgv.RowValidating
If Not ProgrammaticallyDoRowValidation(dgv.CurrentRow.Index) Then
e.Cancel = True
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MessageBox.Show("Buton clicked")
End Sub
End Class
Try running the code and enter a value higher than 100 in the column. You can't click the button because it fails the validation. But if you set the Button's CausesValidation property to False, the validation won't be triggered.
The order of event according to this link is like this:
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and
so on), by calling the Select or SelectNextControl methods, or by
setting the ContainerControl.ActiveControl property to the current
form, focus events occur in the following order: Enter -> GotFocus ->
Leave -> Validating -> Validated -> LostFocus
When you change the focus by using the mouse or by calling the Focus
method, focus events occur in the following order: Enter -> GotFocus
-> LostFocus -> Leave -> Validating -> Validated
So is there any way to know where the user clicked, before RowLeave
does other things (in my case, resulting in the Button.Click to be
never handled), or then from within RowLeave?
Private Sub dgv_RowLeave(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles dgv.RowLeave
ProgrammaticallyDoRowValidation(dgv.CurrentRow.Index) ' This does validation and more.
' But if btnQuit is clicked, I need to know here, or before RowLeave is
' triggered and NOT do this row validation.
' ...
End Sub
This is a bit of a kludge solution, but within the DataGridView.RowLeave event, you can check if the ContainerControl.ActiveControl Property to see if the currently active control is the one you want to test for. In this case the ContainerControl is the Form.
Private Sub dgv_RowLeave(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles dgv.RowLeave
ProgrammaticallyDoRowValidation(dgv.CurrentRow.Index) ' This does validation and more.
' But if btnQuit is clicked, I need to know here, or before RowLeave is
' triggered and NOT do this row validation.
' ...
If Me.ActiveControl Is btnQuit Then
' do something
End If
End Sub
I've created a usercontrol.
I've placed a Button into it.
Now when I click the button, I would like to raise the default Click event.
For that, I added the following code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RaiseEvent Click(sender, e)
End Sub
What am I doing wrong here?
This is the entire code of the usercontrol:
Imports System.ComponentModel
Public Class ucColorButton
<Browsable(True)>
Public Overrides Property BackColor() As Color
Get
Return Me.Button1.BackColor
End Get
Set(value As Color)
Me.Button1.BackColor = value
End Set
End Property
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RaiseEvent Click(sender, e)
End Sub
End Class
The compiler tells me:
There's no RaiseEvent definition for the event "Click"
You don't use RaiseEvent to raise an inherited event. This is why all events should have an associated method. To raise the Click event you call the OnClick method and that is the only place that RaiseEvent is used. If you want to change the behaviour on a Click event then you override that method, otherwise you just accept the default behaviour from the base class. To see how events are properly implemented - and are implemented in the base classes you're inheriting - check this out.
Also, while it technically doesn't matter in this case, you shouldn't really be passing the e parameter from your internal event handler to your external event. You should be creating your own EventArgs object as required by your event.
Finally, if you were to be able to use RaiseEvent, it would be wrong to pass on the sender parameter too. The sender is ALWAYS supposed to be the object that raised the event. In your case, that is the user control, NOT the internal Button. Fortunately, calling OnClick will fix that. If you needed to pass on information about which child control was clicked then you should be defining your own event and passing that information via the e parameter.
I want to make a public sub in my module to to store a repeating procedure. Specifically key press events.
Private Sub txtPass_KeyPress(sender As Object, e As KeyPressEventArgs) _
Handles txtPass.KeyPress
FunctionKeys(Me, sender, e)
End Sub
Public Sub FunctionKeys(form as object, sender as object, _
e as KeyPressEventArgs)
With form
If e.KeyChar = ChrW(Keys.Enter) Then .btnOk_Click(sender, e)
If e.KeyChar = ChrW(Keys.Escape) Then .btnClose_Click(sender, e)
end with
End sub
I guess it would look something like this. Unfortunately, this tells me it can't find a public sub for formname.btnok_click etc. I want to know if there's a way around this. Looking around the net I found I can use the AcceptButton and CancelButton property. But only if i actually have a button to press. My MDI does not have buttons. Just menu. Also, I'm aware I can use formname.close() for the Keys.Escape. But I'd still have a problem with the OK button.
You can make btnOk_Click and btnClose_Click public on all your forms and that'll work.
Here there are two handlers in a particular procedure then how to get which event handler has performed.
for example
Private Sub TextBox1_Events(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged, TextBox1.GotFocus
End Sub
how to get which event has occured.
It is possible using the StackTrace (could be a better way I'm not sure...). Try the following code.
Private Sub TextBox1_Events(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged, TextBox1.GotFocus
Dim s As New StackTrace(True)
For Each f As StackFrame In s.GetFrames
Debug.WriteLine(f.GetMethod.Name)
Next
End Sub
When the text box gets focus the following is written:
TextBox1_Events
OnGotFocus
OnGotFocus
WmSetFocus
Ect…….
Where as when it’s a text changed event
TextBox1_Events
OnTextChanged
OnTextChanged
Ect….
I’m sure you could write something using this to do what you need. But i fully agree with the other guys separate handlers is better.
In this case, you cannot.
If the events were bound to two separate controls, you could check the sender property for the type
If the e argument for the event had some type other than EventArgs (some events use a different arguments type), or the control passed some type derived from EventArgs, then you might be able to check properties on that variable
There aren't any other tricks you could use because events don't provide any sort of data to the handler specifying which event occurred.
With these two events, they're both going to be sent from the same text box, so the first option is out. Also, with both events, they send just an instance of the EventArgs class (not a derived class), so that option is out.
Ultimately, you're going to have to have multiple event handlers to solve this specific problem.
It's not possible. If you're in a situation where you need to know which event occurred, you will always be better off using two separate handlers.
Since you are dealing with 2 events (similar in signature) emitted by the same control the easiest/cleanest way of solving this would be 2 separate event handlers (as suggested by Merlyn Morgan-Graham):
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
'the TextChanged specific code would go here
HandletTextBox1EventInternal(sender, e)
End Sub
Private Sub TextBox1_GotFocus(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.GotFocus
'the GotFocus specific code would go here
HandletTextBox1EventInternal(sender, e)
End Sub
Private Sub HandleTextBox1EventInternal(ByVal sender As System.Object, ByVal e As System.EventArgs)
'code common to GotFocus and TextChanged handlers
End sub
Private Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
What is the keyValue I need for checking for the DELETE key using e.keyValue?
The KeyEventArgs object contains a member called "KeyCode" that you can compare to the "Keys" enumeration.
Note that certain keys may not raise the KeyDown event if they are handled by the default windowing system. I'm not sure, and I can't check it right now, but you may not get the KeyDown event for keys like Tab, Delete, Enter, etc.
You can usually do something like this (this is C#, not VB, but should be similar):
public void MyControl_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
// delete was pressed
}
}
If you set KeyPreview property of form then form will receive key events before the event is passed to the control that has focus. For example if you have textboxes and buttons on the form, normally they(the control that has focus) will capture the key press event. So make sure to set KeyPreview=true
Use this to capture the key code.
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
If e.KeyCode = Keys.Delete Then
'todo
End If
End Sub
Compare e.keyValue with Keys.Delete
Check the Keys enumeration...