我一直在尝试在 Delphi 2010 中使用 Aero 找到一个好看的设计。人们看到的一个明显的用途是玻璃框架被扩展以包括屏幕底部的 OK/Cancel 按钮。我注意到虽然这在 Delphi 2010 中看起来不太正确 - 每个按钮周围都有一个白色边框。
此图显示了问题:前 3 个按钮来自我的应用程序,底部两个按钮来自 Paint.NET 的“图层属性”对话框。
我尝试了 DoubleBuffered 的各种组合以及将控件放在其他控件上的一些组合,但问题仍然存在。有任何想法吗?
我一直在尝试在 Delphi 2010 中使用 Aero 找到一个好看的设计。人们看到的一个明显的用途是玻璃框架被扩展以包括屏幕底部的 OK/Cancel 按钮。我注意到虽然这在 Delphi 2010 中看起来不太正确 - 每个按钮周围都有一个白色边框。
此图显示了问题:前 3 个按钮来自我的应用程序,底部两个按钮来自 Paint.NET 的“图层属性”对话框。
我尝试了 DoubleBuffered 的各种组合以及将控件放在其他控件上的一些组合,但问题仍然存在。有任何想法吗?
如果没有人有干净的解决方案,作为一种解决方法,请TBitBtn
使用DoubleBuffered = false
.
似乎唯一的解决方法是所有者绘制或第三方按钮控件查看 Roy Klever 的 Glass Button,或者如下面链接的 QC 条目中所述,TBitBtn with DoubleBuffered=false,这是上面接受的答案这个问题。
这是 Windows Aero DWM 中的一个错误,或者是 Windows 公共控件中的一个错误,或者是 VCL 类层次结构处理公共控件窗口消息和在玻璃上绘制时的绘制方式中的一个错误。简而言之,windows 常用控件无法在玻璃上正确绘制自己,或者更确切地说,DWM 组合 (Aero) 已损坏。惊喜惊喜。
标准 VCL 按钮组件使用 Windows 公共控件中的 Window 类 BUTTON。
注意TSpeedButton没有使用windows常用控件,不存在这个问题。但是,它也不接受焦点。
看来 Embarcadero 知道这个问题,它是QC # 75246,它已关闭,因为它确实是公共控件库中的一个错误,因为它不会修复,建议使用 TBitBtn。按钮并不孤单,它是一组 QC 报告的一部分,包括面板和其他常用控件。
但是,我有一个商业 TcxButton(开发人员快递组件的一部分),它接受键盘焦点,并且不会产生这个故障。任何使用 Win32 通用控件按钮控件的代码似乎都存在此问题。低级别的 Win32 API 黑客可能会找到解决此问题的方法。我正在调查它。如果我弄清楚了,这个答案将被更新。
一个有趣的细节:TcxButton 具有三种绘图样式,cxButton.LookAndFeel.Kind = {lfOffice11,lfFlat,lfStandard}。选择 lfOffice11 会重新添加此故障。这看起来像是 Vista/Win7 中 aero 中的玻璃功能与常用控件/xptheme 按钮绘制代码之间的奇怪交互。
可能唯一的解决方法是使用完全由应用程序绘制的按钮控件,而不使用 Windows 通用控件按钮,或任何依赖 XP 主题引擎来绘制按钮的按钮控件,在 aero 玻璃窗格上。
编辑:7 月 28 日,Embarcadero 有人关闭了上述 QC 入口,这是一个错误。我敦促他们重新打开它,如果只是为了澄清这是否确实是公共控件 dll 中的 Windows 错误。
如果您想尝试一下,请从 StdCtrls 复制 TButton 和 TCustomButton 类的 VCL 源代码,就像我在这里所做的那样,修改 CNCtlColorBtn,以便您强制发生三件事之一 - PerformEraseBackground、DrawParentBackground 或继承,并查看结果。有趣的东西。
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;
在这里,我提供了一些使 TButton 在 Glass 上看起来正确的代码。不幸的是,它使表单成为“点击投掷”,所以我认为这不是一个好主意。但也许您可以找到一种方法来修复表单的“点击”。
如果您能够使用 win32 api,请尝试像我一样利用 NM_CUSTOMDRAW 通知(不是 ownerdraw)(是的,按钮会发送它,包括单选和复选框。不过,最好使用 WM_CTLCOLORSTATIC。)。这就是它在 C++ 中的完成方式,但想法是一样的。虽然我的想法很好,但碰巧我的按钮在每个程序执行时都会从窗口中消失一次,当它们是自定义绘制时,我需要将鼠标悬停在它们上方以便它们再次可见。这就是为什么我仍在寻找对此的评论。请注意,在单一表单应用程序中重现消失的按钮确实很困难。我在每个项目中都遇到了这种行为。
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;