Link to home
Start Free TrialLog in
Avatar of rajesh_khater
rajesh_khater

asked on

Button click event does not fire when focus is in a DataGridView row

I have a form with a DataGridView and a Button, both having CausesValidation = true.

In thr grid's RowValidating and the button's click event, I am showing a MessageBox to notify that the event has fired.

The problem is:
When the focus is in a grid row and I click the Button, only the grid's RowValidating event fires. Then I have to click the button for a 2nd time for the Button's click event to fire.

Why is this so?

Avatar of ToFro
ToFro
Flag of Finland image

Try displaying the outputs from the events to a multiline TextBox like this:

TextBox1.Text += "This is the Button Click Event" + vbNewLine
...AND
TextBox1.Text += "This is the Row Validating Event" + vbNewLine

I think you will find that both events are raised. Did you discover this problem while trying out the events of the DataGridView, or is this causing an actual problem to your application.
Avatar of rajesh_khater
rajesh_khater

ASKER

Outputing to textbox I can see that both the events fire.

1. What makes the MessageBox stop the normal flow of events?

2. Even if I set breakpoints in each event handler, the problem remains: only RowValidating fires ! So even debugging in the IDE interferes with the flow of events.
The actual problem I am trying to debug is that when the user clicks on any BindingNavigator button, the button's click event fires BEFORE the RowValidating event !

So I tried replicating this problem by putting a separate button on the form. But it led to problems of its own !
Try this.  Sorry, it's VB.NET rather than C#, but I don't think translation should be difficult.  And it's a form I had hanging around for another purpose - which explains the data - but I've just added a button column and some debug code to it for the purposes of this demo.

One form, a datagridview - called dgva - and this code.

Public Class Form3

    Private dt As DataTable

    Private Sub maketable()
        'This code is equivalent to loading a table with a dataadapter
        dt = New DataTable("TableA")
        Dim dc0 As New DataColumn("ID", GetType(Integer))
        dt.Columns.Add(dc0)
        Dim dc1 As New DataColumn("Item", GetType(String))
        dt.Columns.Add(dc1)
        Dim dc2 As New DataColumn("Time", GetType(Integer))
        dt.Columns.Add(dc2)
        For i As Integer = 0 To 9
            Dim drA As DataRow = dt.NewRow
            drA(0) = i
            drA(1) = "ItemA" & i.ToString
            drA(2) = (i * 10) + 10
            dt.Rows.Add(drA)
        Next
    End Sub

    Private Sub Form3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        maketable()
        dgva.DataSource = dt
        Dim bcol As New DataGridViewButtonColumn
        dgva.Columns.Add(bcol)
    End Sub

    Private Sub dgva_CellClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgva.CellClick
        Debug.WriteLine("Cell click " & e.RowIndex & "/" & e.ColumnIndex)
    End Sub

    Private Sub dgva_CellContentClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgva.CellContentClick
        Debug.WriteLine("Cell CONTENT click " & e.RowIndex & "/" & e.ColumnIndex)
    End Sub

    Private Sub dgva_RowValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles dgva.RowValidating
        Debug.WriteLine("Row validating " & e.RowIndex)
    End Sub
End Class

Here's some debug output from me single-clicking in various cells.  

Cell click 0/3
Cell CONTENT click 0/3
Cell click 0/2
Cell click 0/1
Row validating 0
Cell click 1/3
Cell CONTENT click 1/3
Cell click 1/2

The button cells fired both CellClick and CellContentClick, the other cells just CellClick.  RowValidating fired when the row changed.

Roger
I am not talking about button cells here. Please read my question carefully.
You had a problem with the Button in the BindingNavigator.  You tried to overcome it by putting a button on the form.  It didn't work.  If you put a button in the row, it will work.

If you read my answer carefully you will see that I did not say that you were using a button cell.  You have a problem.  I was suggesting a possible solution.  That's all.

Roger
Apologies.  I see that you were trying to "replicate" not "solve" the problem with a button on the form.  So my code would certainly not do that.

Roger
SOLUTION
Avatar of illusio
illusio
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks to both of you for the beautiful analysis. But 2 problems remain:

1. If showing a messagebox in the RowValidating event is a requirement (to immediately notify the user of an error. I dont want to use ErrorProvider, etc.), then is there any way to code the program so that if the user does click on Button, BOTH the RowValidating and button click should fire ??

2. My original problem is that the button click event of the BindingNavigator button fires BEFORE the RowValidating event. How can this be handled ? The easiest way of course is NOT to use the BindingNavigator. But is there any other way?

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi,

