Author Topic: CWindow GroupBox  (Read 6265 times)

0 Members and 1 Guest are viewing this topic.

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 595
  • User-Rate: +11/-8
CWindow GroupBox
« on: May 29, 2011, 04:20:26 PM »
Jose,
  I assumed adding a groupbox would be the same as adding a button it appears I missed something.
My groupbox background is not the same color as the Window. I noticed in your CSED editor that groupboxes  you use do have the same background.

James

Code: [Select]
#COMPILE EXE
#DIM ALL
%UNICODE = 1

' // Include files for external files
%USEXPBUTTON = 1
#INCLUDE ONCE "CWindow.inc"   ' // CWindow class

' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS WSTRINGZ PTR, BYVAL nCmdShow AS LONG) AS LONG

   ' // Create an instance of the class
   LOCAL pWindow AS IWindow
   pWindow = CLASS "CWindow"
   IF ISNOTHING(pWindow) THEN EXIT FUNCTION

   ' // Create the main window
   ' // Note: CW_USEDEFAULT is used as the default value When passing 0's as the width and height
   pWindow.CreateWindow(%NULL, "CWindow with an XPButton", 0, 0, 500, 350, -1, -1, CODEPTR(WindowProc))

   ' // Center the window
   pWindow.CenterWindow

   ' // Add a button
   pWindow.AddXPButton(pWindow.hwnd, %IDCANCEL, "&Close", 350, 250, 75, 23, -1, -1)

   pWindow.AddGroupBox(pWindow.hwnd,-1,"Group #1",16,16,200,100,-1)
  

   ' // Default message pump (you can replace it with your own)
   pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main callback function.
