使用DwmExtendFrameIntoClientArea实现没有Aero Glass的效果

11
使用启用了Aero Glass的DwmExtendFrameIntoClientArea API调用可以正常工作。然而,我希望即使Aero Glass被禁用,它也能像在Windows控制面板中一样工作:

enter image description here

注意,在Aero Glass被禁用时,边框已经延伸到客户区域了吗?当我在我的应用程序中进行DwmExtendFrameIntoClientArea API调用时,返回的HRESULT肯定不是成功的,并且我的应用程序最终看起来像这样:

http://img197.imageshack.us/img197/9629/clientapplication.png

通常情况下,启用Aero Glass时,边框会延伸到导航按钮下方,就像在控制面板中一样。如何做到这一点?显然DwmExtendFrameIntoClientArea并没有起作用。

顺便说一下,如果相关,我的应用程序是一个WPF应用程序。

3个回答

7

Nir的答案是正确的;当禁用组合时,您必须自己绘制该区域。

我可以向您展示我在窗体顶部面板的绘制处理程序中拥有的代码 - 该面板通常负责绘制0x00000000透明黑色以使玻璃出现:

伪代码:

procedure DrawGlassHeaderArea(g: Graphics; r: Rectangle; IsFormFocused: Boolean);
const
   clFakeGlassColor = $00EAD1B9;  //(185, 209, 234) This is the fake foreground glass color (for use when composition is disabled)
   clFakeGlassColorUnfocused = $00F2E4D7; //(215, 228, 242) This is the fake background glass color (for use when composition is disabled)
