DataGridView.RowLeave fires before a Form button.MouseClick - vb.net

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

Related

Why does a form move trigger ResizeEnd?

I use the following code in my form:
Public Class Form1
Private Sub Form1_ResizeEnd(sender As Object, e As EventArgs) Handles MyBase.ResizeEnd
MsgBox("Resized")
End Sub
End Class
When I move my form, it also seems to trigger MyBase.ResizeEnd. Why is that? A move of the panel doesn't change the size, so I don't understand why.
Why does a form move trigger ResizeEnd?
Because this is the documented behavior. From the documentation:
The ResizeEnd event is also generated after the user moves a form, typically by clicking and dragging on the caption bar.
If you want an event that doesn't get triggered when the form is moved, you should use either Resize or SizeChanged. The problem with those two events is that they will be triggered while the form is being resized by the user. To work around that, you may use it with both ResizeBegin and ResizeEnd with a couple of flags to signal when the user actually finishes resizing the form.
Here's a complete example:
Private _resizeBegin As Boolean
Private _sizeChanged As Boolean
Private Sub Form1_ResizeBegin(sender As Object, e As EventArgs) Handles MyBase.ResizeBegin
_resizeBegin = True
End Sub
Private Sub Form1_SizeChanged(sender As Object, e As EventArgs) Handles MyBase.SizeChanged
' This is to avoid registering this as a resize event if it was triggered
' by another action (e.g., when the form is first initialized).
If Not _resizeBegin Then Exit Sub
_sizeChanged = True
End Sub
Private Sub Form1_ResizeEnd(sender As Object, e As EventArgs) Handles MyBase.ResizeEnd
_resizeBegin = False
If _sizeChanged Then
_sizeChanged = False
MessageBox.Show("The form has been resized.")
End If
End Sub
One thing to note is that both ResizeBegin and ResizeEnd are only triggered when the user manually resizes* the form. It does not, however, handle other situations like when the form is resized via code, when the form is maximized, or restored.
* or moves the form, which is the part that we're trying to avoid here.

TabCard event when a user has the intent to change tab

Probably a simple one but a cannot figure out the correct event:
I have a vb.net WinForm with a TabControl. On every TabPage, the user can enter/modify some data and then (hopefully) save it.
To keep things clean, I want to check, if there is unsaved data, when a user changes tabs (and delete it, if not saved).
I am looking for the best event of the TabCard to do so. There is TabControl1.Selecting, .SelectedIndexChanged and .Selected which look promising but they all fire AFTER the Tab changed.
If the user wants to return to save the data, i need to figure out where he came from and show that TabPage again. Also the event would the fire again - Not practicable.
In Conlusion: I am looking for a TabControl Event, that fires after the user clicked another tabcard but before the card actually changes...
Or a better idea to solve this isse another way.
Use the Selecting event. If you don't want to change the tab page, you can cancel the event.
'Here's an example class with a tabControl
Public Class Form1
'this variable stores the currently selected tab
Private activeTab As TabPage
'this initializes the activeTab variable
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
activeTab = TabControl1.SelectedTab
End Sub
'This checks to see if the tab should change or not
Private Sub TabControl1_Selecting(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TabControlCancelEventArgs) Handles TabControl1.Selecting
If (MessageBox.Show(String.Format("Return to {0} tab?", activeTab.Name), "TabControl", MessageBoxButtons.OKCancel) = Windows.Forms.DialogResult.OK) Then
e.Cancel = True
Else
activeTab = e.TabPage
End If
End Sub
End Class

Add logic inside button click inside a custom control

