Announcement

Collapse
No announcement yet.

Comments on Oval Button code in source code forum !

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

  • Chris Boss
    replied
    I would think a lot depends on whether the custom control is expected
    to be used by others or not.

    To make a control more easily integrated it is best to "emulate"
    the techniques found in the standard windows controls (as well as the
    common controls). API programmers will always be familiar with how
    to work with the standard controls, so if you follow their example
    your control will be easier to use.

    Now as far as ActiveX controls (and VCLs, etc) they really don't count
    in the discussion since they are a totally different animal.

    Most languages use the standard controls which work as I explained
    earlier, yet they can handle changing "properties" without a problem.
    Likely it is because ActiveX, etc. are "wrapper" functions and the
    wrapper becomes the calling application and not the real app itself.
    The wrapper (not the control in the dll) handles the creating and
    destroying of resources, which is quite acceptable. The purpose of
    a wrapper function is to "shield" the programmer from mundane
    coding required for an action. The creation and destruction of
    resources via the wrapper function is only reasonable.

    In discussing custom controls, my perspective is "not" that of an
    activeX control or using C++ or VB. It is from the perspective of
    a PB user. PB programmers must work with custom controls as DLLs
    (and not any other type of component model) and write code similiar
    to "pre MFC" C (not C++) API programmers. Personal I think the DLL
    compnent model is quite simple and clean and it produces tighter
    and smaller code.

    The handling of control processes like the standard controls is
    actually quite efficient ! The sendmessage method of communication
    is very effective, since you can pass pointers to structures if
    you need more complex parameter passing. This is how windows itself
    works. Windows is "very" Pointer/structure oriented. The use of
    structures with messages allows "changing" the structure in future
    versions without making previous code obsolete. Many windows
    functions use a "flag" member in the structure to indicate what
    members in the structure exist.

    As a third party developer, I not only am developing custom controls
    but I am encouraging others to develop them as well. By emulating
    the standard techniques found in standard controls, it makes it easier
    for the programmer to get up to speed using new controls.

    I am sure much of what I have discussed sounds like "old 16 bit"
    methodology, but likely this is because most 32 bit compilers today
    have other component models other than DLLs and they are the
    standard. The limited number of third party components available
    in DLL form testifies to this. Yet, it is strange that despite all
    the newer component models, the DLL is at the "core" of Windows
    itself !

    To encourage the typical PowerBasic "style" of "small, fast and tight"
    code, I think the DLL is a "very" useful component model which
    will not become obsolete.

    I admit the above is "only" my own opinion and "not" the gospel truth.
    Yet it makes sense to me to follow the "pattern" set by the
    operating system itself when writing custom controls.


    ------------------

    Leave a comment:


  • Steve Hutchesson
    replied
    I am wary of trying to set rules when designing custom controls, I have on
    and off written them on a needs basis since windows 3.0 and it depends a
    lot on what you need. The can be written in DLLs or as library modules or
    just coded into an application.

    The epitomy of a custom control is one that can be used directly as an
    object which means that it can be plugged in and have parameters passed to
    it without having to think of how it works, only what it does.

    Using the extra window memory is standard stuff and it works well but one
    of the old tricks if the control does not require a title bar is to use
    the memory available for storing extra information. You need to filter
    the WM_SETTEXT message so it cannot mess up the memory but it is a handy
    way of storing a lot of settings if you know how to access the correct
    parts of the memory.

    It would be normal practice to set up the GDI component at startup and
    de-allocate it on exit of each instance of the control and where a
    normal repaint is required, it is usually done on the fly as the control's
    code is called so that the allocation & de-allocation is handled
    automatically.

    It depends on what the control does as to whether it uses SDK style
    messaging to change what is needed or alternatively, individual functions
    that are attached to the control's code to do certain things that messaging
    may not do well, particularly, if you need to pass a lot of parameters,
    messaging is not the best way to do it.

    The old rules apply with memory and GDI objects, what goes up must come
    down so when you allocate something, TEST the return value of the
    de-allocation to make sure you DO deallocate it or you will end up with
    memory leaks.

    Trying to track down a memory leak after the control is up and running is
    like trying to pick fly specks out of pepper. It is always far more
    reliable to write the allocation and de-allocation code at the same time
    and make sure it works before you do the rest of the control.

    Regards,

    [email protected]

    ------------------

    Leave a comment:


  • jcfuller
    replied

    Chris,

    When dealing with window's standard controls I fully agree but how many commercial
    32 bit dll controls are there on the market. Very few, and the ones I am familiar with all
    have methods to change fonts and colors.
    Farpoint's Spread is the one I am very familiar with and it has a number of functions for
    font and color selection.

    All ActiveX and VCL controls have methods or Properties for them too.

    In all of these instances it is the controls responsibility to create and destroy the fonts, pens,
    and brushes not the calling program. This is the way I feel it should be.

    I am aware that the Common Control DLL controls behave like standard controls but I think it's more of a carry
    over from the 16bit world. The 32bit ActiveX wrappers for VB an VC handle all the font, pen, and brush duties.

    James




    ------------------

    Leave a comment:


  • Borje Hagsten
    replied
    Thanks for the info, Chris. I think the difference here is that
    I never have intended to write "real" custom controls in the form
    of DLL's, whatever. My only goal is to write include files for PB,
    that can make it easy to embed neat stuff in our code a fast way.
    Lots of code, one EXE, no DLL's, all in a simple way..

    Still, things need to be done in a proper way. The problem is that
    it often is very hard to find out exactly what is the proper way.
    So many samples, so many different ways. You have been most helpful
    here and I really appreciate it, Chris (and Lance). I really do.

    Just playing around with it so far, but I have some more serious things
    in the makes as soon as I get time to finish them up, like a very fast
    virtual listbox, an unlimited text editor with fast syntax color
    possibilities and a VB-style picture box, etc. If I ever can find
    time to finish them up, that is. Always so much to do, always so
    very little time to do it in..


    ------------------

    Leave a comment:


  • Chris Boss
    replied
    Borje;

    Some points on working with the GDI :

    (1) Since controls "usually" share the DC or the Parent window,
    you must "always" select the original objects back into the DC
    after you finish drawing ! If you create a pen and select it into
    the DC and draw with it, you must select the original pen back in
    again, so the parent DC is as it was before you used it. Controls
    use the Parent DC because this is faster than having their own.

    (2) DEFAULT_GUI_FONT is exactly the same thing as ANSI_VAR_FONT.
    (which is MS Sans Serif 8 point)

    (3) Most controls will send the WM_CTLCOLORxxx messages (ie. WM_CTLCOLORSTATIC)
    to the parent Dialog to see if the parent will set the colors. When
    this is done, the parent will send the control a Brush handle,
    which you can use to draw your control. Do "not" destroy that
    Brush ! The Parent created it, and the parent is expected to
    destroy it.

    (4) If your control creates its own resources (brushes, bitmaps, etc)
    then your control "must" destroy them in the WM_DESTROY message.

    (5) If your control sends one of the WM_CTLCOLORxxx messages to
    the Parent, you should use a "Stock Object" for the colors, if the
    parent doesn't pass a color brush back. This way you don't have to
    worry about destroying any objects.




    ------------------

    Leave a comment:


  • Borje Hagsten
    replied
    Wow, GDI objects. Not that easy to get to grips with, but I feel
    I'm getting closer to understanding them. I looked through all the
    samples I could find here and there are many versions on how to
    deal with them, some probably wrong. Looked at MS recommendations
    and samples and found that for pens, they recommend something like:
    Code:
    LOCAL ps AS PaintStruct, hDC AS LONG
    
    hDC = BeginPaint(hWnd, ps)  
      hPen  = CreatePen(style, width, color)
      hPenOld = SelectObject(hDC, hPen)
    
      'Draw something
      
      SelectObject hDC, hPenOld
      DeleteObject hPen
    EndPaint hWnd, ps
    Where the code between BeginPaint and EndPaint preferable is placed
    in a sub-routine. Okay, so I have changed my sample in the source
    code section to follow this instead, plus changed the font handling
    so it uses a StockObject font handle instead. Easier and safer to
    deal with. Also added a check for the up-down flag in WM_MOUSEMOVE
    event to avoid unnecessary redrawing operations.

    A question about stock object font handles arised. Win32api.hlp
    says DEFAULT_GUI_FONT only applies to Win95(98)(ME?). I have used
    ANSI_VAR_FONT instead, but I'm not sure that is 100% correct for
    button text?


    ------------------

    Leave a comment:


  • Chris Boss
    replied
    JC;

    Standard Windows controls respond to things like the WM_SETFONT
    message or the WM_CTLCOLORxxx (there are multiple color messages.
    xxx stands for the different types) messages where you pass a
    font handle or brush handle. The standard controls will "not"
    destroy those handles (fonts, brushes). You must explicitly
    destroy them in your apps code.

    When writing a custom control, you must assume that SDK coders
    will assume this and that they will destroy the handles themselves.

    If the controls did destroy these resources, you would not be able
    to use a single brush or font with multiple controls (but you
    can). If one control destroyed the font or brush, then they couldn't
    be used again by another control in another dialog.

    When you create a Window using CreateWindow , the default font used
    is the System Font. Windows provides 5 different stock fonts, which
    should never be destroyed (System font, fixed system font, terminal
    font, Ansi font and Ansi Fixed font). Multiple stock brushes are
    available (ie. White, black, null).

    A control like the Button control which does use other colors
    (the end users system wide color scheme), also doesn't let you
    change the colors. It does not respond to the color messages. Likely
    it can create the color brushes and destroy them since there is
    no risk of them being modified by the app creating the button.

    There is a reason why Windows has such a thing as Stock objects !

    If a custom control responds to any "standard" message (ie. WM_SETFONT,
    WM_CTLCOLORSTATIC) that passes a handle (Bitmap, font, brush) it
    must not destroy them !


    ------------------

    Leave a comment:


  • jcfuller
    replied
    Chris,
    Now, if the custom control "allows external" modification of some
    of the GDI resources (ie. It responds to the WM_SETFONT message), it
    should "not" destroy those GDI resources in the WM_DESTROY message !

    The reason is, if the app using the custom control is going to
    change the resources used by the control, like fonts, brushes,
    parent app is responsible for destroying those resources.
    "External modifications"?
    You are just asking for trouble (leaks) going this route. I would have
    a Change_Font Method which would be called and it in turn would call the
    Change Font Dialog. Same for colors.

    This is why many custom controls simply use "Stock Objects"
    (ie. brushes and fonts) by default, rather than create them.
    This way the resources don't have to be destroyed and if the
    parent app changes those resources to created resources, the
    parent is responsible for destroying them, not the control.
    Examples??

    James




    ------------------

    Leave a comment:


  • Chris Boss
    replied
    Borje;

    Another note :

    If a custom control creates its own GDI Resources, it should
    always destroy them in the WM_DESTROY message.

    Now, if the custom control "allows external" modification of some
    of the GDI resources (ie. It responds to the WM_SETFONT message), it
    should "not" destroy those GDI resources in the WM_DESTROY message !

    The reason is, if the app using the custom control is going to
    change the resources used by the control, like fonts, brushes,
    parent app is responsible for destroying those resources.

    This is why many custom controls simply use "Stock Objects"
    (ie. brushes and fonts) by default, rather than create them.
    This way the resources don't have to be destroyed and if the
    parent app changes those resources to created resources, the
    parent is responsible for destroying them, not the control.


    ------------------

    Leave a comment:


  • Borje Hagsten
    replied
    Ah, thank you ever so much , Lance. It helps a lot. GDI stuff has
    always been my weak side. Often feel like I'm fumbling around in
    the dark there so I must learn more about it.

    Changed the code as suggested and also had to change the handling
    of a few pens. Think I've got it right now. At least, running it
    extensively against a resource meter shows no leaks, so..


    ------------------

    Leave a comment:


  • Lance Edmonds
    replied
    Note: I've not examined the brushes and pens objects to see if they are cleaned up, but here is what you need to do to sort out the font:

    1. In OvalBtnProc() define a new variable
    LOCAL hFontOld AS LONG

    2. in the %WM_SETFONT handler, change the SetWIndowLong line:
    IF wParam THEN SetWindowLong hWnd, 4, wParam 'wParam is Font handle

    3. Change the font "loader" in %WM_PAINT
    IF hFont THEN hFontOld = SelectObject(hDC, hFont)

    4. At the end of %WM_PAINT, change the DeleteObject() code:
    IF hFontOld THEN SelectObject hDC, hFontOld

    Now, with these changes, the originating code becomes responsible for deleting the font that it creates, as is normal with any control.

    I hope this helps... don't forget to closely go over the other GDI objects for similar problems...


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

    Leave a comment:


  • Lance Edmonds
    replied
    There are still a couple of problems with the code though...

    1. Do NOT use the %WS_EX_CONTROLPARENT extended style with the CONTROL ADD statements, as this "negates" the %WS_TABSTOP style.

    2. There are some problems with font handling as the font changes when focus changes, and the font handles are not being cleaned up correctly in %WM_PAINT.

    I hope this helps!


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

    Leave a comment:


  • Borje Hagsten
    replied
    Yes, I'm beginning to understand just how useful those extra bytes
    can be, especially for lists, edit and grid controls, etc. I haven't
    really looked into the cc world that much yet, but suddenly realized
    how useful even the simpliest custom control can be for embedding
    into PB code. Thanks a lot for the lesson, Chris - much appreciated! :-)

    Example: If one often uses a special type of label with for example
    3D borders and a special background bitmap or color, etc, one can
    save a lot of time by creating it under a separate class name, complete
    with event handlers and brushes for the drawing operations, etc.

    Simply save it all as an include file and use #INCLUDE and CONTROL ADD
    or CreateWindow(Ex) to embed it in any program. The control takes care
    of itself from there on and we no more have to reconstruct everything
    over and over again, plus of course the code will be a lot easier to
    both read and maintain. Sort of like embedded runtimes, so to speak.. :-)

    The oval button example was my first attempt at this. Not very useful
    maybe and I'm not 100% sure about it yet (seems to work fine though),
    but I think it's a pretty good base for any kind of customized buttons.


    ------------------

    Leave a comment:


  • Chris Boss
    replied
    Borje;

    If you ever need to store more than just Longs for a custom
    controls unique data, the Window bytes can be used to store
    a Handle returned from the windows Global, Local and Virtual
    memory functions. In the WM_CREATE message you create the
    special data storage (ie string data or a UDT) using the Global
    memory functions and put the handle returned in one of the Longs
    in the extra bytes. When needed you get the handle from the extra
    bytes, lock the memory using the handle, access the memory, unlock
    the memory. When the WM_DESTROY message is processed you then
    Free the global memory.

    It is actually quite easy !

    You can store complex UDTs this way.

    A Window should not use more than 40 extra bytes according to some
    info I read somewhere. This means you shouldn't create more than
    10 (4 bytes each) Longs of window data. If you need more, then
    use the Global memory technique described above.



    ------------------

    Leave a comment:


  • Borje Hagsten
    replied
    Me again. Have changed OVALBUTN.INC in the source code section
    to use the cbWndExtra member of WndClass for storing unique data,
    after tips from Chris Boss - Thank you very, very much, Chris. It
    seems to work fine now.. I think.


    ------------------

    Leave a comment:


  • Borje Hagsten
    replied
    Thank you Chris. Lots of interesting things to learn there. I'll
    have a look at it and see what needs to be fixed. At least it's a
    start. :-)

    Funny, I've been testing it quite a lot here and it has worked fine
    all the way, but I can see what you mean and where it may go wrong.


    ------------------

    Leave a comment:


  • Chris Boss
    replied
    Code:
    ' -------------------------------------------------------------------------------------------
    '                           Copyright Christopher R. Boss, 2000
    '                                  Alls Rights Reserved
    '                           This code may be used ROYALTY FREE !
    '                           This code is an example of a future product
    '                           which is a Custom Control SDK.
    '                           see    http://ezgui.com 
    '                           for more info.
    '                           Please send feedback about this code to
    '                           [email protected]
    ' -------------------------------------------------------------------------------------------
    
    #DIM ALL
    #DEBUG ERROR OFF
    #COMPILE DLL
    #INCLUDE "win32api.inc"
    
    ' -------------------------------------------------------------------------------------------
    '                          Custom Class ControlClass Constants and Types
    ' -------------------------------------------------------------------------------------------
    %ControlClassExtraData       = 5     ' # of Extra Data Items (Long) for All Custom Window Classes
                                    ' Data Items will be indexed from 1 in GetControlData function
    ' -------------------------------------------------------------------------------------------
    $ControlClassName            = "MY_CUSTOM_CTRL1"
    ' -------------------------------------------------------------------------------------------
    
    ' -------------------------------------------------------------------------------------------
    '                              Universal Global Variables
    ' -------------------------------------------------------------------------------------------
    GLOBAL DLL_Instance&
    
    ' -------------------------------------------------------------------------------------------
    '                           EZGUI Custom Control Library Declares
    ' -------------------------------------------------------------------------------------------
    DECLARE FUNCTION GetControlLong(BYVAL hWnd AS LONG, BYVAL N&) AS LONG
    DECLARE SUB SetControlLong(BYVAL hWnd AS LONG, BYVAL N&, BYVAL V&)
    
    
    ' -------------------------------------------------------------------------------------------
    '                           Custom Control Control Class Declares
    ' -------------------------------------------------------------------------------------------
    DECLARE SUB RegisterControlClass()
    DECLARE SUB ControlClassPaint(BYVAL hWnd AS LONG, PS AS PAINTSTRUCT)
    DECLARE SUB ControlClassDraw(BYVAL hWnd AS LONG)
    DECLARE SUB BuildBitmap(BYVAL hWnd AS LONG, BYVAL CFlag&)
    
    ' -------------------------------------------------------------------------------------------
    ' -------------------------------------------------------------------------------------------
    
    %MY_CUSTOM_MESSAGE      =   %WM_USER+100
    
    ' -------------------------------------------------------------------------------------------
    '                              DLL Entrance - LibMain
    ' -------------------------------------------------------------------------------------------
    
    FUNCTION LibMain(BYVAL hInstance   AS LONG, _
                     BYVAL fwdReason   AS LONG, _
                     BYVAL lpvReserved AS LONG) EXPORT AS LONG
        SELECT CASE fwdReason
            CASE %DLL_PROCESS_ATTACH    ' =1 - Where DLL starts
                DLL_Instance&=hInstance
                RegisterControlClass
            CASE %DLL_THREAD_ATTACH
            CASE %DLL_THREAD_DETACH
            CASE %DLL_PROCESS_DETACH    ' =0 - Where DLL exits
            CASE ELSE
        END SELECT
        LibMain=1
    END FUNCTION
    
    
    ' -------------------------------------------------------------------------------------------
    '                          Custom Control ControlClass Functions / Subs
    ' -------------------------------------------------------------------------------------------
    SUB RegisterControlClass()
    LOCAL wndclass    AS WndClassEx
    LOCAL szClassName AS ASCIIZ * 80
        szClassName            = $ControlClassName+CHR$(0)
        wndclass.cbSize        = SIZEOF(WndClass)
        wndclass.style         = %CS_HREDRAW OR %CS_VREDRAW OR %CS_PARENTDC OR %CS_DBLCLKS OR %CS_GLOBALCLASS
        wndclass.lpfnWndProc   = CODEPTR(ControlClassWndProc)
        wndclass.cbClsExtra    = 0
        wndclass.cbWndExtra    = %ControlClassExtraData*4
        wndclass.hInstance     = DLL_Instance&
        wndclass.hIcon         = %NULL
        wndclass.hCursor       = LoadCursor( %NULL, BYVAL %IDC_ARROW )
        wndclass.hbrBackground = GetStockObject( %WHITE_BRUSH )
        wndclass.lpszMenuName  = %NULL
        wndclass.lpszClassName = VARPTR( szClassName )
        wndclass.hIconSm       = %NULL
        RegisterClassEx wndclass
    END SUB
    
    ' -------------------------------------------------------------------------------------------
    
    FUNCTION ControlClassWndProc(BYVAL hWnd   AS LONG, _
                     BYVAL Msg    AS LONG, _
                     BYVAL wParam AS LONG, _
                     BYVAL lParam AS LONG) EXPORT AS LONG
    
    LOCAL RV&, hParent AS LONG
    
    '  If message is processed then set FUNCTION=0 and then EXIT FUNCTION
    
    SELECT CASE Msg
         CASE %MY_CUSTOM_MESSAGE
            ' New Control Color passed in wParam
            ' You could pass almost any type of info about your control using
            ' this technique. Even a pointer to a complex UDT could be passed.
            ' A single Long value representing a RGB color is just an example
            ' used for this sample project.
            SetControlLong hWnd, 5, wParam
            ControlClassDraw hWnd
            InvalidateRect hWnd, BYVAL %NULL, %TRUE
            FUNCTION=RV&
            EXIT FUNCTION
        CASE %WM_PAINT
            ControlClassPaint hWnd
            FUNCTION=0
            EXIT FUNCTION
        CASE %WM_ERASEBKGND
            FUNCTION=0
            EXIT FUNCTION
        ' -----------------------------------------------------------
        CASE %WM_SETCURSOR
        CASE %WM_LBUTTONDBLCLK
            hParent=GetParent(hWnd)
            IF hParent<>0 THEN
                ' Uses the Standard Static control message
                SendMessage hParent, %WM_COMMAND, MAKLNG(GetWindowLong(hWnd,%GWL_ID),%STN_DBLCLK), hWnd
            END IF
        CASE %WM_LBUTTONDOWN
            hParent=GetParent(hWnd)
            IF hParent<>0 THEN
                ' Uses the Standard Static control message
                SendMessage hParent, %WM_COMMAND,MAKLNG(GetWindowLong(hWnd,%GWL_ID),%STN_CLICKED), hWnd
            END IF
        CASE %WM_ENABLE
    
        CASE %WM_GETDLGCODE
    
        CASE %WM_CREATE
            ' --------------------------------------
            ' Set Default Color
            SetControlLong hWnd, 5, RGB(255,255,0)
            ' Set any Default values for control here
            ' --------------------------------------
            BuildBitmap hWnd, 1
        CASE %WM_DESTROY
            BuildBitmap hWnd, -1
        CASE %WM_SIZE
            BuildBitmap hWnd, 0
            ' ControlClassDraw hWnd
            InvalidateRect hWnd, BYVAL %NULL, %TRUE
        CASE ELSE
    END SELECT
    
    FUNCTION = DefWindowProc(hWnd,Msg,wParam,lParam)
    
    END FUNCTION
    
    ' -------------------------------------------------------------------------------------------
    
    SUB BuildBitmap(BYVAL hWnd AS LONG, BYVAL CFlag&)
    LOCAL R AS RECT, hDC AS LONG, hBmp AS LONG, hDC2 AS LONG
    LOCAL hOldBmp AS LONG, W&, H&
    ' CFlag&=1 for Creation, =0 for Resize, -1 for destroy
    IF CFlag&<=0 THEN
        ' Delete Previous DC and Bitmap
        hDC=GetControlLong(hWnd, 1)
        hBmp=GetControlLong(hWnd, 2)
        DeleteObject hBmp
        DeleteDC hDC
        SetControlLong hWnd, 1, 0
        SetControlLong hWnd, 2, 0
        SetControlLong hWnd, 3, 0
        SetControlLong hWnd, 4, 0
    END IF
    IF CFlag&>=0 THEN
        hDC2=GetDC(hWnd)
        hDC=CreateCompatibleDC(hDC2)
        GetClientRect hWnd, R
        W&=R.nRight-R.nLeft
        H&=R.nBottom-R.nTop
        hBmp=CreateCompatibleBitmap(hDC2, W&, H&)
        SelectObject hDC, hBmp
        DeleteDC hDC2
        SetControlLong hWnd, 1, hDC
        SetControlLong hWnd, 2, hBmp
        SetControlLong hWnd, 3, W&
        SetControlLong hWnd, 4, H&
        ControlClassDraw hWnd
    END IF
    END SUB
    
    ' -------------------------------------------------------------------------------------------
    
    SUB ControlClassDraw(BYVAL hWnd AS LONG)
    LOCAL hDC AS LONG, W&, H&, L&, ATL&, tbuffer$, CFlag&
    LOCAL hParent AS LONG, hBrush AS LONG, OldhBrush AS LONG, C&
    IF IsWindow(hWnd) THEN
        hDC=GetControlLong(hWnd, 1)
        W&=GetControlLong(hWnd, 3)
        H&=GetControlLong(hWnd, 4)
        C&=GetControlLong(hWnd, 5)
        '  ------------------------------------
        '  Draw your control here into the memory DC (not the window DC).
        '  The memory DC has a Bitamp associated with it already.
        '  The code below is just sample code.
        '  ------------------------------------
    
        ' In this example lParam is simply an RGB color value
        hBrush=CreateSolidBrush(C&)
        OldhBrush=SelectObject(hDC, hBrush)
        PatBlt hDC, 0,0, W&, H&, %PATCOPY
        SelectObject hDC, OldhBrush
        DeleteObject hBrush
    END IF
    END SUB
    
    ' -------------------------------------------------------------------------------------------
    
    SUB ControlClassPaint(BYVAL hWnd AS LONG)
    LOCAL PS AS PAINTSTRUCT
    LOCAL hDC AS LONG, R AS RECT, tbuffer$, L&, ATL&
    LOCAL memDC AS LONG
    ' TYPE PAINTSTRUCT
    '   hdc AS LONG
    '   fErase AS LONG
    '   rcPaint AS Rect
    '   fRestore AS LONG
    '   fIncUpdate AS LONG
    '   rgbReserved(1 TO 32) AS BYTE
    ' END TYPE
        IF IsWindow(hWnd) THEN
            hDC=BeginPaint(hWnd, PS)
            memDC=GetControlLong(hWnd, 1)
            ' ------------------------------------------------
            BitBlt hDC, PS.rcPaint.nLeft, PS.rcPaint.nTop, _
                        PS.rcPaint.nRight-PS.rcPaint.nLeft, PS.rcPaint.nBottom-PS.rcPaint.nTop, _
                        memDC, PS.rcPaint.nLeft, PS.rcPaint.nTop, %SRCCOPY
            EndPaint hWnd, PS
        END IF
    END SUB
    
    ' -------------------------------------------------------------------------------------------
    
    
    ' -------------------------------------------------------------------------------------------
    '                             EZGUI Custom Control Library
    ' -------------------------------------------------------------------------------------------
    
    FUNCTION GetControlLong(BYVAL hWnd AS LONG, BYVAL N&) AS LONG
    LOCAL I&, RV&
    RV&=0
    IF N&>=1 AND N&<=%ControlClassExtraData THEN
        I&=(N&-1)*4
        IF IsWindow(hWnd) THEN
            RV&=GetWindowLong(hWnd, I&)
        END IF
    END IF
    FUNCTION=RV&
    END FUNCTION
    
    ' -------------------------------------------------------------------------------------------
    
    SUB SetControlLong(BYVAL hWnd AS LONG, BYVAL N&, BYVAL V&)
    LOCAL I&
    IF N&>=1 AND N&<=%ControlClassExtraData THEN
        I&=(N&-1)*4
        IF IsWindow(hWnd) THEN
            SetWindowLong hWnd, I&, V&
        END IF
    END IF
    END SUB
    
    ' -------------------------------------------------------------------------------------------


    ------------------

    Leave a comment:


  • Chris Boss
    started a topic Comments on Oval Button code in source code forum !

    Comments on Oval Button code in source code forum !

    First, let me say to Borje Hagsten, you did a good job on
    the Oval Button code.

    There are a few things you need to change to make the button
    code work properly though.

    First, you "never" use Global variables for tracking data for a
    custom control. You need to add some "extra" window bytes when you
    register the control class. These bytes (4 per Long) are used to
    store unique data for each "instance" of the control window.

    Things like handles to Bitmaps, MemDCs, Brushes and regions should
    be stored in those extra bytes using SetWindowLong. You create all
    the GDI resources in the WM_CREATE message, put their handles in the
    extra bytes using SetWindowLong, retrieve the handles when you need
    them (ie. WM_PAINT) using GetWindowLong and then destroy the resources
    in WM_DESTROY.

    Using "Global" variables for tracking unique data per instance of
    a custom control, will cause serious problems with how your control
    works. I noticed in your code (I tested it), the buttons would
    respond to mouse clicks a few times and then stop responding.

    I'll post a sample custom control's source code below to demonstrate
    how to use the extra window bytes. (The control doesn't do much, but
    simply demonstrates the proper design techniques for custom controls)

    For more info on writing custom controls, get the book :

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

    The book is for Windows 3.1, but the basic concepts haven't
    changed.



    ------------------
Working...
X