begin
   if Dwm.IsCompositionEnabled then
   begin
      g.FillRectangle(r, 0x00000000); //fill rectangle with transparent black
   end
   else
      //Composition disabled; fake it like Microsoft does

      //The color to use depends if the form has focused or not
      Color glassColor;
      if (IsFormFocused) then
         c = clFakeGlassColor 
      else
         c = clFakeGlassColorUnfocused;

      g.FillRectangle(r, glassColor); //fill rectangle with fake color


      //Now we have to draw the two accent lines along the bottom
      Color edgeHighlight = ColorBlend(Colors.White, glassColor, 0.33); //mix 33% of glass color to white
      Color edgeShadow = ColorBlend(Colors.Black, glassColor, 0.33); //mix 33% of glass color to black

      //Draw highlight as 2nd-last row:
      g.DrawLine(edgeHighlight, Point(r.Left, r.Bottom-2), Point(r.Right, r.Bottom-2);

      //Draw shadow on the very last row:
      g.DrawLine(edgeHighlight, Point(r.Left, r.Bottom-1), Point(r.Right, r.Bottom-1);
   end;
end;

使用示例

procedure MyForm.PaintBox1Paint(PaintEventArgs e)
begin
   DrawGlassHeaderArea(e.Graphics, PaintBox1.ClientRectangle, this.HasFocus); 
end;

奖励截图

enter image description here

2014年7月9日更新

@JakePetroules是对的,我错了。用于虚假玻璃的"蓝色"并没有硬编码到Windows中。而且可以使用GetThemeColor访问。

我编写了所有可用颜色(TMT_COLOR)的代码,这些颜色适用于Window类:

enter image description here

注意:有关类、部件和状态的更多信息,请参见Aero样式类、部件和状态

当使用:

  • : Window
  • 部件: WP_CAPTION
  • 状态: n/a (StateID未用于Caption部件,也未用于整个Window类)

并获取颜色代码propertyID:

  • TMT_FILLCOLORHINT: 当窗口具有焦点时
  • TMT_BORDERCOLORHINT: 当窗口没有焦点时

您将获得两个重要的颜色:

enter image description here

我现在使用的伪代码来获取虚假玻璃颜色:

public Color GetFakeClassColor(Boolean isWindowFocused=true)
{
   static Color fakeGlass= 0x00B8D0E9; //the correct answer anyway

   if ((GetThemeAppProperties() && STAP_ALLOW_CONTROLS) == 0)
      return fakeGlass;

   hTheme = OpenThemeData(GetDesktopWindow(), "Window");
   if (hTheme = 0)
      return fakeGlass;

   Int32 propID;
   if (isWindowFocused)
       propID= TMT_FILLCOLORHINT; //The color used as a fill color hint for custom controls.
   else
       propID= TMT_BORDERCOLORHINT; //The color used as a border color hint for custom controls.

   DWORD rgb;
   if (Failed(GetThemeColor(hTheme, WP_CAPTION, 0, propID, ref rgb))
      return fakeGlass;

   Result = new Color(rgb);
}

实际上,由于我使用Delphi,我的实际代码如下:

function GetFakeGlassColor(IsWindowFocused: Boolean=True): TColor;
var
    ted: TThemedElement;
    hTheme: THandle;
    propID: Integer;
    rgb: DWORD;
begin
    Result := $00B8D0E9; //the correct answer anyway

    //We can't use the ThemeServcies.ThemesEnabled, as that mistakenly checks for version 6 of the common controls library
    //Themes can be enabled without using ComCtl V6, or common controls at all
    if not ThemeServices.ThemesAvailable then
        Exit;
    if (GetThemeAppProperties and STAP_ALLOW_CONTROLS) = 0 then
        Exit;

    htheme := ThemeServices.Theme[teWindow];
    if hTheme = 0 then
        Exit;

    if IsWindowFocused then
        propID := TMT_FILLCOLORHINT //The color used as a fill color hint for custom controls.
    else
        propID := TMT_BORDERCOLORHINT; //The color used as a border color hint for custom controls.

    if Failed(GetThemeColor(hTheme, WP_CAPTION, 0, propID, {var}rgb)) then
        Exit;

    Result := rgb;
end;

ColorBlend 在数学上是做什么的? - Jake Petroules
@JakePetroules:“Windows 7基本主题不尊重用户分配的颜色……”我认为微软回退到单一硬编码颜色的行为是错误的,Windows UX团队也这样认为。并非所有微软开发人员都遵循UX指南。正如Raymond Chen经常指出的那样:微软内部的人有时和外部开发人员一样“做错事”。ColorBlend只是执行color1*(1-mix) + color2*(mix)(mix=0 ==> 纯色1,mix=1.0 ==> 纯色2)。 - Ian Boyd
@IanBoyd 我的意思是,如果标题栏本身不能改变颜色,那么你可能只需要硬编码蓝色来保持一致的体验。如果目标是扩展框架,则窗口颜色必须与标题栏颜色匹配,否则该效果将无法正确实现。撇开观点不谈,感谢ColorBlend逻辑,但我实际上没有在任何地方观察到它,坦白说我的应用程序看起来有点奇怪。你能给我指一个例子吗? - Jake Petroules
@JakePetroules 你说得对 - 这个问题是关于将玻璃扩展到客户端区域以及在组合被禁用时如何处理它。在我的脑海中,我想到了我的应用程序,其中其他UI元素遵循“Windows”颜色方案(即使在Windows 2000 / XP上,并且组合被禁用)。基于硬编码的单个“伪玻璃”颜色来构建UI是错误的,在这种情况下,对于“假”玻璃,它需要是那种蓝灰色。除非您使用“Windows Classic”颜色方案,即使是Windows也会选择灰色而不是钢蓝色。 - Ian Boyd
有人可以贴一个 WPF 的例子吗? - Nitin Chaudhari
显示剩余4条评论

2
您需要自己绘制窗口背景。不应像之前的帖子所建议的那样硬编码颜色,而是使用主题函数来检索它们,如下所示(半伪代码):
DWORD rgb;
HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
GetThemeColor(hTheme, WP_CAPTION, CS_ACTIVE,
    <is active window> ? TMT_FILLCOLORHINT : TMT_BORDERCOLORHINT, &rgb);

// Can use these functions to retrieve the individual RGB values
BYTE r = GetRValue(rgb);
BYTE g = GetGValue(rgb);
BYTE b = GetBValue(rgb);

这些颜色即使用户在控制面板中更改标题栏颜色也会保持正确(不像使用COLOR_ACTIVECAPTION / COLOR_GRADIENTACTIVECAPTION)。在尝试获取主题颜色之前,你还应该使用IsThemeActive()检查主题是否处于活动状态。
常量的值供快速参考:
  • WP_CAPTION:1
  • CS_ACTIVE:1
  • TMT_FILLCOLORHINT:3821
  • TMT_BORDERCOLORHINT:3822

一个稍微简单的方法是获取一个存储的 HBRUSH,如下所示:::GetSysColorBrush(COLOR_GRADIENTACTIVECAPTION) - c00000fd

2

你需要将其涂成类似框架的样式。

你需要使用DwmIsCompositionEnabled检查DWM是否启用,并处理WM_DWMCOMPOSITIONCHANGED以检测DWM状态的更改。

然后,你需要分开绘制窗口的方式,如果DWM已启用,则使用DwmExtendFrameIntoClientArea,如果禁用,则自己绘制“框架”。

我不知道如何在WPF中复制Aero框架(在我的应用程序中,我有自己的颜色方案,而且我没有使用Auro框架)。

这很烦人,但当DWM被禁用时,系统会回退到XP风格的绘图,DWM的任何服务都不起作用,即使与玻璃效果无关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接