?
Solved

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

Posted on 2007-07-20
21
Medium Priority
?
4,470 Views
Last Modified: 2007-10-18
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?

0
Comment
Question by:rajesh_khater
  • 8
  • 7
  • 4
  • +1
20 Comments
 
LVL 6

Expert Comment

by:ToFro
ID: 19529387
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.
0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19529399
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.
0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19529474
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 !
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 34

Expert Comment

by:Sancler
ID: 19529812
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
0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19530144
I am not talking about button cells here. Please read my question carefully.
0
 
LVL 34

Expert Comment

by:Sancler
ID: 19530286
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
0
 
LVL 34

Expert Comment

by:Sancler
ID: 19530470
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
0
 
LVL 7

Assisted Solution

by:illusio
illusio earned 1200 total points
ID: 19531191
Problem with the messagebox showing and the second validate event not fireing...

Quite complex but it's very predictable. If you want a real brain-teaser involving this I can post an example in which the validating event (and errornous handling of it) will cause two controls to have focus on the same time ( -- if you ever encounter this in a project, your eyes will just pop out your head, untill you come to understand the prob -- )

In fact what is happening with the textboxes can rather easily be displayed if you use the System.Diagnotics.Debug.Writeline( ... ), open your app in debug mode, not making it fullscreen, so you can watch the lines coming through. You attach events and put debug output on the ENTER, LEAVE, GOTFOCUS, LOSTFOCUS, VALIDATING and VALIDATE events. Perform this with the messagebox displaying and without (put a checkbox on your form to disable displaying your messagebox, and compare the results)

If you monitor this you will see that the focus shifts away before the leave of the old field takes place (you're still in the validating event so the leave can't take place). the button gets focus, the validating fires, the focus from the button moves to the messagebox (without the button loosing focus), the messagebox closes, the form gets focus again and the button gets focus a second time. the button gets clicked and should trigger a validation but one of the "focusses" on the button remains so it doesn't entirely looses focus, resulting in the validation for the button not fireing. The thing the button does is finished and the focus gets back to the button. Tthe lostfocus which has happened when the button click did it's work has cleaned up remaining focustrash and everything is back to normal.

In validating events you focus should never shift. In general the messagebox will cause no problem as long as you don't shift the focus away to a third control before the gotfocus/lostfocus cycle has finished.

Kind regards,
Peter
0
 
LVL 34

Assisted Solution

by:Sancler
Sancler earned 800 total points
ID: 19533351
Peter

I don't disagree with your conclusion in your final paragraph.  And I agree that, in broad terms, your description of what's going on is on the right lines.  But at the detail level my experience, hence my analysis, is a bit different.

I'm in row 3 of a datagridview - dgva.  I click once on an external button.  I have debug reporting code in (amongst other places) the following subs

    Private Sub dgva_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles dgva.LostFocus
        If loading Then Exit Sub
        Debug.WriteLine("Grid lost focus")
        Debug.WriteLine(Me.ActiveControl.Name)
    End Sub

    Private Sub dgva_RowValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles dgva.RowValidating
        If loading Then Exit Sub
        Debug.WriteLine("Row validating " & e.RowIndex)
        Debug.WriteLine(Me.ActiveControl.Name)
        If showmessagebox Then
            Debug.WriteLine("Validating message box to show")
            Debug.WriteLine(Me.ActiveControl.Name)
            MsgBox("Row validating")
            Debug.WriteLine("Validating message box shown")
            Debug.WriteLine(Me.ActiveControl.Name)
        End If
    End Sub

    Private Sub Button1_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.GotFocus
        If loading Then Exit Sub
        Debug.WriteLine("Button got focus")
        Debug.WriteLine(Me.ActiveControl.Name)
    End Sub

    Private Sub Button1_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.LostFocus
        If loading Then Exit Sub
        Debug.WriteLine("Button lost focus")
        Debug.WriteLine(Me.ActiveControl.Name)
    End Sub

As you can see from the code, in each case the second line of each pair of debug lines is the .ActiveControl.

This is the debug output without a message box in the RowValidating Event

Grid lost focus
dgva
Row validating 3
Button1
Button got focus
Button1
Button click

and this is the output with the message box

Grid lost focus
dgva
Row validating 3
Button1
Validating message box to show
Button1
Button lost focus
Button1
Button got focus
Button1
Validating message box shown
Button1
Button got focus
Button1

What I find interesting here in terms of the reporting (as I say, the real sequence may be as you describe it) is that the first LostFocus for the Button occurs before any GotFocus has been reported for the Button.  Yes, it is already reported as the ActiveControl, so it must have _some_ sort of operational "focus".  And that LostFocus is then balanced by GotFocus.  But it seems to indicate that just relying on the "focus" reporting to try to tease these sequences out may leave some gaps.

Secondly, you say "the focus from the button moves to the messagebox (without the button loosing focus)" but - in terms of the reporting above - the button does lose "focus" to the RowValidating message box.

Thirdly, in your description you say "the messagebox closes, the form gets focus again and the button gets focus a second time. the button gets clicked ...".  But in my test with the message box, the point is that the click event of the button DOESN'T fire.  As the question says "I have to click the button for a 2nd time for the Button's click event to fire."  So it's not, on my analysis, that "Tthe lostfocus which has happened when the button click did it's work has cleaned up remaining focustrash and everything is back to normal": it is that any cleaning up happens BEFORE the button click does its work.  

