【问题标题】:Show Transparent Loading Spinner above other Controls在其他控件上方显示透明加载微调器
【发布时间】:2016-05-13 22:38:25
【问题描述】:

我正在使用微调器控件。我希望控件支持透明背景色。画弧线的时候,中间有一个空白区域,我希望那个空间是真正透明的,这样我就可以在它后面放另一个控件,它不会被微调器覆盖。

我尝试覆盖 CreateParams void。
我还将样式设置为支持透明颜色。
尝试覆盖 OnPaintBackground void,但我无法实现真正​​的透明背景色。

那么,你能建议我做什么?

【问题讨论】:

  • 您可以在这篇文章中看到一个透明的微调器。 Why does the designer slowing when two custom controls has timer added? 此处描述了相同的技术来制作透明图片框和标签。 How to make two transparent layer with c#?
  • @Reza Aghaei 看到这两个帖子,但我无法获得真正的透明度。我还复制了您链接的第一篇文章中的 SpinningCircles 控件。但是那个并没有实现真正透明的背景色。
  • 至少对于我分享的第二个链接,您可以看到包含透明图片框和透明标签的屏幕截图。究竟是什么问题?
  • 在设计器或调试时使用它时,它后面的所有控件都被灰色纯色覆盖(表单的背景色
  • 我再次检查了它,它在设计器和运行时都能正常工作。可能您已将颜色设置为BackColor。您应该将Transparent 设置为BackColor

标签: c# .net winforms custom-controls gdi+


【解决方案1】:

要制作透明层,您应该覆盖控件的绘制并按此顺序绘制控件,首先在同一容器中绘制您控制下的所有控件(基于 z-index ) 在位图上。 然后在控件的图形上绘制该位图。 最后绘制控件的内容。 另外你的控件的BackColor应该是Color.Transparent

作为制作透明层的另一个选项,您可以在绘制时从您的控件中排除某些区域。

在以下示例中,我使用了第一种技术并创建了 2 个控件。一个spinning circles 透明控件。和一个transparent picturebox 控件。

在这两个示例中,我使用了加载行之间的延迟来显示微调器。

示例 1 - 使用 SpinningCircles 控件

SpinningCircles 控件绘制圆圈并支持透明度。控件在设计时不动画,但在运行时动画。不可见时也不会消耗资源。

示例 2 - 使用透明图片框控件和透明动画 gif TransparentPictureBox 控件支持透明度,因此我使用了动画 gif 作为其图像,如您所见,gif 显示正确。

示例 1 代码 - SpinningCircles

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
public class SpinningCircles : Control
{
    int increment = 1;
    int radius = 4;
    int n = 8;
    int next = 0;
    Timer timer;
    public SpinningCircles()
    {
        timer = new Timer();
        this.Size = new Size(100, 100);
        timer.Tick += (s, e) => this.Invalidate();
        if (!DesignMode)
            timer.Enabled = true;
        SetStyle(ControlStyles.AllPaintingInWmPaint |
                 ControlStyles.OptimizedDoubleBuffer |
                 ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
                 ControlStyles.SupportsTransparentBackColor, true);
        BackColor = Color.Transparent;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        if (Parent != null && this.BackColor == Color.Transparent)
        {
            using (var bmp = new Bitmap(Parent.Width, Parent.Height))
            {
                Parent.Controls.Cast<Control>()
                      .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
                      .Where(c => c.Bounds.IntersectsWith(this.Bounds))
                      .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
                      .ToList()
                      .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));

                e.Graphics.DrawImage(bmp, -Left, -Top);
            }
        }
        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
        int length = Math.Min(Width, Height);
        PointF center = new PointF(length / 2, length / 2);
        int bigRadius = length / 2 - radius - (n - 1) * increment;
        float unitAngle = 360 / n;
        if (!DesignMode)
            next++;
        next = next >= n ? 0 : next;
        int a = 0;
        for (int i = next; i < next + n; i++)
        {
            int factor = i % n;
            float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
            float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
            int currRad = radius + a * increment;
            PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
            e.Graphics.FillEllipse(Brushes.Black, c1.X, c1.Y, 2 * currRad, 2 * currRad);
            using (Pen pen = new Pen(Color.White, 2))
                e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
            a++;
        }
    }
    protected override void OnVisibleChanged(EventArgs e)
    {
        timer.Enabled = Visible;
        base.OnVisibleChanged(e);
    }
}

