tags:

views:

1325

answers:

3

Hi,

I wrote an attached property that I can set on a window to extend the glass frame into the client area (using the DwmExtendFrameIntoClientArea API). It works fine in most cases. Now I want my window to be borderless, so I set the following attributes on my window :

    WindowStyle="None"
    ResizeMode="NoResize"
    Background="Transparent"
    u:WinUtil.EnableGlass="True"
    ShowInTaskbar="False"

But with these attributes, the glass doesn't show up at all : my window just has a transparent background. If I set ResizeMode to CanResize, the glass is shown, but I don't want the window to be resizable.

I suspect it is due to the fact that the glass effect is obtained by extending the non-client frame into the client area : with WindowStyle = None and ResizeMode = NoResize, there is no non-client frame, so there's nothing to extend. When I enable resizing, it creates a frame around the window, so the frame can be extended.

I guess it should be possible to create a window that has a thin border, no title bar, and can't be resized, by setting the appropriate WS_* bits, but I don't know which ones exactly

So my questions are :

  1. Which style bits should be set or unset to have the desired appearance and behavior ?
  2. How can I initialize the window's style bits ? The Window class doesn't seem to have anything like Windows Forms CreateParams property... Is it OK to set these bits after the handle has been created ?
  3. I found the HwndSource class that could be an answer to question 2, but it seems a bit complex to use if you're not a Win32 expert... Would it be a sensible solution to my problem ?

Any advice is welcome

+4  A: 

Have you tried using DwmEnableBlurBehindWindow? This enables you to make a specific part of a window's client area transparent.

Michael
It looks promising, thanks. Now I have to figure out how to use it, but it doesn't look too bad ;)
Thomas Levesque
No luck so far, my window stays desperately black...
Thomas Levesque
OK, got it... I had to set the `CompositionTarget.BackgroundColor` to `Transparent`, and `AllowsTransparency` to true. Thanks !
Thomas Levesque
+1  A: 

Thomas, I am trying to get this same thing to work in Delphi. What exactly did you do to get it to work? Could you post the code? Thanks.

Gregg Guarnera
I'm not sure my code would help you, since it's very WPF-specific... Basically, I used DwmEnableBlurBehindWindow, passing the HWND of the window, with DWM_BLURBEHIND.fEnable set to true, and DWM_BLURBEHIND.hRgnBlur set to NULL (IntPtr.Zero)
Thomas Levesque
+4  A: 

I had a Window that I wanted to give just a glass boarder (no title bar and non-resizable) and ran into the same problem as you. You cannot accomplish this just by setting the Window's style. My solution was to set ResizeMode="CanResize" and WindowStyle="None" then handle the WM_NCHITTEST event to convert resizable border hits to non-resizable border hits. It was also necessary to modify the Window's style to disable maximizing and minimizing (using Windows shortcuts) and the system menu:

private void Window_SourceInitialized(object sender, EventArgs e)
{
    System.Windows.Interop.HwndSource source = (System.Windows.Interop.HwndSource)PresentationSource.FromVisual(this);
    source.AddHook(new System.Windows.Interop.HwndSourceHook(HwndSourceHook));

    IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
    IntPtr flags = GetWindowLongPtr(hWnd, -16 /*GWL_STYLE*/);
    SetWindowLongPtr(hWnd, -16 /*GWL_STYLE*/, new IntPtr(flags.ToInt64() & ~(0x00010000L /*WS_MAXIMIZEBOX*/ | 0x00020000L /*WS_MINIMIZEBOX*/ | 0x00080000L /*WS_SYSMENU*/)));
}

private static IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case 0x0084 /*WM_NCHITTEST*/:
            IntPtr result = DefWindowProc(hwnd, msg, wParam, lParam);
            if (result.ToInt32() >= 10 /*HTLEFT*/ && result.ToInt32() <= 17 /*HTBOTTOMRIGHT*/ )
            {
                handled = true;
                return new IntPtr(18 /*HTBORDER*/);
            }
            break;
    }
    return IntPtr.Zero;
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

This gives you a Window in Windows 7 suitable for notification area flyouts (e.g. the clock or volume flyouts). BTW, you can reproduce the shading at the bottom of the flyout by creating a control of height 44 and setting it's background:

<Control.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="{x:Static SystemColors.GradientActiveCaptionColor}" Offset="0"/>
        <GradientStop Color="{x:Static SystemColors.InactiveBorderColor}" Offset="0.1"/>
    </LinearGradientBrush>
</Control.Background>