web-dev-qa-db-fra.com

Dessin au-dessus des contrôles à l'intérieur d'un panneau (C # WinForms)

Je sais que cette question a été posée plusieurs fois, mais jusqu'à présent, je n'ai pas réussi à trouver une bonne solution.

J'ai un panneau avec un autre contrôle dessus.
Je veux tracer une ligne dessus et sur tous les contrôles du panneau

J'ai rencontré 3 types de solutions (aucune d'entre elles n'a fonctionné comme je le souhaitais):

  1. Obtenez le bureau DC et dessinez sur l'écran.
    Cela s'appuiera sur d'autres demandes si elles chevauchent le formulaire.

  2. Substitution des "CreateParams" du panneau:

=

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

// NOTE J'ai aussi essayé de désactiver WS_CLIPSIBLINGS

puis dessiner la ligne OnPaint (). Mais ... Comme OnPaint du panneau est appelé avant OnPaint des contrôles qu'il contient, le dessin des contrôles à l'intérieur peint simplement en haut de la ligne.
J'ai vu quelqu'un suggérer d'utiliser un filtre de message pour écouter les messages WM_Paint et utiliser une minuterie, mais je ne pense pas que cette solution soit "bonne pratique" ou efficace.
Qu'est-ce que tu ferais ? Décidez que les contrôles à l'intérieur ont fini de dessiner après X ms et réglez la minuterie sur X ms?


Cette capture d'écran montre le panneau avec WS_CLIPSIBLINGS et WS_CLIPCHILDREN désactivés.
La ligne bleue est peinte sur le panneau OnPaint, et simplement peinte par les zones de texte et l'étiquette.
La ligne rouge est peinte sur le dessus uniquement parce qu'elle n'est pas peinte à partir de l'OnPaint du panneau (elle est en fait peinte à la suite d'un clic sur un bouton)
alt text


3ème: Création d'un calque transparent et dessin au-dessus de ce calque.
J'ai créé un contrôle transparent en utilisant:

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

Le problème est toujours, en plaçant le contrôle transparent au-dessus du panneau et de tous ses contrôles.
J'ai essayé de l'amener à l'avant en utilisant: "BringToFront ()", mais cela n'a pas semblé aider.
Je l'ai mis dans le gestionnaire OnPaint () du contrôle de ligne.
Dois-je essayer de le mettre ailleurs?
- Cela crée également un problème avec un autre contrôle au-dessus du panneau. (attraper les clics de souris etc.)

Toute aide serait grandement appréciée!

** EDIT: La ligne noire est un échantillon de ce que j'essayais de faire. (fenêtres utilisées pour peindre)

alt text

40
dtroy

Il s'avère que c'est beaucoup plus facile que je ne le pensais. Merci de n'avoir accepté aucune de mes autres réponses. Voici le processus en deux étapes pour créer un Fline ( f loating ligne - désolé, il est tard):

alt text

Étape 1 : Ajoutez un UserControl à votre projet et nommez-le "Fline". Ajoutez ce qui suit aux instructions using:

using System.Drawing.Drawing2D;

Étape 2 : ajoutez ce qui suit à l'événement Fline's Resize:

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

Compilez, puis faites glisser un Fline sur votre formulaire ou panneau. Important: le BackColor par défaut est le même que le formulaire, donc changez le BackColor de Fline en Rouge ou quelque chose d'évident (dans le concepteur). Une bizarrerie bizarre à ce sujet est que lorsque vous le faites glisser dans le concepteur, il apparaît comme un bloc solide jusqu'à ce que vous le libériez - ce n'est pas énorme.

Ce contrôle peut apparaître devant ou derrière tout autre contrôle. Si vous définissez Enabled sur false, il sera toujours visible mais n'interférera pas avec les événements de la souris sur les contrôles en dessous.

Vous voudrez bien sûr améliorer cela à vos fins, mais cela montre le principe de base. Vous pouvez utiliser la même technique pour créer un contrôle de la forme que vous aimez (mon test initial en a fait un triangle).

Mise à jour : cela fait aussi un Nice one-liner dense. Mettez-le simplement dans l'événement Resize de votre 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}));
19
MusiGenesis

