Dibujando sobre los controles dentro de un panel (C # WinForms)

Sé que esta pregunta fue hecha más de una vez, pero hasta ahora no he podido encontrar una buena solución.

Tengo un panel con otro control sobre él.
Quiero trazar una línea sobre él y encima de todos los controles en el panel

Encontré 3 tipos de soluciones (ninguna funcionó como yo quería):

  1. Obtenga el escritorio DC y Draw en la pantalla.
    Esto se basará en otras aplicaciones si se superponen al formulario.

  2. Anulando los “CreateParams” del panel:

=

protected override CreateParams CreateParams { get { CreateParams cp; cp = base.CreateParams; cp.Style &= ~0x04000000; //WS_CLIPSIBLINGS cp.Style &= ~0x02000000; //WS_CLIPCHILDREN return cp; } } 

// NOTA También he intentado deshabilitar WS_CLIPSIBLINGS

y luego dibujando la línea OnPaint (). Pero … Como OnPaint del panel se llama antes que OnPaint de los controles en él, el dibujo de los controles en el interior simplemente pinta en la parte superior de la línea.
He visto a alguien sugerir usar un filtro de mensajes para escuchar los mensajes de WM_PAINT, y usar un temporizador, pero no creo que esta solución sea “buena práctica” o efectiva.
Qué harías ? ¿Decidió que los controles internos habían terminado de dibujarse después de X ms y configuró el temporizador en X ms?


Esta captura de pantalla muestra el panel con WS_CLIPSIBLINGS y WS_CLIPCHILDREN desactivados.
La línea Azul está pintada en el Panel OnPaint, y simplemente está pintada por los cuadros de texto y la etiqueta.
La línea roja está pintada en la parte superior solo porque no está pintada desde el panel OnPaint (en realidad está pintada como resultado del clic de un botón).
texto alternativo


3º: crear una capa transparente y dibujar en la parte superior de esa capa.
Creé un control transparente usando:

 protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT return cp; } } 

El problema sigue siendo poner el control transparente encima del Panel y todos sus controles.
Intenté llevarlo al frente usando: “BringToFront ()”, pero no pareció ayudar.
Lo puse en el controlador OnPaint () del control de línea.
¿Debería intentar ponerlo en otro lugar?
– Esto también crea problemas con tener otro control en la parte superior del panel. (captando los clics del mouse, etc.)

¡Cualquier ayuda sería muy apreciada!

** EDITAR: La línea negra es una muestra de lo que estaba tratando de hacer. (utilizó pintura de ventanas para pintar)

texto alternativo

Resulta que esto es mucho más fácil de lo que pensaba. Gracias por no aceptar ninguna de mis otras respuestas. Aquí está el proceso de dos pasos para crear una Fline ( línea de pérdida, lo siento, es tarde):

texto alternativo http://sofes.miximages.com/c%23/www.freeimagehosting.net

Paso 1 : Agregue un UserControl a su proyecto y asígnele el nombre “Fline”. Agregue lo siguiente a las instrucciones de uso:

 using System.Drawing.Drawing2D; 

Paso 2 : agregue lo siguiente al evento de cambio de tamaño de Fline:

 int wfactor = 4; // half the line width, kinda // create 6 points for path Point[] pts = { new Point(0, 0), new Point(wfactor, 0), new Point(Width, Height - wfactor), new Point(Width, Height) , new Point(Width - wfactor, Height), new Point(0, wfactor) }; // magic numbers! byte[] types = { 0, // start point 1, // line 1, // line 1, // line 1, // line 1 }; // line GraphicsPath path = new GraphicsPath(pts, types); this.Region = new Region(path); 

Comstack, y luego arrastra una Fline en tu formulario o panel. Importante: el BackColor predeterminado es el mismo que el del formulario, por lo tanto, cambie BackColor de la Fline a Rojo o algo obvio (en el diseñador). Una peculiaridad extraña sobre esto es que cuando lo arrastra en el diseñador, se muestra como un bloque sólido hasta que lo libera, no es un gran problema.

Este control puede aparecer delante o detrás de cualquier otro control. Si configura Enabled en false, seguirá siendo visible, pero no interferirá con los eventos del mouse en los controles que se encuentran debajo.

Querrá mejorar esto para sus propósitos, por supuesto, pero esto muestra el principio básico. Puedes usar la misma técnica para crear un control de cualquier forma que quieras (mi prueba inicial de esto hizo un triángulo).

Actualización : esto también hace un buen trazador de líneas denso y denso. Simplemente coloque esto en el evento Resize de su UserControl:

 this.Region=new Region(new System.Drawing.Drawing2D.GraphicsPath(new Point[]{new Point(0,0),new Point(4,0),new Point(Width,Height-4),new Point(Width,Height),new Point(Width-4,Height),new Point(0,4)},new byte[]{0,1,1,1,1,1})); 

Si desea que la línea sea simplemente una línea horizontal o vertical simple, coloque otro panel (deshabilitado para que no capte ningún evento del mouse) en el panel principal, establezca su altura (o ancho) en 3 o 4 píxeles (o lo que quieras), y llevarlo al frente. Si necesita cambiar dónde está la línea durante el tiempo de ejecución, puede mover el panel y hacer que sea visible e invisible. Así es como se ve:

texto alternativo http://sofes.miximages.com/c%23/www.freeimagehosting.net

Incluso puede hacer clic en cualquier lugar que desee, y las líneas no interfieren en absoluto. La línea se dibuja sobre cualquier tipo de control (aunque la parte desplegable de un ComboBox o un DatePicker todavía se muestra por encima de la línea, lo que de todos modos es bueno). La línea azul es exactamente la misma cosa pero enviada a atrás.

Si, esto se puede hacer. El problema es que el panel y los controles son todos ventanas separadas (en el sentido API) y, por lo tanto, todas las superficies de dibujo separadas. No hay una sola superficie de dibujo para obtener este efecto (que no sea la superficie de la pantalla de nivel superior, y se considera de mala educación dibujar todo eso).

El truco ( toser) es trazar la línea en el panel debajo de los controles, y también dibujarlo en cada uno de los controles, lo que resulta en esto (que persistirá incluso al hacer clic en los botones y mover el mouse alrededor )

texto alternativo http://sofes.miximages.com/c%23/www.freeimagehosting.net

Cree un proyecto de winforms (que debería venir con Form1 por defecto). Agregue un panel (llamado “panel1”) y dos botones (“botón1” y “botón2”) en el panel como se muestra. Agregue este código en el constructor del formulario:

 panel1.Paint += PaintPanelOrButton; button1.Paint += PaintPanelOrButton; button2.Paint += PaintPanelOrButton; 

y luego agregue este método al código del formulario:

 private void PaintPanelOrButton(object sender, PaintEventArgs e) { // center the line endpoints on each button Point pt1 = new Point(button1.Left + (button1.Width / 2), button1.Top + (button1.Height / 2)); Point pt2 = new Point(button2.Left + (button2.Width / 2), button2.Top + (button2.Height / 2)); if (sender is Button) { // offset line so it's drawn over the button where // the line on the panel is drawn Button btn = (Button)sender; pt1.X -= btn.Left; pt1.Y -= btn.Top; pt2.X -= btn.Left; pt2.Y -= btn.Top; } e.Graphics.DrawLine(new Pen(Color.Red, 4.0F), pt1, pt2); } 

Algo así debe dibujarse en el evento Paint de cada control para que la línea persista. Es fácil dibujar directamente los controles en .NET, pero cualquier cosa que dibuje se borrará cuando alguien haga clic en el botón o mueva el mouse sobre él (a menos que se vuelva a dibujar perpetuamente en los eventos de Paint, como aquí).

Tenga en cuenta que para que esto funcione, cualquier control dibujado debe tener un evento Paint. Estoy seguro de que tendrá que modificar esta muestra para lograr lo que necesita. Si se le ocurre una buena función generalizada para esto, publíquelo.

Actualización: este método no funcionará para las barras de desplazamiento, cuadros de texto, cuadros combinados, listas de vista, o básicamente cualquier cosa con un elemento de tipo cuadro de texto como parte de él (y no porque solo compensa los botones en el ejemplo anterior; simplemente no puede dibujar al principio de un cuadro de texto, al menos no de su evento Paint, al menos no si eres yo). Espero que eso no sea un problema.

Un panel de formularios de Windows es un contenedor para controles. Si desea dibujar algo encima de otros controles dentro de un panel, entonces lo que necesita es otro control (en la parte superior del orden z).

Afortunadamente, puede crear controles de formularios de Windows que tengan bordes no rectangulares. Mire esta técnica: http://msdn.microsoft.com/en-us/library/aa289517(VS.71).aspx

Para dibujar algo en la pantalla, use un control de etiqueta y desactive AutoSize. A continuación, conéctelo al evento Paint y configure Tamaño y Propiedades de región.

Aquí hay una muestra de código:

 private void label1_Paint(object sender, PaintEventArgs e) { System.Drawing.Drawing2D.GraphicsPath myGraphicsPath = new System.Drawing.Drawing2D.GraphicsPath(); myGraphicsPath.AddEllipse(new Rectangle(0, 0, 125, 125)); myGraphicsPath.AddEllipse(new Rectangle(75, 75, 20, 20)); myGraphicsPath.AddEllipse(new Rectangle(120, 0, 125, 125)); myGraphicsPath.AddEllipse(new Rectangle(145, 75, 20, 20)); //Change the button's background color so that it is easy //to see. label1.BackColor = Color.ForestGreen; label1.Size = new System.Drawing.Size(256, 256); label1.Region = new Region(myGraphicsPath); } 

La única solución simple en la que puedo pensar es crear controladores de eventos Paint para cada control sobre el que quieras pintar. Luego coordine el dibujo de líneas entre estos manejadores. Esta no es la solución más conveniente, sin embargo, esto le dará la capacidad de pintar encima de los controles.

El botón Asumir es un control secundario del panel:

 panel.Paint += new PaintEventHandler(panel_Paint); button.Paint += new PaintEventHandler(button_Paint); protected void panel_Paint(object sender, PaintEventArgs e) { //draw the full line which will then be partially obscured by child controls } protected void button_Paint(object sender, PaintEventArgs e) { //draw the obscured line portions on the button } 

Haga un nuevo LineControl: Control de esta manera:

luego llame a BringToFront () después de InitializeComponent

 public partial class MainForm : Form { public MainForm() { InitializeComponent(); this.simpleLine1.BringToFront(); } } using System; using System.Windows.Forms; using System.Drawing; using System.Collections.Generic; public class SimpleLine : Control { private Control parentHooked; private List controlsHooked; public enum LineType { Horizontal, Vertical, ForwardsDiagonal, BackwardsDiagonal } public event EventHandler AppearanceChanged; private LineType appearance; public virtual LineType Appearance { get { return appearance; } set { if (appearance != value) { this.SuspendLayout(); switch (appearance) { case LineType.Horizontal: if (value == LineType.Vertical) { this.Height = this.Width; } break; case LineType.Vertical: if (value == LineType.Horizontal) { this.Width = this.Height; } break; } this.ResumeLayout(false); appearance = value; this.PerformLayout(); this.Invalidate(); } } } protected virtual void OnAppearanceChanged(EventArgs e) { if (AppearanceChanged != null) AppearanceChanged(this, e); } public event EventHandler LineColorChanged; private Color lineColor; public virtual Color LineColor { get { return lineColor; } set { if (lineColor != value) { lineColor = value; this.Invalidate(); } } } protected virtual void OnLineColorChanged(EventArgs e) { if (LineColorChanged != null) LineColorChanged(this, e); } public event EventHandler LineWidthChanged; private float lineWidth; public virtual float LineWidth { get { return lineWidth; } set { if (lineWidth != value) { if (0 >= value) { lineWidth = 1; } lineWidth = value; this.PerformLayout(); } } } protected virtual void OnLineWidthChanged(EventArgs e) { if (LineWidthChanged != null) LineWidthChanged(this, e); } public SimpleLine() { base.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Selectable, false); base.SetStyle(ControlStyles.SupportsTransparentBackColor, true); base.BackColor = Color.Transparent; InitializeComponent(); appearance = LineType.Vertical; LineColor = Color.Black; LineWidth = 1; controlsHooked = new List(); this.ParentChanged += new EventHandler(OnSimpleLineParentChanged); } private void RemoveControl(Control control) { if (controlsHooked.Contains(control)) { control.Paint -= new PaintEventHandler(OnControlPaint); if (control is TextboxX) { TextboxX text = (TextboxX)control; text.DoingAPaint -= new EventHandler(text_DoingAPaint); } controlsHooked.Remove(control); } } void text_DoingAPaint(object sender, EventArgs e) { this.Invalidate(); } private void AddControl(Control control) { if (!controlsHooked.Contains(control)) { control.Paint += new PaintEventHandler(OnControlPaint); if (control is TextboxX) { TextboxX text = (TextboxX)control; text.DoingAPaint += new EventHandler(text_DoingAPaint); } controlsHooked.Add(control); } } private void OnSimpleLineParentChanged(object sender, EventArgs e) { UnhookParent(); if (Parent != null) { foreach (Control c in Parent.Controls) { AddControl(c); } Parent.ControlAdded += new ControlEventHandler(OnParentControlAdded); Parent.ControlRemoved += new ControlEventHandler(OnParentControlRemoved); parentHooked = this.Parent; } } private void UnhookParent() { if (parentHooked != null) { foreach (Control c in parentHooked.Controls) { RemoveControl(c); } parentHooked.ControlAdded -= new ControlEventHandler(OnParentControlAdded); parentHooked.ControlRemoved -= new ControlEventHandler(OnParentControlRemoved); parentHooked = null; } } private void OnParentControlRemoved(object sender, ControlEventArgs e) { RemoveControl(e.Control); } private void OnControlPaint(object sender, PaintEventArgs e) { int indexa =Parent.Controls.IndexOf(this) , indexb = Parent.Controls.IndexOf((Control)sender); //if above invalidate on paint if(indexa < indexb) { Invalidate(); } } private void OnParentControlAdded(object sender, ControlEventArgs e) { AddControl(e.Control); } private System.ComponentModel.IContainer components = null; private void InitializeComponent() { components = new System.ComponentModel.Container(); } protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT return cp; } } protected override void OnLayout(LayoutEventArgs levent) { switch (this.Appearance) { case LineType.Horizontal: this.Height = (int)LineWidth; this.Invalidate(); break; case LineType.Vertical: this.Width = (int)LineWidth; this.Invalidate(); break; } base.OnLayout(levent); } protected override void OnPaintBackground(PaintEventArgs pevent) { //disable background paint } protected override void OnPaint(PaintEventArgs pe) { switch (Appearance) { case LineType.Horizontal: DrawHorizontalLine(pe); break; case LineType.Vertical: DrawVerticalLine(pe); break; case LineType.ForwardsDiagonal: DrawFDiagonalLine(pe); break; case LineType.BackwardsDiagonal: DrawBDiagonalLine(pe); break; } } private void DrawFDiagonalLine(PaintEventArgs pe) { using (Pen p = new Pen(this.LineColor, this.LineWidth)) { pe.Graphics.DrawLine(p, this.ClientRectangle.X, this.ClientRectangle.Bottom, this.ClientRectangle.Right, this.ClientRectangle.Y); } } private void DrawBDiagonalLine(PaintEventArgs pe) { using (Pen p = new Pen(this.LineColor, this.LineWidth)) { pe.Graphics.DrawLine(p, this.ClientRectangle.X, this.ClientRectangle.Y, this.ClientRectangle.Right, this.ClientRectangle.Bottom); } } private void DrawHorizontalLine(PaintEventArgs pe) { int y = this.ClientRectangle.Height / 2; using (Pen p = new Pen(this.LineColor, this.LineWidth)) { pe.Graphics.DrawLine(p, this.ClientRectangle.X, y, this.ClientRectangle.Width, y); } } private void DrawVerticalLine(PaintEventArgs pe) { int x = this.ClientRectangle.Width / 2; using (Pen p = new Pen(this.LineColor, this.LineWidth)) { pe.Graphics.DrawLine(p,x, this.ClientRectangle.Y, x, this.ClientRectangle.Height); } } } 

Editar: Añadido soporte diagonal

He agregado algo de soporte para los controles que se vuelven a pintar cuando obtienen el foco.

los cuadros de texto y los cuadros combinados no funcionarán, ya que necesitarás hacer los tuyos propios y enlazar los comandos paintish de la siguiente manera:

 public class TextboxX : TextBox { public event EventHandler DoingAPaint; protected override void WndProc(ref Message m) { switch ((int)m.Msg) { case (int)NativeMethods.WindowMessages.WM_PAINT: case (int)NativeMethods.WindowMessages.WM_ERASEBKGND: case (int)NativeMethods.WindowMessages.WM_NCPAINT: case 8465: //not sure what this is WM_COMMAND? if(DoingAPaint!=null)DoingAPaint(this,EventArgs.Empty); break; } base.WndProc(ref m); } } 

No está probado y estoy seguro de que puedes mejorarlo

EDITAR Encontré una manera de deshacerme del problema de la pintura recursiva que tenía. Entonces, ahora, para mí, esto se ve muy, muy, muy cerca de lo que quieres lograr.

Esto es lo que podría hacer. Utiliza el enfoque n. ° 3 delineado en la pregunta original. El código es algo extenso porque están involucradas tres clases:

  1. Una clase privada llamada DecorationCanvas. Esto se deriva de Panel y usa WS_EX_TRANSPARENT para proporcionar un canvas transparente para dibujar nuestras cosas
  2. La clase del panel en sí, lo llamé DecoratedPanel, deriva del Panel
  3. Una clase de diseñador llamada DecoratedPanelDesigner para el panel, para asegurarse de que ZOrder se pueda conservar durante el tiempo de Diseño.

El enfoque básico es:

  • En el constructor de DecoratedPanel, crea una instancia de DecorationCanvas y agrégala a la colección DecoratedPanel Controls.
  • Anule OnControlAdded y OnControlRemoved, para enganchar / desenganchar automáticamente eventos de pintura para controles secundarios, y para asegurarse de que DecorationCanvas permanezca en la parte superior de ZOrder.
  • Siempre que un control contenido pinta, invalida el rectángulo DecorativeCanvas correspondiente.
  • Anule OnResize y OnSizeChanged para asegurarse de que DecorationCanvas tenga el mismo tamaño que DecoratedPanel. (Traté de lograr esto usando la propiedad Anchor, pero falló de alguna manera).
  • Proporcione un método interno para restablecer DecorationCanvas ZOrder desde DecoratedPanelDesigner.

Funciona bien en mi sistema (VS2010 / .net4 / Windows XP SP3). Aquí está el código:

 using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.Design; namespace WindowsFormsApplication3 { [Designer("WindowsFormsApplication3.DecoratedPanelDesigner")] public class DecoratedPanel : Panel { #region decorationcanvas // this is an internal transparent panel. // This is our canvas we'll draw the lines on ... private class DecorationCanvas : Panel { public DecorationCanvas() { // don't paint the background SetStyle(ControlStyles.Opaque, true); } protected override CreateParams CreateParams { get { // use transparency CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT return cp; } } } #endregion private DecorationCanvas _decorationCanvas; public DecoratedPanel() { // add our DecorationCanvas to our panel control _decorationCanvas = new DecorationCanvas(); _decorationCanvas.Name = "myInternalOverlayPanel"; _decorationCanvas.Size = ClientSize; _decorationCanvas.Location = new Point(0, 0); // this prevents the DecorationCanvas to catch clicks and the like _decorationCanvas.Enabled = false; _decorationCanvas.Paint += new PaintEventHandler(decoration_Paint); Controls.Add(_decorationCanvas); } protected override void Dispose(bool disposing) { if (disposing && _decorationCanvas != null) { // be a good citizen and clean up after yourself _decorationCanvas.Paint -= new PaintEventHandler(decoration_Paint); Controls.Remove(_decorationCanvas); _decorationCanvas = null; } base.Dispose(disposing); } void decoration_Paint(object sender, PaintEventArgs e) { // --- PAINT HERE --- e.Graphics.DrawLine(Pens.Red, 0, 0, ClientSize.Width, ClientSize.Height); } protected override void OnControlAdded(ControlEventArgs e) { base.OnControlAdded(e); if (IsInDesignMode) return; // Hook paint event and make sure we stay on top if (!_decorationCanvas.Equals(e.Control)) e.Control.Paint += new PaintEventHandler(containedControl_Paint); ResetDecorationZOrder(); } protected override void OnControlRemoved(ControlEventArgs e) { base.OnControlRemoved(e); if (IsInDesignMode) return; // Unhook paint event if (!_decorationCanvas.Equals(e.Control)) e.Control.Paint -= new PaintEventHandler(containedControl_Paint); } ///  /// If contained controls are updated, invalidate the corresponding DecorationCanvas area ///  ///  ///  void containedControl_Paint(object sender, PaintEventArgs e) { Control c = sender as Control; if (c == null) return; _decorationCanvas.Invalidate(new Rectangle(c.Left, c.Top, c.Width, c.Height)); } protected override void OnResize(EventArgs eventargs) { base.OnResize(eventargs); // make sure we're covering the panel control _decorationCanvas.Size = ClientSize; } protected override void OnSizeChanged(EventArgs e) { base.OnSizeChanged(e); // make sure we're covering the panel control _decorationCanvas.Size = ClientSize; } ///  /// This is marked internal because it gets called from the designer /// to make sure our DecorationCanvas stays on top of the ZOrder. ///  internal void ResetDecorationZOrder() { if (Controls.GetChildIndex(_decorationCanvas) != 0) Controls.SetChildIndex(_decorationCanvas, 0); } private bool IsInDesignMode { get { return DesignMode || LicenseManager.UsageMode == LicenseUsageMode.Designtime; } } } ///  /// Unfortunately, the default designer of the standard panel is not a public class /// So we'll have to build a new designer out of another one. Since Panel inherits from /// ScrollableControl, let's try a ScrollableControlDesigner ... ///  public class DecoratedPanelDesigner : ScrollableControlDesigner { private IComponentChangeService _changeService; public override void Initialize(IComponent component) { base.Initialize(component); // Acquire a reference to IComponentChangeService. this._changeService = GetService(typeof(IComponentChangeService)) as IComponentChangeService; // Hook the IComponentChangeService event if (this._changeService != null) this._changeService.ComponentChanged += new ComponentChangedEventHandler(_changeService_ComponentChanged); } ///  /// Try and handle ZOrder changes at design time ///  ///  ///  void _changeService_ComponentChanged(object sender, ComponentChangedEventArgs e) { Control changedControl = e.Component as Control; if (changedControl == null) return; DecoratedPanel panelPaint = Control as DecoratedPanel; if (panelPaint == null) return; // if the ZOrder of controls contained within our panel changes, the // changed control is our control if (Control.Equals(panelPaint)) panelPaint.ResetDecorationZOrder(); } protected override void Dispose(bool disposing) { if (disposing) { if (this._changeService != null) { // Unhook the event handler this._changeService.ComponentChanged -= new ComponentChangedEventHandler(_changeService_ComponentChanged); this._changeService = null; } } base.Dispose(disposing); } ///  /// If the panel has BorderStyle.None, a dashed border needs to be drawn around it ///  ///  protected override void OnPaintAdornments(PaintEventArgs pe) { base.OnPaintAdornments(pe); Panel panel = Control as Panel; if (panel == null) return; if (panel.BorderStyle == BorderStyle.None) { using (Pen p = new Pen(SystemColors.ControlDark)) { p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; pe.Graphics.DrawRectangle(p, 0, 0, Control.Width - 1, Control.Height - 1); } } } } } 

Déjame saber lo que piensas …

Creo que la mejor manera es heredar el control del que desea trazar una línea. Reemplace el método OnPaint, llame a base.Paint () desde adentro, luego dibuje la línea usando la misma instancia gráfica. Al mismo tiempo, también puede tener un parámetro específico en el que se dibujará la línea, para que pueda controlar la línea directamente desde su formulario principal.

El código original debe ser:

  protected override CreateParams CreateParams { get { CreateParams cp; cp = base.CreateParams; cp.Style &= 0x7DFFFFFF; //WS_CLIPCHILDREN return cp; } } 

Esto funciona !!

¿Qué le parece esta solución # 1 (Obtenga el escritorio DC y Draw en la pantalla):

  1. Obtenga el DC de escritorio y el objeto de gráficos para ese DC [Graphics.fromHDC (…)]
  2. Establezca la propiedad Clip del objeto Graphics resultante para que sea la región actualmente visible de su formulario. (No he investigado aún cómo encontrar la región visible de un formulario)
  3. Haz tu renderizado de gráficos