Sorry for some incorrect details in the explanation I wrote above. The thing is that the LostFocus and GotFocus always works correct because it's trigger by the OS, it's the Enter and Leave (who surround the validation events) that get mixed in the process. That for the first impurity in my explanation, and many thanks to Roger for double checking and correcting. Further more this is a problem that you don't have here - it's something that far more easy...
The button thing: I could have slapped me on the forhead for that (I need a hard slap - I surely wasn't thinking completely straight...). If you click the button (keydown) everything gets prepared for the click. If then, with the button in focus and "clickeddown-state" you release your mousebutton (keyup) you get a "CLICK". Unfortunately: you click, focus shifts to the messagebox and then you can't continue with the click on the button... Very obvious - should have realized this... (feel like a rookie now).

The BindingNavigator is a very nifty thing with the look an feel of a "Control" but in fact it isn't. If you click one of the buttons to most strangest things happen and among them is that your focus doesn't shift (horrible and very handy at the same time) Lucky for you the solution to your problem is very easy (as long as you have connected you BindingNavigator correctly to your data).
Just implement your click as follows:
        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            if (((sender as ToolStripButton).Owner as BindingNavigator).Validate())
            {
                System.Diagnostics.Debug.WriteLine("Bark");
            }
        }

Hope I get this post completely right in one shot.

Kind regards,
Peter

BTW: the focus problem thing arises commonly if you have a user control that you use in a user control and then let the validate events fire with in the validating event a manual focus shift to control outside the embedded usercontrols.

Thank you Sancler and Illusio.

Sancler, in your Post ID: 19540929,
you have yourself said there are two shortcomings.

>> First, it would cause the ProxyClick sub to fire if the user were able to tab directly from the datagridview to the button.

How do I take care of the first shortcoming. This is not an acceptable behaviour as the user may not intend to click the button.

Two questions:

1. How do I get to know that the Button has become the ActiveControl as a result of mouse click or by tabbing into the button?

2. I think showing MessageBox in the RowValidating event is a fairly common situation. I may need to prompt the user Yes/No before taking an action, or show immediate error message. So, given this fairly common situation, does everybody have to work around this Button click problem in such a painful manner?
 
>>
How do I take care of the first shortcoming.
<<

You arrange the tab-order so that it is not possible for the user to tab directly from the datagridview to the button concerned.

But the burden of what both illusio and I have said is that the design of the system which Visual Studio provides in this respect is such that putting code in a datagridview's RowValidating Event which changes focus is inadvisable.  We have tried to explain why we hold that view.  

It may be that that design of what Visual Studio provides is wrong.  You say that "showing MessageBox in the RowValidating event is a fairly common situation".  I have no personal experience that would confirm that that statement is correct but, if it _is_ correct, then it would appear that the design _is_ wrong in that it is not an appropriate tool for that purpose.  

It does however, appear to be appropriate for the assumptions that seem to underlie its design.  Those appear to be that the app may need to check if entries are correct and, depending on the result, allow or cancel the move out of the row and, if the move is cancelled, explain why using the relevant row's ErrorText property.  It does not appear to envisage any questions being asked of the user.  As a tool for meeting those assumptions, it appears to be fit for purpose.  

If those assumptions are not correct in relation to your app then it is not fit for _that_ purpose.  But, rather than attempting to overcome shortcomings that arise from using a tool designed for one purpose for a different purpose, my own preference in a similar situation would be to revisit my own requirements/assumptions to see whether they could be adapted to fit the tool, or to seek a different tool that was fit for my purpose.

Roger
Which tool is fit for this purpose?

Purpose is:

1. Present data in a grid like control to the user where he can do in-place add/edit/delete. Control should support normal windows controls like Combos, CheckBox, etc.

2. When the user tries to move out of a row to any other row in the same grid or to an altogether different control, then I MAY need to show a MessageBox. The MessageBox may have a single OK button, or may have Yes No buttons.

In some cases, the code will set e.Cancel to true depending on the MessageBox Yes No button that the user clicks.

3. If for any reason, the event is cancelled by my code, focus change should not happen and the user action of clicking or tabbing into another control should not trigger the event for that action.

4. If the event is not cancelled by my code, then whatever action the user did (like clicking on a separate Button) should trigger the event it is supposed to.
I am not aware of one that is useable "out of the box".  That does not mean that no such control is available, just that I personally do not know of one.

But it would seem, to me, perfectly possible to "roll your own" based on the datagridview.  Ideas to that end have been floated above.

So far as I can see, your original question has been answered

>>
The problem is:
When the focus is in a grid row and I click the Button, only the grid's RowValidating event fires. Then I have to click the button for a 2nd time for the Button's click event to fire.

Why is this so?
<<

I therefore feel that this question should now be closed.

Roger
I think it is also implicitly implied that I want a workaround for the problem. And although you showed how I can call the Click procedure programmatically, yet it has a problem when the user tabs to the button.

I cannot rearrange the tab order, as the form has only a grid and a few buttons.

anyways, you have made a lot of valid points in this thread. But I will just wait a little if somebody can suggest a control that can fit my requirements.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
if this issue is solved to your satisfaction - you might want to split the points.

Kind regards,
Peter
Peter

Much neater

Roger