示例 2 代码 - 透明图片框代码

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
class TransparentPictureBox : PictureBox
{
    public TransparentPictureBox()
    {
        this.BackColor = Color.Transparent;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        if (Parent != null && this.BackColor == Color.Transparent)
        {
            using (var bmp = new Bitmap(Parent.Width, Parent.Height))
            {
                Parent.Controls.Cast<Control>()
                      .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
                      .Where(c => c.Bounds.IntersectsWith(this.Bounds))
                      .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
                      .ToList()
                      .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));

                e.Graphics.DrawImage(bmp, -Left, -Top);
            }
        }
        base.OnPaint(e);
    }
}

【讨论】:

  • 这是一个很老的问题,但如果您能验证答案或让我知道您在使用解决方案时遇到任何问题,那就太好了:)
  • 怎么称呼?
  • @Moeez 只需在表单上正确放置一个控件实例,然后显示(并更新它)并在需要时隐藏它。例如看看this post
【解决方案2】:

我稍微更改了 Reza 的代码 (SpinningCircles) 以添加半透明背景。我想和你分享。 (注意:微调器长度固定为 100,应作为组件属性添加)

public partial class WorkingPanel : UserControl
{
    #region Constants
    private static readonly Int32 kSpinnerLength = 100;
    #endregion

    #region Fields
    private Int32 increment = 1;
    private Int32 radius = 4;
    private Int32 n = 8;
    private Int32 next = 0;
    private Timer timer = null;
    #endregion

    #region Constructor
    public WorkingPanel()
    {
        this.Size = new Size(100, 100);

        timer = new Timer();
        timer.Tick += (s, e) => this.Invalidate();

        if (!DesignMode)
            timer.Enabled = true;

        SetStyle(ControlStyles.AllPaintingInWmPaint |
                 ControlStyles.OptimizedDoubleBuffer |
                 ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
                 ControlStyles.SupportsTransparentBackColor, true);

        BackColor = Color.Transparent;
    }
    #endregion

    #region Methods (Protected - Override)
    protected override void OnPaint(PaintEventArgs e)
    {
        if (null != Parent && (this.BackColor.A != 255 || this.BackColor == Color.Transparent))
        {
            using (var bmp = new Bitmap(Parent.Width, Parent.Height))
            {
                Parent.Controls.Cast<Control>()
                      .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
                      .Where(c => c.Bounds.IntersectsWith(this.Bounds))
                      .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
                      .ToList()
                      .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));

                e.Graphics.DrawImage(bmp, -Left, -Top);

                if (this.BackColor != Color.Transparent)
                    e.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, Width, Height));
            }
        }

        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

        Int32 length = kSpinnerLength;
        PointF center = new PointF(Width / 2, Height / 2);
        Int32 bigRadius = length / 2 - radius - (n - 1) * increment;
        float unitAngle = 360 / n;

        if (!DesignMode)
            next++;

        next = next >= n ? 0 : next;
        Int32 a = 0;
        for (Int32 i = next; i < next + n; i++)
        {
            Int32 factor = i % n;
            float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
            float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
            Int32 currRad = radius + a * increment;
            PointF c1 = new PointF(c1X - currRad, c1Y - currRad);

            e.Graphics.FillEllipse(Brushes.White, c1.X, c1.Y, 2 * currRad, 2 * currRad);

            using (Pen pen = new Pen(Color.White, 2))
                e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);

            a++;
        }
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        timer.Enabled = Visible;

        base.OnVisibleChanged(e);
    }
    #endregion
}

【讨论】:

  • 你是如何实现的?创建类并编写以下代码不显示它:using(var F = new SpinningCircle()) { F.Show(); System.Threading.Thread.Sleep(4000); }
  • 我刚刚将 WorkingPanel 添加到表单中,瞧。之后,您可以设置锚点或停靠属性以填充整个表单。
【解决方案3】:

从CrApHeR的code修改为添加Designer Properties

如何使用

将此类添加到您的项目中

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;

