Announcement

Collapse
No announcement yet.

Superclassed Ownerdrawn Button

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

  • BOB MECHLER
    replied
    Also thanks to Dominic for the excellent reference to the container control of the textbox and lookup button. The different approaches contributed for DDT and SDK warrant study.

    Bob Mechler

    Leave a comment:


  • BOB MECHLER
    replied
    Before I saw your post I came up with this. It seems to work ok. Not sure I needed all the parameters but I did get gradient ownerdrawn buttons.

    Code:
    CASE %WM_DRAWITEM
    
            lDisPtr = CBLPARAM
            IF @lDisPtr.CtlType = %ODT_BUTTON THEN
              CALL PaintGRLabelBg(CBHNDL,CBCTL,CBWPARAM,CBLPARAM)    
              FUNCTION = %True                       
            END IF


    Code:
        SUB PaintGRLabelBg(pCBHNDL&,pCBCTL&,pCBWPARAM&,pCBLPARAM&,pSelected&)
           LOCAL lZStr  AS ASCIIZ * 64 
           LOCAL Str AS STRING
           LOCAL V_hFont&
           LOCAL V_hCtl&
           LOCAL V_rc AS Rect
           LOCAL V_hdc&
           CONTROL GET TEXT pCBHNDL&,pCBCTL& TO Str
           CONTROL HANDLE  pCBHNDL&,pCBCTL& TO V_hCtl&     ' get the handle to the control
           V_hdc& = GetDc(V_hCtl&)
          ' this paints the actual label body
           LOCAL Xin AS LONG
           LOCAL Yin AS LONG
           DIM vert(1) AS TRIVERTEX
           DIM gRect AS GRADIENT_RECT
           GetClientRect V_hCtl&, V_rc
           Xin = V_rc.nRight - V_rc.nLeft            ' don't want to go all the way to the right, or we'll paint over the border
           Yin = V_rc.nBottom - V_rc.nTop            ' don't want to go all the way to the bottom, or we'll paint over the border
           Temp& = PB_BACK&
           cRed&   = Temp& MOD 256
           cGreen& = (Temp& \ 256) MOD 256
           cBlue&  = ((Temp& \ 256) \ 256) MOD 256 
           'cRed& = 192
           'cGreen& = 192
           'cBlue& = 60
           vert(0).x      = 0                      ' start at pixel 2 so we don't paint over the border
           vert(0).y      = 0                     ' start at pixel 22 so that we don't paint over the tabs
           vert(0).Red    = SetColor(cRed&)
           vert(0).Green  = SetColor(cGreen&)
           vert(0).Blue   = SetColor(cBlue&)
           vert(0).Alpha  = &h0000
           vert(1).x      = Xin
           vert(1).y      = Yin
           vert(1).Red    = SetColor(cRed& - 60)
           vert(1).Green  = SetColor(cGreen& - 60)
           vert(1).Blue   = SetColor(cBlue& - 60)
           vert(1).Alpha  = &h0000
           gRect.UpperLeft  = 0
           gRect.LowerRight = 1
           RoundRect V_hdc&,V_rc.nLeft - 1,V_rc.nTop - 1,V_rc.nRight + 1,V_rc.nBottom + 1,4,4
           GradientFill V_hdc&, vert(0), 2, gRect, 1, %GRADIENT_FILL_RECT_v
           SelectObject V_hdc&,V_hFontCap&
           lZstr = Str                            ' copy the text to the nul terminated string
           SetBkMode V_hdc&, %Transparent             ' set the bg for transparent
           SetTextColor V_hdc&, %BLACK 'GetSysColor(%COLOR_BTNTEXT)   ' set the foreground color to black
           TextOut V_hdc&, V_rc.nLeft + 4 , V_rc.nTop + 3 , lZStr, LEN (lZStr)  'draw the text
        END SUB
    Bob Mechler

    Leave a comment:


  • Chris Boss
    replied
    I like the idea mentioned about reflecting (forwarding) the WM_DRAWITEM message to the controls window procedure (if superclassed).

    ie.

    %BM_MyDraw = %WM_USER + 400

    Then in response to the WM_DRAWITEM message do this:

    Code:
    CASE %WM_DRAWITEM
       LOCAL pDI AS DRAWITEMSTRUCT PTR 
       pDI =  CBLPARAM
       IF @pDI.CtlType = %ODT_BUTTON THEN
          FUNCTION=SendMessage(@pDI.hwnditem, %WM_DRAWITEM, CBWPARAM, CBLPARAM)
         EXIT FUNCTION
       END IF
    This way you can handle all the drawing in the superclassed controls window procedure which is handy if you put it in a DLL.

    Leave a comment:


  • Chris Holbrook
    replied
    Originally posted by Dominic Mitchell View Post
    This stuff has been discussed at length before, see for example
    http://www.powerbasic.com/support/pb...custom+control
    Great link, wish I had found it a year ago!

    Originally posted by Dominic Mitchell View Post
    If the control is subclassed/superclassed, and if you don't want to use a container, you can
    also use message reflection. That is, the WM_DRAWITEM message is sent from the parent window procedure to the subclassed/superclassed procedure for the control.
    I use a global array of window handles and original classnames so that control can be passed back to the original classproc when appropriate.

    Leave a comment:


  • Dominic Mitchell
    replied
    Owerdrawn tab control.
    Code:
    ODT_TAB = 101

    Leave a comment:


  • Dominic Mitchell
    replied
    GetClassName

    You can test any window handle to see what window class it is. It is a great way to test in WM_DRAWITEM what class generated the message.
    But if he wants to know if it is a button, all he has to do is examine the value of the CtlType
    member of the DRAWITEMSTRUCT struct. GetClassName fails if the control is superclassed, this
    test does not.

    Leave a comment:


  • Dominic Mitchell
    replied
    This stuff has been discussed at length before, see for example
    http://www.powerbasic.com/support/pb...custom+control

    If the control is subclassed/superclassed, and if you don't want to use a container, you can
    also use message reflection. That is, the WM_DRAWITEM message is sent from the parent window
    procedure to the subclassed/superclassed procedure for the control.

    Leave a comment:


  • Michael Mattias
    replied
    Code:
    LOCAL pDI AS DRAWITEMSTRUCT PTR 
    
    .....
        CASE %WM_DRAWITEM
           pDI =  CBLPARAM
          SELECT CASE @pDI.CtlId
               CASE %BUTTON_1 
                   CALL ButtonOneProc
    
                CASE %BUTTON_2 
                   CALL ButtonTwoProc
      
            .....

    Leave a comment:


  • Chris Boss
    replied
    Bob,

    A very useful API function is:

    GetClassName

    You can test any window handle to see what window class it is. It is a great way to test in WM_DRAWITEM what class generated the message.

    Leave a comment:


  • BOB MECHLER
    replied
    I like it Chris.

    I'll also need to add a test for buttons since WM_DRAWITEM handles ownerdrawing of several other controls.

    IF @lDisPtr.CtlType = %ODT_BUTTON for the drawing routine since I also want to owerdraw the tab labels on my tab control.

    Bob Mechler

    Leave a comment:


  • Chris Boss
    replied
    Bob,

    An even simpler method would be to simply write a subroutine which does the drawing for ownerdraw buttons which can be called in the Dialogs WM_DRAWITEM message. That message will only be sent for ownerdraw controls and not other controls.

    ie.

    Code:
    CASE %WM_DRAWITEM
        FUNCTION=MyButtonDrawRoutine(CBWPARAM, CBLPARAM)
        EXIT FUNCTION
    Do all your drawing in the routine you write.

    To make it more powerful, have the routine read back the text from the button control (send WM_GETTEXT to the control).

    You can parse out the text and embed macros to define the drawing process.

    For example embed macros like this:

    "My Buttons Text {{RED}}"

    By embeding macros in double brackets, you can parse out any macros and leave only the text. This way you can then define the macros for each button, such as colors, font, etc. in the buttons text . This makes it easy to use PBForms to design the dialog. You simply write the buttons text plus macros in the designers text field for the controls.

    Leave a comment:


  • Michael Mattias
    replied
    ahead and try it for a normal(e.g. BS_PUSHBUTTON) button. I guarantee you are in for a shock.
    I did start playing with it.

    Shock? No.

    Chagrin at forgetting that when you try to "make your own button" control a lot of the info you need to make it act like a button is not readily available? Yes.

    While the focus rectangle handling is the same for BS_OWNERDRAW 'standard' buttons and "superclass-created" buttons, with the latter you have to keep track of the "pushed state" yourself, which necessitates processing the WM_LBUTTONUP notification, as well as WM_Keyup/keydown looking for <spacebar> and storing that current pushed state.. as well as send notifications to the parent for some of those actions.

    On the plus side, I think its only the pushed state is what you have to monitor, as you can always ask for the size and location (GetWindowRect) and the focused state (compare GetFocus() with this hwnd).

    Darned, I should have (but didn't) try BM_GETSTATE message. I wonder if that might not make life easier. I'll try that later today or maybe tomorrow and see if I can post a little baby demo.

    Ok, so the button is a bad example. Let's go back to the label, that's a lot cleaner.


    MCM

    Leave a comment:


  • BOB MECHLER
    replied
    The container class? Is there an example? I've never used one.

    I didn't have any luck using WM_PAINT in the SuperClass of the button or label. It either GPF'd or drew it partially.

    There are some really nice examples I've found of gradient buttons (which is what I really want). None of them are superclassed though.

    I want to superclass a gradient button in order to give it additional default functionality. One would be to call a function I'll call ValidateForm(param,param,....) which will have the same name in all programs just code that returns the id's of fields not yet matching that program's needs or that it's ready to save the record or do the processing. That way the SuperClassed button will look exactly the same and be reusable by just supplying the location and size.

    I'll look for some container class examples next.

    Bob Mechler

    Leave a comment:


  • Chris Boss
    replied
    When superclassing a control (a new class based on an existing class, which is different than subclassing) you have to be careful in how you process the window procedure messages. You have to make sure you don't work against the original class and how it works.

    If you want to build a custom class using ownerdraw, it may be better to build your own custom class (not a superclass) which acts as a container. Then have the container control create the ownerdraw control as a child of itself. Then the container class can handle the ownerdraw messages from its ownerdrawn child control.

    So the child control can get messages sent to its container, you can have the container class forward messages to the child control. This way the container could support all the messages of the child.

    Leave a comment:


  • Dominic Mitchell
    replied
    Don't use OwnerDraw style and just do it on WM_PAINT in superclass procedure.

    This is kind of the whole idea behind superclassing... "It's just like <something> except for (in this case, the WM_PAINT processing).
    These statements, I am afraid, are not correct by any stretch of the imagination.

    Go ahead and try it for a normal(e.g. BS_PUSHBUTTON) button. I guarantee you are in for a shock.

    Leave a comment:


  • Michael Mattias
    replied
    Depends on what you are trying to accomplish with that label.

    Not that you can do a hell of a lot with a label anyway, but hey, it's your label.

    Leave a comment:


  • BOB MECHLER
    replied
    Thanks. And probably switch to a Superclassed Label instead of a button, right?

    Bob Mechler

    Leave a comment:


  • Michael Mattias
    replied
    Don't use OwnerDraw style and just do it on WM_PAINT in superclass procedure.

    This is kind of the whole idea behind superclassing... "It's just like <something> except for (in this case, the WM_PAINT processing).

    No, I have no clue if using 'DDT' is going to interfere with this, but it will work just fine with SDK-style windows.

    Leave a comment:


  • BOB MECHLER
    started a topic Superclassed Ownerdrawn Button

    Superclassed Ownerdrawn Button

    What event in the SuperClass CallBack procedure can be used to draw it?

    I know how to use WM_DRAWITEM in a dialog callback. Do owernerdrawn buttons, even if superclassed, have to rely on the parent control's WM_DRAWITEM event?

    Bob Mechler
Working...
X