Windows 7风格的Dropshadow采用无边框forms

精简版:

目标: C#中无边界WinForm中的深色,黑暗,Windows 7阴影


已知的现有解决方案1:使用CreateParams的简单XP风格的阴影。

问题:太弱,太轻,太难看。


已知的现有解决方案2:用位图替换forms的GDI。

问题:失去使用控件的function,仅作为启动画面使用。


这篇文章的目标:找到这个问题的中位数解决方案或者更好的解决方案。

。 。 。

长版:

(编辑:我指的是沿着任何窗体边界的阴影,如果不清楚的话。)我知道有一种方法可以在C#中使用以下方法制作XP风格的阴影:

C#Code 1 – 简单的XP风格的阴影(问题:轻,弱,丑)

// Define the CS_DROPSHADOW constant private const int CS_DROPSHADOW = 0x00020000; // Override the CreateParams property protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ClassStyle |= CS_DROPSHADOW; return cp; } } 

但是,我试图弄清楚如何使它们看起来像Windows 7中的做(更深和更大的阴影),并且无法找出这样做的最佳方式。

我现在创建了一个方法,它可以让我覆盖整个表单GDI,看起来就像一个启动画面(不是我的信用):

C#代码2:用位图替换表单GDI(问题:不能使用表单控件,难以维护GUI)

  public void SetBitmap(Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); // 1. Create a compatible DC with screen; // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC; // 3. Call the UpdateLayeredWindow. IntPtr screenDc = Win32.GetDC(IntPtr.Zero); IntPtr memDc = Win32.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap oldBitmap = Win32.SelectObject(memDc, hBitmap); Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); Win32.Point pointSource = new Win32.Point(0, 0); Win32.Point topPos = new Win32.Point(Left, Top); Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); blend.BlendOp = Win32.AC_SRC_OVER; blend.BlendFlags = 0; blend.SourceConstantAlpha = opacity; blend.AlphaFormat = Win32.AC_SRC_ALPHA; Win32.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); } finally { Win32.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { Win32.SelectObject(memDc, oldBitmap); Win32.DeleteObject(hBitmap); } Win32.DeleteDC(memDc); } } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style return cp; } } 

但是,这确实给了我一个完整的32位背景(因为我需要手动添加投影),但我失去了创建可见的表单元素的能力。

所以基本上,我试图找出这两种方法之间的中位数。 在不失去其他function/导致过度重新绘制要求的情况下,会给我深深阴影的东西。

好吧,经过大约4个小时的头脑风暴和编码,我终于开发出了一个解决方案。 基本上,我做了2个表格。

表格#1 :通过修改和组合8个图像(4个角落梯度+每个方向4个线性渐变)创建阴影,并使用我在上面发布的第二个代码( C#代码2:用位图替换表格GDI )将它们设置为背景。 代码几乎解释了它。

 public partial class Dropshadow : Form { public Dropshadow(Form parentForm) { /*This bit of code makes the form click-through. So you can click forms that are below it in z-space */ int wl = GetWindowLong(this.Handle, -20); wl = wl | 0x80000 | 0x20; SetWindowLong(this.Handle, -20, wl); InitializeComponent(); //Makes the start location the same as parent. this.StartPosition = parentForm.StartPosition; parentForm.Activated += ParentForm_Activated; //Fires on parent activation to do a this.BringToFront() this.Deactivate += This_Deactivated; //Toggles a boolean that ensures that ParentForm_Activated does fire when clicking through (this) parentForm.Closed += ParentForm_Closed; //Closes this when parent closes parentForm.Move += ParentForm_Move; //Follows movement of parent form //Draws border with standard bitmap modifications and merging /* Omitted function to avoid extra confusion */ Bitmap getShadow = DrawBlurBorder(parentForm.ClientSize.Width, parentForm.ClientSize.Height); /* **This code was featured in the original post:** */ SetBitmap(getShadow, 255); //Sets background as 32-bit image with full alpha. this.Location = Offset; //Set within DrawBlurBorder creates an offset } private void ParentForm_Activated(object o, EventArgs e) { /* Sets this form on top when parent form is activated.*/ if (isBringingToFront) { /*Hopefully prevents recusion*/ isBringingToFront = false; return; } this.BringToFront(); /* Some special tweaks omitted to avoid confusion */ } private void This_Deactivated(object o, EventArgs e) { /* Prevents recusion. */ isBringingToFront = true; } /* Closes this when parent form closes. */ private void ParentForm_Closed(object o, EventArgs e) { this.Close(); } /* Adjust position when parent moves. */ private void ParentForm_Move(object o, EventArgs e) { if(o is Form) this.Location = new Point((o as Form).Location.X + Offset.X, (o as Form).Location.Y + Offset.Y); } } 

表单#2 :这只是在启动时启动了drophadow表单,我还创建了一些接口,以允许进一步的集成和灵活性,我省略了以避免额外的混淆。 基本上确保Dropshadow表单没有从活动表单中删除鼠标的方法,并且如果Dropshadow表单位于顶部,则不会强制用户必须单击两次按钮。

谢谢,Corylulu。

这里有一个可行的课程。

 var f = new Dropshadow(this) { BorderRadius = 40, ShadowColor = Color.Blue }; f.RefreshShadow(); 

DEMO

DrawShadow创建了一个像位图一样的阴影,但还不完美。 这个课程并不完美,但它确实有效。

顺便说一句,我不知道如何在任务栏中隐藏阴影表格。 如果表单消失,则设置ShowInTaskBar = false

编辑

我重写了这个类,现在看起来像这样,真正的DropShadow。

来源就在这里

你应该知道的一件事是这个类不考虑border-radius (采用css格式)。

主要财产是

  • 则shadowColor
  • ShadowV
  • ShadowH
  • ShadowSpread
  • ShadowBlur

该属性与css box-shadow相同,请参见此处

这些财产

  • ShadowSpread
  • ShadowBlur
  • 则shadowColor

需要您手动调用RefreshShadow()

转到演示项目