Windows 7风格的Dropshadow采用无边框形式

cor*_*ulu 13 .net c# user-interface windows-7 winforms

精简版:

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


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

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


已知的现有解决方案2:用位图替换形式的GDI.

问题:失去使用控件的功能,仅作为启动画面使用.


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

...

长版:

(编辑:我指的是沿着任何窗体边界的阴影,如果不清楚的话.)我知道有一种方法可以在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我试图弄清楚如何使它们看起来像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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

cor*_*ulu 8

好吧,经过大约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);
    }
 }
Run Code Online (Sandbox Code Playgroud)

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