0

I willt ry to be exact as possible, I have a main form we will call it "Form1" in the Form1 the user have the possibility, throught a menu strip to launch another form, we will call it "Form2", in the Form2 I have another menustrip and a datagridview.

My problem when the user launch the Form2 I have to click with the left button 2 time before the menu strip get "activated" what I mean is

  • The form2 load
  • I click on the menu strip item : "Menu"
  • Nothing happen
  • I click another time the menu strip scroll down

it's really anoying and I can't fix this because I have no idea what is causing it

if you guys have any kind of solution or need more information just tell me.

This is the Form2 named Ajout.vb

Imports System.Data.OleDb
Imports System.Globalization

Public Class Ajout
    Private dSet As DataSet
    Private dAdapter As New OleDbDataAdapter
    Private bJustEdit As Boolean = False

    Sub New(ByRef dSet As DataSet, ByRef dAdapter As OleDbDataAdapter)
        InitializeComponent()
        Me.dSet = dSet
        Me.dAdapter = dAdapter
    End Sub

    Private Sub Ajout_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        With DataGridView1
            .DataSource = dSet
            .DataMember = "Articles_table"
            .Columns("Prix").DefaultCellStyle.Format = "C2"
            Console.WriteLine(.Columns("Prix").ValueType)
            For Each clm As DataGridViewColumn In .Columns
                clm.SortMode = DataGridViewColumnSortMode.NotSortable
            Next
        End With
    End Sub

    Private Sub DataGridView1_DataError(ByVal sender As Object, ByVal e As DataGridViewDataErrorEventArgs) Handles DataGridView1.DataError
        'Quelle est l'erreur
        If (TypeOf (e.Exception) Is ConstraintException) Then
            'Envoie une message box pour informer l'utilisateur avec une possibiliter
            If (drMessageBoxFuntion(0) = Windows.Forms.DialogResult.Yes) Then
                'Possibiliter choisi on regarde si le sender est bien de datagridview pour eviter un erreur de cast
                If (TypeOf sender Is DataGridView) Then
                    'Nouvelle variable car sa devien inlisible avec beaucoup de cast
                    Dim dgvLocal As DataGridView = CType(sender, DataGridView)
                    Dim sNameValue As String = dgvLocal.Rows(e.RowIndex).Cells("Nom").Value.ToString
                    Dim dgvOriginalCells As DataGridViewCellCollection = dgvLocal.Rows(dgvFindRowsBy_Name(sNameValue).Index).Cells
                    Dim dgvErrorCells As DataGridViewCellCollection = dgvLocal.Rows(e.RowIndex).Cells

                    'Regarde si le prix est pareille si faux propose a l'utilisateur d'updater le prix avec le nouveau
                    'ou de simplement ajouter la quantite
                    If (Not Equals(dgvOriginalCells("Prix").Value.ToString, dgvErrorCells("Prix").Value.ToString)) Then
                        'Prix different propose de mettre a jour
                        If (drMessageBoxFuntion(1) = Windows.Forms.DialogResult.Yes) Then
                            dgvOriginalCells("Prix").Value = dErrorRow()
                            dgvOriginalCells("Quantité").Value += dgvErrorCells("Quantité").Value
                        Else
                            dgvOriginalCells("Quantité").Value += dgvErrorCells("Quantité").Value
                        End If
                    Else
                        dgvOriginalCells("Quantité").Value += dgvErrorCells("Quantité").Value
                    End If
                    'Sender inconnu 
                Else
                    MsgBox("Une erreur inconnu est survenu, aucune operation n'a été effectué, vous n'avez qu'à recommencer")
                End If
            End If

            'Si lettre entré, msg pour informer le reste est gerer par le dataset qui n'accepte que
            'les chiffre, car defini dans la database
        ElseIf (TypeOf (e.Exception) Is FormatException) Then
            MsgBox("Vous ne pouvez pas entrer de lettre dans la collone de prix ou de quantité")
        End If
    End Sub


    'Fonction contenant plusieur message box
    Private Function drMessageBoxFuntion(ByVal index As Integer) As DialogResult
        If (index = 0) Then
            Dim drAction As DialogResult = MessageBox.Show("Vous ne pouvez pas ajouter deux fois le même nom," & _
                                               " voulez vous ajouter la quantité du nouvelle arti" & _
                                               "cle a celui deja présent ?", _
                                               "Erreur, article double.", _
                                               MessageBoxButtons.YesNo, _
                                               MessageBoxIcon.Question)
            Return drAction
        ElseIf (index = 1) Then
            Dim drAction As DialogResult = MessageBox.Show("Le prix de vos deux article, de même genre, diffère." & _
                                                           "Voulez-vous mettre a jour le prix à jour (oui), ou " & _
                                                           "simplement ajouter la quantité a l'item déjà présent (no" & _
                                                           "n).", "Erreur, prix different", _
                                                            MessageBoxButtons.YesNo, _
                                                            MessageBoxIcon.Question)
            Return drAction
        End If
        Return Nothing
    End Function

    'Retourne une datagridviewROW et non l'index car permet d'avoir l'objet
    'et donc de prendre la methode index ou autre methode si le besoin est
    Private Function dgvFindRowsBy_Name(ByVal oNameValue As Object) As DataGridViewRow
        For Each row As DataGridViewRow In DataGridView1.Rows
            If row.Cells.Item("Nom").Value = oNameValue Then
                Return row
            End If
        Next
        Return Nothing
    End Function

    'Message pre programmer dans une array permettant l'affichage de message avec une input
    Private Function dErrorRow() As Single
        Dim dNewValue As Object = InputBox("Nouveau prix, entrez uniquement un nombre s'il vous plait.")

        Do Until IsNumeric(dNewValue)
            dNewValue = InputBox("Nouveau prix, entrez uniquement un nombre s'il vous plait.")
        Loop

        Return CType(dNewValue, Single)
    End Function

    Private Sub DataGridView1_RowValidating(sender As Object, e As DataGridViewCellCancelEventArgs) Handles DataGridView1.RowValidating
        If (TypeOf sender Is DataGridView) Then
            Dim dgvRow As DataGridViewRow = CType(sender, DataGridView).Rows(e.RowIndex)
            If (Not ((IsDBNull(dgvRow.Cells("Nom").Value) Or IsDBNull(dgvRow.Cells("Prix").Value) Or IsDBNull(dgvRow.Cells("Quantité").Value)) = (IsDBNull(dgvRow.Cells("Nom").Value) And IsDBNull(dgvRow.Cells("Prix").Value) And IsDBNull(dgvRow.Cells("Quantité").Value)))) Then
                MsgBox("Veuillez remplir toute les cases.")
                e.Cancel = True
            End If
        End If
    End Sub

    Private Sub DataGridView1_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
        If (TypeOf sender Is DataGridView) Then
            Dim dgvRow As DataGridViewRow = CType(sender, DataGridView).Rows(e.RowIndex)

            If (Not dgvRow.IsNewRow) Then
                If (e.ColumnIndex = 1) Then
                    Dim dTemp As Single

                    If (Not Single.TryParse(e.FormattedValue.ToString, NumberStyles.Currency, CultureInfo.CurrentCulture, dTemp) OrElse dTemp < 0) Then
                        MsgBox("La valeur ne doit pas être négative ni être une lettre. Ne laisser pas la case vide non plus.")
                        e.Cancel = True
                    End If
                ElseIf (e.ColumnIndex = 2) Then
                    Dim iTemp As Integer

                    If (Not Integer.TryParse(e.FormattedValue.ToString, NumberStyles.Currency, CultureInfo.CurrentCulture, iTemp) OrElse iTemp < 0) Then
                        MsgBox("La valeur ne doit pas être négative ni être une lettre. Ne laisser pas la case vide non plus.")
                        e.Cancel = True
                    End If
                End If
            End If
            dgvRow.Selected = False
        End If
    End Sub

    Private Sub EnregistrerToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles EnregistrerToolStripMenuItem.Click
        If dSet.HasChanges Then
            Try
                Using con = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source = Mokmeuh.accdb")
                    con.Open()
                    Dim cmb As New OleDbCommandBuilder(dAdapter)
                    dAdapter.Update(dSet.Tables("Articles_Table"))
                End Using
            Catch ex As Exception

            End Try
        End If
    End Sub

    Private Sub AnnulerToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AnnulerToolStripMenuItem.Click
        dSet.RejectChanges()
        DataGridView1.Refresh()
    End Sub
