Search code examples
c#winformsdwm

Borderless Form Dropshadow


So I have the following code:

#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

This makes a Dropshadow using GDI. The only issue however, is I had to make it keep a 1 pixel height border on the top (it can be any edge, just top is hardest to notice on my app).

This makes a line on my app at the top essentially degrading viewing experience.

Is it possible to do this with no border at all?

(The bottomHeight = 1 code is where its all about. If I set it to 0, and topHeight to 1, the line will be on the bottom. Setting all of them to 0, shows no dropshadow at all.)

Turns out, its to do with my padding, I need to leave 1 pixel line empty on atleast 1 edge for the Dropshadow to work. I chose to use Padding to make that 1 pixel line and I set the top padding to 1. This sets the line at the top. The bottomHeight = 1 doesnt matter at all. It's just there as it requires atleast one of them to be non 0.

If I remove the Padding and Top Line etc. And in the CreateParams overide, if I remove the aero enabled check, it shows a dropshadow similar like this: enter image description here


Solution

  • This is a Form class that uses DWM to render it's borders/shadow.

    As described, you need to register an Attribute, DWMWINDOWATTRIBUTE, and the related Policy, DWMNCRENDERINGPOLICY, settings it's value to Enabled.
    Then set the attribute with DwmSetWindowAttribute() and the desired effect with DwmExtendFrameIntoClientArea(), DwmEnableBlurBehindWindow() and so on.

    All the declarations needed are here.

    This is the Form class (named "Borderless", in a spark of creativity).
    I tried to make it look like what you already have posted, to minimize the "impact".

    The Form is a standard WinForms Form with 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);
        }
    }
    

    These are all the declarations needed, plus others that might become useful.
    Note that I only use the `internal` attribute form Win32 APIs, which are called using helpr methods.

    It's a partial class because the Winapi class is a extensive class library. You can change it to whatever you are used to.

    I suggest to keep the [SuppressUnmanagedCodeSecurityAttribute] attribute for the Win32 APIs declarations.

    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;
            }
    
            public static bool IsNonClientRenderingEnabled(IntPtr hWnd)
            {
                int gwaEnabled = 0;
                int result = SafeNativeMethods.DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.NCRenderingEnabled, ref gwaEnabled, sizeof(int));
                return gwaEnabled == 1;
            }
    
            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;
            }
        }
    }