' ========================================================================================
FUNCTION WindowProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG

   ' // Process window mesages
   SELECT CASE uMsg

      CASE %WM_COMMAND
         SELECT CASE LO(WORD, wParam)
            CASE %IDCANCEL
               ' // If the Escape key has been pressed...
               IF HI(WORD, wParam) = %BN_CLICKED THEN
                  ' // ... close the application by sending a WM_CLOSE message
                  SendMessage hwnd, %WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

      CASE %WM_DESTROY
         ' // End the application
         PostQuitMessage 0
         EXIT FUNCTION

   END SELECT

   ' // Pass unprocessed messages to Windows
   FUNCTION = DefWindowProc(hwnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================



Offline José Roca

  • Administrator
  • Hero Member
  • *****
  • Posts: 2481
  • User-Rate: +204/-0
Re: CWindow GroupBox
« Reply #1 on: May 29, 2011, 07:34:29 PM »
 
The default styles for the main window are %WS_OVERLAPPEDWINDOW OR %WS_CLIPCHILDREN OR %WS_CLIPSIBLINGS. The WS_CLIPCHILDREN style causes a window to exclude the areas occupied by child windows when the window paints its client area. However, a BS_GROUPBOX style window is a static control that never erases its background. Erasing the background removes any controls or buttons that appear within the group box.

Therefore, use %WS_OVERLAPPEDWINDOW OR %WS_CLIPSIBLINGS instead of -1

Code: [Select]
pWindow.CreateWindow(%NULL, "CWindow with an XPButton", 0, 0, 500, 350, %WS_OVERLAPPEDWINDOW OR %WS_CLIPSIBLINGS, -1, CODEPTR(WindowProc))

or subclass the groupbox and erase its background.

See: http://support.microsoft.com/kb/79982

Offline Dominic Mitchell

  • Jr. Member
  • **
  • Posts: 64
  • User-Rate: +11/-5
    • Prometheus Software
Re: CWindow GroupBox
« Reply #2 on: May 29, 2011, 09:02:01 PM »
Sometimes one has to wonder about some of the articles in the Microsoft Knowledge Base.
That article is out of date and uses terms that are misleading.

The groupbox belongs to the "Button" class not the "Static" class.

The solutions to this problem range from fiddling with window styles to rolling your own groupbox.
My favourite solution is number 2. It is simple and handles the WS_CLIPSIBLINGS style(resizeable windows).

Note: the groupbox should be created after the controls it groups when the controls
on the parent have the WS_CLIPSIBLINGS style.


Solution 1 - no WS_CLIPCHILDREN style

This solution creates the parent window without the WS_CLIPCHILDREN style. This works fine on windows
that are not resizeable, but can cause nasty flickering on ones that are resizeable.

Solution 2 - add WS_EX_TRANSPARENT extended style

This solution keeps the WS_CLIPCHILDREN style on the parent window and adds the WS_EX_TRANSPARENT extended
window style to the groupbox. This works well on both nonresizeable and resizeable windows.
Also, flickering is significantly reduced.

Solution 3 - paint background
This solution keeps the WS_CLIPCHILDREN style on the parent window and subclasses the groupbox in order
to paint it background.
This works well on both nonresizeable and resizeable windows.  Also, flickering is significantly reduced.

Code: [Select]
'-------------------------------------------------------------------------------
'
' PROCEDURE: Form1_Group1_SubclassProc
' PURPOSE:   Processes messages for the subclassed Button window.
'
'-------------------------------------------------------------------------------

FUNCTION Form1_Group1_SubclassProc _
  ( _
  BYVAL hWnd    AS DWORD, _ ' control handle
  BYVAL uMsg    AS DWORD, _ ' type of message
  BYVAL wParam  AS DWORD, _ ' first message parameter
  BYVAL lParam  AS LONG _   ' second message parameter
  ) EXPORT AS LONG

  LOCAL trc             AS RECT
  LOCAL lpOldWndProc    AS DWORD    ' address of original window procedure
  LOCAL lMsgResult      AS LONG     ' value returned to message after message is processed

  lpOldWndProc = GetProp(hWnd, "OLDWNDPROC")

  SELECT CASE uMsg
    CASE %WM_DESTROY
      ' Remove control subclassing
      SetWindowLong hWnd, %GWL_WNDPROC, RemoveProp(hWnd, "OLDWNDPROC")

    CASE %WM_ERASEBKGND
      GetClientRect hWnd, trc
      FillRect hDC, trc, GetClassLong(GetParent(hWnd), %GCL_HBRBACKGROUND)
      ' The message was processed
      FUNCTION = %TRUE     
      EXIT FUNCTION
  END SELECT

  FUNCTION = CallWindowProc(lpOldWndProc, hWnd, uMsg, wParam, lParam)

END FUNCTION


Solution 4 - roll your own
A groupbox is a very simple control(read easy to write).
This solution will eliminate flicker.
Dominic Mitchell
Phoenix Visual Designer
http://www.phnxthunder.com

Offline Dominic Mitchell

  • Jr. Member
  • **
  • Posts: 64
  • User-Rate: +11/-5
    • Prometheus Software
Re: CWindow GroupBox
« Reply #3 on: May 29, 2011, 09:16:09 PM »
By the way, adding the WS_EX_COMPOSITED style to the parent or to the parent and children significantly reduces flicker or eliminates it.
But this is an extended style that needs to be checked against various controls before adding  it willy-nilly.
Dominic Mitchell
Phoenix Visual Designer
http://www.phnxthunder.com

Offline José Roca

  • Administrator
  • Hero Member
  • *****
  • Posts: 2481
  • User-Rate: +204/-0
Re: CWindow GroupBox
« Reply #4 on: May 29, 2011, 09:53:29 PM »
 
Thanks very much for your detailed information. Very useful, as always. For a quick fix, I will add the WS_EX_TRANSPARENT style as the default. Later, maybe I will write a custom control. This one looks nice: http://www.codeproject.com/KB/miscctrl/XGroupBox.aspx

Offline Patrice Terrier

  • ROMs
  • Hero Member
  • *****
  • Posts: 936
  • User-Rate: +62/-1
    • www.zapsolution.com
Re: CWindow GroupBox
« Reply #5 on: May 30, 2011, 07:35:56 AM »
I had to write a specific subroutine just to handle that one in WinLIFT, to process correctly the WM_PAINT / WM_PRINTCLIENT messages  ::)

Code: [Select]
        CASE %CTRL_GROUPBOX
              IF wParam = 0 THEN
                 CALL BeginPaint(hWnd, ps): hDC = ps.hDC
              ELSE
                 hDC = wParam
              END IF
              CALL skPaintGroupBox(hWnd, hDC)
              IF wParam = 0 THEN CALL EndPaint(hWnd, ps)

Code: [Select]
FUNCTION skPaintGroupBox(BYVAL hWnd AS LONG, BYVAL hDC AS LONG) AS LONG

    LOCAL rc AS RECT
    LOCAL LenLabel, x1, y1, x2, y2, stW, stH, gbX, gbY, Xin, Yin, nUseColor, n1, nBorder AS LONG
    LOCAL yOffset, ofX, ofY, UseFont, nStrFormat AS LONG
    LOCAL sLabel AS STRING

    Item& = skChild(hWnd)
    IF Item& THEN
       IF Child(Item&).CtrlType = %CTRL_GROUPBOX THEN
          gbX = 0 ' Child(Item&).rc.nLeft
          gbY = 0 ' Child(Item&).rc.nTop
          Xin = Child(Item&).rc.nRight
          Yin = Child(Item&).rc.nBottom

        ' 06-22-2003 Fix to handle empty label (George W. Bleck)
          sLabel = skGetCTLtext(hWnd)