End Class

More precision

enter image description here

Mokmeuh
  • 865
  • 3
  • 11
  • 25
  • The behaviour you are describing does not respond to any standard situation. Your problems are provoked by something in your code. Please, post it (the relevant bits). – varocarbas Oct 06 '13 at 13:27
  • I've updated it, I gave you the code of the Form2 – Mokmeuh Oct 06 '13 at 13:36
  • You are including two different Click events for two different MenuItems (which one are you referring to?). Also there is not enough information to tell; for example: dSet.HasChanges is the heading condition for one of these events (does it change or not?!). Please, clarify all this. – varocarbas Oct 06 '13 at 13:47
  • I don't think the inside of the event should change anythings for this since they are related to the datagrid view anyways yo answer your question yes dSet would be true if not the database is not updated also I have a MenuToolStripMenuItem who register 3 sub item like so `Me.MenuToolStripMenuItem.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.EnregistrerToolStripMenuItem, Me.AnnulerToolStripMenuItem, Me.QuitterToolStripMenuItem})` What is not working is the main menu who i have to click 2 time for it to drop down the 3 other item – Mokmeuh Oct 06 '13 at 13:51
  • I've put a picture, if I can be more clear tell me I will try to give you as much information i can – Mokmeuh Oct 06 '13 at 13:55
  • In your code, there are just two methods associated with click events, the ones for the menuItems "Annuler" and "Enregistrer". I cannot tell you what happens when you click on "Menu" if I cannot see the method associated with its Click event. – varocarbas Oct 06 '13 at 14:00
  • There's no method with this event, it has always happend from what I see, like in form1 I have a menu strip and there's no event for the first dropdown and the drop still occurs on the frist click – Mokmeuh Oct 06 '13 at 14:02
  • I thought that you were doing something on the associated click event method; if you are not doing anything there, what you claim is certainly weird. I guess that has to do with the Form (the GUI part) being "waiting" for the corresponding populations to be performed. You should do a step-by-step execution to understand what does the code (how long it takes) when Form2 is started; remove some of the initial actions and see the effect on the problematic twice click, etc. Also you might want to generate the given click event just to set a break point there and do some tests. – varocarbas Oct 06 '13 at 14:08
  • Let me try this then, be back in not so long – Mokmeuh Oct 06 '13 at 14:09
  • By the way I've found this http://stackoverflow.com/questions/3057473/disable-the-requirement-to-double-click-an-unfocused-window-when-clicking-on-a I experience the same issue but I can,t get the answer since first it's c# and a little to bit advenced for me – Mokmeuh Oct 06 '13 at 14:21
  • Writing this code in VB.NET is straightforward but bear in mind that with that you are "hooking" at the most basic level (you call this function, for example, when wanting to track any key pressed when any program is focused). Anyway... I thought that it was something related to your specific code (never had this issue). Let me do some quick test to see if I can replicate it and perhaps can come up with a less aggressive solution. – varocarbas Oct 06 '13 at 14:26
  • Yes, thank and By the way I'm sure it's not the code because I've just realized that, when I quit Form2 and go back in Form1 I have to click 2 time, so the activation and then I can click where ever I want also I've tried something like me.activate() not working – Mokmeuh Oct 06 '13 at 14:28
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/38684/discussion-between-mokmeuh-and-varocarbas) – Mokmeuh Oct 06 '13 at 14:28
  • I cannot replicate your conditions. If I open a new form, the new form is selected by default and I can click on the MenuItem without any problem (1 click). Can you please show how are you calling Form2. Also bear in mind that what you are looking for is "Focus()"; if the new form is not "active", it should be enough with Form2.Focus() (not sure why in the aforementioned link proposed a so intrincate, low-level approach). – varocarbas Oct 06 '13 at 14:40
  • Gave you all this in chat let us continu there – Mokmeuh Oct 06 '13 at 14:45

