在 Winforms 控件中投影?

Posted

技术标签:

【中文标题】在 Winforms 控件中投影?【英文标题】:Drop shadow in Winforms Controls? 【发布时间】:2011-01-28 15:25:55 【问题描述】:

有没有办法给控件添加阴影?

是否有任何与此功能相关的控件?

【问题讨论】:

我现在该怎么办?没有一个答案真正解决了这个问题...... Simon 下面的建议甚至适用于非矩形形状(通过区域),至少在 Windows 7 上是这样。 【参考方案1】:

这是一个有争议的观点,你不用代码就可以做到。 将主面板边框样式设置为固定单。 在其下方创建 3 个面板,每个面板在每个方向上都大 1 个像素。 3 个面板中的每一个都具有较浅的灰色阴影。 不完美,但便宜又容易。

panel with pseudo-shadow

【讨论】:

【参考方案2】:

最佳答案确实会产生阴影,但我个人对此并不满意,原因如下:

它只适用于矩形(当然,WinForms 控件都是矩形,但我们可能希望在其他情况下使用它) 更重要的是:它并不平滑。它看起来不像其他程序中的其他阴影那么自然。 最后,配置有点烦人。

因此,由于所有这些事情,我最终为我的项目编写了自己的,我想我会在这里分享它:

public partial class Form1 : Form

    List<Control> shadowControls = new List<Control>();
    Bitmap shadowBmp = null;
    public Form1()
    
        InitializeComponent();
        shadowControls.Add(panel1);
        this.Refresh();
    

    private void Form1_Paint(object sender, PaintEventArgs e)
    
        if (shadowBmp == null || shadowBmp.Size != this.Size)
        
            shadowBmp?.Dispose();
            shadowBmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
        
        foreach (Control control in shadowControls)
        
            using (GraphicsPath gp = new GraphicsPath())
            
                gp.AddRectangle(new Rectangle(control.Location.X, control.Location.Y, control.Size.Width, control.Size.Height));
                DrawShadowSmooth(gp, 100, 60, shadowBmp);
            
            e.Graphics.DrawImage(shadowBmp, new Point(0, 0));
        
    
    private static void DrawShadowSmooth(GraphicsPath gp, int intensity, int radius, Bitmap dest)
    
        using (Graphics g = Graphics.FromImage(dest))
        
            g.Clear(Color.Transparent);
            g.CompositingMode = CompositingMode.SourceCopy;
            double alpha = 0;
            double astep = 0;
            double astepstep = (double)intensity / radius / (radius / 2D);
            for (int thickness = radius; thickness > 0; thickness--)
            
                using (Pen p = new Pen(Color.FromArgb((int)alpha, 0, 0, 0), thickness))
                
                    p.LineJoin = LineJoin.Round;
                    g.DrawPath(p, gp);
                
                alpha += astep;
                astep += astepstep;
            
        
    

在此实现中,添加到shadowControls 的所有控件都将涂上平滑阴影。您应该能够为非矩形形状实现此功能,因为生成阴影的主要功能采用GraphicsPath。请注意,在将阴影绘制到窗体之前将阴影绘制到另一个位图很重要,因为主函数需要SourceCopy 的合成模式才能工作,这意味着如果您不先将阴影绘制到另一个表面,阴影后面的任何东西将被完全替换,透明度方面无用。我正在回答 10 年前的问题,但希望这对某人有所帮助!

【讨论】:

这可以应用于单个控件的 Paint(例如 Panel)而不是应用于父控件的 Paint 吗?不确定它是否是设计使然,但应用到父 Paint 是我可以让它工作的唯一方法......这是迄今为止我找到的唯一好的解决方案,谢谢。【参考方案3】:

这个问题已经存在 6 年了,需要一个答案。我希望任何需要这样做的人都可以从我的解决方案中推断出任何控制集的答案。我有一个面板,想在每个子控件下方绘制一个阴影 - 在这种情况下是一个或多个面板(但该解决方案应该适用于其他控件类型,只需进行一些小的代码更改)。

