问题描述
短版:
目标:在 C# 中的无边框 WinForm 中创建一个深而暗的 Windows 7 阴影
Goal: A deep, dark, Windows 7 dropshadow in borderless WinForm in C#
已知的现有解决方案 1: 使用 CreateParams 的简单 XP 样式阴影.
Known existing solutions 1: Simple XP-style dropshadow using CreateParams.
问题:太弱,太轻,太丑.
已知的现有解决方案2:将表单的GDI替换为位图.
Known existing solutions 2: Replace GDI of form with bitmap.
问题:失去使用控件的能力,只能用作启动画面.
Problem: Lose the ability to use controls, only functional as a splash screen.
这篇文章的目标:找到这个问题的中位解决方案或一个更好的解决方案.
Objective by this post: Find a median solution to this problem or an all together better one.
...
加长版:
(如果不清楚的话,我指的是沿着任何窗口窗体的边框的投影.)我知道有一种方法可以使用 C# 制作 XP 样式的阴影:
( I am referring to the drop-shadow going along the border of any windows form, if that wasn't clear.) I understand that there is a way to make XP style dropshadows in C# using:
C# 代码 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 中的那样(更深和更大的阴影),但无法找出最好的方法.
However, I am trying to figure out how to make them appear like the do in Windows 7 (deeper and larger shadows) and can't figure out the best way of doing this.
我现在创建了一个方法,它可以让我覆盖整个 GDI 表单并看起来像一个启动屏幕(信用不是我的):
I have a method now created that will let me override the entire form GDI and appear like a splash screen would (credit not mine):
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 位背景(因为我需要手动添加阴影),但我无法创建可见的表单元素.
However, this does give me a full 32-bit background (as I require to add the dropshadow manually), but I lose the ability to create form elements that are visible.
所以基本上,我试图找出这两种方法之间的中位数.在不丢失其他功能/导致过度重绘要求的情况下,可以给我提供深而暗的阴影.
So basically, I am trying to figure out a median between these two methods. Something that will give me deep and dark drop shadows without losing other functionality / causing excessive repainting requirements.
推荐答案
好的,经过大约 4 个小时的头脑风暴和编码,我终于制定了解决方案.基本上,我做了2个表格.
Okay, so after about 4 hours of brainstorming and coding, I have finally developed a solution. Basically, I made 2 forms.
Form #1:通过修改和组合 8 个图像(每个方向 4 个角渐变 + 4 个线性渐变)创建阴影,并使用我上面发布的第二个代码将它们设置为背景(C# 代码 2:将表单 GDI 替换为位图).代码几乎解释了它.
Form #1: Create the dropshadow by modifying and combining 8 images (4 corners gradients + 4 linear gradients for each direction) and set them as a background using the second code I posted above (C# Code 2: Replace form GDI with Bitmap). Code pretty much explains it.
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);
}
}
Form #2:这只是在启动时启动 dropshadow 表单,我还创建了一些接口以允许进一步的集成和灵活性,我省略了这些接口以避免额外的混乱.基本上是确保 Dropshadow 表单不会从活动表单中移除鼠标点击的方法,并且如果 Dropshadow 表单位于顶部,则不会强制用户必须单击按钮两次.
Form #2: This just launches the dropshadow form at launch and I also created some interfaces to allow further integration and flexibility that I omitted to avoid extra confusion. Basically methods to ensure that the Dropshadow form was not taking away mouse clicks from the active form and wouldn't force the user to have to click a button twice if the Dropshadow form was on top.
这篇关于无边框形式的 Windows 7 样式 Dropshadow的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!