2 Answers2

4

This is a flaw in the MenuStrip and ToolStrip classes, they don't properly handle the WM_MOUSEACTIVATE notification. These classes have plenty of quirks, they support window-less controls and getting them to emulate the exact same behavior as a normal Windows window is never not a problem. These quirks didn't get addressed, the Winforms team got broken up shortly after the .NET 2.0 release to dedicate resources to WPF.

This should be a comment but there are already too many to have the solution noticeable, LarsTech already came up with a good solution. You'll find it in this answer.

To do this is VB.NET, right-click your project and click Add + Class. Paste this code:

Public Class MyMenuStrip
    Inherits MenuStrip

    Protected Overrides Sub WndProc(ByRef m As Message)
        '' Set focus on WM_MOUSEACTIVATE message
        If m.Msg = &H21 AndAlso Me.CanFocus AndAlso Not Me.Focused Then
            Me.Focus()
        End If
        MyBase.WndProc(m)
    End Sub

End Class

Click Build + Build. You'll now have the MyMenuStrip item at the top of the toolbox. Drop it on your form, replacing the existing MenuStrip.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

Appenrently according to this

Windows requires a click to activate a window before a second click will select a button. How can I change this?

It appears that, just like the Mac OS X, Windows has made a GUI style decision that requires one to first activate a window BEFORE it will allow any controls to be selected. However, Windows only does this for specific controls, like ToolStrip and MenuStrip. When your window is NOT active, these controls are not sent the mouse DOWN event. I'm not sure how WinForms enforces this GUI guideline, but perhaps it uses a message filter using Application.AddMessageFilter().

So I've found a work around, it may be sloppy, but this is what I've found.

Private Sub MenuToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles MenuToolStripMenuItem.Click
    If (TypeOf sender Is ToolStripMenuItem) Then
        CType(sender, ToolStripMenuItem).ShowDropDown()
    End If
End Sub
Community
  • 1
  • 1
Mokmeuh
  • 865
  • 3
  • 11
  • 25