[DesignerCategory("Code")]
public class Spinner : Control
{
    private int next = 0;
    private Timer timer = new Timer();

    public Spinner()
    {
        SetStyle(ControlStyles.UserPaint, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);

        Size = new Size(100, 100);
        BackColor = Color.Transparent;

        timer.Tick += (s, e) => Invalidate();
        if (!DesignMode)
        {
            timer.Enabled = true;
        }
    }

    [Browsable(true)]
    [Category("Appearance")]
    public int NodeCount { get; set; } = 8;

    [Browsable(true)]
    [Category("Appearance")]
    public int NodeRadius { get; set; } = 4;

    [Browsable(true)]
    [Category("Appearance")]
    public float NodeResizeRatio { get; set; } = 1.0f;

    [Browsable(true)]
    [Category("Appearance")]
    public Color NodeFillColor { get; set; } = Color.Black;

    [Browsable(true)]
    [Category("Appearance")]
    public Color NodeBorderColor { get; set; } = Color.White;

    [Browsable(true)]
    [Category("Appearance")]
    public int NodeBorderSize { get; set; } = 2;

    [Browsable(true)]
    [Category("Appearance")]
    public int SpinnerRadius { get; set; } = 100;

    protected override void OnPaint(PaintEventArgs e)
    {
        if (null != Parent && (BackColor.A != 255 || BackColor == Color.Transparent))
        {
            using (Bitmap bmp = new Bitmap(Parent.Width, Parent.Height))
            {
                foreach (Control control in GetIntersectingControls(Parent))
                {
                    control.DrawToBitmap(bmp, control.Bounds);
                }

                e.Graphics.DrawImage(bmp, -Left, -Top);

                if (BackColor != Color.Transparent)
                {
                    using (Brush brush = new SolidBrush(BackColor))
                    {
                        e.Graphics.FillRectangle(brush, 0, 0, Width, Height);
                    }
                }
            }
        }

        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

        PointF center = new PointF(Width / 2, Height / 2);
        int bigRadius = (int)(SpinnerRadius / 2 - NodeRadius - (NodeCount - 1) * NodeResizeRatio);
        float unitAngle = 360 / NodeCount;

        if (!DesignMode)
        {
            next++;
        }

        next = next >= NodeCount ? 0 : next;

        for (int i = next, a = 0; i < next + NodeCount; i++, a++)
        {
            int factor = i % NodeCount;
            float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
            float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
            int currRad = (int)(NodeRadius + a * NodeResizeRatio);
            PointF c1 = new PointF(c1X - currRad, c1Y - currRad);

            using (Brush brush = new SolidBrush(NodeFillColor))
            {
                e.Graphics.FillEllipse(brush, c1.X, c1.Y, 2 * currRad, 2 * currRad);
            }

            using (Pen pen = new Pen(Color.White, NodeBorderSize))
            {
                e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
            }
        }
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        timer.Enabled = Visible;
        base.OnVisibleChanged(e);
    }

    private IOrderedEnumerable<Control> GetIntersectingControls(Control parent)
    {
        return parent.Controls.Cast<Control>()
            .Where(c => parent.Controls.GetChildIndex(c) > parent.Controls.GetChildIndex(this))
            .Where(c => c.Bounds.IntersectsWith(Bounds))
            .OrderByDescending(c => parent.Controls.GetChildIndex(c));
    }
}

Spinner 控件从Toolbox 拖到您的表单中

或者,您可以在运行时使用此代码添加它

Spinner spinner = new Spinner();
spinner.Location = new Point(50, 50);
Controls.Add(spinner);

然后,您可以从Designer Properties 类别Appearance 更改其属性

您可能想要自定义的主要属性是:

  • NodeCount(默认:8)
  • NodeRadius(默认值:4)
  • NodeResizeRatio(默认值:1.0f)
  • NodeFillColor(默认:黑色)
  • NodeBorderColor(默认:白色)
  • NodeBorderSize(默认:2)
  • SpinnerRadius(默认值:100)
  • BackColor(默认:透明)(支持 alpha 透明度)
    BackColor = Color.FromArgb(50, Color.Blue.R, Color.Blue.G, Color.Blue.B))
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多