Announcement

Collapse
No announcement yet.

Small ownerdraw function - anything missing or wrong? CODE

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

  • Small ownerdraw function - anything missing or wrong? CODE

    Assuming I am trying to make the smallest simplest example, is there anything in the function below that is wrong or could be done way better?
    Sometimes the examples here are so complex I have to take them apart and put them back together again to figure out what is going on.
    I love super simple examples - for simple things.

    Code:
    FUNCTION OWNERDRAW_BUTTON_1(hWin AS DWORD, CTL AS LONG, WPARAM AS LONG, LPARAM AS LONG)AS LONG
    
    LOCAL lpDis AS DRAWITEMSTRUCT PTR : lpDis = LPARAM
    
    'You should save the current value for things you are going to alter so you can put them back in cleanup - font, pen, brush
    
    LOCAL TEXT_FONT AS DWORD
    LOCAL hBrush    AS DWORD
    LOCAL hOldFont  AS DWORD
    LOCAL sTxt      AS STRING
    
    CONTROL GET TEXT hWin, CTL TO sTxt
    
    TEXT_FONT = MakeFontEx("Segoe UI", 9, 00, 0, 0)
    
    hBrush   = CreateSolidBrush(%YELLOW) '<-- Make a brush and set it to yellow
    hOldFont = SelectObject(@lpDis.hDC, TEXT_FONT) '<-- First it sets the new font, second it saves the old font into hOldFont
    
    SetBkColor @lpDis.hDC, %YELLOW
    SetTextColor @lpDis.hDC, %RED
    
    FillRect @lpDis.hDc, @lpDis.rcItem, hBrush 'Fill in the button backgrund usint - hBrush = CreateSolidBrush(TEXT_BACKGROUND)
    
    DrawText @lpDis.hDC, BYVAL STRPTR(sTxt), -1, @lpDis.rcItem, %DT_SINGLELINE OR %DT_CENTER OR %DT_VCENTER
    
    
    'Clean up
    DeleteObject hBrush 'When you no longer need the HBRUSH object, call the DeleteObject function to delete it.
    SelectObject(@lpDis.hDC, hOldFont) 'An application should always replace a new object with the original, default object after it has finished drawing with the new object.
    
    
    END FUNCTION

  • #2
    I think the point is - Anytime you call SelectObject you should put it back the way you found it. And any objects you "create" should be deleted.

    Comment


    • #3
      Here is a full demo:
      Ownerdraw and Sub-Class

      Code:
      #COMPILE EXE
      #DIM ALL
      #INCLUDE "WIN32API.INC"
      #RESOURCE MANIFEST, 1, "XPTheme.xml"
      
      #INCLUDE "FONTS.INC"
      #INCLUDE "COLORS.INC"
      '#INCLUDE "DEBUG_DIALOG.INC"
      
      ENUM TEST
      LABEL_1
      BUTTON_1
      BUTTON_2
      BUTTON_3
      BUTTON_4
      END ENUM
      
      '------------------------------------------------------------------------------
      FUNCTION PBMAIN () AS LONG
      
      LOCAL DIALOG_WIDTH AS LONG
      LOCAL DIALOG_HEIGHT AS LONG
      
      DIALOG_WIDTH = AfxScaleX(1000/1.5)
      DIALOG_HEIGHT = AfxScaleY(600/1.5)
      
      LOCAL hDlg AS DWORD
      
      DIALOG NEW PIXELS, %HWND_DESKTOP, "TEST", 0, 0, DIALOG_WIDTH, DIALOG_HEIGHT, %WS_POPUP OR %WS_CAPTION OR %WS_SYSMENU OR %WS_VISIBLE OR %DS_CENTER OR %DS_NOFAILCREATE OR %DS_SETFONT TO hDlg
      
      CONTROL ADD LABEL, hDlg, %TEST.LABEL_1, "", 0,1,1000,40
      CONTROL SET COLOR hDlg, %TEST.LABEL_1, %WHITE, %WHITE
      
      CONTROL ADD BUTTON, hDlg, %TEST.BUTTON_1, "Activate Card" , 0, 01, 150, 39, %WS_CHILD OR %WS_VISIBLE OR %BS_TEXT OR %BS_OWNERDRAW OR %BS_CENTER OR %BS_VCENTER, %WS_EX_LEFT OR %WS_EX_LTRREADING
      CONTROL ADD BUTTON, hDlg, %TEST.BUTTON_2, "Deactivate Card" , 150, 01, 150, 39, %WS_CHILD OR %WS_VISIBLE OR %BS_TEXT OR %BS_OWNERDRAW OR %BS_CENTER OR %BS_VCENTER, %WS_EX_LEFT OR %WS_EX_LTRREADING
      
      'DEBUG_DIALOG(hDlg)
      DIALOG SHOW MODAL hDlg, CALL TEST_CALLBACK
      
      
      END FUNCTION
      '------------------------------------------------------------------------------
      GLOBAL ORIGNAL_PROC_HANDLE AS DWORD
      '------------------------------------------------------------------------------
      CALLBACK FUNCTION TEST_CALLBACK
      
      SELECT CASE AS LONG CB.MSG
      
      CASE %WM_INITDIALOG
      ORIGNAL_PROC_HANDLE = SetWindowLong(GetDlgItem(CB.HNDL, %TEST.BUTTON_1), %GWL_WndProc, CODEPTR(TEST_BUTTON_1_SUBCLASS_PROC))
      ORIGNAL_PROC_HANDLE = SetWindowLong(GetDlgItem(CB.HNDL, %TEST.BUTTON_2), %GWL_WndProc, CODEPTR(TEST_BUTTON_1_SUBCLASS_PROC))
      
      CASE %WM_DRAWITEM
      
      SELECT CASE CB.CTL
      CASE %TEST.BUTTON_1
      OWNERDRAW_BUTTON_TYPE_1(CB.HNDL, CB.CTL, CB.WPARAM, CB.LPARAM)
      
      CASE %TEST.BUTTON_2
      OWNERDRAW_BUTTON_TYPE_1(CB.HNDL, CB.CTL, CB.WPARAM, CB.LPARAM)
      END SELECT
      
      CASE %WM_PAINT
      DRAW_MENU_LINES(CB.HNDL)
      
      
      
      CASE %WM_COMMAND
      
      SELECT CASE AS LONG CB.CTL
      CASE %TEST.BUTTON_1
      'DEBUG_IT "%TEST.BUTTON_1"
      CASE %TEST.BUTTON_2
      'DEBUG_IT "%TEST.BUTTON_2"
      END SELECT
      
      END SELECT
      
      END FUNCTION
      '------------------------------------------------------------------------------
      FUNCTION DRAW_MENU_LINES(hWnd AS DWORD)AS LONG
      
      LOCAL PS AS PAINTSTRUCT
      LOCAL hDC AS DWORD
      LOCAL hPen AS DWORD
      LOCAL Orignal_hPen AS DWORD
      
      hdc = BeginPaint(hWnd, PS)
      
      hPen = createpen(%ps_solid, 1, %RGB_GAINSBORO)
      Orignal_hPen = selectobject(hDC, hPen)
      
      
      movetoex hdc, 0, 0, BYVAL %null
      lineto hdc, 2500,0
      
      movetoex hdc, 0, 41, BYVAL %null
      lineto hdc, 2500,41
      
      EndPaint(hwnd, PS)
      
      END FUNCTION
      '------------------------------------------------------------------------------
      FUNCTION TEST_BUTTON_1_SUBCLASS_PROC(BYVAL hWnd AS LONG, BYVAL uMsg AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
      
      STATIC MouseOverFlag AS LONG
      STATIC Old_hWnd AS DWORD
      
      IF hWnd <> Old_hWnd THEN
      Old_hWnd = hWnd
      MouseOverFlag = 0
      END IF
      
      LOCAL TME AS tagTRACKMOUSEEVENT
      
      TME.cbSize = 16
      TME.dwFlags = %TME_HOVER OR %TME_LEAVE
      TME.hwndTrack = hWnd
      TME.dwHoverTime = 0
      
      
      
      SELECT CASE AS LONG uMsg
      
      CASE %WM_MOUSEFIRST
      IF MouseOverFlag = 0 THEN
      TRACKMOUSEEVENT(TME)
      BUTTON_MOUSEOVER(hWnd, 1)
      MouseOverFlag = 1
      END IF
      
      CASE %WM_MOUSELEAVE
      MouseOverFlag = 0
      BUTTON_MOUSEOVER(hWnd, 0)
      
      END SELECT
      
      
      FUNCTION = CallWindowProc(ORIGNAL_PROC_HANDLE, hWnd, uMsg, wParam, lParam)
      
      END FUNCTION
      '------------------------------------------------------------------------------
      FUNCTION BUTTON_MOUSEOVER(hWnd AS DWORD, M_OVER AS LONG)AS LONG
      
      
      LOCAL NORMAL_COLOR AS LONG
      LOCAL HOVER_COLOR AS LONG
      
      LOCAL NORMAL_TEXT_COLOR AS LONG
      LOCAL HOVER_TEXT_COLOR AS LONG
      
      NORMAL_COLOR = %WHITE
      HOVER_COLOR = RGB(230,255,255)
      
      
      NORMAL_TEXT_COLOR = %BLACK
      HOVER_TEXT_COLOR = %BLACK
      
      
      LOCAL hDC AS DWORD
      LOCAL CtrID AS DWORD
      LOCAL hBrush AS DWORD
      LOCAL hRect AS RECT
      LOCAL sTxt AS STRING
      LOCAL hCtrl AS DWORD
      LOCAL parent_handle AS DWORD
      
      hDC = GetDC(hWnd) 'A handle to the window whose DC is to be retrieved.
      CtrID = GetDlgCtrlID(hWnd) 'If the function succeeds, the return value is the identifier of the control.
      hCtrl = GetDlgItem(hWnd, CtrID) 'If the function succeeds, the return value is the window handle of the specified control.
      
      parent_handle = GetParent(hWnd)
      CONTROL GET TEXT parent_handle, CtrID TO sTxt
      
      GetClientRect(hWnd, hRect)
      
      SelectObject(hdc, MakeFontEx("Roboto", 9, 500, 0, 0))
      
      SELECT CASE M_OVER
      
      CASE 0
      hBrush = CreateSolidBrush (NORMAL_COLOR) 'CreateSolidBrush (NORMAL_COLOR)
      SetBkColor hDC, (NORMAL_COLOR)
      SetTextColor hDC, (NORMAL_TEXT_COLOR)
      
      CASE 1
      hBrush = CreateSolidBrush (HOVER_COLOR)
      SetBkColor hDC, (HOVER_COLOR)
      SetTextColor hDC, (HOVER_TEXT_COLOR)
      
      END SELECT
      
      FillRect hDc, hRect, hBrush 'Fill in the button backgrund
      DrawText hDC, BYVAL STRPTR(sTxt), -1, hRect, %DT_SINGLELINE OR %DT_CENTER OR %DT_VCENTER
      DeleteObject hBrush 'Delete hBrush - From CreateSolidBrush
      ReleaseDC(hWnd, hDC)
      
      END FUNCTION
      
      '------------------------------------------------------------------------------
      'TYPE DRAWITEMSTRUCT DWORD
      ' CtlType AS DWORD ' UINT
      ' CtlID AS DWORD ' UINT
      ' itemID AS DWORD ' UINT
      ' itemAction AS DWORD ' UINT
      ' itemState AS DWORD ' UINT
      ' hwndItem AS DWORD ' HWND
      ' hDC AS DWORD ' HDC
      ' rcItem AS RECT ' RECT
      ' itemData AS DWORD ' ULONG_PTR
      'END TYPE
      '------------------------------------------------------------------------------
      FUNCTION OWNERDRAW_BUTTON_TYPE_1(hWin AS DWORD, CTL AS LONG, WPARAM AS LONG, LPARAM AS LONG)AS LONG
      
      LOCAL lpDis AS DRAWITEMSTRUCT PTR : lpDis = LPARAM
      
      LOCAL hFont AS DWORD
      LOCAL Orignal_hFont AS DWORD
      LOCAL hBrush AS DWORD
      LOCAL sTxt AS STRING
      
      CONTROL GET TEXT hWin, CTL TO sTxt
      
      hFont = MakeFontEx("Roboto", 9, 500, 0, 0)
      Orignal_hFont = SelectObject(@lpDis.hDC, hFont) '<-- This does two things: First it sets the new font, second it saves the old font into hOldFont
      
      
      
      hBrush = CreateSolidBrush(%WHITE) '<-- Make a brush and set it to white
      
      SetBkColor @lpDis.hDC, %WHITE
      
      SetTextColor @lpDis.hDC, %BLACK
      
      FillRect @lpDis.hDc, @lpDis.rcItem, hBrush '<--Fill in the button backgrund usint - hBrush = CreateSolidBrush(TEXT_BACKGROUND)
      
      DrawText @lpDis.hDC, BYVAL STRPTR(sTxt), -1, @lpDis.rcItem, %DT_SINGLELINE OR %DT_CENTER OR %DT_VCENTER
      
      
      'The DeleteObject function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object.
      'After the object is deleted, the specified handle is no longer valid.
      
      'Clean up
      DeleteObject hFont 'When you no longer need the HBRUSH object, call the DeleteObject function to delete it.
      DeleteObject hBrush
      
      SelectObject(@lpDis.hDC, Orignal_hFont) 'An application should always replace a new object with the original, default object after it has finished drawing with the new object.
      
      END FUNCTION
      '------------------------------------------------------------------------------

      Comment


      • #4
        Incomplete code (include files) - cannot compile, but see some strange things.
        In FUNCTION BUTTON_MOUSEOVER, you create a font using MakeFontEx without storing the result for later SelectObject / DeleteObject.
        Also, in FUNCTION DRAW_MENU_LINES, you create a pen, but no SelectObject / DeleteObject.
        A tip - when you do SelectObject, original pen/font/whatever is returned and new/created pen/font/whatever is selected into the Device Context. So often it's it's perfectly ok to use same variable (here hPen) for storing the returned object, like
        '
        Code:
          hPen = CreatePen(%PS_SOLID, 1, %RGB_GAINSBORO)  ' hPen = new created object
          hPen = SelectObject(hDC, hPen)                  ' hPen is original object - new created is selected into DC
            ' Draw something - MovetoEx/LineTo..
          hPen = SelectObject(hDC, hPen)                  ' Restore original object - hPen becomes new created again
          DeleteObject hPen                               ' Delete new created (hPen).
        '
        And this of course leeds to simplifying further (I usually don't like doing this because have learned the hard way that it's easy to miss something and then it becomes much harder to find ev. misstake later on) like:
        '
        Code:
          hPen = SelectObject(hDC, CreatePen(%PS_SOLID, 1, %RGB_GAINSBORO)) ' hPen = original hPen
            ' Draw something - MovetoEx/LineTo..
          DeleteObject SelectObject(hDC, hPen)  ' Delete new, restore original hPen
        '

        Comment


        • #5
          Thanks Borje!

          Comment


          • #6
            I think the point is - Anytime you call SelectObject you should put it back the way you found it. And any objects you "create" should be deleted.
            Somebody last week was asking how to create a "memory leak." .... good demo of how to avoid doing so here.

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

            Comment

            Working...
            X