Announcement

Collapse
No announcement yet.

Button warning..

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Button warning..

    A very sad day for the civilized world, but still we must try to go on..

    Writing own button has thaught me a lot about standard ones. Found a bit
    dangerous thing about standard buttons that may be good to know: If you
    use the arrow keys to move between them and press Enter when a button has
    focus, you may trigger wrong button!

    Don't know what MS have been thinking. If to use Tab key, no problems. Right
    button will be triggered. The sample code below shows. Compile and start,
    then press Right arrow to give Cancel button focus and press Enter. MSGBOX
    will tell Cancel button was triggered. Okay, but repeat: when Msgbox is
    closed, OK button will get focus again. Press Right arrow once more to
    give Cancel focus and press Enter. MSGBOX tells OK button has been triggered!!!
    Imagine question was "Format drive C?" ..

    Simple fix: Don't look for CBCTL (LOWRD(wParam)) in WM_COMMAND. Instead use
    GetDlgCtrlID(GetFocus). See sample below and test the different ways. Just
    tell it, so you know that just because an MS button has focus, it doesn't
    mean it will be triggered if user presses Enter..
    Code:
    #COMPILE EXE
    #INCLUDE "WIN32API.INC"
    DECLARE CALLBACK FUNCTION DlgMainProc
     
    '********************************************************************
    ' Main entrance - create dialog and controls
    '********************************************************************
    FUNCTION WINMAIN (BYVAL hInst AS LONG, BYVAL hPrevInstance AS LONG, _
                      lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG
     
      LOCAL hDlg AS LONG
      DIALOG NEW 0, "Press Right arrow, then Enter - repeat",,, 200, 100, %WS_SYSMENU TO hDlg
     
      CONTROL ADD BUTTON, hDlg, %IDOK, "ID&OK",           4, 4, 50, 14
      CONTROL ADD BUTTON, hDlg, %IDCANCEL, "ID&CANCEL",  54, 4, 50, 14
     
      DIALOG SHOW MODAL hDlg, CALL DlgMainProc
    END FUNCTION
     
    '********************************************************************
    ' Main message handling procedure
    '********************************************************************
    CALLBACK FUNCTION DlgMainProc
      SELECT CASE CBMSG
         CASE %WM_COMMAND
            IF CBCTLMSG = %BN_CLICKED THEN
               SELECT CASE CBCTL  'CASE GetDlgCtrlID(GetFocus)  fixes problem!
                  CASE %IDCANCEL : Msgbox "%IDCANCEL"
                  CASE %IDOK     : Msgbox "%IDOK"
               END SELECT
            END IF
      END SELECT
    END FUNCTION

    ------------------

  • #2
    Don't know what MS have been thinking
    Call me a naif, but if CBCTL does not return the same value as GetDlgCtrlID(GetFocus), and lParam is not available under DDT, and using the API call rather than the DDT function produces the correct value, wouldn't that mean the problem is located several hundred miles south of Redmond WA?

    I mean, I use SDK-style programming all the time, where LOWRD(wParam) is the control ID, and I've never gotten the wrong control on a button click.

    MCM

    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Had to test. Same problem in SDK, so a Redmond-based problem. Even
      stranger - it seems it only happens every other time. SDK test:
      Code:
      '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
      ' Simple SDK-style template
      '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
      #COMPILE EXE
      #INCLUDE "WIN32API.INC"
       
      '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
      ' Main entrance
      '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
      FUNCTION WINMAIN (BYVAL hInst AS LONG, BYVAL hPrevInstance AS LONG, _
                        lpCmdLine   AS ASCIIZ PTR, BYVAL iCmdShow AS LONG) AS LONG
       
        LOCAL hWnd AS LONG, hBtn1 AS LONG, hBtn2 AS LONG, style AS LONG
        LOCAL Msg        AS tagMsg
        LOCAL szAppName  AS ASCIIZ * 50
        LOCAL wndclass   AS WndClassEx
        LOCAL rc         AS RECT
       
        szAppName              = "MYPROG"
        wndclass.cbSize        = SIZEOF(WndClass)
        wndclass.style         = %CS_HREDRAW OR %CS_VREDRAW
        wndclass.lpfnWndProc   = CODEPTR( WndProc )
        wndclass.hInstance     = hInst
        wndclass.hCursor       = LoadCursor( %NULL, BYVAL %IDC_ARROW )
        wndclass.hbrBackground = GetStockObject(%LTGRAY_BRUSH)
        wndclass.lpszClassName = VARPTR( szAppName )
       
        IF registerClassEx(wndclass) = 0 THEN EXIT FUNCTION 'what else to do..?
       
        SystemParametersInfo %SPI_GETWORKAREA, BYVAL 0, VARPTR(rc), 0 'for centering on screen
       
        'Create a window using the registered class
        hWnd = CreateWindow(szAppName, _                           ' window class name
                            "Press Right arrow, then Enter - repeat", _ ' window caption
                            %WS_CAPTION OR %WS_SYSMENU, _          ' window style
                            ((rc.nRight - rc.nLeft) - 300) / 2, _  ' initial x position
                            ((rc.nBottom - rc.nTop) - 200) / 2, _  ' initial y position
                            300, _                                 ' initial x size
                            200,_                                  ' initial y size
                            %NULL, _                               ' parent window handle
                            %NULL, _                               ' window menu handle
                            hInst, _                               ' program instance handle
                            BYVAL %NULL)                           ' creation parameters
       
        style = %WS_CHILD + %WS_VISIBLE + %WS_TABSTOP
        hBtn1 = CreateWindow("BUTTON", "&OK", style, 70, 140, 80, 22, hwnd, %IDOK, hInst, %NULL)
        hBtn2 = CreateWindow("BUTTON", "E&xit", style, 150, 140, 80, 22, hwnd, %IDCANCEL, hInst, %NULL)
        SetFocus hBtn1
       
        'Display the window on the screen
        ShowWindow hWnd, iCmdShow : UpdateWindow hWnd
       
        'Main message loop:
         While GetMessage(Msg, %NULL, 0, 0)
            If IsDialogMessage (hWnd, Msg) Then
            Else
               TranslateMessage Msg
               DispatchMessage  Msg
            End If
         Wend
       
        FUNCTION = msg.wParam
      END FUNCTION
       
      '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
      ' Main dialog procedure
      '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
      FUNCTION WndProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
                        BYVAL wParam AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG
       
        SELECT CASE wMsg
           CASE %WM_COMMAND
              IF HIWRD(wParam) = %BN_CLICKED THEN
                 SELECT CASE LOWRD(wParam)  'GetDlgCtrlID(GetFocus)  'fixes problem!
                    CASE %IDOK     : MSGBOX "IDOK"
                    CASE %IDCANCEL : MSGBOX "%IDCANCEL"
                 END SELECT
                 SetFocus GetDlgItem(hWnd, %IDOK) 'return focus to first control, IDOK
              END IF
       
           CASE %WM_DESTROY
              PostQuitMessage 0
              FUNCTION = 0 : EXIT FUNCTION
       
        END SELECT
        FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam)
      END FUNCTION

      ------------------

      Comment


      • #4
        In both cases results depends of IsDialogMessage.
        I avoid %IDOK, %IDCANCEL as button's id.s to have a possibility to recognize real "click".

        ------------------
        E-MAIL: [email protected]

        Comment


        • #5
          I concur.

          ------------------
          Lance
          PowerBASIC Support
          mailto:[email protected][email protected]</A>
          Lance
          mailto:[email protected]

          Comment


          • #6
            Just wanted to tell, since it can give dangerous result. Like I said,
            imagine question was "Format drive C?", etc. Using other id's, also
            strange result, but not as dangerous. Then if dialog gives button focus
            (ie, first control in tab order), Right arrow + Enter works/works not,
            every other time. No danger if it sometimes doesn't work, but..

            Personally, think I'll avoid using MS buttons in "commercial" code from here on..


            ------------------

            Comment


            • #7
              Borje;
              In standard sdk style GUI programming the tab and arrows keys work as expected.
              However, keyboard handling in Microsoft Windows, as noted by some authors,
              seems to have been an afterthought. When one tries to change the keyboard focus
              programmatically in a window that contain a pushbutton with an id of IDOK
              things go awry. There are also related visual problems with the BS_PUSHBUTTON/
              BS_DEFPUSHBUTTON styles.
              In other words the problem is that call to SetFocus in WM_COMMAND.
              SetFocus and IsDialogMessage ain't that great a couple.
              To return the focus to the first control do the following-
              Code:
                SELECT CASE wMsg
                   CASE %WM_COMMAND
                      IF HIWRD(wParam) = %BN_CLICKED THEN
                         SELECT CASE LOWRD(wParam)
                            CASE %IDOK     : MSGBOX "IDOK"
                            CASE %IDCANCEL : MSGBOX "%IDCANCEL"
                         END SELECT
                         PostMessage hWnd, %WM_KEYDOWN, %VK_TAB, 0  
                      END IF
              The following is off topic but may be useful to lurkers.
              Your WM_COMMAND filter as it stands does not handle the default behaviour
              of windows with IsDialogMessage() in the message loop. The test

              IF HIWRD(wParam) = %BN_CLICKED THEN

              prevents the following cases from being trapped
              (they all generate IDCANCEL but with HIWRD(wParam) = 0)-
              1. User closing the window via the system menu.
              2. User closing the window via the x button in the title bar.
              3. User closing the window by pressing the Escape key.

              I would rewrite the code as follows(no need for WM_SYSCOMMAND)-
              1. filter on control ids(usually unique numeric values set by programmer)
              2. filter on notifications(numeric values may not be unique and are set by MS)
              Code:
                CASE %WM_COMMAND
                  SELECT CASE LOWRD(wParam)
                    CASE %IDC_APPLY
                      IF HIWRD(wParam) = %BN_CLICKED THEN
                        MSGBOX "APPLY"
                      END IF
              
                    CASE %IDOK
                      MSGBOX "IDOK"
              
                    CASE %IDCANCEL
                      MSGBOX "%IDCANCEL"
                  END SELECT
              [This message has been edited by Dominic Mitchell (edited September 12, 2001).]
              Dominic Mitchell
              Phoenix Visual Designer
              http://www.phnxthunder.com

              Comment


              • #8
                Theory is correct, yes - but %BN_CLICKED = 0, so Esc still works.
                Reason for testing BN_CLICKED is so button isn't triggered on wrong
                notification.

                ------------------
                Forgot to add: If a button has BS_NOTIFY style, it can cause highly
                unpredictable results if not BN_CLICKED is tested. Not sure for regular
                button, but think things like CONTROL DISABLE (EnableWindow) triggers
                notification. To be on safe side - always test BN_CLICKED.


                [This message has been edited by Borje Hagsten (edited September 12, 2001).]

                Comment


                • #9
                  (My $.02)

                  I am an old DOS programmer and feel more comfortable using the keys then the mouse at times.

                  From experience, I NEVER use the enter key as it would trigger the default button, rather, I use the spacebar to activate the focused key.

                  ------------------
                  George W. Bleck
                  Senior System Engineer
                  KeySpan Corporation
                  <b>George W. Bleck</b>
                  <img src='http://www.blecktech.com/myemail.gif'>

                  Comment

                  Working...
                  X