Announcement

Collapse
No announcement yet.

Custom Controls/Sub Classing

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

  • Custom Controls/Sub Classing

    Hello Everybody!


    I have a few questions about windows control classes and
    subclassing, so here's the questions.


    1. Can I assume that all windows controls (EDIT,BUTTON...) where
    created with the class style CS_OWNDC?

    2. When subclassing a control is it propper to get the original
    controls callback pointer from GetClassLong?

    3. Is there any way to get a custom control created with PBDLL
    to show up in VisualStudio? I have asked this before but I could
    figure out what the answer ment.


    ------------------
    Cheers

  • #2
    Mark;

    (1) I believe the custom controls do NOT use CS_OWNDC. They use the
    parent windows DC which means when you draw into the controls DC
    you need to restore the attributes of the DC when you are done.

    (2) You should use GetWindowLong to get the controls original
    window procedure (callback). If the control was already subclassed
    (ie. A tooltip control will subclass controls it is attached to)
    you will get the "current" window procedure address, not the original
    , but this is proper because if you used GetClassLong to get the
    "original" Window procedure, and used it in your subclass routine,
    you would "override" any other subclassing that may previously have
    implimented. By using GetWindowLong, you can subclass a control multiple
    times with no problem.

    (3) The original Dialog Editor (that comes with PB) does have
    special functions that must be defined in a custom control for
    the custom controls to be recognized by the Editor. I do not
    know if Visual Studio still supports those functions.

    For more info see the book :

    Windows Custom Controls
    By William Smith and Robert Ward
    Published by R&D Publications, Inc.
    USA

    The book is about 16 bit Windows, but the majority
    of info is still the same for 32 bit windows. There
    are just a few differences in 32 bit windows.

    I couldn't find any books for 32 bit windows about
    building custom controls that are not ActiveX, but
    simply DLLs.


    ------------------
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

    Comment


    • #3
      Hello Chris!


      I am making a button replacment control and I want the font to be
      the same as the dialogs. It states in the Win32 (Rector/Newcomer)
      book that each device context has a default font,brush,pen,etc...
      so if I use the same DC as the controls parent by specifing
      CS_PARENTDC then I should get the same font right? I have tried
      the above and it doesn't work.

      ------------------
      Cheers

      Comment


      • #4
        Mark,
        Since you have Newcomer and Rector hop over to page 225.
        Unfortunately, when a control class is set to CS_PARENTDC
        the only thing it inherits from the parent class is the clipping
        region(client area). What a joke!
        This is actually very eay to test using what I like to call lazy
        controls(e.g. groupbox). Put the groupbox in a window then use a
        large bitmap or large font for its caption. Now select a small
        bitmap or font for it caption(do not redraw the entire window just
        the groupbox). You should now be left with a messed up window.

        ------------------
        Dominic Mitchell
        Dominic Mitchell
        Phoenix Visual Designer
        http://www.phnxthunder.com

        Comment


        • #5
          Mark,

          I can post you the code for subclassing a windows control but I have a toy
          on my web site that does it correctly so a download of about 12k will solve
          that one.
          http://www.pbq.com.au/home/hutch/pbdll50.htm

          The file is subclass.zip

          For a button replacement control, starting from scratch is a lot more
          profitable where modifying an existing control (superclassing) definitely
          has its limitations.

          CreateWindowEx() and a normal WndProc style message handling procedure
          give you the maximum control when designing custom controls. The main trick
          is to use the extra window memory to store settings for each control called
          from the app that uses it.

          You will need to get the swing of SetCapture() / ReleaseCapture() when
          handling button up & down states from any of the WM_(mouse) messages but
          it is reasonably straight forward stuff. You normally send a WM_COMMAND
          message back to the parent so it knows when the button has been pressed
          ands you can dial in more or less any variation you want to get the effect
          you like.

          You have a number of options when drawing the UP and DOWN states of the button,
          roll your own GDI line function to frame the window, BitBlt() bmp images
          for the 2 states, you can use either DrawText() or TextOut() to display text
          on the button face in any color or font you like.

          Once you get the swing of this stuff, you have got under the interface of
          windows and can do basically anything you like.

          Regards,

          [email protected]

          ------------------
          hutch at movsd dot com
          The MASM Forum - SLL Modules and PB Libraries

          http://www.masm32.com/board/index.php?board=69.0

          Comment


          • #6
            Hello All!

            I have already made my custom button control from scratch and here
            is the code for it. The big thing is that I wish I didn't have to
            select the font or setup the colors each time WM_PAINT rolls around

            Code:
            #compile dll
            #register none
            #include "win32api.inc"
            
            rem function declares
            declare function RegisterCustomControls(byval hInstance as long) as long
            declare function UnregisterCustomControls(byval hInstance as long) as long
            declare function InitStrataControls() as long
            
            
            rem main dll entry point
            function LibMain(byval hInstance as long, byval fwdReason as long, byval lpvReserved as long) export as long
                select case (fwdReason)
                    case %DLL_PROCESS_ATTACH
                        function = RegisterCustomControls(hInstance)
            
                    case %DLL_PROCESS_DETACH
                        function = UnregisterCustomControls(hInstance)
            
                    case %DLL_THREAD_ATTACH
                        function = RegisterCustomControls(hInstance)
            
                    case %DLL_THREAD_DETACH
                        function = UnregisterCustomControls(hInstance)
            
                end select
            end function
            
            function InitStrataControls() export as long
            end function
            
            
            rem register custom classes
            function RegisterCustomControls(byval hInstance as long) as long
                local szClassName as asciiz * 80
                local wcex as WNDCLASSEX
            
            
                rem setup class name
                szClassName = "StrataButton"
            
            
                rem setup class structure
                wcex.cbSize = sizeof(wcex)
                wcex.style = %CS_HREDRAW or %CS_VREDRAW or %CS_GLOBALCLASS or %CS_PARENTDC
                wcex.lpfnWndProc = codeptr(StrataButtonProc)
                wcex.cbClsExtra = 0
                wcex.cbWndExtra = 0
                wcex.hInstance = hInstance
                wcex.hIcon = %NULL
                wcex.hIconSm = %NULL
                wcex.hCursor = LoadCursor(%NULL, byval %IDC_ARROW)
                wcex.hbrBackground = %COLOR_APPWORKSPACE
                wcex.lpszClassName = varptr(szClassName)
                wcex.lpszMenuName = %NULL
            
            
                rem register control class
                function = RegisterClassEx(wcex)
            end function
            
            rem unregister custom classes
            function UnregisterCustomControls(byval hInstance as long) as long
                local szClassName as asciiz * 80
            
            
                rem setup class name
                szClassName = "StrataButton"
            
            
                rem unregister control classes
                function = UnregisterClass(szClassName, hInstance)
            end function
            
            
            rem custom control handler
            function StrataButtonProc(byval hWnd as long, byval message as long, byval wParam as long, byval lParam as long) export as long
                local szMessage as asciiz * 64
                local ps as PAINTSTRUCT
                local rc as RECT
                local hdc as long
            
            
                select case (message)
                    case %WM_LBUTTONDOWN
                        SetCapture hWnd
                        SetWindowLong hWnd, %GWL_USERDATA, %TRUE
                        InvalidateRect hWnd, byval %NULL, %TRUE
                        exit function
            
            
                    case %WM_LBUTTONUP
                        ReleaseCapture
                        SetWindowLong hWnd, %GWL_USERDATA, %FALSE
                        InvalidateRect hWnd, byval %NULL, %TRUE
                        SendMessage GetParent(hWnd), %WM_COMMAND, maklng(GetWindowLong(hWnd,%GWL_ID),%BN_CLICKED), hWnd
                        exit function
            
            
                    case %WM_PAINT
                        hdc = BeginPaint(hWnd, ps)
                        GetWindowText hWnd, szMessage, sizeof(szMessage)
                        GetClientRect hWnd, rc
                        SelectObject hdc, SendMessage(GetParent(hWnd),%WM_GETFONT,0,0)
                        SetTextColor hdc, GetSysColor(%COLOR_BTNTEXT)
                        SetBkColor hdc, GetSysColor(%COLOR_BTNFACE)
                        FillRect hdc, rc, GetSysColorBrush(%COLOR_BTNFACE)
                        DrawText hdc, szMessage, len(szMessage), rc, %DT_CENTER or %DT_SINGLELINE or %DT_VCENTER
            
                        if GetWindowLong(hWnd,%GWL_USERDATA) then
                            DrawEdge hDC, rc, %BDR_SUNKENINNER, %BF_RECT
                        else
                            DrawEdge hDC, rc, %BDR_RAISEDINNER, %BF_RECT
                        end if
            
                        EndPaint hWnd, ps
                        exit function
            
                end select
            
            
                rem call default window procedure
                function = DefWindowProc(hWnd, message, wParam, lParam)
            end function

            Does anybody have any pointers for me on this code?

            ------------------
            Cheers

            Comment


            • #7
              Neat code! However, I have a couple of points for you to ponder... please think of these as *constructive* comments!

              1. I don't believe it is necessary to unregister the window class when a thread detaches, and likewise when a thread attaches. Window classes are process-local, not thread-local. Other than those issues, the rest of the code looks fairly thread safe (although I;ve not gone through it thoroughly). You should definitely test any custom control in a multi-threaded app before unleashing it upon the world...

              For the lurkers: While Windows automatically unregisters window classes on app termination, it is good that you have handled this in your code because DLL's that register window classes MUST unregister their window classes during process detach under Windows 2000/NT.

              2. Your custom control callback returns zero (by default) for several messages - this may or may not need to be addressed.

              3. Your control does not support disabled/grayed state.

              4. There is no handling for "external" code to query the state of the custom control (ie, query whether the control is Pressed, Normal or Disabled, etc).

              5. You could add support for %WM_SETREDRAW - handling this may be overkill, so YMMV.

              6. You could add support for double clicks and "pushlike" styles.

              7. There is no error checking in your code at all.

              8. You are selecting a font into the devioce context during %WM_PAINT but do not restore the original font handle.

              9. When painting the button, you could send a %WM_CTLCOLORBUTTON message to the parent window procedure - that way you could allow the user to customize the control's color scheme. Since standard buttons do not actually allow the color scheme to change, you'll add an "important" feature the overcomes the standard buttons' limitation.

              I hope that these ideas are of some help. Remember, I said they were *constructive* comments! If you can cater to all of these, you'll have a great custom button control!

              PS: did I mention support for BMP images? <cheeky grin!>



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

              Comment


              • #8
                thanks!


                *constructive* comments well taken! in our application, screen space
                is at a premium so i have to make new controls for every thing in
                order to reclame as much space as possible. for example, my button
                uses only a single line border verses the normal inner and outer lines.
                because this control is only going to be used for its original purpose
                i have kept the "styles" and "features" to a minimum. this is my first
                crack at custom controls and it seems to be going well. i really
                appreciate all the advice i've been given in these forums. i feel
                like i should be paying you all tuition fees!

                btw: did any of you see my contribution to the source code forum?

                "convert vs resource.h files" http://www.powerbasic.com/support/pb...ad.php?t=22838

                this utility converts the resource.h files generated by visualstudio
                into a powerbasic resource.inc file. i made it because i was sick
                of editing every time i made a change. now all i have to do is...

                rc [resource.rc]
                pbres [resource.res]
                makinc [resource.h]

                wish: maybe the nice powerbasic people will put this into there compiler?




                ------------------
                cheers

                Comment


                • #9
                  Mark,

                  Nice code example to which we can build on, Thank You! The fact you are using it
                  for your own use 'vs' distributing the DLL, I personally feel that this
                  would be better served if it was an include file. The code is small, why
                  have an extra DLL. I have already cut and pasted it into my include
                  file and made some modifications to it.

                  To give the button a nicer effect when the button is depressed, offset the
                  text by a couple of pixels.

                  <edited, added some more effects to make it look like a real normal button>
                  <from here you can modify color,font,add bitmap etc, good starting point >

                  Code:
                  '----------------------------------------------------------------------------
                  'rem custom control handler
                  '----------------------------------------------------------------------------
                  function StrataButtonProc(byval hWnd as long, byval message as long, byval wParam as long, byval lParam as long) export as long
                   
                      local  szMessage as asciiz * 64
                      local  ps        as PAINTSTRUCT
                      local  rc        as RECT
                      local  rc2       as RECT
                      local  hdc       as long
                      static fDown     as long
                      static fGotFocus as long
                      local  pt        as POINTAPI
                   
                      select case (message)
                            
                          case %WM_KILLFOCUS
                   
                              'remove focus band
                              InvalidateRect hWnd, byval %NULL, %TRUE
                              fGotFocus = 0
                              function = 0
                              exit function
                   
                          case %WM_MOUSEMOVE
                              pt.x = LOWRD( lParam)
                              pt.y = HIWRD( lParam)
                   
                              'is the mouse still inside our button while SetCapture
                              if fDown = 1 Then
                                   GetClientRect hWnd, rc
                                   IF  PtInRect( rc, pt.x,pt.y ) = %FALSE THEN
                                       ReleaseCapture
                                       fDown = 0
                                       InvalidateRect hWnd, byval %NULL, %TRUE
                                   End IF
                              end if
                   
                          case %WM_LBUTTONDOWN
                              SetCapture hWnd
                              fDown = 1
                              fGotFocus = 1
                              SetFocus hWnd
                              InvalidateRect hWnd, byval %NULL, %TRUE
                              function = 0
                              exit function
                   
                          case %WM_LBUTTONUP
                              ReleaseCapture
                              fDown = 0
                              InvalidateRect hWnd, byval %NULL, %TRUE
                              SendMessage GetParent(hWnd), %WM_COMMAND, maklng(GetWindowLong(hWnd,%GWL_ID),%BN_CLICKED), hWnd
                              function = 0
                              exit function
                   
                          case %WM_ERASEBKGND
                               FUNCTION = 0
                               EXIT FUNCTION
                   
                          case %WM_PAINT
                   
                              hdc = BeginPaint(hWnd, ps)
                   
                              GetWindowText hWnd, szMessage, sizeof(szMessage)
                              GetClientRect hWnd, rc
                              GetClientRect hWnd, rc2
                   
                              SelectObject hdc, SendMessage(GetParent(hWnd),%WM_GETFONT,0,0)
                   
                              SetTextColor hdc, GetSysColor(%COLOR_BTNTEXT)
                              SetBkColor hdc, GetSysColor(%COLOR_BTNFACE)
                   
                              FillRect hdc, rc, GetSysColorBrush(%COLOR_BTNFACE)
                   
                              if fDown = 1 Then
                                  'button down
                                  rc2.nLeft = rc.nLeft +2
                                  rc2.nTop  = rc.nTop  +2
                                  DrawText hdc, szMessage, len(szMessage), rc2, %DT_CENTER or %DT_SINGLELINE or %DT_VCENTER
                                  DrawEdge hDC, rc, %EDGE_SUNKEN , %BF_RECT
                   
                              else
                                  'button up
                                  DrawText hdc, szMessage, len(szMessage), rc, %DT_CENTER or %DT_SINGLELINE or %DT_VCENTER
                                  DrawEdge hDC, rc, %EDGE_RAISED  , %BF_RECT
                              end if
                   
                              If fGotFocus = 1 Then
                                  InflateRect rc,-4,-4
                                  DrawFocusRect hDC,rc
                              End IF
                   
                              EndPaint hWnd, ps
                               
                              function = 0
                              exit function
                   
                      end select
                   
                      'rem call default window procedure
                      function = DefWindowProc(hWnd, message, wParam, lParam)
                  end function
                  And thanks again for the code!
                  Best regards, Jules
                  mailto:[email protected][email protected]</A>


                  [This message has been edited by Jules Marchildon (edited November 24, 2000).]

                  Comment


                  • #10
                    Hey Jules!


                    I just remembered these from the MSDN...

                    Code:
                    CopyRect rc2, rc
                    OffsetRect rc2, 2, 2
                    You could use CopyRect instead of calling GetClientRect two times
                    and OffsetRect to offse the text RECT. I was using them before to
                    do what you suggested but I got lazy when I redid the control (smile).


                    There are a couple of reasons for going with a DLL for my controls.

                    1. Very easy to update the controls without having to recompile all
                    the source code.

                    2. If I want to try a "new look" with my application all I have to
                    do is change the DLL to one with diffrent button drawing code.

                    3. Just becuase I wanted to learn about DLL's.

                    I am really happy that I can finally give somthing back to this
                    forum instead of always being the one to ask questions!




                    ------------------
                    Cheers

                    Comment


                    • #11
                      Compliments Mark,

                      A nicely done piece of code, we need a custom control expert floating
                      around here as a lot of people are interested in this type of code
                      so keep up the good work.

                      regards,

                      [email protected]

                      ------------------
                      hutch at movsd dot com
                      The MASM Forum - SLL Modules and PB Libraries

                      http://www.masm32.com/board/index.php?board=69.0

                      Comment


                      • #12
                        Thank you!


                        I also have some code that subclasses an edit control to allow it
                        to use the mouse wheel. This only works with numerical edit boxes
                        but It does save on keyboard typing. Just click on the control and
                        scroll the mouse wheel and watch the numbers roll up and down.

                        If anybody is interested please let me know and I'll be happy to
                        post the code here!



                        ------------------
                        Cheers

                        Comment

                        Working...
                        X