Delphi buttons show white border on Aero glass
Asked Answered
O

4

10

I have been trying to find a good-looking design using Aero in Delphi 2010. One of the obvious uses one sees, is where the glass frame is extended to include the OK/Cancel buttons at the bottom of the screen. I notice though that this doesn't look quite right in Delphi 2010 - there is a white border all around each button.

This image shows the problem: the top 3 buttons are from my app, the bottom two were taken from Paint.NET's Layer Properties dialog.

White borders around Delphi controls

I tried various combinations of DoubleBuffered and a few combinations of placing the controls on other controls first, but the problem remains. Any ideas?

Openhearted answered 26/7, 2010 at 11:0 Comment(4)
do you have the sources for a minimal application showing this behaviour? (.dfm and .pas of a simple form will do)Enchondroma
@Jeroen: I can reproduce that behaviour with a simple form containing a TButton and setting glassframe so that the button is on glass. I just cannot connect to QC in the moment so I cannot find out if there is a similar error already filed.Hepner
Interesting behaviour - when button.doublebuffered is true, but form doublebuffered is false, I get one color (black) around the button and when both are true, I get a different color (as above,gray or clBtnFace), and when the button is not double-buffered the text looks grayed out. So it's impossible to use a button on glass in delphi 2010 without it looking like crap?Geny
related question: WinForms people aren't having any fun with glass either: #1870639Geny
H
5

If no one has a clean solution, as a workaround use TBitBtn with DoubleBuffered = false.

Hepner answered 26/7, 2010 at 14:28 Comment(4)
+1 for out-of the box thinking. But of course it's never that simple :-( BitBtn looks fine without double-buffering when the form has no double buffering. But switch it on for the form, and BitBtn looks just the same. Switch it off for the form, and your labels disappear. Aaargh!Openhearted
TSpeedButton looks fine when I try it here. However it doesn't accept keyboard focus.Geny
In Delphi XE, TBitBtn works perfectly - irrespective of the DoubleBuffered setting on either the TBitBtn or the form. That's my preferred solution for now.Openhearted
Setting DoubleBuffered to False is the solution to a myriad of problems!Protoplasm
G
2

It appears that the only workaround is owner-draw, or a third-party button control Check out the Glass Button by Roy Klever or, as stated in the QC entry linked below, TBitBtn with DoubleBuffered=false, which was the accepted answer above to this question.

This is a bug in Windows Aero DWM or else a bug in the windows common controls, or a bug in the way the VCL class hierachy handles common control window messages and painting when painting on glass. In short, windows common controls do not paint themselves properly on glass, or rather DWM composition (Aero) is broken. Surprise surprise.

The standard VCL button component uses the Window Class BUTTON from Windows Common Controls.

Note that TSpeedButton does not use the windows common control, and does not have this problem. however, it also does not accept focus.

It appears Embarcadero knows about this issue, it is QC # 75246, which is closed because it is really a bug in the common controls library, as Won't Fix, with the suggestion to use TBitBtn. Buttons are not alone, this is part of a group of QC reports including panels, and other common controls.

However I have a commercial TcxButton (part of developer express components) which accepts keyboard focus, and does not draw this glitch. Any code that uses the Win32 common control button control appears to have this problem. It may be possible that a low level Win32 API hacker might find a workaround for this. I am looking into it. This answer will be updated if I figure it out.

One interesting detail: TcxButton has three drawing styles, cxButton.LookAndFeel.Kind = {lfOffice11,lfFlat,lfStandard}. Selecting lfOffice11 adds this glitch back in. It looks like a strange interaction between the glass feature in aero in Vista/Win7 and the common control/xptheme button drawing code.

It may be that the only workaround is to use a completely app-drawn button control and to not use Windows common controls buttons, or any button control that relies upon the XP theme engine to draw buttons, on an aero glass pane.

Edit: July 28, someone at Embarcadero has closed the above QC Entry, which was a mistake. I am urging them to reopen it, if only to clarify if this is indeed a Windows bug in the common controls dll.

If you wish to play around, make a copy of the VCL source code for the TButton and TCustomButton classes from StdCtrls, as I have done here, modify CNCtlColorBtn, so that you force one of three things to happen - PerformEraseBackground, DrawParentBackground or inherited, and see the results. Interesting stuff.

procedure TCustomGlassButton.CNCtlColorBtn(var Message: TWMCtlColorBtn);
begin
  PerformEraseBackground(Self, Message.ChildDC);
  Message.Result := GetStockObject(NULL_BRUSH);
(*
  with ThemeServices do
    if ThemesEnabled then
    begin
      if (Parent <> nil) and Parent.DoubleBuffered then
        PerformEraseBackground(Self, Message.ChildDC)
      else
        DrawParentBackground(Handle, Message.ChildDC, nil, False);
      { Return an empty brush to prevent Windows from overpainting we just have created. }
      Message.Result := GetStockObject(NULL_BRUSH);
    end
    else

      inherited;
  *)
end;

Some interesting reading on Vista era glass/DWM/aero APIs (C++ developers blog)

Geny answered 27/7, 2010 at 20:23 Comment(0)
R
1

Here I'm providing some code that makes TButton look right on Glass. Unfortunately it makes the form "click-throw", so I don't think it's a good idea. But maybe you can find a way to fix the form's "click-throw".

Rayshell answered 30/7, 2010 at 8:36 Comment(0)
H
0

if you are able to use win32 api, try exploiting NM_CUSTOMDRAW notification (not ownerdraw), as i do (yes, buttons DO send it, including radio and checkboxes. For these it is best to use WM_CTLCOLORSTATIC though.). This is how it is done in C++, but the idea is the same. While my idea is good, it so happens that my buttons do DISAPPEAR once per program execution from the window, when they are customdrawn and i need to hover mouse over them so they are visible again. That's why i'm still looking for comments for this. Note that it is really difficult to reproduce disappearing buttons in one-form applications. I hovewer am experiencing this behaviour in every project.

case WM_NOTIFY:
  switch(((LPNMHDR)lParam)->code){
  case NM_CUSTOMDRAW:
    {
       NMHDR *nmh=(NMHDR*)lParam;
       //these 6000 through 6004 are button identifiers assigned by me
       if(nmh->idFrom >= 6000 && nmh->idFrom <= 6004){
         switch(((LPNMCUSTOMDRAW)nmh)->dwDrawStage){
           case CDDS_PREERASE:
             //BackgroundBrush is a HBRUSH used also as window background
             FillRect(((LPNMCUSTOMDRAW)nmh)->hdc, &((LPNMCUSTOMDRAW)nmh)->rc, BackgroundBrush);
             break;
         }
    }
    break;
}
break;
Harkness answered 3/7, 2011 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.