My guess, therefore, is that, unlike with normal mouse selection (selection by tabbing is different anyway) the GotFocus and the Click instructions get separated by the focus movements back to the RowValidating messagebox and it is the Click instruction itself that gets lost, somehow forgotten, in the behind the scenes manoeuvring.  That is, at least outwardly (although the reported order of firing of focus events is different), we get the sort of behaviour that would result from tabbing out of the grid to the button, and then SEPARATELY having to click on the button or press a key to fire it.

I doubt, although I cannot be certain, that these differences are a consequence of my working in VB.NET rather than C#.  I doubt, in any event, that they really matter.  The bottom line is that it happens.

Roger
0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19539657
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?

0
 
LVL 34

Assisted Solution

by:Sancler
Sancler earned 800 total points
ID: 19540929
On 1.  Probably.  It will depend on details I don't know.  But I can use the debug output I posted earlier to explain the idea.

You will note from that that, at the end of the sequence of focus transfers that showing the RowValidation MessageBox involved, Button1 remained the ActiveControl even though its Click never functioned.  If that were to be the full code in that sub, it would be possible to put a check on what was the ActiveControl at that point and, if it was the button, then perform that click from the RowValidating sub itself.  Here's some code with which I just tested, and it worked.

    Private Sub dgva_RowValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles dgva.RowValidating
        If loading Then Exit Sub
        Debug.WriteLine("Row validating " & e.RowIndex)
        Debug.WriteLine(Me.ActiveControl.Name)
        If showmessagebox Then
            Debug.WriteLine("Validating message box to show")
            Debug.WriteLine(Me.ActiveControl.Name)
            MsgBox("Row validating")
            Debug.WriteLine("Validating message box shown")
            Debug.WriteLine(Me.ActiveControl.Name)
        End If
        If Me.ActiveControl.Name = "Button1" Then
            ProxyClick()
        End If
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        ProxyClick()
    End Sub

    Private Sub ProxyClick()
        MsgBox("Button click")
    End Sub

If the RowValidating Event is called from within the datagridview, or by movement out of the datagridview to some other control, the final test proves False, so the ProxyClick sub is not called.  But if the RowValidating Event is called by a Click (which fails) on the button, the final test produces True, so that ProxyClick sub is called by the RowValidating code.  If the button is clicked in the normal course of things, then the ProxyClick sub is called by that.

But it's only an idea.  As it stands, it has two shortcomings of which I am aware (and perhaps others).  First, it would cause the ProxyClick sub to fire if the user were able to tab directly from the datagridview to the button.  That may be acceptable, but it would not be standard behaviour in those circumstances: normally, having tabbed to a button the user would have to click it or press a key to get its Click to fire.  Second, it relies on the sequence in the RowValidating sub being, or at least ending up with, the particular focus situation that this stripped down example provides.  I think it quite likely that the real RowValidating sub code may be more complex that this.  So the detail which produces a similar result for the real code may need to be different, or it may even preclude this approach altogether.  You will need to work on it.  But the debug reporting illustrated in the code I've posted earlier might help towards finding a workable solution based on this general concept.

On 2, an idea that occurs to me is that although the phenomenon you mention may be the default for the standard buttons that are added by the designer to the BindingNavigator, I don't think it is necessarily so for buttons that you add yourself.  So it may be possible to code round the issue by substituting your own buttons for those auto-generated.  But that is even more "only an idea": brain-storming, really.  I haven't tested and, even if the idea was in essence workable, the specifics would depend on what purpose the button was supposed to serve.

Roger
0
 
LVL 7

Expert Comment

by:illusio
ID: 19545906
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.

0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19574078
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?
 
0
 
LVL 34

Expert Comment

by:Sancler
ID: 19576429
>>
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
0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19580640
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.
0
 
LVL 34

Expert Comment

by:Sancler
ID: 19580984
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
0
 
LVL 1

Author Comment

by:rajesh_khater
ID: 19581012
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.
0
 
LVL 7

Accepted Solution

by:
illusio earned 1200 total points
ID: 19581167
Two points:
1/ if you use the bindingnavigator (the one you originally used appearantly) adding the code as shown in my previous post you don't have problems.
2/ The proxyclick: more general solution (not having tabbing problem and not requiring to modify the click handler)
        private void dataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
        {
            Control c = null;
            if (MouseButtons == MouseButtons.Left)
                c = this.GetChildAtPoint(this.PointToClient(MousePosition));

            if (MessageBox.Show("Yes for ok, No for not ok", "Test", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
                e.Cancel = true;
            else if (c != null)
            {
                if (c is Button)
                {
                    (c as Button).BeginInvoke(new ProxyClickDelegate(this.ProxyClick), c as Button);
                }
                else if (c != sender)
                    c.Focus();
            }
        }

        private delegate void ProxyClickDelegate(Button target);
        private void ProxyClick(Button target)
        {
            target.PerformClick();
        }

Kind regards,
Peter
0
 
LVL 7

Expert Comment

by:illusio
ID: 19581175
if this issue is solved to your satisfaction - you might want to split the points.

Kind regards,
Peter
0
 
LVL 34

Expert Comment

by:Sancler
ID: 19581309
Peter

Much neater

Roger
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
This article aims to explain the working of CircularLogArchiver. This tool was designed to solve the buildup of log file in cases where systems do not support circular logging or where circular logging is not enabled
Screencast - Getting to Know the Pipeline
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
Suggested Courses
Course of the Month16 days, 8 hours left to enroll

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question