Announcement

Collapse
No announcement yet.

COMBOBOX tip..

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

  • COMBOBOX tip..

    A tip for handling ignored selections in a combobox. Background: If a
    combobox list is dropped in order to change a selection, but action is
    cancelled by clicking beside the list, whatever, previous selection is
    reinstated and a %CBN_SELENDOK notification message still is triggered.
    Nothing has changed, but since a %CBN_SELENDOK is triggered, code there
    also will be activated (like, repeated).

    We can see this in for example PBedit's sub/function finder combo, where
    this behaviour can cause some frustration, because an ignored selection
    still triggers latest one and cursor jumps back to start of that one. Not
    fun if we were doing work elsewhere and just wanted to have a look in the
    sub/function finder list..

    Not PBedit's fault - it's default Windows behaviour, but sometimes we want
    to decide behaviour ourselves, and then following simple way can be used.

    I looked at message order and found that a %CBN_SELENDCANCEL is sent before
    %CBN_SELENDOK, so by using this, one can make sure the ignore is respected
    all the way. Just set a static flag in %CBN_SELENDCANCEL and let this flag
    decide action in %CBN_SELENDOK, like:
    Code:
    CALLBACK FUNCTION DlgProc
      SELECT CASE CBMSG  'wMsg
         CASE %WM_INITDIALOG
            STATIC gCBflag AS BYTE
     
         CASE %WM_COMMAND
            SELECT CASE CBCTL  'LOWRD(wParam)
               CASE %IDCOMBO
                  SELECT CASE CBCTLMSG  'HIWRD(wParam)
                     CASE %CBN_SELENDCANCEL 'trap this one to enable proper ignore
                        gCBflag = 1
     
                     CASE %CBN_SELENDOK
                        IF gCBflag THEN     'if user cancels, really ignore
                           gCBflag = 0 : EXIT FUNCTION
                        END IF
                        BEEP   'whatever..
                  END SELECT
            END SELECT
      END SELECT
    END FUNCTION

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

  • #2
    Is the message order guaranteed in that situation?

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

    Comment


    • #3
      You can also store the last selected index and compare that.

      Not the same..

      ------------------
      http://www.hellobasic.com
      hellobasic

      Comment


      • #4
        Only thing guaranteed is that nothing is guaranteed. Edwin's solution
        can be better in some situations, but what if we want to reselect last
        item again?

        Like in PBedit's case - sometimes we just want to look at list, and
        sometimes we want to repeat selection to go to beginning of a sub
        or function again. Have implemented "flag-way" in my own editor and
        am pleased with result so far..


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

        Comment


        • #5
          Okay, tested with other code and got different result. Lance is right
          as usual, message order cannot be trusted and result may be that if one
          really selects something, one has to repeat selection to make it work.

          So, bad tip - ignore! See it works in own editor because I use sub-classed
          combo and do other stunts on select. But even then, I get unpredictable
          result when I switch between documents and a new list is created.

          Ah well, back to the drawing board again..


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

          Comment


          • #6
            Borje --
            "Canceled" means Esc ?
            If so, you can subclass - WM_CHAR exactly will be before SELENDOK.

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

            Comment


            • #7
              Okay, found a working way by looking at coordinates. Could have been so simple,
              if only MS had provided the correct info. Found that %CB_GETDROPPEDCONTROLRECT
              returns a rect that starts from entire control's top, including edit part - down
              to height given at creation time, not visible list's real height. Typical MS logic..

              So, one has to grab both window rect, number of items, item height and cursor
              pos, then calculate real list rect from this for compare. How important is this?
              Depends on importance of code, of course. If combobox selection starts a nuclear
              plant in different ways, I guess it can be a bit important..

              Okay, so we know that MS combobox does not always ignore properly if we click
              outside list or press Escape - instead it repeats last selection. Following
              fixes this stupid and potentially dangerous behaviour:
              Code:
                               CASE %CBN_SELENDOK
                                  LOCAL itemCount AS LONG, itemHeight AS LONG, visItems AS LONG
                                  LOCAL rc AS RECT, rc2 AS RECT, pt AS POINTAPI
               
                                  GetCursorPos pt
                                  GetWindowRect CBLPARAM, rc2
                                  itemCount  = SendMessage(CBLPARAM, %CB_GETCOUNT, 0, 0)
                                  itemHeight = SendMessage(CBLPARAM, %CB_GETITEMHEIGHT, 0, 0)
                                  SendMessage CBLPARAM, %CB_GETDROPPEDCONTROLRECT, 0, VARPTR(rc)
               
                                  'calculate visible dropdown list's real rect
                                  rc.nTop    = rc.nTop + rc2.nBottom - rc2.nTop
                                  visItems   = MIN&(itemCount, (rc.nBottom - rc.nTop) \ itemHeight)
                                  rc.nBottom = rc.nTop + visItems * itemHeight
               
                                  IF PtInRect(rc, pt.x, pt.y) = 0 OR _
                                     (GetKeyState(%VK_ESCAPE) AND &H8000) THEN EXIT FUNCTION
               
                                  BEEP 'or start a space shuttle to Mars, whatever..

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

              Comment


              • #8
                Borje;

                Have you looked at using the ComboBoxEx() from the common controls instead?
                This will send a %CBEN_ENDEDIT via %WM_NOTIFY and by using the structure passed
                to you, you can determine if you need to return a False to process the notification
                or True to ignore it and preserves the last action.

                Regards,
                Jules

                Comment


                • #9
                  ComboBoxEx has nice ability to easily insert images, but still don't
                  like it. Don't like MS way to draw selection around text only. Can be
                  confusing for some users.

                  Better to use ownerdrawn combo and have better control of things. Did
                  quick test and see that ComboBoxEx have same problem, it triggers last
                  selection when one tries to ignore.


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

                  Comment


                  • #10
                    Borje;

                    Yes, I understand the trade-offs between the standard control and the
                    extended control w/wo OwnerDraw, but just wanted to add one more point...

                    ComboBoxEx have same problem, it triggers last
                    selection when one tries to ignore.
                    If you are trapping the old messages via WM_COMMAND then yes, same
                    problem exist. But using the new messages via WM_NOTIFY, the %CBEN_ENDEDIT
                    will allow you to look at the NMCBEENDEDIT structure member .iWhy for
                    either of the following values;

                    CBENF_DROPDOWN
                    CBENF_ESCAPE
                    CBENF_KILLFOCUS
                    CBENF_RETURN

                    and you can return false to allow the control to display the selected item or
                    return true to abort the edit selection in the case Esc/Killfocus.

                    Regards,
                    Jules

                    Comment

                    Working...
                    X