Announcement

Collapse
No announcement yet.

Simple way to subclass controls

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

  • Simple way to subclass controls

    Code is here http://www.powerbasic.com/support/pb...686#post393686

    This is an attempt to make the code as simple and easily deployed as possible.

    Any number of controls of the specified type can be subclassed in the same way - similar effect to superclassing.

  • #2
    Code:
    macro mAddSubclassedTextBox (CtlId, txt, X, Y, W, H)
        macrotemp oldproc
        dim oldproc as dword
        control add textbox, hiddendlghandle, CtlId, txt, X, Y, W, H
        oldproc = setwindowlong(getdlgitem(hiddendlghandle, CtlId), %GWL_WNDPROC, codeptr(TBsubclassCB))
    ....

    You might want to make the callback procname one of the macro parameters. That would allow you to use the same macro but specify different callback procedures for different "subclassed textbox controls" on the same screen.
    Michael Mattias
    Tal Systems Inc.
    Racine WI USA
    mmattias@talsystems.com
    http://www.talsystems.com

    Comment


    • #3
      Originally posted by Michael Mattias View Post
      You might want to make the callback procname one of the macro parameters. That would allow you to use the same macro but specify different callback procedures for different "subclassed textbox controls" on the same screen.
      Yes, but that throws the onus of message handling back on the subclass proc. My preference is to do it in the dialog proc, for example like:

      Code:
              case %FROM_SUBCLASSED_TEXTBOX
                  select case as long cb.wparam ' control id is returned in cb.wparam
                       case 1001, 1002, 1009
                              ' some processing
                       case 1003, 1004, 1005, 1006, 1007, 1008
                              ' some other processing
                  end select
      Last edited by Chris Holbrook; 1 Dec 2011, 03:07 PM.

      Comment


      • #4
        >Yes, but that throws the onus of message handling back on the subclass proc.

        Um, isn't that the whole idea of subclassing? That the programmer can handle notification messages not otherwise available in an application-specific manner?

        I have applications which have multiple controls sharing a subclass proc, and applications where different controls of the same class use different subclass procedures.

        "procname=macro param" was just a thought....a little extra "Power."
        Michael Mattias
        Tal Systems Inc.
        Racine WI USA
        mmattias@talsystems.com
        http://www.talsystems.com

        Comment


        • #5
          Originally posted by Michael Mattias View Post
          >Yes, but that throws the onus of message handling back on the subclass proc.

          Um, isn't that the whole idea of subclassing? ...
          That is how subclassing is implemented, but it is not always friendly to the objectives of the application, and in such cases a method of directing events which can only be trapped by subclassing/superclassing to a single procedure is appropriate.

          The comments at the top of the code make my purpose clear, "...example to show how subclassing can be used to reflect messages received
          by a control to its parent dialog..."

          BTW can you think of an alternative (to subclassing) way of turning off the right-click menu on a textbox? My brain has already signed off...

          Comment


          • #6
            Hi Chris(H)!
            Re:
            ... alternative (to subclassing) way of turning off the right-click menu on a textbox?
            My first thought was that there is a WM_InitMenuPopup message sent when a popup appears, but it seems that message is sent to the child window, not the parent. Under DDT that takes subclassing to capture, but can't it be captured in an SDK app? I'm not familiar enough with SDK code to know for sure.

            My next idea was this code, which works, but poorly because it flashes the textbox control:
            Code:
            'Compilable Example:
            #Compile Exe
            #Dim All
            #Include "Win32API.inc"
            Global hDlg,hTextBox As Dword
            %IDC_TextBox = 500
            Function PBMain() As Long
               Dialog New Pixels, 0, "Parent",300,300,200,100, %WS_OverlappedWindow To hDlg
               Control Add TextBox, hDlg, %IDC_TextBox, "Hello",10,10,180,80
               Control Handle hDlg, %IDC_TextBox To hTextBox
               Dialog Show Modal hDlg Call DlgProc
            End Function
            
            CallBack Function DlgProc() As Long
               Select Case Cb.Msg
                 Case %WM_SetCursor  'MouseMove
                     Select Case Hi(Word, Cb.LParam)
                        Case %WM_RButtonDown
                            If GetDlgCtrlID (Cb.WParam) = %IDC_TextBox Then
                                Control Disable hDlg, %IDC_TextBox
                            End If
                        Case %WM_RButtonUp
                            Control Enable hDlg, %IDC_TextBox
                     End Select
               End Select
            End Function
            I'll think on it some more.

            Comment


            • #7
              Oh, and I also tried sending the TextBox a WM_CancelMode message (instead of the Control Disable statement. That didn't work.

              MSDN had this, which suggested it might work:
              When the WM_CANCELMODE message is sent, the DefWindowProc function cancels internal processing of standard scroll bar input, cancels internal menu processing, and releases the mouse capture.
              It seems like there ought to be something that would work by replacing the Control Disable statement in my code, but it's not obvious just yet.

              Comment


              • #8
                Hey Chris(H)!
                This is much closer - the very tiniest of flicker in Win7, Aero mode - and slightly more in non-Aero mode.
                Code:
                'Compilable Example:
                #Compile Exe
                #Dim All
                #Include "Win32API.inc"
                Global hDlg,hTextBox As Dword
                %IDC_TextBox = 500
                Function PBMain() As Long
                   Dialog New Pixels, 0, "Parent",300,300,200,100, %WS_OverlappedWindow To hDlg
                   Control Add TextBox, hDlg, %IDC_TextBox, "Hello",10,10,180,80
                   Control Handle hDlg, %IDC_TextBox To hTextBox
                   Dialog Show Modal hDlg Call DlgProc
                End Function
                
                CallBack Function DlgProc() As Long
                   Select Case Cb.Msg
                     Case %WM_SetCursor  'MouseMove
                         Select Case Hi(Word, Cb.LParam)
                            Case %WM_RButtonUp
                                If GetDlgCtrlID (Cb.WParam) = %IDC_TextBox Then
                                    PostMessage hTextBox, %WM_CancelMode, 0, 0
                                End If
                         End Select
                   End Select
                End Function
                Basically this code closes the menu as soon as it opens. What we're looking for is to stop it from appearing in the first place.

                Comment


                • #9
                  Could PeekMessage be used to block the %WM_RButtonDown or %WM_RButtonUp as required on that particular control?
                  Rod
                  "To every unsung hero in the universe
                  To those who roam the skies and those who roam the earth
                  To all good men of reason may they never thirst " - from "Heaven Help the Devil" by G. Lightfoot

                  Comment


                  • #10
                    Could always use a hook
                    Rgds, Dave

                    Comment


                    • #11
                      Hi Dave,
                      I thought of that (I like my new hammer!), but don't know enough about menus to know what class (or other window info) to look for. I'll go nose around MSDN for some clues.

                      Comment


                      • #12
                        Hi Dave/Chris(H),
                        Ok, I cheated. I didn't go to MSDN. I just iterated the class names that came up and found #32768, tried it, and it seems to do the trick with no flickering.

                        Code:
                        'Compilable Example:
                        #Compile Exe
                        #Dim All
                        #Include "Win32API.inc"
                        Global hDlg,hTextBox,ghHook As Dword
                        %IDC_TextBox = 500
                        Function PBMain() As Long
                           Dialog New Pixels, 0, "Parent",300,300,200,100, %WS_OverlappedWindow To hDlg
                           Control Add TextBox, hDlg, %IDC_TextBox, "Hello",10,10,180,80
                           Control Handle hDlg, %IDC_TextBox To hTextBox
                           Dialog Show Modal hDlg Call DlgProc
                        End Function
                        
                        CallBack Function DlgProc() As Long
                           Select Case Cb.Msg
                             Case %WM_SetCursor  'MouseMove
                                 Select Case Hi(Word, Cb.LParam)
                                    Case %WM_RButtonUp
                                        If GetDlgCtrlID (Cb.WParam) = %IDC_TextBox Then
                                            ghHook = SetWindowsHookEx(%WH_CBT, CodePtr(MenuProc), GetModuleHandle(""), GetCurrentThreadId)
                                        End If
                                 End Select
                           End Select
                        End Function
                        
                        Function MenuProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
                            Local szTemp As Asciiz * %Max_Path
                            Function = CallNextHookEx(ByVal ghHook, ByVal nCode, ByVal wParam, ByVal lParam)
                            If nCode < 0 Then Exit Function
                            If nCode = %HCBT_ACTIVATE Then UnhookWindowsHookEx ghHook
                            If nCode = %HCBT_CREATEWND Then
                                GetClassName wParam, szTemp, %Max_Path      ' for each window / control as it is created
                                If UCase$(szTemp) = "#32768" Then Function = 1   '@cst.cy = @cst.cy - 130                'dialog
                            End If
                        End Function

                        Comment


                        • #13
                          In hindsight, I went to MSDN and found this
                          #32768 The Class For a menu.

                          Comment


                          • #14
                            Hmm, yesterday's "rich edit control in text mode" might have a solution....

                            Send EM_SETEVENTMASK, with the MSGFILTER structure referring to WM_RBUTTONUP and process the resulting EN_MSGFILTER notification....

                            Return Value

                            If the control should process the event, the message returns a zero value.

                            If the control should ignore the event, the message returns a nonzero value.
                            It's a thought...
                            Michael Mattias
                            Tal Systems Inc.
                            Racine WI USA
                            mmattias@talsystems.com
                            http://www.talsystems.com

                            Comment


                            • #15
                              I was thinking of a different hammer - WH_MOUSE
                              Code:
                              #Dim All
                              #Include "win32API.inc"
                               
                              Global ghMoHook As Dword
                               
                              Function MouseProc(ByVal nCode As Long, ByVal wParam As Dword, ByVal lParam As Long) As Dword
                               Local msh As MOUSEHOOKSTRUCT Ptr, szTemp As Asciiz * %Max_Path
                                Function = CallNextHookEx(ghMoHook, nCode, wParam, lParam)  ' House keeping - call next hook in chain
                                If nCode < 0 Then Exit Function
                                If nCode = %HC_ACTION Then msh = lParam
                                  If wParam = %WM_RButtonUp Then GetClassName @msh.hwnd, szTemp, %Max_Path  ' for each window / control as it is created
                                    If UCase$(szTemp) = "EDIT" Then
                                    Function = 1
                                    SendMessage GetParent(@msh.hwnd), %WM_User + 501, wParam, lParam     ' Pass params to DlgProc
                                  End If
                              End Function
                              '------------------/MouseProc
                               
                              CallBack Function DlgProc()
                                Select Case As Long CbMsg
                                  Case %WM_InitDialog
                                    ghMoHook = SetWindowsHookEx(%WH_MOUSE, CodePtr(MouseProc), GetModuleHandle(""), GetCurrentThreadId)
                               
                                  Case %WM_Destroy
                                    UnhookWindowsHookEx ghMoHook                         ' House Keeping - unhook mouse on exit
                               
                                  Case %WM_Command
                                    Select Case CbCtlMsg
                                      Case %BN_Clicked
                                        If CbCtl = %IdCancel Then
                                          Dialog End CbHndl
                                        End If
                                    End Select
                               
                                  Case %WM_User + 501                                   ' msg from mouseproc.
                                    Dialog Set Text CbHndl, Time$ + " Mouse R Key up"
                               
                                End Select
                              End Function
                              '------------------/
                               
                              Function PBMain() As Long
                               Local hDlg As Dword
                                Dialog New 0, "Test", , , 240, 60, %WS_Caption Or %WS_SysMenu, 0 To hDlg
                                  Control Add TextBox, hDlg, 101, "Right-Click here :)", 15, 10, 205, 15
                                  Control Add Label,   hDlg, 102, "Mouse Hook Test", 15, 35, 135, 10
                                  Control Add Button,  hDlg, %IdCancel, "Done", 160, 35, 50, 15
                                  Control Set Focus hDlg, %IdCancel
                               
                                Dialog Show Modal hDlg, Call DlgProc
                              End Function
                              '------------------/PBMain
                              Rgds, Dave

                              Comment

                              Working...
                              X