WPF无边框窗口的DropShadow

Tri*_*ock 24 windows wpf transparency dropshadow

我有一个WPF窗口,WindowStyle设置为none.有没有什么方法可以强制这个窗口放下一个阴影(就像你在WindowStyle不是没有时得到的那个)?我不想将AllowTransparency设置为true,因为它会影响性能.而且我也不想禁用硬件渲染(我在某处读到透明度在禁用时效果更好).

cpr*_*ack 33

我写了一个小实用程序类,它能够完全按照你想要的方式执行:将标准阴影放在无边框上WindowAllowsTransparency设置为false.

你只需要调用该DropShadowToWindow(Window window)方法.您最好在窗口的构造函数之后进行此调用InitializeComponent(),但即使在显示窗口后调用它也会起作用.

using System;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

public static class DwmDropShadow
{
    [DllImport("dwmapi.dll", PreserveSig = true)]
    private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);

    [DllImport("dwmapi.dll")]
    private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset);

    /// <summary>
    /// Drops a standard shadow to a WPF Window, even if the window is borderless. Only works with DWM (Windows Vista or newer).
    /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
    /// as AllowsTransparency involves a huge performance issue (hardware acceleration is turned off for all the window).
    /// </summary>
    /// <param name="window">Window to which the shadow will be applied</param>
    public static void DropShadowToWindow(Window window)
    {
        if (!DropShadow(window))
        {
            window.SourceInitialized += new EventHandler(window_SourceInitialized);
        }
    }

    private static void window_SourceInitialized(object sender, EventArgs e)
    {
        Window window = (Window)sender;

        DropShadow(window);

        window.SourceInitialized -= new EventHandler(window_SourceInitialized);
    }

    /// <summary>
    /// The actual method that makes API calls to drop the shadow to the window
    /// </summary>
    /// <param name="window">Window to which the shadow will be applied</param>
    /// <returns>True if the method succeeded, false if not</returns>
    private static bool DropShadow(Window window)
    {
        try
        {
            WindowInteropHelper helper = new WindowInteropHelper(window);
            int val = 2;
            int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);

            if (ret1 == 0)
            {
                Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 };
                int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
                return ret2 == 0;
            }
            else
            {
                return false;
            }
        }
        catch (Exception ex)
        {
            // Probably dwmapi.dll not found (incompatible OS)
            return false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这很好,除了我遇到打开子窗口(也有阴影)的问题,它减少了父窗口的阴影,然后当关闭子窗口时,将其完全删除.奇怪的错误,没有找到导致它的原因.当childwindow不使用drophadow时,它似乎也会这样做) (2认同)
  • 注意:当我尝试在本机应用程序中使用此方法时,直到我将边距设置为非零才会出现阴影.(对于无边框窗口,实际值似乎并不重要,只要它们与0不同). (2认同)
  • 尽管这种方法很有前途,但还存在另一个问题.如果你从应用程序切换然后再回来,阴影就会消失. (2认同)
  • 不要使用“ System.Drawing.Printing.Margins”,它会在某些机器上引起奇怪的图形故障。而是在本地将结构定义为@Omer Ran,如下所示。 (2认同)

小智 7

帕特里克的答案很有效,除非托管win32窗口.当发生这种情况时,您会注意到托管窗口"被淘汰"(看起来窗口正在将"玻璃板"效果应用于整个托管窗口).在本地定义结构时,这种奇怪的行为是固定的,例如

[StructLayout(LayoutKind.Sequential)]
public struct Margins
{
    public int Left;
    public int Right;
    public int Top;
    public int Bottom;
}  
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常好的观点.值得注意的是,这个问题只能在一小部分机器上重现,所以它特别讨厌. (2认同)