无边框形式阴影

Posted

技术标签:

【中文标题】无边框形式阴影【英文标题】:Borderless Form Dropshadow 【发布时间】:2018-08-29 21:42:13 【问题描述】:

所以我有以下代码:

#region Dropshadow
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn
(
    int nLeftRect,
    int nTopRect,
    int nRightRect,
    int nBottomRect,
    int nWidthEllipse,
    int nHeightEllipse
);
[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
[DllImport("dwmapi.dll")]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(ref int pfEnabled);
private bool m_aeroEnabled;
public struct MARGINS

    public int leftWidth;
    public int rightWidth;
    public int topHeight;
    public int bottomHeight;

protected override CreateParams CreateParams 
    get 
        m_aeroEnabled = CheckAeroEnabled();
        CreateParams cp = base.CreateParams;
        if (!m_aeroEnabled) 
            cp.ClassStyle |= 0x00020000;
        

        return cp;
    

private bool CheckAeroEnabled()

    if (Environment.OSVersion.Version.Major >= 6) 
        int enabled = 0;
        DwmIsCompositionEnabled(ref enabled);
        return (enabled == 1) ? true : false;
    
    return false;

protected override void WndProc(ref Message m)

    switch (m.Msg) 
        case 0x0085:
            if (m_aeroEnabled) 
                int v = 2;
                DwmSetWindowAttribute(Handle, 2, ref v, 4);
                MARGINS margins = new MARGINS() 
                    bottomHeight = 1,
                    leftWidth = 0,
                    rightWidth = 0,
                    topHeight = 0
                ;
                DwmExtendFrameIntoClientArea(Handle, ref margins);
            
            break;
        default:
            break;
    
    base.WndProc(ref m);

#endregion

这会使用 GDI 制作 Dropshadow。 然而,唯一的问题是我必须让它在顶部保持一个 1 像素高度的边框(它可以是任何边缘,只是顶部在我的应用中最难注意到)。

这使我的应用程序顶部出现一条线,这实际上会降低观看体验。

有没有可能做到完全没有边框?

(bottomHeight = 1 代码就是它的全部。如果我将它设置为 0,topHeight 设置为 1,则该行将位于底部。将它们全部设置为 0,根本不会显示阴影.)

事实证明,这与我的填充有关,我需要在至少 1 个边缘上留 1 个像素线为空,以便 Dropshadow 工作。我选择使用 Padding 来制作 1 像素线,并将顶部填充设置为 1。这将线设置在顶部。 bottomHeight = 1 根本不重要。它就在那里,因为它要求其中至少有一个不为 0。

如果我删除 Padding 和 Top Line 等。并且在 CreateParams 覆盖中,如果我删除启用 aero 的检查,它会显示类似这样的阴影:

【问题讨论】:

我真的不知道。但是您是否尝试过负边距(-1)?这应该会触发“玻璃板”效应。 @NigelWhatling 这会在此处产生一条透明线,您可以在其上看到聚焦和未聚焦的不透明度变化。 :// 顺便说一句,我必须在顶部放置 1 的填充,这样它才能正常工作。如果我的 CEFSharp 浏览器 UI 覆盖了整个表单,则它不起作用。 您需要注册您的窗口设置,在您的 Form.Load() 事件中使用 DWMNCRENDERINGPOLICY 值 = Enabled 调用 DwmSetWindowAttribute(),您还必须在第一个事件中调用 DwmExtendFrameIntoClientArea()时间,指定边距。然后,您应该处理WM_DWMCOMPOSITIONCHANGED,而不是 WM_PAINT。您只需将边距设置为 (0,1,0,1) => Right 和 Bottom 设置为 1 即可启用阴影。或全部为 -1 以启用特殊的玻璃板功能。 DWMNCRENDERINGPOLICY 是已设置的 int 2。我不认为它需要在 Form.Load() 中设置。它在 WndProc 中被调用。 DwmExtendFrameIntoClientArea() 我已经尝试将所有设置为-1。它不起作用,因为我的控件覆盖了整个表单,它需要 1 个边缘为空,因为它出于某种原因会产生阴影。 【参考方案1】:

我将bottomHeight 设置为3,我发现边框高度包含在表单大小中(表单大小没有改变)。 所以我给这个表单设置了一个BackgroundImage,边框被图片隐藏了。

【讨论】:

【参考方案2】:

这是一个使用 DWM 呈现其边框/阴影的 Form 类。

如上所述,您需要注册一个属性DWMWINDOWATTRIBUTE,并且相关的策略DWMNCRENDERINGPOLICY 将其值设置为启用。 然后用DwmSetWindowAttribute()设置属性,用DwmExtendFrameIntoClientArea()DwmEnableBlurBehindWindow()等设置想要的效果。

所有需要的声明都在这里。

这是 Form 类(命名为“Borderless”,激发了创造力)。 我试图让它看起来像您已经发布的内容,以尽量减少“影响”。

表单是标准的 WinForms 表单,带有FormBorderStyle = None

public partial class Borderless : Form

    public Borderless() => InitializeComponent();

    protected override void OnHandleCreated(EventArgs e) 
        base.OnHandleCreated(e);
        WinApi.Dwm.DWMNCRENDERINGPOLICY Policy = WinApi.Dwm.DWMNCRENDERINGPOLICY.Enabled;
        WinApi.Dwm.WindowSetAttribute(this.Handle, WinApi.Dwm.DWMWINDOWATTRIBUTE.NCRenderingPolicy, (int)Policy);
        if (DWNCompositionEnabled())  WinApi.Dwm.WindowBorderlessDropShadow(this.Handle, 2); 
        //if (DWNCompositionEnabled())  WinApi.Dwm.WindowEnableBlurBehind(this.Handle); 
        //if (DWNCompositionEnabled())  WinApi.Dwm.WindowSheetOfGlass(this.Handle); 
    

    private bool DWNCompositionEnabled() => (Environment.OSVersion.Version.Major >= 6)
                                         ? WinApi.Dwm.IsCompositionEnabled()
                                         : false;

    protected override void WndProc(ref Message m)
    
        switch (m.Msg)
        
            case (int)WinApi.WinMessage.WM_DWMCOMPOSITIONCHANGED:
                
                    WinApi.Dwm.DWMNCRENDERINGPOLICY Policy = WinApi.Dwm.DWMNCRENDERINGPOLICY.Enabled;
                    WinApi.Dwm.WindowSetAttribute(this.Handle, WinApi.Dwm.DWMWINDOWATTRIBUTE.NCRenderingPolicy, (int)Policy);
                    WinApi.Dwm.WindowBorderlessDropShadow(this.Handle, 2);
                    m.Result = (IntPtr)0;
                
                break;
            default:
                break;
        
        base.WndProc(ref m);
    

这些是所需的所有声明,以及其他可能有用的声明。 请注意,我仅使用 Win32 API 形式的 internal 属性,这些 API 使用帮助程序方法调用。

这是一个部分类,因为Winapi 类是一个扩展类库。您可以将其更改为您习惯的任何内容。

我建议保留[SuppressUnmanagedCodeSecurityAttribute] Win32 API 声明的属性。

public partial class WinApi

    public enum WinMessage : int
    
        WM_DWMCOMPOSITIONCHANGED = 0x031E,          //The system will send a window the WM_DWMCOMPOSITIONCHANGED message to indicate that the availability of desktop composition has changed.
        WM_DWMNCRENDERINGCHANGED = 0x031F,          //WM_DWMNCRENDERINGCHANGED is called when the non-client area rendering status of a window has changed. Only windows that have set the flag DWM_BLURBEHIND.fTransitionOnMaximized to true will get this message.
        WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320,    //Sent to all top-level windows when the colorization color has changed.
        WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321        //WM_DWMWINDOWMAXIMIZEDCHANGE will let you know when a DWM composed window is maximized. You also have to register for this message as well. You'd have other windowd go opaque when this message is sent.
    

    public class Dwm

        public enum DWMWINDOWATTRIBUTE : uint
        
            NCRenderingEnabled = 1,     //Get only atttribute
            NCRenderingPolicy,          //Enable or disable non-client rendering
            TransitionsForceDisabled,
            AllowNCPaint,
            CaptionButtonBounds,
            NonClientRtlLayout,
            ForceIconicRepresentation,
            Flip3DPolicy,
            ExtendedFrameBounds,
            HasIconicBitmap,
            DisallowPeek,
            ExcludedFromPeek,
            Cloak,
            Cloaked,
            FreezeRepresentation
        

        public enum DWMNCRENDERINGPOLICY : uint
        
            UseWindowStyle, // Enable/disable non-client rendering based on window style
            Disabled,       // Disabled non-client rendering; window style is ignored
            Enabled,        // Enabled non-client rendering; window style is ignored
        ;

        // Values designating how Flip3D treats a given window.
        enum DWMFLIP3DWINDOWPOLICY : uint
        
            Default,        // Hide or include the window in Flip3D based on window style and visibility.
            ExcludeBelow,   // Display the window under Flip3D and disabled.
            ExcludeAbove,   // Display the window above Flip3D and enabled.
        ;

        public struct MARGINS
        
            public int leftWidth;
            public int rightWidth;
            public int topHeight;
            public int bottomHeight;

            public MARGINS(int LeftWidth, int RightWidth, int TopHeight, int BottomHeight)
            
                leftWidth = LeftWidth;
                rightWidth = RightWidth;
                topHeight = TopHeight;
                bottomHeight = BottomHeight;
            

            public void NoMargins()
            
                leftWidth = 0;
                rightWidth = 0;
                topHeight = 0;
                bottomHeight = 0;
            

            public void SheetOfGlass()
            
                leftWidth = -1;
                rightWidth = -1;
                topHeight = -1;
                bottomHeight = -1;
            
        


        [SuppressUnmanagedCodeSecurityAttribute]
        internal static class SafeNativeMethods
        
            //https://msdn.microsoft.com/en-us/library/windows/desktop/aa969508(v=vs.85).aspx
            [DllImport("dwmapi.dll")]
            internal static extern int DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);

            //https://msdn.microsoft.com/it-it/library/windows/desktop/aa969512(v=vs.85).aspx
            [DllImport("dwmapi.dll")]
            internal static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);

            //https://msdn.microsoft.com/en-us/library/windows/desktop/aa969515(v=vs.85).aspx
            [DllImport("dwmapi.dll")]
            internal static extern int DwmGetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);

            //https://msdn.microsoft.com/en-us/library/windows/desktop/aa969524(v=vs.85).aspx
            [DllImport("dwmapi.dll")]
            internal static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);

            [DllImport("dwmapi.dll")]
            internal static extern int DwmIsCompositionEnabled(ref int pfEnabled);
        

        public static bool IsCompositionEnabled()
        
            int pfEnabled = 0;
            int result = SafeNativeMethods.DwmIsCompositionEnabled(ref pfEnabled);
            return (pfEnabled == 1) ? true : false;
        

        public static bool IsNonClientRenderingEnabled(IntPtr hWnd)
        
            int gwaEnabled = 0;
            int result = SafeNativeMethods.DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingEnabled, ref gwaEnabled, sizeof(int));
            return (gwaEnabled == 1) ? true : false;
        

        public static bool WindowSetAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE Attribute, int AttributeValue)
        
            int result = SafeNativeMethods.DwmSetWindowAttribute(hWnd, Attribute, ref AttributeValue, sizeof(int));
            return (result == 0);
        


        public static bool WindowEnableBlurBehind(IntPtr hWnd)
        
            //Create and populate the Blur Behind structure
            DWM_BLURBEHIND Dwm_BB = new DWM_BLURBEHIND(true);

            int result = SafeNativeMethods.DwmEnableBlurBehindWindow(hWnd, ref Dwm_BB);
            return (result == 0);
        

        public static bool WindowExtendIntoClientArea(IntPtr hWnd, WinApi.Dwm.MARGINS Margins)
        
            // Extend frame on the bottom of client area
            int result = SafeNativeMethods.DwmExtendFrameIntoClientArea(hWnd, ref Margins);
            return (result == 0);
        

        public static bool WindowBorderlessDropShadow(IntPtr hWnd, int ShadowSize)
        
            MARGINS Margins = new MARGINS(0, ShadowSize, 0, ShadowSize);
            int result = SafeNativeMethods.DwmExtendFrameIntoClientArea(hWnd, ref Margins);
            return (result == 0);
        

        public static bool WindowSheetOfGlass(IntPtr hWnd)
        
            MARGINS Margins = new MARGINS();
            Margins.SheetOfGlass();

            //Margins set to All:-1 - Sheet Of Glass effect
            int result = SafeNativeMethods.DwmExtendFrameIntoClientArea(hWnd, ref Margins);
            return (result == 0);
        

        public static bool WindowDisableRendering(IntPtr hWnd)
        
            DWMNCRENDERINGPOLICY NCRP = DWMNCRENDERINGPOLICY.Disabled;
            int ncrp = (int)NCRP;
            // Disable non-client area rendering on the window.
            int result = SafeNativeMethods.DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingPolicy, ref ncrp, sizeof(int));
            return (result == 0);
        
    

【讨论】:

以上是关于无边框形式阴影的主要内容,如果未能解决你的问题,请参考以下文章

winform 怎么实现无边框阴影

转 无边框四周阴影

Win10无边框对话框加阴影

Win10无边框对话框加阴影

WINFORM 无边框窗体 阴影与移动

C#-WinForm-无边框窗体的移动和阴影-API