Borderless Window with Drop Shadow

DropShadow for WPF Borderless Window

I have written a little utility class that is able to do exactly what you want: drop a standard shadow over a borderless Window but having AllowsTransparency set to false.

You just have to call the DropShadowToWindow(Window window) method. It is preferred that you make this call just after the window's constructor's InitializeComponent(), but it will work even if you call it after the window is shown.

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

Borderless window with shadow and blurbehind

var accent = new AccentPolicy()
{
AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND,
AccentFlags = 0x20 | 0x40 | 0x80 | 0x100,
};

the set Value of AccentFlags will give your window a dropshadow effect like menu and calendar of Windows 10 does
Sample Image

Sample Image

How to add a dropshadow to a borderless window in WPF

The window does not get smaller, when you maximize it. The Window contains the Border as well as the drop-shadow, so what you perceive as smaller is just the Margin of the border inside the window.

You can create a Style with a trigger that checks for the Maximized state of the parent Window.

<Style x:Key="BorderWindowStyle" TargetType="{x:Type Border}">
<Setter Property="Margin" Value="10"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Black"
Direction="270"
BlurRadius="10"
ShadowDepth="3" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Maximized">
<Setter Property="Margin" Value="0"/>
<Setter Property="Effect" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>

Remove the properties that are already set in the style from your Border and reference the style.

<Border Style="{StaticResource BorderWindowStyle}">
<Grid Background="White" />
</Border>

Borderless Form Dropshadow

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);
}
}
}


Related Topics



Leave a reply



Submit