由于必须在控件容器的表面上绘制控件的投影,我们首先向容器的 Paint() 事件添加一个函数。

Container.Paint += dropShadow;

dropShadow() 看起来像这样:

    private void dropShadow(object sender, PaintEventArgs e)
    
        Panel panel = (Panel)sender;
        Color[] shadow = new Color[3];
        shadow[0] = Color.FromArgb(181, 181, 181);
        shadow[1] = Color.FromArgb(195, 195, 195);
        shadow[2] = Color.FromArgb(211, 211, 211);
        Pen pen = new Pen(shadow[0]);
        using (pen)
        
            foreach (Panel p in panel.Controls.OfType<Panel>())
            
                Point pt = p.Location;
                pt.Y += p.Height;
                for (var sp = 0; sp < 3; sp++)
                
                    pen.Color = shadow[sp];
                    e.Graphics.DrawLine(pen, pt.X, pt.Y, pt.X + p.Width - 1, pt.Y);
                    pt.Y++;
                
            
        
    

显然,您可以从容器的集合中选择不同的控件类型,并且可以通过一些细微的调整来改变阴影的颜色和深度。

【讨论】:

我对它进行了测试,它确实有效,我确实对其进行了更改...更改了您的 DrawLine 并添加了第二个... e.Graphics.DrawLine(pen, pt.X + sp, pt .Y, pt.X + p.Width - 1 + sp, pt.Y); e.Graphics.DrawLine(pen, p.Right + sp, p.Top + sp, p.Right + sp, p.Bottom + sp);它看起来像这样:imgur.com/MfaR39s 嗨,不错的调整 - 我只在模拟 Material Design(尤其是卡片)时应用了底部阴影,但我认为这种组合应该满足任何寻求解决方案的人。【参考方案4】:

如果您可以改为使用 WPF,那么 WPF 中就有,由于 GDI+ 的功能有限,我认为 Windows 窗体中没有替代方案。

【讨论】:

如果他编写自定义控件并在重写的 Paint 方法中添加阴影效果,他可能会做到。 @FrustratedWithFormsDesigner - 是的,这就是我帖子中的那篇文章正在做的事情。做一件相对简单的事情,这仍然是一种可怕、可怕的方式! 你提到的那篇文章已经不存在了 编辑后,这个答案并没有真正提供太多信息。我想原来链接的文章has been moved here.【参考方案5】:

你必须像这样覆盖CreateParamsproperty:

private const int CS_DROPSHADOW = 0x00020000;
protected override CreateParams CreateParams

    get
    
        // add the drop shadow flag for automatically drawing
        // a drop shadow around the form
        CreateParams cp = base.CreateParams;
        cp.ClassStyle |= CS_DROPSHADOW;
        return cp;
    

【讨论】:

太棒了!有没有像这样的其他巧妙技巧的完整列表? @FrustratedWithFormsDesigner:哈哈——我爱你的昵称 @FrustratedWithFormsDesigner:你想到了什么技巧? @FrustratedWithFormsDesigner:CS_DROPSHADOW 是一种类风格。还有其他的,请参见 SDK 安装路径中的msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx 和 WinUser.h。 @SharpUrBrain:通常你不会在 Loaded() 事件中这样做,而是在 InitializeComponents() 的构造函数中这样做。您也可以尝试在构造函数中添加以下内容:SetStyle(ControlStyles.DoubleBuffer, true); UpdateStyles();

以上是关于在 Winforms 控件中投影?的主要内容,如果未能解决你的问题,请参考以下文章

ColorZone 不会在 WPF MaterialDesign 中投影

在 mongodb 中投影集合类型数组的大小

在 OpenGL 中投影纹理

C# WinForms 有滑块控件吗?

在 Pig 中投影分组元组

C# WinForms 用鼠标拖动控件