'         // 4.70
          UseFont = skFontPlus()
          CALL GdipCreateStringFormat(0, 0, nStrFormat)
          CALL GdipSetStringFormatTrimming(nStrFormat, %StringTrimmingCharacter)

          LenLabel = LEN(sLabel)
          IF LenLabel = 0 THEN sLabel = "W"
'         // 4.70
          CALL skGetPlusTextSize(hDC, sLabel, UseFont, nStrFormat, stW, stH)
          IF LenLabel = 0 THEN sLabel = "": stW = 0
        ' 06-22-2003 End fix

          stW = stW + 4: yOffset = stH \ 2

          IF LenLabel THEN

             '//4.01 modified
             n1 = 1: x1 = gbX + 0: y1 = gbY + yOffset: x2 = Xin: y2 = Yin - yOffset
             nUseColor = skGetSysColor(%SKCOLOR_3DRIGHTBOTTOM): nBorder = 8
             GOSUB PaintGroupLine

             '//4.01 modified
             n1 = 0: x1 = gbX + 1: y1 = gbY + yOffset + 1: x2 = Xin - 2: y2 = Yin - yOffset - 2
             nUseColor = skGetSysColor(%SKCOLOR_3DLEFTTOP): nBorder = 7
             GOSUB PaintGroupLine

           ' 06-22-2003 Fix to handle empty label (George W. Bleck)
             CALL skChildoffset(hWnd, ofX, ofY)
             ofX = ofX + 8
           ' Create OFF screen bitmap to avoid flickering AND allow smooth display
           ' ---------------------------------------------------------------------
             hDCTemp& = skOFFscreen(hDC, stW, stH, 1)
             hTmpBmp& = CreateCompatibleBitmap(hDCTemp&, stW, stH)
             CALL SelectObject(hDCTemp&, hTmpBmp&)
             CALL skAlphaBlend(hDCTemp&, 0, 0, Xin, Yin, skGetHdcMemBmp(skPopupOwner(hWnd)), ofX, ofY, Xin, Yin)
             CALL DeleteObject(hTmpBmp&)
           ' End of OFF screen drawing, fast BitBlt to the target display Window
           ' -------------------------------------------------------------------
             CALL skOFFscreen(0, gbX + 8, gbY + 0, 0)
           ' 06-22-2003 End fix

             CALL SetRect(rc, gbX + 10, gbY + 0, gbX + 10 + stW, gbY + 0 + stH)

'            // 4.54
             IF skGetSystemMetrics(%SK_OUTER_GLOW) THEN
                CALL BlurTextPlus(hDC, sLabel, rc, UseFont, nStrFormat)
             END IF
'            // 4.70
             CALL skDrawText(hDC, slabel, rc, UseFont, zColorARGB(255, skGetSysColor(%SKCOLOR_WINDOWTEXT)), nStrFormat)
          ELSE
             CALL skDrawRect3D(hDC, gbX + 0, gbY + yOffset, Xin, Yin - yOffset, skGetSysColor(%SKCOLOR_3DRIGHTBOTTOM), skGetSysColor(%SKCOLOR_3DRIGHTBOTTOM))
             CALL skDrawRect3D(hDC, gbX + 1, gbY + yOffset + 1, Xin - 2, Yin - yOffset - 2, skGetSysColor(%SKCOLOR_3DLEFTTOP), skGetSysColor(%SKCOLOR_3DLEFTTOP))
          END IF
          CALL GdipDeleteStringFormat(nStrFormat)
       ELSE
          CALL skDrawChildBorder(hWnd) ' I need this to draw borders with WM_PRINT for dialog and TAB control
       END IF
    END IF
    FUNCTION = %TRUE ' continue enumeration of children...
    EXIT FUNCTION

PaintGroupLine:
    x2 = x1 + x2 - 1: y2 = y1 + y2 - 1
    CALL zDrawLine(hDC, x1, y2, x1, y1, zColorARGB(255, nUseColor), 1, %DashStyleSolid)
    CALL zDrawLine(hDC, x1, y1, x1 + nBorder, y1, zColorARGB(255, nUseColor), 1, %DashStyleSolid)
    CALL zDrawLine(hDC, x1 + stW + nBorder, y1, x2 + 1, y1, zColorARGB(255, nUseColor), 1, %DashStyleSolid)
    CALL zDrawLine(hDC, x2, y1, x2, y2, zColorARGB(255, nUseColor), 1, %DashStyleSolid)
    CALL zDrawLine(hDC, x1, y2, x2 + n1, y2, zColorARGB(255, nUseColor), 1, %DashStyleSolid)
    RETURN

END FUNCTION

As you can see, something not trivial, especially to let it work in composited mode.

Indeed, you better have to write your own custom control, but then no way for a theme engine to skin it on the fly  ;D

...
« Last Edit: May 30, 2011, 07:49:03 AM by Patrice Terrier »
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com