Show Transparent Loading Spinner above other Controls

前端 未结 2 672
青春惊慌失措
青春惊慌失措 2020-11-29 07:08

I am working in a spinner control. I want the control to support transparent backcolor. When the arc is drawn, there is a blank space in the middle, I want that space to be

相关标签:
2条回答
  • 2020-11-29 07:28

    To make a transparent layer, you should override painting of the control and draw the control in this order, First draw all controls in the same container which are under your control (based on z-index) on a bitmap. Then draw that bitmap on graphics of your control. At last draw content of your control. Also the BackColor of your control should be Color.Transparent.

    Also as another option to make transparent layer, you can exclude some regions from your control when drawing.

    In the following samples I used the first technique and created 2 controls. A spinning circles transparent control. and a transparent picturebox control.

    In both samples I used a delay between loading rows to showing a spinner make sense.

    Sample 1 - Using a SpinningCircles Control

    SpinningCircles control draws circles and supports transparency. The control doesn't animate at design-time, but it animates at run-time. Also it doesn't consume resources when it is not visible.

    Sample 2 - Using a TransparentPictureBox Control and a transparent animated gif TransparentPictureBox control supports transparency, so I used an animated gif as its image and as you can see, the gif is showing correctly.

    Sample 1 Code - 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);
        }
    }
    

    Sample 2 Code - TransparentPictureBox Code

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-29 07:29

    I slightly changed Reza's code (SpinningCircles) to add a semi-transparent background. I wanted to share it with you. (NOTE: The spinner length was fixed to 100 and should be added as a component property)

    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
    }
    
    0 讨论(0)
提交回复
热议问题