Si vous souhaitez que la ligne ne soit qu'une simple ligne horizontale ou verticale, placez un autre panneau (désactivé pour qu'il ne détecte aucun événement de souris) sur le panneau principal, définissez sa hauteur (ou largeur) sur 3 ou 4 pixels (ou tout ce que vous voulez), et mettez-le devant Si vous devez modifier l'emplacement de la ligne pendant l'exécution, vous pouvez simplement déplacer le panneau et le rendre visible et invisible. Voici à quoi ça ressemble:

alt text

Vous pouvez même cliquer où vous le souhaitez et les lignes n'interfèrent pas du tout. La ligne est tracée sur n'importe quel type de contrôle (bien que la partie déroulante d'un ComboBox ou d'un DatePicker soit toujours affichée au-dessus de la ligne, ce qui est bon de toute façon). La ligne bleue est exactement la même chose mais renvoyée.

9
MusiGenesis

Oui, cela peut être fait. Le problème est que le panneau et les commandes qui s'y trouvent sont toutes des fenêtres distinctes (au sens de l'API), et donc toutes des surfaces de dessin distinctes. Il n'y a pas de surface de dessin sur laquelle dessiner pour obtenir cet effet (autre que la surface d'écran de niveau supérieur, et il est considéré comme impoli de dessiner partout).

L'astuce (cough - hack - cough) consiste à tracer la ligne sur le panneau sous les contrôles, et à la tracer également sur chacun des contrôles eux-mêmes, résultant en ceci (qui persistera même lorsque vous cliquez sur les boutons et déplacez la souris):

alt text

Créez un projet Winforms (qui devrait être fourni avec Form1 par défaut). Ajoutez un panneau (nommé "panel1") et deux boutons ("button1" et "button2") sur le panneau comme indiqué. Ajoutez ce code dans le constructeur du formulaire:

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

puis ajoutez cette méthode au code du formulaire:

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

Quelque chose comme cela doit être dessiné dans l'événement Paint de chaque contrôle pour que la ligne persiste. Il est facile de dessiner directement sur les contrôles dans .NET, mais tout ce que vous dessinez est effacé lorsque quelqu'un clique sur le bouton ou déplace la souris dessus (à moins qu'il ne soit constamment redessiné dans les événements Paint, comme ici).

Notez que pour que cela fonctionne, tout contrôle dessiné doit avoir un événement Paint. Je suis sûr que vous devrez modifier cet exemple pour obtenir ce dont vous avez besoin. Si vous trouvez une bonne fonction généralisée pour cela, veuillez la poster.

Mise à jour: cette méthode ne fonctionnera pas pour les barres de défilement, les zones de texte, les zones de liste déroulante, ou fondamentalement tout ce qui contient une chose de type zone de texte (et non pas parce qu'elle ne compense que les boutons dans l'exemple ci-dessus - vous ne pouvez tout simplement pas dessiner sur haut d'une zone de texte, du moins pas de son événement Paint, du moins pas si vous êtes moi). Espérons que ce ne sera pas un problème.

6
MusiGenesis

Un panneau Windows Forms est un conteneur pour les contrôles. Si vous souhaitez dessiner quelque chose au-dessus d'autres contrôles dans un panneau, alors ce dont vous avez besoin est un autre contrôle (en haut de l'ordre z).

Heureusement, vous pouvez créer des contrôles de formulaires Windows qui ont des bordures non rectangulaires. Regardez cette technique: http://msdn.Microsoft.com/en-us/library/aa289517 (VS.71) .aspx

Pour dessiner simplement quelque chose à l'écran, utilisez un contrôle d'étiquette et désactivez AutoSize. Attachez ensuite à l'événement Paint et définissez les propriétés de taille et de région.

Voici un exemple de code:

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);
}
5
Matt Brunell

Créez un nouveau LineControl: contrôle comme ceci:

puis appelez BringToFront () après le 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<Control> 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<Control>();

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

Edit: Ajout du support diagonal

J'ai ajouté un support pour les contrôles qui repeignent lorsqu'ils obtiennent le focus.

les zones de texte et les zones de liste déroulante ne fonctionneront pas telles quelles, vous devrez créer les vôtres et y accrocher des commandes de peinture comme ceci:

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

Ce n'est pas testé et je suis sûr que vous pouvez l'améliorer

4
Hath

