Enable Vista glass effect on a borderless WPF window

房东的猫 提交于 2019-12-12 07:16:54

问题


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


回答1:


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




回答2:


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>


来源:https://stackoverflow.com/questions/1324398/enable-vista-glass-effect-on-a-borderless-wpf-window

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!