WPF无边框窗口的DropShadow

时间:2010-07-30 14:14:16

标签: wpf windows transparency dropshadow

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

5 个答案:

答案 0 :(得分:31)

我编写了一个能够完全符合您要求的实用程序类:将标准阴影放在无边框Window上,但将AllowsTransparency设置为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;
        }
    }
}

答案 1 :(得分:6)

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

[StructLayout(LayoutKind.Sequential)]
public struct Margins
{
    public int Left;
    public int Right;
    public int Top;
    public int Bottom;
}  

答案 2 :(得分:5)

使用Microsoft WPF Shell Integration Library,更简单,性能更佳

答案 3 :(得分:3)

如果您允许窗口调整边框大小,通过将ResizeMode设置为CanResize,您将获得操作系统投影。然后,您可以将MaxWidthMinWidthMaxHeightMinHeight设置为阻止调整大小的值。

如果你有一个没有样式的无边框窗口,你必须在你自己的可视树中提供窗口的所有外观,包括阴影,因为这种设置组合与你不想要的一样操作系统提供了什么。

编辑:

从那时起,如果你的窗口大小是固定的,只需添加一些drophadow,可能是<Rectangle/>作为<Canvas/>

内容中的第一个元素

类似的东西:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" AllowsTransparency="True" Background="Transparent" WindowStyle="None">
    <Canvas>
        <Rectangle Fill="#33000000" Width="100"  Height="100"/>
        <Rectangle Fill="#FFFF0000" Width="95"  Height="95" />
    </Canvas>
</Window>

请注意,第一个Fill的{​​{1}}属性部分透明,您也可以使用Rectangle的{​​{1}}属性。您可以使用自己的图形或不同的形状来自定义投影的外观。

请注意,这违反了OpacityRectangle的要求,但您没有选择:如果您想要透明度,则必须允许。

答案 4 :(得分:0)

为什么不用与&#34;窗口相同的对象创建阴影&#34;但更大,背后。

<Window x:Class="WPF_Custom_Look.ShadowWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="ShadowWindow" Height="400" Width="450" ResizeMode="NoResize" Background="Transparent" AllowsTransparency="True" WindowStyle="None">
<Grid>
    <Rectangle Fill="Black" Width="330" Opacity="0.5" Height="279">
        <Rectangle.Effect>
            <BlurEffect Radius="30"/>
        </Rectangle.Effect>
    </Rectangle>
    <Rectangle Fill="#FFFDFDFD" Width="312"  Height="260"/>

</Grid>

或者,如果您需要透明标题栏,则可以将其替换为<Border>

<Canvas>
    <Border BorderBrush="Black" BorderThickness="7" Height="195" Width="304" Canvas.Left="53" Canvas.Top="25">
        <Border.Effect>
            <BlurEffect Radius="20"/>
        </Border.Effect>
    </Border>
    <Rectangle Fill="#FF86B0F9" Width="285"  Height="177" Opacity="0.7" Canvas.Left="62" Canvas.Top="34" MouseDown="Border_MouseDown"/>
    <Rectangle Fill="#FFFDFDFD" Width="285"  Height="143" Canvas.Left="62" Canvas.Top="68"/>
</Canvas>

编辑:我刚注意到OP希望将AllowTransparency设置为False。我无法看到阴影在没有它的情况下发挥作用#Tr; True&#34 ;, thouth。