0

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

Jimi
  • 29,621
  • 8
  • 43
  • 61
Ma Dude
  • 477
  • 1
  • 5
  • 17
  • I don't really know. But have you tried a negative margin (-1)? That is supposed to trigger the "sheet of glass" effect. – Nigel Whatling Mar 20 '18 at 22:48
  • @NigelWhatling That causes a transparent line here and you can see the focused and unfocused opacity changes on it. :/ – Ma Dude Mar 21 '18 at 00:14
  • By the way, I have to put padding of 1 at the top for it to work at all. If my CEFSharp Browser UI covers the entire form, it doesnt work. – Ma Dude Mar 21 '18 at 00:16
  • You need to register your Window settings, calling `DwmSetWindowAttribute()` with a `DWMNCRENDERINGPOLICY` value = `Enabled` in your Form.Load() event, where you also have to call `DwmExtendFrameIntoClientArea()` the first time, specifying the margins. Then, you should process [WM_DWMCOMPOSITIONCHANGED](https://msdn.microsoft.com/en-us/library/windows/desktop/dd388199(v=vs.85).aspx), not WM_PAINT. You just need to set your margins to (0,1,0,1) => Right and Bottom to 1 to enable the shadow. Or All to -1 to enable the special Sheet of Glass feature. – Jimi Mar 21 '18 at 02:13
  • DWMNCRENDERINGPOLICY is int `2` which is already set. I dont believe it needs to be set in Form.Load(). its being called in WndProc. the `DwmExtendFrameIntoClientArea()` I already tried setting all to -1. It doesnt work as my control covers the entire form, it needs 1 edge to be empty for it do a dropshadow for some reason. – Ma Dude Mar 21 '18 at 17:46
  • You forgot the "@[nickname]". I'm here by chance. *I dont believe it needs to be set in Form.Load()*. This is not something you believe in. It's something you do or you don't. Since you can do it, try it out. You never know. You can set the `MARGINS` to whatever you want, but until you haven't registered `DwmSetWindowAttribute()`, you get nothing out of it. `Form.Load()` is a good place where to register the Attributes request. – Jimi Mar 21 '18 at 19:02
  • @Jimi You dont seem to understand. Its NOT required to be set in Form.Load. The code I showed works fine. It just requires 1 edge of the application to not have a control on. And since my control takes the entire Form Height and Width, theres no edge. Resulting in no Dropshadow. – Ma Dude Mar 21 '18 at 22:43
  • From what you are showing (the Image in your question), no, it's not working. That effect, the normal shadow, is created no matter what. You could cut out all of your code, same result. `DwmSetWindowAttribute()` creates a completly different effect, very visible and distinct (if it's actually working). – Jimi Mar 21 '18 at 22:49
  • @Jimi Wow your right, Very sorry for the misunderstanding :) So, I should still do an aero enabled check right? And I have it setup without any wndProc setup and the crap bottom and right dropshadow is still there, but the proper dropshadow is also there under it. Im assuming the wndProc is to remove it correct? (EDIT: Seems to have been the CreateParams that did the crap dropshadow, removing it fixed it. So whats the wndProc for exactly then?) – Ma Dude Mar 22 '18 at 00:00
  • When you have registered the request (and it looks like you have now), you just need your `WndProc` to handle `WM_DWMCOMPOSITIONCHANGED`, sent when Aero state has changed. If you receive this message, you have to repeat the registration (the same). Look at MSDN docs for `DwmSetWindowAttribute()`. This procedure is explained there (if I remember correctly). – Jimi Mar 22 '18 at 00:16
  • @Jimi When and why would WM_DWMCOMPOSITIONCHANGED ever be called? – Ma Dude Mar 22 '18 at 00:17
  • It is sent to top level windows when DWM Composition is changed. You can also test it with `DwmIsCompositionEnabled()`. – Jimi Mar 22 '18 at 00:20
  • @Jimi I cant seem to find what the case 0xyyzz thing is for it. Also it says Windows 8 doesnt pump the message. Would this not be an issue? – Ma Dude Mar 22 '18 at 00:22
  • It's not an issue for Windows 8-10. It could be for Windows 7. You just need to hadle the said Message. Look, give me a moment, I'll write down a modified version of your code so you can build it up from there. – Jimi Mar 22 '18 at 00:32

2 Answers2

1

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) ? 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);
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
0

I set the bottomHeight to 3, and I found that the border height was included in the form size (the form's size didn't change). So I set a BackgroundImage to this form, and the border was hidden by the image.

David Buck
  • 3,752
  • 35
  • 31
  • 35