I am making a custom control that looks like this (there is a label on the right of the button):
I want the user to be able to define what the button does in his code, but still execute some code on the click event on top of the user's code.
What I want to execute :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.Button1.Enabled = False
Label1.ForeColor = Color.Gray
Label1.Text = "In Progress"
End Sub
then after this is executed the user's on click event would trigger.
How can I achieve this?
With "the user" you mean a developer which uses that user control, right? Well, the most common thing is to raise an event which the next developer can use to implement his own logic. This is exactly the same as you do - you use the Click-Event of the button.
So basically, take your code and add the RaiseEvent below:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.Button1.Enabled = False
Label1.ForeColor = Color.Gray
Label1.Text = "In Progress"
' this does not affect your code but provides a "hook" for
' other developers
RaiseEvent OnButtonClick(Button1)
End Sub
Now you need to define the event itself like this ...
Public Event OnButtonClick(ByVal sender As Control)
... btw, you can pass other stuff (or nothing at all) as arguments. Sending the button as sender is just a habit.
A developer using your user control can attach a so called "Handler" to implement code as soon as the button was clicked, for example:
AddHandler UserControl1.OnButtonClick, AddressOf OnUserControlButtonClick
This code line should only be executed once, so typically it is placed in the Form_Load event.
Now, in this case the button click is routed to a method called OnUserControlButtonClick() which meets the signature of the event: that means it has one argument which is the sender.
Private Sub OnUserControlButtonClick(ByVal sender as Control)
' custom logic here ...
End Sub
There are so many examples on the web, you could start here.

How can I change the CheckChanged when clicking the same object in VB 2010?

I want to know if it is possible in VB 2010 to changed the CheckChanged of a CheckBox when clicking the same object. For Example:
I have a 1 picturebox named pic1 and a checkbox named chck1, If I click the pic1, the chck1 must be checked but If I click again the pic1, chck1 must be unchecked and I click again the pic1, chck1 must be check again and so on..
I really don't have an idea if it is working or impossible in VB 2010, I hope someone can help me. Thank you very much.
If it's a WinForm, then you just have to implement something like this:
you have your PictureBox and your Checkbox, and you just have to add a clickhandler to your picturebox like this:
private void pictureBox1_Click(object sender, EventArgs e)
{
checkBox1.Checked = !checkBox1.Checked;
}
This Method always negates the Checked-State of the Checkbox (it's way simpler than a if/else)
checkbox1.Checked contains the checked-state, so that is how you can uncheck/check it.
Edit: i did it in c#, sorry,
in VB.NET it would be something like
Private Sub pictureBox1_Click(sender As Object, e As EventArgs)
checkBox1.Checked = Not checkBox1.Checked;
End Sub
In a WinForms application just add the event handler for the click event of your PictureBox.
You could that easily with the Form Designer or, if using code, then write
' In the form constructor
Public Sub Form1()
' First initialize your form controls'
InitializeComponent()
' then add the event handler for the picturebox click event'
AddHandler pic1.Click, AddressOf pic1_Click
End Sub
Private Sub pic1_Click(sender As Object, e As EventArgs)
' toogle the checked state of the checkbox'
chk1.Checked = Not chk1.Checked
End Sub
As said below from Mr Neolisk you could also shorten this code simply adding the Handles clause to the pic1_Click event thus removing the code in the Form constructor
Private Sub pic1_Click(sender As Object, e As EventArgs) Handles pic1.Click
' toogle the checked state of the checkbox'
chk1.Checked = Not chk1.Checked
End Sub

How to call GridView RowEditing with button outside the GridView?

I have a gridview that is populated and a button outside the gridview that I want to enable editing on the selected row when clicked. I have this in the code behind. What goes in the btn_click event to invoke the grid view editing?
Protected Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As GridViewEventArgs)
GridView1.EditIndex = e.NewEditIndex
FillGrid()
End Sub
Protected Sub btnEdit_Click(ByVal sender as Object, ByVal e As System.EventArgs) Handles btnEdit.Click
What goes here??
End Sub
There is a problem with this approach.
"GridView1_RowEditing" is expecting a row index, so it can turn on "EditItemTemplate" accordingly, correct?
But If you want to click on button outside of Gridview and make entire Gridview editable, you shouldn't trigger GridView1_RowEditing, since you don't know what editindex to pass.
You need to implement editable control(textbox) as part of "ItemTemplate", not in "EditItemTemplate".
And visibility of this control would be controlled by the outside button you have created, which will flag the visibility on / off.
Please review following link, this demonstrates how it should be implemented.
http://highoncoding.com/Articles/219_GridView_All_Rows_in_Edit_Mode.aspx