La seule solution simple à laquelle je peux penser est de créer des gestionnaires d'événements Paint pour chaque contrôle que vous souhaitez peindre. Coordonnez ensuite le dessin au trait entre ces gestionnaires. Ce n'est pas la solution la plus pratique, mais cela vous donnera la possibilité de peindre sur en haut des contrôles.

En supposant que le bouton est un contrôle enfant du panneau:

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
}
3
asponge

EDIT J'ai trouvé un moyen de me débarrasser du problème de peinture récursive que j'avais. Donc, maintenant, cela me semble très, très, très proche de ce que vous voulez réaliser.

Voici ce que je pourrais trouver. Il utilise l'approche n ° 3 décrite dans la question d'origine. Le code est un peu long car trois classes sont impliquées:

  1. Une classe privée appelée DecorationCanvas. Cela dérive de Panel et utilise WS_EX_TRANSPARENT pour fournir une toile transparente sur laquelle dessiner nos trucs.
  2. La classe de panneau elle-même, je l'ai appelée DecoratedPanel, elle dérive de Panel
  3. Une classe de concepteur appelée DecoratedPanelDesigner pour le panneau, pour vous assurer que le ZOrder peut être conservé pendant la phase de conception.

L'approche de base est la suivante:

  • Dans le constructeur de DecoratedPanel, créez une instance de DecorationCanvas et ajoutez-la à la collection DecoratedPanel Controls.
  • Substituez OnControlAdded et OnControlRemoved, pour accrocher/décrocher automatiquement les événements Paint pour les contrôles enfants et pour vous assurer que DecorationCanvas reste au-dessus du ZOrder.
  • À chaque fois qu'un contrôle contenu est peint, invalidez le rectangle DecorationCanvas correspondant.
  • Remplacez OnResize et OnSizeChanged pour vous assurer que DecorationCanvas a la même taille que DecoratedPanel. (J'ai essayé d'accomplir cela en utilisant la propriété Anchor, mais cela a échoué d'une manière ou d'une autre).
  • Fournissez une méthode interne pour réinitialiser le DecorationCanvas ZOrder à partir de DecoratedPanelDesigner.

Fonctionne très bien sur mon système (VS2010/.net4/Windows XP SP3). Voici le code:

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

    /// <summary>
    /// If contained controls are updated, invalidate the corresponding DecorationCanvas area
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    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;
    }

    /// <summary>
    /// This is marked internal because it gets called from the designer
    /// to make sure our DecorationCanvas stays on top of the ZOrder.
    /// </summary>
    internal void ResetDecorationZOrder()
    {
      if (Controls.GetChildIndex(_decorationCanvas) != 0)
        Controls.SetChildIndex(_decorationCanvas, 0);
    }

    private bool IsInDesignMode
    {
      get
      {
        return DesignMode || LicenseManager.UsageMode == LicenseUsageMode.Designtime;
      }
    }
  }

  /// <summary>
  /// 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 ...
  /// </summary>
  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);
    }

    /// <summary>
    /// Try and handle ZOrder changes at design time
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    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);
    }

    /// <summary>
    /// If the panel has BorderStyle.None, a dashed border needs to be drawn around it
    /// </summary>
    /// <param name="pe"></param>
    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);
        }
      }
    }
  }
}

Laissez-moi savoir ce que vous pensez ...

2
takrl

Le code d'origine doit être:

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

Cela marche !!

1
John Bryan

Je pense que la meilleure façon est d'hériter du contrôle sur lequel vous voulez tracer une ligne. Remplacez la méthode OnPaint, appelez base.Paint () de l'intérieur, après cela tracez la ligne en utilisant la même instance graphique. Dans le même temps, vous pouvez également avoir un paramètre qui spécifie à quel point la ligne doit être tracée, afin que vous puissiez contrôler la ligne directement à partir de votre formulaire principal.

1
faulty

Que diriez-vous de cette prise sur la solution # 1 (Obtenez le bureau DC et dessinez sur l'écran):

  1. Obtenez le bureau DC et l'objet graphique pour cela DC [Graphics.fromHDC (...)]
  2. Définissez la propriété Clip de l'objet Graphics résultant pour être la région actuellement visible de votre formulaire. (Je n'ai pas encore recherché comment trouver la région visible d'un formulaire)
  3. Faites votre rendu graphique.
1
Ski