11

Using the DwmExtendFrameIntoClientArea API call with Aero Glass enabled works just fine. However, I want it to work when Aero Glass is disabled as well, like how it works in the Windows control panel:

enter image description here

Notice how the frame has extended into the client area, even though Aero Glass is disabled? When I make the DwmExtendFrameIntoClientArea API call in my application, the returned HRESULT is definitely not success, and my application ends up looking like this:

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

Normally, with Aero Glass enabled, the border stretches down to underneath the navigation buttons, like in the control panel. How do I do this? DwmExtendFrameIntoClientArea clearly isn't working.

By the way, if it is relevant, my application is a WPF application.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Alex
  • 3,429
  • 4
  • 36
  • 66

3 Answers3

7

Nir's answer is correct; when composition is disabled you have to draw that area yourself.

i can show you the code i have in the paint handler of the panel at the top of my form - the panel normally responsible for drawing the 0x00000000 transparent black to make the glass appear:

Psuedo-code:

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;

Sample usage

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

Bonus Screenshot

enter image description here

Update 7/9/2014

@JakePetroules was right, and i was wrong. The "blue" used for fake glass is not hard-coded into Windows. And it is accessible using GetThemeColor.

I coded up all the available colors (TMT_COLOR) available for a Window class:

enter image description here

Note: For more information about Classes, Parts, and States, see Aero Style Classes, Parts, and States

When using:

  • Class: Window
  • Part: WP_CAPTION
  • State: n/a (StateID is not used for the Caption part, nor the entire Window class)

and fetch the color code propertyID:

  • TMT_FILLCOLORHINT: for when the window has focus
  • TMT_BORDERCOLORHINT: for when the window does not have focus

you get the two important colors:

enter image description here

The pseudo-code i now use to get the fake glass color:

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);
}

In reality, since i use Delphi, my actual code is:

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;
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • Just to confirm: the Basic window frame colors need to be color-picked and hardcoded, right? That's what I'm currently doing in my application. I haven't found any enums that I can use for those colors. – BoltClock Oct 21 '12 at 09:08
  • @BoltClock: Yes. With desktop composition disabled, Microsoft applications fallback to the fixed color of a "Windows Basic" theme. One might argue that the Microsoft behavior of falling back to a single hard-coded color is wrong (Microsoft design-team is the who says that is wrong - [in their UX style guidelines](http://msdn.microsoft.com/en-us/library/windows/desktop/aa511283.aspx#theme) In my own applications i fallback to a color based on `GetSysColor(COLOR_ACTIVECAPTION)` or `COLOR_GRADIENTACTIVECAPTION`. But using a fixed color is easier - if wrong. – Ian Boyd Oct 22 '12 at 00:09
  • What does ColorBlend do, mathematically? – Jake Petroules Dec 08 '12 at 10:19
  • Actually, in this specific case, hardcoding the colors is better. The Windows 7 Basic theme does *not* respect user-assigned colors for the title bar, therefore using them will result in your app looking wrong (notice that Control Panel continues to use a blue color despite what the user picks). – Jake Petroules Dec 09 '12 at 04:14
  • @JakePetroules *"The Windows 7 Basic theme does *not* repsect user-assigned colors..."* i would argue the the Microsoft behavior of falling back to a single hard-coded color is wrong - as would the Windows UX team. Not all Microsoft developers follow the UX guidelines. As Raymond Chen often points out: people inside Microsoft are sometimes just as guilty of "doing the wrong thing" as outside developers. `ColorBlend` just does `color1*(1-mix) + color2*(mix)` (mix=0 ==> pure color1, mix=1.0 ==> pure color2) – Ian Boyd Dec 09 '12 at 15:26
  • @IanBoyd My Point was that if the title bars themselves can't change color, you may as well just hardcode the blue to maintain a consistent experience. If the goal is an extended frame, then the window color *must* match the title bar color or that effect is not correctly achieved. Opinions aside, thanks for the ColorBlend logic, but I haven't actually observed that anywhere and frankly my app looks a bit odd with it. Could you point me to an example? – Jake Petroules Dec 10 '12 at 02:20
  • @JakePetroules You are right about that - this question is about extending glass into client area and how to handle it when composition is disabled. In my head i was thinking about my applications, where the rest of the UI elements follow the "Windows" color scheme (even on Windows 2000/XP, and with composition disabled). It's wrong to base the UI on a hard-coded single *"pseudo-glass"* color, in this case, for *"fake"* glass, it needs to be that blue-grey. Unless when you're using "Windows Classic" color scheme, and not then even **Windows** uses the steel-blue color; opting for *grey*. – Ian Boyd Dec 10 '12 at 18:24
  • @IanBoyd I just realized I screwed up my previous question, too. When I said "but I haven't actually observed that anywhere and frankly my app looks a bit odd with it" I was referring to the highlight and shadow lines your code draws at the bottom of the rectangle. – Jake Petroules Dec 10 '12 at 23:07
  • Can someone please paste an example of this for WPF? – Nitin Chaudhari Feb 08 '13 at 01:53
2

You need to paint the window background yourself. You should not actually hard-code the colors as previous posts have suggested, but use the theme functions to retrieve them, like so (semi-pseudocode):

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);

These colors will remain correct even if the user changes title bar colors in the control panel (unlike using COLOR_ACTIVECAPTION / COLOR_GRADIENTACTIVECAPTION). You should also check that themes are active using IsThemeActive() before attempting to get theme colors.

The values of the constants for quick reference:

  • WP_CAPTION: 1
  • CS_ACTIVE: 1
  • TMT_FILLCOLORHINT: 3821
  • TMT_BORDERCOLORHINT: 3822
Jake Petroules
  • 23,472
  • 35
  • 144
  • 225
  • A slightly simpler way would be to get a stock `HBRUSH` as such: `::GetSysColorBrush(COLOR_GRADIENTACTIVECAPTION)` – c00000fd Dec 02 '14 at 10:38
2

You have to paint it to be frame-like yourself.

You have to use DwmIsCompositionEnabled to check if the DWM is enabled and handle WM_DWMCOMPOSITIONCHANGED to detect DWM state changed.

Then you have to have to separate way of drawing the window, if DWM is enabled you use DwmExtendFrameIntoClientArea, if it's disabled you draw the "frame" yourself.

I have no idea how to duplicate the Aero frame in WPF (in my app I have my own color scheme and I'm not using the Auro frame).

This is annoying but when the DWM is disabled the system falls back to XP-style drawing and none of the DWM's services work - even those that aren't related to the glass effect.

Nir
  • 29,306
  • 10
  • 67
  • 103