Announcement

Collapse
No announcement yet.

Multiple Textbox Subclassing

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

  • Multiple Textbox Subclassing

    I'm the guy that points people who ask about Subclassing to the Sample folder for a good example. Now I need subclassing help {sigh}.

    I want to have multiple Textboxes to use the same Subclass but am unable to tell which TB "calls" the SC. See Function SubClassProc in the example. The CTL Msgbox never gets hit. Been trying different stuff for ... well, you know how long.


    '
    Code:
    'Example of subclassing multiple textboxes
    'PBWIN 9.01 - WinApi 05/2008 - XP Pro SP3
    #Dim All 
    #Compile Exe  
    #Optimize SPEED
    #Debug Display On'off for production code
     
    #Include "WIN32API.INC"
    #Include "COMDLG32.INC"
    #Include "InitCtrl.inc"
    '
    Global hdlg As Dword                
    Global g_TB_Id(), g_TB_Handle() As Dword
    %Id_Exit_Btn = 1000
    ' 
    Macro Common_Locals 'Macro easier than retyping and maintains coding consistency
      Global Dlg_hght, Dlg_Wd As Long 'Global in case want to use in Controls
      Local spcr, Stile, Row, col, ht, wd, Longest,ctr, ln, ln1, i As Long
      Local  l, s As String
      Local hfont As Dword
    End Macro  
    '
    CallBack Function PBMain_Dialog_Processor              
      Common_Locals                                           
      Select Case CbMsg     'This is TO determine the message TYPE 
         '       
         Case %WM_INITDIALOG'<- Initialiaton when the program loads 
             '--------------------------------------------------------------
          ' %WM_INITDIALOG is sent right before the dialog is shown.
          ' A good place to initiate variables and controls, etc.
          ' Let's Subclass the TextBox and store the returned pointer
          ' to the original TextBox procedure with DIALOG SET USER.
          '--------------------------------------------------------------
          Local hEdit, oldProc As Dword
          'dunno why but this is needed FIRST
          Control Handle hdlg, g_TB_Id(3) To hEdit
          oldProc = SetWindowLong(hEdit, %GWL_WNDPROC, CodePtr(SubClassProc))
          Dialog Set User CB.Hndl, 1, oldProc '<<< only 8 spaces avl
    '
    '     Create Handles For subclassing All textboxes 
          For ctr = LBound(g_TB_Id()) To UBound(g_TB_Id())                
            Control Handle hdlg, g_TB_Id(ctr) To hEdit
            oldProc = SetWindowLong(hEdit, %GWL_WNDPROC, CodePtr(SubClassProc))
            g_TB_Handle(ctr) = OldProc 
           Control Set Text hdlg, g_tb_id(ctr), Using$("#, ", g_tb_Handle(ctr)) 'show handles
          Next ctr
     
         Case %WM_SYSCOMMAND 'Traps Any Alt key but only F4 closes              
         '
         Case %WM_COMMAND  'This processes command messages
           Select Case CbCtl
             Case %Id_Exit_Btn
               Select Case CbCtlMsg        
                  Case 0
                    Dialog End CbHndl 'Applikation beenden
               End Select
           End Select
      End Select
    End Function
    '       
    '====================================================================
    Function SubClassProc(ByVal hWnd As Dword, ByVal wMsg As Dword, _
                          ByVal wParam As Dword, ByVal lParam As Long) As Long
    '--------------------------------------------------------------------
      ' SubClass procedure
      '------------------------------------------------------------------
      Local lRes, oldProc As Dword
      Local ctl As Long
      '------------------------------------------------------------------
      ' Messages shall normally be passed on to the original procedure
      ' with CallWindowProc for processing, which is why we stored the
      ' return value from SetWindowLong in the dialog's USER memory.
      ' However, it is perfectly ok to break some messages by not
      ' passing them on to the original procedure - see below.
      ' We'll use the GetParent API call to get parent dialog's handle.
      '------------------------------------------------------------------
      Dialog Get User hdlg, 1 To oldProc
      If oldProc = 0 Then Exit Function 'not assigned
     
      'find intercepted control id
      ctl = lParam 'Hi(Dword, wparam) 'not Lo(Dword, wparam)
      Select Case ctl
         Case g_tb_id(2), g_tb_id(10)
          ? "hit"
          Exit Function
      End Select
      '------------------------------------------------------------------
      Select Case As Long wMsg
      '------------------------------------------------------------------
      '
    '  Case %Wm_command
    '      Exit Function
      '
      Case %WM_GETDLGCODE
          ' TextBoxes have a tendency to select all text when they receive
          ' focus. We can change this behaviour by altering the return
          ' value from CallWindowProc under %WM_GETDLGCODE, like follows:
          lRes = CallWindowProc (oldProc, hWnd, wMsg, wParam, lParam)
          If (lRes And %DLGC_HASSETSEL) = %DLGC_HASSETSEL Then
              lRes = lRes Xor %DLGC_HASSETSEL
              Function = lRes
              Exit Function
          End If
      '------------------------------------------------------------------
      Case %WM_CHAR       ' we can break some keys here
          ' Note: Must use the VkKeyScan API to get correct scan code.
          Select Case Lo(Byte, VkKeyScan(wParam))
          Case %VK_SPACE  ' Let's break the Space bar
              MsgBox "Spaces are not allowed here!", _
                     %MB_TASKMODAL, "SubClass message"
              Exit Function
          End Select
      '------------------------------------------------------------------
      Case %WM_PASTE  ' just for fun - break Paste.
          ' Try to paste something in the TextBox and see what happens.
          ' Remove the next 2 lines to make it work again.
          MsgBox "Paste is not allowed here!", _
                 %MB_TASKMODAL, "SubClass message"
          Exit Function
      End Select
      '------------------------------------------------------------------
      ' Pass on messages to original control procedure
      Function = CallWindowProc(oldProc, hWnd, wMsg, wParam, lParam)
    End Function
    '
    Function PBMain
      Common_Locals
      Local hEdit, oldProc As Dword
      ctr = 32
      ReDim g_TB_Id(1 To ctr), g_TB_Handle(1 To ctr)
      For ctr = LBound(g_TB_Id()) To UBound(g_TB_Id())                
         g_TB_Id(ctr) = 1000 + ctr  'assign ids
         Row = Row + ht + spcr
      Next ctr
     
       Stile = Stile Or %WS_CAPTION
       Stile = Stile Or %WS_SYSMENU
       Stile = Stile Or %WS_THICKFRAME 
       Stile = Stile Or %WM_HELP 
       Stile = Stile Or %WS_Border  'doesn't do anything
     
      Dlg_hght = 250
      Dlg_Wd = 220
     
      Dialog New Pixels, hdlg, "Multi Textbox Subclass Demo", , , Dlg_Wd, Dlg_Hght, Stile To hdlg 'centered
     
      Font New "Consolas", 10 To hfont
     
      spcr = 5
      Row = 10
      col = 10
      Wd = 45
      Ht = 20
      For ctr = LBound(g_TB_Id()) To UBound(g_TB_Id())                
         Control Add TextBox, hdlg, g_TB_Id(ctr), Using$("#,### ", g_tb_Handle(ctr)), _
                 Col, Row, wd, ht
         Control Set Font hdlg, g_TB_Id(ctr), hfont
         Row = Row + ht + spcr  
          If Row > Dlg_hght - ht - 25 Then 'past bottom - next column
             Row = 10
             Col = Col + Wd + spcr
             If col > Dlg_Wd Then Exit For 'no more room at the inn
          End If
      Next ctr
    '
     
       ht = 25   
       Wd = Dlg_Wd - 20
       Col = 10 'center
       Row = Dlg_hght - Ht - 2 'Just off bottom
         Control Add Button, hdlg, %Id_Exit_Btn, "Abandon Ship", col, row, Wd, Ht
     
         Dialog Show Modal hDlg   Call PBMain_Dialog_Processor
    End Function  'Applikation befursmucken
    '
    =================================
    When your only tool is a hammer,
    everything looks like a nail.
    (FREDH - Salon's Table Talk)
    =================================
    Last edited by Gösta H. Lovgren-2; 21 Aug 2009, 08:23 PM.
    It's a pretty day. I hope you enjoy it.

    Gösta

    JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
    LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

  • #2
    Hi Gösta,

    Just a quick "hit and run" reply because I'm on my way to bed!

    In your SubClassProc, you seem to be thinking lParam is the control's ID - it's not. You can use:
    Code:
    WINDOW GET ID hWnd TO ctl
    instead, and the MsgBox will fire, but I suggest you have Task Manager ready, because the way it's currently coded, it will fire A LOT!. I used this block instead:
    Code:
      STATIC lCount AS LONG
      'find intercepted control id
      'ctl = lParam 'Hi(Dword, wparam) 'not Lo(Dword, wparam)
       IF lCount < 10 THEN
          WINDOW GET ID hWnd TO ctl
         SELECT CASE ctl
            CASE g_tb_id(2), g_tb_id(10)
             ? "hit"
             EXIT FUNCTION
         END SELECT
          INCR lCount
       END IF
    That's all I have time for. I hope it helps!

    Regards,

    Pete.

    Comment


    • #3
      Thanks Pete. You were right on the money.

      As for using the Task Mgr to end a program, I'm quite adept at that {grin}. I quite frequently need it when programming {arrggghhh}.

      Removing the Msgbox & Exit Function took care of the multiple hits problem (at least for now).

      What a great great forum. A solid explanatory non-subtle easily understandable on the money to the point non-lecturing uncondescending non-cryptic answer in (just over) an hour.

      ===========================================
      "It has become appallingly obvious
      our technology has exceeded our humanity."
      Albert Einstein (1879-1955)
      ===========================================
      Last edited by Gösta H. Lovgren-2; 21 Aug 2009, 10:22 PM.
      It's a pretty day. I hope you enjoy it.

      Gösta

      JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
      LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

      Comment


      • #4
        All the text box controls will have the same value for the old window procedure address, so you only need to store it one time in a Global variable. They are all the same window class, so the window procedure address will always be the same.

        A cleaner way to do this, is to superclass the textbox control and create a new class.

        To superclass a control class you use the API:

        GetClassInfoEx

        It will return a structure similiar to the one used in the RegisterClassEx function. You call RegisterClassEx, but you use your own window procedure address.

        In your window procedure you pass the messages back to the address of the original window procedure you get from GetClassInfoEx. It can be stored in a Global variable.

        Now when you create the new control class, you use a different class name (what ever you define it to be in RegisterClassEx).

        Superclassing is better than subclassing, because you create a new class name, don't have to subclass every control you create and also the superclass window procedure gets the WM_CREATE message, which a subclass window procedure will never get.
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        http://twitter.com/EZGUIProGuy

        Comment


        • #5
          Your answer, as always, makes good sense, Chris, but Pete's is much simpler (for a DDT guy like me) to implement and does exactly what I need at this time. I barely understand Subclassing, much less SuperClassing. Forays into the API are always adventuresome for me and at this stage in life I've had enough excitement to last me out.

          ''
          Code:
          'Example of subclassing multiple textboxes
          'PBWIN 9.01 - WinApi 05/2008 - XP Pro SP3
          #Dim All 
          #Compile Exe  
          #Optimize SPEED
          #Debug Display On'off for production code
           
          #Include "WIN32API.INC"
          #Include "COMDLG32.INC"
          #Include "InitCtrl.inc"
          '
          Global hdlg As Dword                
          Global g_TB_Id(), g_TB_Handle() As Dword
          %Id_Exit_Btn = 1000
          ' 
          Macro Common_Locals 'Macro easier than retyping and maintains coding consistency
            Global Dlg_hght, Dlg_Wd As Long 'Global in case want to use in Controls
            Local spcr, Stile, Row, col, ht, wd, Longest,ctr, ln, ln1, i As Long
            Local  l, s As String
            Local hfont As Dword
          End Macro  
          '
          CallBack Function PBMain_Dialog_Processor              
            Common_Locals                                           
            Select Case CbMsg     'This is TO determine the message TYPE 
               '       
               Case %WM_INITDIALOG'<- Initialiaton when the program loads 
                   '--------------------------------------------------------------
                ' %WM_INITDIALOG is sent right before the dialog is shown.
                ' A good place to initiate variables and controls, etc.
                ' Let's Subclass the TextBox and store the returned pointer
                ' to the original TextBox procedure with DIALOG SET USER.
                '--------------------------------------------------------------
                Local hEdit, oldProc As Dword
                'dunno why but this is needed FIRST
                Control Handle hdlg, g_TB_Id(3) To hEdit 'could be any TB id
                oldProc = SetWindowLong(hEdit, %GWL_WNDPROC, CodePtr(SubClassProc))
                Dialog Set User CB.Hndl, 1, oldProc 
          '
          '     Create Handles For subclassing All textboxes 
                For ctr = LBound(g_TB_Id()) To UBound(g_TB_Id())                
                  Control Handle hdlg, g_TB_Id(ctr) To hEdit
                  oldProc = SetWindowLong(hEdit, %GWL_WNDPROC, CodePtr(SubClassProc))
                  g_TB_Handle(ctr) = OldProc 
          '       Control Set Text hdlg, g_tb_id(ctr), Using$("#, ", g_tb_Handle(ctr)) 'show handles
                Next ctr
               
               Case %WM_SYSCOMMAND 'Traps Any Alt key but only F4 closes              
               '
               Case %WM_COMMAND  'This processes command messages
                 Select Case CbCtl
                   Case %Id_Exit_Btn
                     Select Case CbCtlMsg        
                        Case 0
                          Dialog End CbHndl 'Applikation beenden
                     End Select
                 End Select
            End Select
          End Function
          '       
          '====================================================================
          Function SubClassProc(ByVal hWnd As Dword, ByVal wMsg As Dword, _
                                ByVal wParam As Dword, ByVal lParam As Long) As Long
          '--------------------------------------------------------------------
            ' SubClass procedure
            '------------------------------------------------------------------
            Local lRes, oldProc As Dword
            Local ctl As Long
            '------------------------------------------------------------------
            ' Messages shall normally be passed on to the original procedure
            ' with CallWindowProc for processing, which is why we stored the
            ' return value from SetWindowLong in the dialog's USER memory.
            ' However, it is perfectly ok to break some messages by not
            ' passing them on to the original procedure - see below.
            ' We'll use the GetParent API call to get parent dialog's handle.
            '------------------------------------------------------------------
            Dialog Get User hdlg, 1 To oldProc 'Subclass created?
            If oldProc = 0 Then Exit Function 'not assigned
            
            'find intercepted control id
            Window Get Id hWnd To ctl
            Select Case ctl
               Case g_tb_id(2), g_tb_id(10) 'test works
          '      ? "hit"
          '      Exit Function
            End Select
            '------------------------------------------------------------------
            Select Case As Long wMsg
            '------------------------------------------------------------------
            '
          '  Case %Wm_command
          '      Exit Function
            '
            Case %WM_GETDLGCODE
                ' TextBoxes have a tendency to select all text when they receive
                ' focus. We can change this behaviour by altering the return
                ' value from CallWindowProc under %WM_GETDLGCODE, like follows:
                lRes = CallWindowProc (oldProc, hWnd, wMsg, wParam, lParam)
                If (lRes And %DLGC_HASSETSEL) = %DLGC_HASSETSEL Then
                    lRes = lRes Xor %DLGC_HASSETSEL
                    Function = lRes
                    Exit Function
                End If
            '------------------------------------------------------------------
            Case %WM_CHAR       ' we can break some keys here
                ' Note: Must use the VkKeyScan API to get correct scan code.
                Select Case Lo(Byte, VkKeyScan(wParam))
                Case %VK_SPACE  ' Let's break the Space bar
                    MsgBox "Spaces are not allowed here!", _
                           %MB_TASKMODAL, "SubClass message"
                    Exit Function
                End Select
            '------------------------------------------------------------------
            Case %WM_PASTE  ' just for fun - break Paste.
                ' Try to paste something in the TextBox and see what happens.
                ' Remove the next 2 lines to make it work again.
                MsgBox "Paste is not allowed here!", _
                       %MB_TASKMODAL, "SubClass message"
                Exit Function
            End Select
            '------------------------------------------------------------------
            ' Pass on messages to original control procedure
            Function = CallWindowProc(oldProc, hWnd, wMsg, wParam, lParam)
          End Function
          '
          Function PBMain
            Common_Locals
            Local hEdit, oldProc As Dword
            ctr = 32
            ReDim g_TB_Id(1 To ctr), g_TB_Handle(1 To ctr)
            For ctr = LBound(g_TB_Id()) To UBound(g_TB_Id())                
               g_TB_Id(ctr) = 1000 + ctr  'assign ids
               Row = Row + ht + spcr
            Next ctr
            
             Stile = Stile Or %WS_CAPTION
             Stile = Stile Or %WS_SYSMENU
             Stile = Stile Or %WS_THICKFRAME 
             Stile = Stile Or %WM_HELP 
             Stile = Stile Or %WS_Border  'doesn't do anything
              
            Dlg_hght = 250
            Dlg_Wd = 220
             
            Dialog New Pixels, hdlg, "Multi Textbox Subclass Demo", , , Dlg_Wd, Dlg_Hght, Stile To hdlg 'centered
            
            Font New "Consolas", 10 To hfont
           
            spcr = 5
            Row = 10
            col = 10
            Wd = 45
            Ht = 20
            For ctr = LBound(g_TB_Id()) To UBound(g_TB_Id())                
               Control Add TextBox, hdlg, g_TB_Id(ctr), Using$("#,### ", g_tb_Id(ctr)), _
                       Col, Row, wd, ht
               Control Set Font hdlg, g_TB_Id(ctr), hfont
               Row = Row + ht + spcr  
                If Row > Dlg_hght - ht - 25 Then 'past bottom - next column
                   Row = 10
                   Col = Col + Wd + spcr
                   If col > Dlg_Wd Then Exit For 'no more room at the inn
                End If
            Next ctr
          '
                   
             ht = 25   
             Wd = Dlg_Wd - 20
             Col = 10 'center
             Row = Dlg_hght - Ht - 2 'Just off bottom
               Control Add Button, hdlg, %Id_Exit_Btn, "Abandon Ship", col, row, Wd, Ht
            
               Dialog Show Modal hDlg   Call PBMain_Dialog_Processor
          End Function  'Applikation beenden
          '
          ============================
          In three words I can sum up
          everything
          I've learned about life:
          it goes on.
          Robert Frost (1874-1963)
          ============================
          It's a pretty day. I hope you enjoy it.

          Gösta

          JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
          LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

          Comment


          • #6
            >WINDOW GET ID hWnd TO ctl

            For those not using PB/Win 9+, the WinAPI may be used ...
            Code:
               ctl  =  GetDlgCtrlID (hWnd)
            Both WINDOW GET ID and GetDlgCtrlID() are more reliable than using lparam and wparam, since wparam and lparam only contain references to the sending window when wMsg is one of several specific values. eg, when wMSg = WM_SIZE, wparam = type of sizing command and lparam = new dimensions. When wMsg = BM_CLICK, wParam contains mouse button states and lparam the location of the cursor.

            MCM
            Last edited by Michael Mattias; 22 Aug 2009, 08:57 AM.
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Thanks, M. Looks like Window Get Id is just another (neater) way of saying GetDlgCtrlID.

              On to the next hurdle: VK_Tab doesn't get hit but VK_Space does
              Code:
                '------------------------------------------------------------------
                Case %WM_CHAR       ' we can break some keys here
                    ' Note: Must use the VkKeyScan API to get correct scan code.
                    Select Case Lo(Byte, VkKeyScan(wParam))
                    Case %VK_SPACE  ' Let's break the Space bar
                        MsgBox "Spaces are not allowed here!", _
                               %MB_TASKMODAL, Using$("#, Control", ctl)
                        Exit Function
                     '
                     Case %VK_Tab '<<< does not get hit but VK_Space does
                       ?"Tabbed"& Str$(ctl):Exit Function 
                    End Select
              Tried both with and wo %WS_Tabstop.

              ============================================
              "A poem is never finished, only abandoned."
              Paul Valery (1871-1945)
              ============================================
              It's a pretty day. I hope you enjoy it.

              Gösta

              JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
              LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

              Comment


              • #8
                Style has no effect... you get tab when the user hits (of all things) the <TAB> key.

                However, I think you will want to look at what you are returning in response to WM_GETDLGCODE.

                (Hint: If you WANT TABS....)

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

                Comment


                • #9
                  Originally posted by Michael Mattias View Post
                  However, I think you will want to look at what you are returning in response to WM_GETDLGCODE.

                  (Hint: If you WANT TABS....)

                  MCM
                  As usual your mysterious "hints' are of little value to me. I tried remming the Wm_GetDlgCode (which PB says cancel entire tb selection upon entry) but no difference. I don't understand why VK_Tab is not trapped the same as VK_Space.


                  =============================
                  A language is a more ancient
                  and inevitable
                  thing than any state.
                  Joseph Brodsky
                  =============================
                  It's a pretty day. I hope you enjoy it.

                  Gösta

                  JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                  LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                  Comment


                  • #10
                    QUESTION ONE (the "tossup" question; first to buzz in with correct answer gets the bonus question)

                    What are the names of the valid DLGC_xxxx equates which may be combined (OR'd) to form a valid return from WM_GETDLGCODE?

                    Ok, you buzzed in first, and that is correct.

                    Now, for your BONUS question .. that is, if you are not already ' ing yourself ....

                    Do any of these names look like they might have something to do with tabs?
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      While your reply is "cute' (I'm sure you think so) many would find it aggravating (at least I do). And not in the least helpful. Which is probably your intention. No wonder newbies are reluctant/hesitant to post.

                      Elsewhere I found the answer I was looking for.
                      Code:
                        Case %WM_GETDLGCODE
                              Function = %DLGC_WANTALLKEYS: Exit Function
                      in case others are interested.

                      ====================================================
                      "I'm not a member of any organized political party,
                      I'm a Democrat!"
                      Will Rogers (1879-1935)
                      ====================================================
                      It's a pretty day. I hope you enjoy it.

                      Gösta

                      JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                      LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                      Comment


                      • #12
                        I was thinking of DLGC_WANTTAB
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          >>While your reply is "cute' (I'm sure you think so) many would find it aggravating (at least I do).

                          Teehee.. Never mind the quality, feel the width my son!
                          Rgds, Dave

                          Comment


                          • #14
                            Gosta,

                            I may have missed why you asked the question.

                            When you created the textboxes, couldn't you have just keep the textbox handles in an array, then used Select Case to act on the hWnd that sent the message?

                            This example doesn't do an array - it's just for demo. But in your case, where you used Do Loops to create multiple textboxes, the code is even simpler than this example.

                            The second example uses the Windw Get ID command (I only did it to confirm to myself that it works).

                            I guess as long as the textboxes have sequential IDs, or the hWnds are in the array in the same order as the control IDs, they're both equally useful? I'd be interested in knowing which of the two pieces of information is better for you in your case - hWnd or CtlID - and why.

                            Code:
                            'Compilable Example: (could be an array of hWnds)
                            'In this example, subclassing is used to prevent the normal context menu from
                            'appearing when a textbox is right-mouse clicked - with multiple textboxes using
                            'the same subclassing procedure.  An application might provide its own custom 
                            'context menu. Right-click any box.
                            #Compile Exe
                            #Dim All
                            #Include "Win32API.inc"
                            Global hDlg As Dword, hTB1 As Dword, hTB2 as Dword, hTB3 as Dword, OldProc&
                            %ID_Control1 = 500 : %ID_COntrol2 = 501 : %ID_Control3 = 502
                            Function PBMain() As Long
                               Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
                               Control Add TextBox, hDlg, %ID_Control1, "TB1", 20,10,120,20
                               Control Add TextBox, hDlg, %ID_Control2, "TB2!", 20,40,120,20
                               Control Add TextBox, hDlg, %ID_Control3, "TB3!", 20,70,120,20
                               Control Handle hDlg, %ID_Control1 to hTB1
                               Control Handle hDlg, %ID_Control2 to hTB2
                               Control Handle hDlg, %ID_Control3 to hTB3
                               Dialog Show Modal hDlg Call DlgProc
                            End Function
                            
                            CallBack Function DlgProc() As Long
                               Select Case CB.Msg
                                  Case %WM_InitDialog
                                     OldProc& = SetWindowLong(GetDlgItem(hDlg, %ID_Control1), %GWL_WndProc, Codeptr(NewProc))  'subclass
                                     OldProc& = SetWindowLong(GetDlgItem(hDlg, %ID_Control2), %GWL_WndProc, Codeptr(NewProc))  'subclass
                                     OldProc& = SetWindowLong(GetDlgItem(hDlg, %ID_Control3), %GWL_WndProc, Codeptr(NewProc))  'subclass
                               End Select
                            End Function
                            
                            Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
                               Select Case Msg
                                  Case %WM_ContextMenu
                                     Select Case hWnd
                                        Case hTB1 : MsgBox "TB1"
                                        Case hTB2 : MsgBox "TB2"
                                        Case hTB3 : MsgBox "TB3"
                                     End Select
                                     Function = 0 : Exit Function
                               End Select
                               Function = CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
                            End Function
                            Code:
                            'Compilable Example:  (get the Ctrl ID each time the subclassing procedure is called).
                            'In this example, subclassing is used to prevent the normal context menu from
                            'appearing when a textbox is right-mouse clicked - with multiple textboxes using
                            'the same subclassing procedure.  An application might provide its own custom 
                            'context menu. Right-click any box.
                            #Compile Exe
                            #Dim All
                            #Include "Win32API.inc"
                            Global hDlg As Dword, hTB1 As Dword, hTB2 as Dword, hTB3 as Dword, OldProc&
                            %ID_Control1 = 500 : %ID_COntrol2 = 501 : %ID_Control3 = 502
                            Function PBMain() As Long
                               Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
                               Control Add TextBox, hDlg, %ID_Control1, "TB1", 20,10,120,20
                               Control Add TextBox, hDlg, %ID_Control2, "TB2!", 20,40,120,20
                               Control Add TextBox, hDlg, %ID_Control3, "TB3!", 20,70,120,20
                               Dialog Show Modal hDlg Call DlgProc
                            End Function
                            
                            CallBack Function DlgProc() As Long
                               Select Case CB.Msg
                                  Case %WM_InitDialog
                                     OldProc& = SetWindowLong(GetDlgItem(hDlg, %ID_Control1), %GWL_WndProc, Codeptr(NewProc))  'subclass
                                     OldProc& = SetWindowLong(GetDlgItem(hDlg, %ID_Control2), %GWL_WndProc, Codeptr(NewProc))  'subclass
                                     OldProc& = SetWindowLong(GetDlgItem(hDlg, %ID_Control3), %GWL_WndProc, Codeptr(NewProc))  'subclass
                               End Select
                            End Function
                            
                            Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
                               Select Case Msg
                                  Case %WM_ContextMenu
                                     Local Ctl as Dword
                                     Window Get Id hWnd TO Ctl            
                                     MsgBox Str$(Ctl)
                                     Function = 0 : Exit Function
                               End Select
                               Function = CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
                            End Function
                            Last edited by Gary Beene; 22 Aug 2009, 09:12 PM.

                            Comment


                            • #15
                              When you created the textboxes, couldn't you have just keep the textbox handles in an array, then used Select Case to act on the hWnd that sent the message?
                              Because he didn't want to store the data twice ==> good design. Makes the code much more maintainable and reusable.

                              Just imagine... what would it take to change that screen to add one more control which must be handled the same way as the first three? Using an array you have to resize the array, add another statement to save a handle, and change the subclass procedure to extend the range searched.

                              By using the "WINDOW ID" or 'GetDlgCtrlID' you have to do...nothing.



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

                              Comment


                              • #16
                                Gary, Have added Handle & Id Arrays to your first example.
                                '[code]
                                '
                                Code:
                                'http://www.powerbasic.com/support/pbforums/showthread.php?p=321597#post321597
                                'PBWIN 9.01 - WinApi 05/2008 - XP Pro SP3
                                'Compilable Example: (could be an array of hWnds) 
                                'In this example, subclassing is used to prevent the normal context menu from
                                'appearing when a textbox is right-mouse clicked - with multiple textboxes using
                                'the same subclassing procedure.  An application might provide its own custom 
                                'context menu. Right-click any box.
                                #Compile Exe
                                #Dim All
                                #Include "Win32API.inc"
                                 
                                Global hDlg As Dword, hTB1 As Dword, hTB2 As Dword, hTB3 As Dword, OldProc&   
                                Global g_TB_Hndl() As Dword, g_TB_Id() As Long '<< Added
                                Global hfont1, hfont2 As Dword
                                 
                                %ID_Control1 = 500 : %ID_COntrol2 = 501 : %ID_Control3 = 502
                                 
                                Function PBMain() As Long
                                   '<< Added
                                  Local ctr, row, col, wd, ht, lb, ub As Long
                                   Font New "Consolas", 8 To hfont1
                                   Font New "Arial", 16 To hfont2
                                  ctr = 5
                                  Dim g_TB_Id(1 To ctr), g_TB_Hndl(1 To ctr) 
                                  lb = 1
                                  ub = 5
                                ' 
                                  Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
                                   '<< Added
                                  col = 20
                                  Row = 10
                                  wd = 160
                                  ht = 20
                                  For ctr = lb To UB
                                     g_TB_Id(ctr) = 500 + ctr
                                     Control Add TextBox, hDlg, g_TB_Id(ctr), Using$("TB #, ", g_TB_Id(ctr)), col, row, wd, ht Call SubClass_Processor
                                     Control Handle hdlg, g_TB_Id(ctr) To g_TB_Hndl(ctr)
                                     Row = Row + ht + 10
                                   Next ctr
                                 
                                '   Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
                                '   Control Add TextBox, hDlg, %ID_Control1, "TB1", 20,10,120,20
                                '   Control Add TextBox, hDlg, %ID_Control2, "TB2!", 20,40,120,20
                                '   Control Add TextBox, hDlg, %ID_Control3, "TB3!", 20,70,120,20
                                '   Control Handle hDlg, %ID_Control1 To hTB1
                                '   Control Handle hDlg, %ID_Control2 To hTB2
                                '   Control Handle hDlg, %ID_Control3 To hTB3
                                   Dialog Show Modal hDlg Call DlgProc
                                End Function
                                CallBack Function DlgProc() As Long
                                   Local ctr, lb, ub As Long
                                   lb = LBound(g_TB_Hndl())
                                   ub = UBound(g_TB_Hndl())
                                   Select Case CB.Msg
                                      Case %WM_InitDialog
                                        For ctr = lb To UB  '<< Added
                                           OldProc& = SetWindowLong(GetDlgItem(hDlg, g_TB_Id(ctr)), %GWL_WndProc, CodePtr(SubClass_Processor))  'subclass
                                        Next ctr
                                   End Select
                                End Function
                                 '
                                Function SubClass_Processor(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
                                    '<< Added
                                   Local Idd, ctr, lb, ub As Long 
                                 
                                   lb = LBound(g_TB_Hndl())
                                   ub = UBound(g_TB_Hndl())
                                 
                                   Select Case Msg
                                      Case %WM_ContextMenu
                                          '<< Added
                                         For ctr = lb To ub '<< catches all possible Handles [B]'<< see next page of forum messages[/B]
                                           If hWnd = g_TB_Hndl(ctr) Then  'handle matched
                                               'do stuff here
                                              Control Set Text hdlg, g_TB_Id(ctr), "For method"
                                              Control Set Font  hdlg, g_TB_Id(ctr), hfont2
                                              Control Set Focus hdlg, g_TB_Id(ctr)
                                             Exit For
                                           End If   
                                         Next ctr    
                                         Exit Function
                                '        ? "Contexted",,"%WM_ContextMenu" 'show hit
                                         Function = 0 : Exit Function
                                   End Select
                                   Function = CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
                                End Function
                                 
                                '
                                Strange though. The Handles only get trapped every OTHER run in the SubClassProcessor (NewProc) using a Select Case but EVERY time using a For loop. Select Case sometimes traps odd numbered handleson some runs but not others. Peculiar.

                                Actually the For loop works well in that it provides the TB id at the same time (no nettlesome API call needed) {grin} and catches the handles every run.

                                Next Day - REMOVED Select Case method - See messages further on in Forum for details on why it didn't work.

                                =========================================
                                Never let the future disturb you.
                                You will meet it, if you have to,
                                with the same weapons of reason
                                which today arm you against the present.
                                Marcus Aurelius Antoninus (121-180 A.D.)
                                =========================================
                                Last edited by Gösta H. Lovgren-2; 25 Aug 2009, 07:26 AM. Reason: Clarified (see bolded). Bad editing first time posted. Was misleading. - Updated code
                                It's a pretty day. I hope you enjoy it.

                                Gösta

                                JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                                LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                                Comment


                                • #17
                                  I replaced the code just above to better demonstrate the difference between using Select Case and a For loop in the SubclassProc. On muliple runs some runs the Select gets hit (only every other Textbox) and the next run it never gets hit. Really puzzling. Perhaps I'm somehow misusing the Select?

                                  ==============================================
                                  "The truth is more important than the facts."
                                  Frank Lloyd Wright (1868-1959)
                                  ==============================================
                                  It's a pretty day. I hope you enjoy it.

                                  Gösta

                                  JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                                  LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                                  Comment


                                  • #18
                                    >Font New "Consolas", 8 To hfont1
                                    >Font New "Arial", 16 To hfont2

                                    You might be running into a font limit here. These two statements are executed every time you enter the subclass procedure (which is A LOT) , and I can't tell that you ever destroy these objects. "Strange" things might be happening.

                                    I think I'd make those font handles STATIC and add a "bFontCreated" var , also STATIC. IF ISFALSE(bFontCreated) create the fonts and set the flag.

                                    That, or create those fonts outside your subclass procedure and store 'em in a DIALOG | CONTROL SET USER variable.
                                    Last edited by Michael Mattias; 24 Aug 2009, 08:58 AM.
                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      You might try changing your variable named 'Id' to something that is not a reserved word.
                                      Rod
                                      In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.

                                      Comment


                                      • #20
                                        M, good point. Hadn't occurred to me. I'll change the listed code to Global and assign the fonts in PB Main.

                                        Rodney, good point. Using a Reserved Word as a variable is not a good practice. Always knew it was a RW (blue colored in editor) but only recently used it as one (Window Get ID) as a result of recent other thread.

                                        On both points in my (weak) defense, this was only a quicky to demo arrays for multi subclassing.

                                        Thx both.

                                        (Little later) Posted code changed to reflect above. I must remark, however, while finding coding weaknesses (Form), no one apparently RAN (Function) the code to offer reasoning for the aberrant behavior (some runs recognizing the Select Case under %WM_ContextMenu and some not). Is Form more important than Function? I've always tended to believe otherwise, myself.

                                        ==========================================
                                        "I have a hammer!
                                        I can put things together!
                                        I can knock things apart!
                                        I can alter my environment at will
                                        and make an incredible din all the while!
                                        Ah, it's great to be male!"
                                        Calvin
                                        ==========================================
                                        Last edited by Gösta H. Lovgren-2; 24 Aug 2009, 07:37 PM.
                                        It's a pretty day. I hope you enjoy it.

                                        Gösta

                                        JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                                        LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                                        Comment

                                        Working...
                                        X