DwmExtendFrameIntoClientArea without Aero Glass
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:
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开发者_如何学C application is a WPF application.
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
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:
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 focusTMT_BORDERCOLORHINT
: for when the window does not have focus
you get the two important colors:
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;
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.
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
精彩评论