c# .net winforms custom-controls gdi+

c# - Mostrar Transparent Loading Spinner sobre otros controles



.net winforms (2)

Estoy trabajando en un control giratorio. Quiero que el control admita backcolor transparente. Cuando se dibuja el arco, hay un espacio en blanco en el medio, quiero que ese espacio sea verdaderamente transparente, de modo que pueda poner otro control detrás de él y no quede cubierto por la ruleta.

Intenté anular el vacío CreateParams.
También configuré el estilo para admitir TransparentColor.
Intenté anular el vacío de OnPaintBackground, pero no puedo lograr el color de fondo transparente real.

Entonces, ¿qué puedes sugerirme que haga?


Cambié ligeramente el código de Reza (SpinningCircles) para agregar un fondo semitransparente. Quería compartir con ustedes. (NOTA: La longitud de la ruleta se fijó en 100 y debe agregarse como una propiedad del componente)

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 }


Para crear una capa transparente, debe anular la pintura del control y dibujar el control en este orden. Primero dibuje todos los controles en el mismo contenedor que está bajo su control (basado en el índice z) en un mapa de bits. Luego dibuje ese mapa de bits en los gráficos de su control. Por fin dibuja el contenido de tu control. También el BackColor de su control debe ser Color.Transparent .

También como otra opción para crear una capa transparente, puede excluir algunas regiones de su control al dibujar.

En las siguientes muestras utilicé la primera técnica y creé 2 controles. Un círculo giratorio de control transparente. y un control de cuadro transparente .

En ambas muestras utilicé un retraso entre cargar filas para mostrar que una ruleta tiene sentido.

Muestra 1: uso de un control SpinningCircles

SpinningCircles control SpinningCircles dibuja círculos y admite transparencia. El control no se anima en tiempo de diseño, pero se anima en tiempo de ejecución. Además, no consume recursos cuando no es visible.

Ejemplo 2: uso de un control TransparentPictureBox y un gif animado TransparentPictureBox control TransparentPictureBox admite la transparencia, por lo que utilicé un gif animado como imagen y, como puede ver, el gif se muestra correctamente.

Código de ejemplo 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); } }

Código de ejemplo 2 - Código TransparentPictureBox

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); } }