无边界表格阴影
Borderless Form Dropshadow
所以我有以下代码:
#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 工作。我选择使用填充来制作那条 1 像素的线,并将顶部填充设置为 1。这会将线设置在顶部。 bottomHeight = 1 根本不重要。它就在那里,因为它要求至少其中之一是非 0。
如果我删除了 Padding 和 Top Line 等。并且在 CreateParams overide 中,如果我删除了 aero enabled 检查,它会显示类似这样的阴影:
这是一个使用 DWM 呈现它的表单 class borders/shadow。
如上所述,您需要注册一个属性,DWMWINDOWATTRIBUTE
, and the related Policy, DWMNCRENDERINGPOLICY
,将其值设置为已启用。
然后用DwmSetWindowAttribute()
and the desired effect with DwmExtendFrameIntoClientArea()
, DwmEnableBlurBehindWindow()
等设置属性。
所有需要的声明都在这里。
这是表格 class(在创意的火花中命名为 "Borderless")。
我试着让它看起来像你已经发布的那样,以尽量减少 "impact".
该表单是一个标准的 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);
}
}
这些是所有需要的声明,加上其他可能有用的声明。
请注意,我只使用 internal
属性形式的 Win32 API,它们是使用 helpr 方法调用的。
它是部分 class 因为 Winapi
class 是一个扩展的 class 库。您可以将其更改为您习惯的任何内容。
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);
}
}
}
我将 bottomHeight
设置为 3,我发现边框高度包含在表单大小中(表单的大小没有改变)。
所以我给这个表格设置了一个BackgroundImage
,边框被图片隐藏了。
所以我有以下代码:
#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 工作。我选择使用填充来制作那条 1 像素的线,并将顶部填充设置为 1。这会将线设置在顶部。 bottomHeight = 1 根本不重要。它就在那里,因为它要求至少其中之一是非 0。
如果我删除了 Padding 和 Top Line 等。并且在 CreateParams overide 中,如果我删除了 aero enabled 检查,它会显示类似这样的阴影:
这是一个使用 DWM 呈现它的表单 class borders/shadow。
如上所述,您需要注册一个属性,DWMWINDOWATTRIBUTE
, and the related Policy, DWMNCRENDERINGPOLICY
,将其值设置为已启用。
然后用DwmSetWindowAttribute()
and the desired effect with DwmExtendFrameIntoClientArea()
, DwmEnableBlurBehindWindow()
等设置属性。
所有需要的声明都在这里。
这是表格 class(在创意的火花中命名为 "Borderless")。
我试着让它看起来像你已经发布的那样,以尽量减少 "impact".
该表单是一个标准的 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);
}
}
这些是所有需要的声明,加上其他可能有用的声明。
请注意,我只使用 internal
属性形式的 Win32 API,它们是使用 helpr 方法调用的。
它是部分 class 因为 Winapi
class 是一个扩展的 class 库。您可以将其更改为您习惯的任何内容。
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);
}
}
}
我将 bottomHeight
设置为 3,我发现边框高度包含在表单大小中(表单的大小没有改变)。
所以我给这个表格设置了一个BackgroundImage
,边框被图片隐藏了。