Announcement

Collapse
No announcement yet.

Detecting checkbox flip in treeview

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

    Detecting checkbox flip in treeview

    I understand that when a checkbox (or anything) is clicked inside a treeview, I will get a %WM_NOTIFY, and that if I grab CBLPARAM.idfrom it will match the handle of my treeview, and that if I look at CBLPARAM.hdr.Code, it will match %NM_CLICK... but I'm sort of lost at that point in getting the handle of the item that was clicked on.

    I assumed CBLPARAM.itemNew.hItem or something like that, but no luck.

    What I'm trying to do is detect when a checkbox has been flipped on a treeview. Previously I was just scanning the entire thing for changes every time there was a click, but there has to be an easier way to do it.

    #2
    Sorry, using version 9...

    CASE %WM_NOTIFY
    IF CB.NMID = %IDC_CITIESTREE THEN
    IF CB.NMCODE = %NM_CLICK THEN
    ...if we get here, the treeview IDC_CITIESTREE has been clicked on, but how do I know which of the elements has been clicked on?

    Comment


      #3
      Do you want to know if it has been clicked, or do you want to know if that click has changed the selected item? (This came up here not too long ago).

      Depending on which one you want, you process WM_NOTIFY/NM_CLICK or WM_NOTIFY/TVN_SELCHANGED ( or TVN_SELCHANGING if you want to disallow the selection of this item)

      FWIW, you get TVN_SELCHANGED when the user uses the keyboard as well as the mouse to change selection.

      If it's really "click" you want, you have to figure out where the click occured, then use the Treeview_HitTest function to figure out where on which item (if any) the cursor was when the mouse was clicked. .. then you can use Treeview_getCheckState to query the check status.

      (I can't find a TVN_ITEMCHANGED which handles state changes! There must be one somewhere!)

      HINT:
      THis will be about ten lines of code. You will probably want to put it into a reusable function such as "Treeview_WhereWasClick" so you only have to do it ONCE.

      HINT#2
      I am sure the code is posted here, but not in the source code forum.


      MCM
      Last edited by Michael Mattias; 12 Nov 2009, 01:40 PM.
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


        #4
        Thanks -- I want to know which one was clicked on. So it sounds like there's no simple one-call return function... I'm surprised, I would have though there would be something that returned a handle to the item that was clicked on, and some of the data structures implied that was what they could do.

        I have another Treeview question while we're at it -- if I want to select all or deselect all (of the the checkboxes), how can I do that in bulk, without flicker issues? Can I temporarily turn off redrawing and then make all the changes and then turn it back on.

        Comment


          #5
          So it sounds like there's no simple one-call return function... I'm surprised, I would have though there would be something that returned a handle to the item that was clicked
          There will be - after you write it. Here's a start:
          Code:
          FUNCTION OnWhichItemDidClickHappen (hTV AS LONG, NMH NMHDR) AS LONG
          '  Return: hItem on which click occurred, or null if click occurred 
          '             on the control BUT NOT ON AN ITEM AND YES THIS HAPPENS
          
          END FUNCTION
          BTW, make sure you want CLICKS and not "CHANGE OF SELECTION BY EITHER CLICK OR KEYBOARD." Last thing you want is for the user to complain about 'keys don't work' (and you KNOW that is how it will come back to you!)

          Also, "selected" with a treeview does NOT mean "the checkbox is checked". You can have only one selection at a time, but many items may be checked!

          (I know someone posted a tree view with check boxes demo here. Try a search on "TVS_CHECKBOXES" and you should find it, since that style had to be included)

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

          Comment


            #6
            Hahaha... I can't tell if you're messing with me or if you're giving me useful advice or both. It's the guts of that function that I'm stuck on, not its declaration.

            Comment


              #7
              I don't mess with people. All statements above are feither fact as I know it or my honest "best guess" at what you should do next. If my facts are shown to be wrong, I will promptly acknowledge and correct.

              However, I am in no position to judge if the suggestions or advice I offer are useful.

              >It's the guts of that function that I'm stuck on

              I wasn't about to pre-empt the fun you will have creating it. That would not be nice at all, would it?
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


                #8
                Thanks, admittedly I am under no particular deadline so I have been having fun slowly figuring it all out. I do console apps 99% of the time, so I'm still fairly unfamiliar with GUI programming.

                Comment


                  #9
                  PB will give you the rudiments of the event, there's a lot more buried in the structure which is passed to the window via the NMHDR Structure, AKA in PB it's CB.NMHDR, and that header is common across a bunch of notification systems, for the treeview control, you need to:
                  Code:
                  LOCAL pnmtv                 AS NM_TREEVIEW POINTER
                  and there's something to note here, the CB.NMHDR is a pointer, CB.NMCODE, CB.NMHWND, & CB.NMID are all derived from the structure, alias:
                  Code:
                  TYPE NMHDR
                    hwndFrom AS DWORD
                    idfrom AS DWORD
                    CODE AS LONG    ' used for messages, so needs to be LONG, not DWORD...
                  END TYPE
                  And when you look at NM_TREEVIEW:
                  Code:
                  TYPE NM_TREEVIEW
                      hdr     AS NMHDR
                      action  AS DWORD
                      itemOld AS TV_ITEM
                      itemNew AS TV_ITEM
                      ptDrag  AS POINTAPI
                  END TYPE
                  See? And if you're following along;
                  Code:
                          CASE %WM_NOTIFY
                              IF ISTRUE isLoading THEN EXIT FUNCTION
                              SELECT CASE CB.NMID
                                  CASE %IDTV_FOXTREE
                                      IF CB.NMCODE = %TVN_SELCHANGED THEN
                                          ' make image here
                                          SELECT CASE CB.NMID
                                              CASE %IDTV_FOXTREE
                                                  DisplaySelectedImage CB.HNDL, CB.NMID, CB.NMHDR
                  My code sets up a function call at this point, but in the function, I then use the above pointer declaration to gain access to the itemOld and itemNew elements of the structure:
                  Code:
                  SUB DisplaySelectedImage(hDialog AS DWORD, idControl AS DWORD, pnmhdr AS DWORD)
                  LOCAL pnmtv                 AS NM_TREEVIEW POINTER
                  LOCAL pitem                 AS TV_ITEM
                  LOCAL BumpX                 AS LONG
                  LOCAL hControl, hBitmap     AS DWORD
                  LOCAL KeyControl            AS INTEGER
                  
                      KeyControl = (GetKeyState(%VK_CONTROL) AND &H8000)
                      IF ISTRUE SetCurrentShape(hDialog, idControl) THEN
                          pnmtv = pnmhdr
                          IF @pnmtv.action = %TVC_BYMOUSE THEN
                              CONTROL HANDLE hDialog, %IDTV_FOXTREE TO hControl
                              IF (ISTRUE @pnmtv.itemOld.hItem) AND (ISFALSE KeyControl) THEN
                                  pitem.hItem = @pnmtv.itemOld.hItem
                                  pitem.mask = %TVIF_STATE
                                  pitem.stateMask = %TVIS_BOLD
                                  BumpX = TreeView_GetItem(hControl, pitem)
                                  pitem.state = pitem.state AND (NOT %TVIS_BOLD)
                                  BumpX = TreeView_SetItem(hControl, pitem)
                              END IF
                              IF ISTRUE @pnmtv.itemNew.hItem THEN
                                  pitem.hItem = @pnmtv.itemNew.hItem
                                  pitem.mask = %TVIF_STATE
                                  pitem.stateMask = %TVIS_BOLD
                                  BumpX = TreeView_GetItem(hControl, pitem)
                                  pitem.state = pitem.state OR %TVIS_BOLD
                                  BumpX = TreeView_SetItem(hControl, pitem)
                              END IF
                          END IF
                          hBitmap = MakeFshBitmap(CurrentShape, CurrentFrame, Fox)
                          CONTROL SEND hDialog, %idf_imagedisplay, %UM_SETIMAGE, _
                              hBitmap, Fox.ComboPosition(CurrentShape, CurrentFrame)
                      END IF
                  
                  END SUB
                  Furcadia, an interesting online MMORPG in which you can create and program your own content.

                  Comment


                    #10
                    I think what you are going to need to do here is two things...

                    1. On click, check to see of the click was an item or not
                    2. On <spacebar> (use WM_NOTIFY/TVN_KEYDOWN) check to see what item was currently selected.

                    I think the checkbox status will be toggled automatically on either, meaning you really don't have to do anything vis-a-vis toggling the check state.

                    But we really don't know what it is you want to do when the user clicks (or selects?)

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

                    Comment


                      #11
                      Originally posted by Michael Mattias View Post
                      I think the checkbox status will be toggled automatically on either, meaning you really don't have to do anything vis-a-vis toggling the check state.
                      It's automatic.
                      Furcadia, an interesting online MMORPG in which you can create and program your own content.

                      Comment


                        #12
                        Well, my first google search using the phrase "treeview using tvs_checkboxes" return this link. It says that when a user clicks on the state icon that an NM_CLICK notification is sent and the TVM_HITTEST message will return TVHT_ONITEMSTATEICON. Unfortunately, the treeview doesn't actually change the state until after the NM_CLICK notification is sent so you can't just check the state on an NM_CLICK.

                        His suggestion is to send a custom message to your dialog procedure when you get an NM_CLICK and TVM_HITTEST returns TVHT_ONITEMSTATEICON.

                        Here is my conversion of the code at the above linked converted to PB:

                        Code:
                        ' make sure you have these includes
                        #INCLUDE "Win32API.inc"
                        #INCLUDE "CommCtrl.inc"
                        
                        ' add this equate to the start of your program
                        %UM_CHECKSTATECHANGE = %WM_USER + 100
                        
                        ' add these variable declarations in your dialog procedure
                        LOCAL dPos  AS DWORD
                        LOCAL tHTI  AS TVHITTESTINFO
                        
                        ' add the following case to your SELECT CASE CB.MSG
                        CASE %WM_NOTIFY
                            SELECT CASE CB.NMID
                                CASE %IDC_TREEVIEW1
                                    IF CB.NMCODE = %NM_CLICK THEN
                                        dPos = GetMessagePos
                                        tHTI.pt.x = LO(WORD, dPos)
                                        tHTI.pt.y = HI(WORD, dPos)
                                        MapWindowPoints %HWND_DESKTOP, CB.NMHWND, tHTI.pt, 1
                                        TreeView_HitTest CB.NMHWND, tHTI
                                        IF tHTI.flags AND %TVHT_ONITEMSTATEICON THEN
                                            PostMessage CB.HNDL, %UM_CHECKSTATECHANGE, 0, tHTI.hItem
                                        END IF
                                    END IF
                            END SELECT
                        
                        CASE %UM_CHECKSTATECHANGE
                            hItemChanged = CB.LPARAM ' handle of the item whose state icon was clicked
                            ' you can now get the state of the item
                        Hopefully I ddin't screw anything up as I didn't test this code.
                        Jeff Blakeney

                        Comment


                          #13
                          send a custom message to your dialog procedure when you get an NM_CLICK and TVM_HITTEST returns TVHT_ONITEMSTATEICON.
                          There's a good example of this technique in the Three State Checkbox Treeview example posted in the Source Code forum by Steve Mc Gregor..

                          Actually that example addresses just about all the questions raised by this thread
                          Rgds, Dave

                          Comment

                          Working...
                          X
                          😀
                          🥰
                          🤢
                          😎
                          😡
                          👍
                          👎