代码之家  ›  专栏  ›  技术社区  ›  TripShock

WPF无边框窗口的DropShadow

  •  22
  • TripShock  · 技术社区  · 15 年前

    5 回复  |  直到 15 年前
        1
  •  35
  •   cprcrack    8 年前

    我已经编写了一个小的实用程序类,它能够完全满足您的需要:在无边界图像上放置一个标准阴影 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;
            }
        }
    }
    
        2
  •  7
  •   Omer Ran    11 年前

    Patrick的答案很好用,除了win32窗口是托管的。 当这种情况发生时,您会注意到托管窗口被“冲掉”(看起来windows正在将“玻璃板”效果应用于整个托管窗口)。 例如

    [StructLayout(LayoutKind.Sequential)]
    public struct Margins
    {
        public int Left;
        public int Right;
        public int Top;
        public int Bottom;
    }  
    
        3
  •  3
  •   Rob Perkins    15 年前

    如果允许窗口调整边框大小,请通过设置 ResizeMode CanResize MaxWidth , MinWidth , MaxHeight MinHeight 设置为阻止调整大小的值。

    如果您有一个没有样式的无边框窗口,那么您必须在自己的视觉树中提供该窗口的所有外观,包括一个阴影,因为这种设置组合与表示您不想要操作系统提供的内容是一样的。

    从那时起,如果窗口大小是固定的,只需添加dropshadow,也许作为一个 <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 第一个的属性 Rectangle 是部分透明的,您也可以使用 Opacity 矩形 . 您可以使用自己的图形或其他形状,自定义放置阴影的外观。

    AllowsTransparency False ,但你别无选择:如果你想要透明,你必须允许它。

        4
  •  0
  •   Carol    10 年前

    为什么不创建一个与你的“窗口”相同的物体的阴影,但是在它后面更大。

    <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>