【问题标题】:Drawing on top of controls inside a panel (C# WinForms)在面板内的控件顶部绘图(C# WinForms)
【发布时间】:2008-11-12 02:37:24
【问题描述】:

我知道这个问题已经被问了好几次了,但到目前为止我还没有找到一个好的解决方案。

我有一个面板,上面有其他控件。
我想在它上面和面板中所有控件的顶部画一条线

我遇到了 3 种类型的解决方案(没有一种按我想要的方式工作):

  1. 获取桌面 DC 并在屏幕上绘制。
    如果它们与表单重叠,这将利用其他应用程序。

  2. 覆盖面板的“CreateParams”:

=

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

//注意我也试过禁用 WS_CLIPSIBLINGS

然后画线 OnPaint()。 但是...由于面板的 OnPaint 在其中的控件的 OnPaint 之前被调用, 内部控件的绘制只是简单地绘制在行的顶部。
我看到有人建议使用消息过滤器来收听 WM_PAINT 消息,并使用计时器,但我认为这种解决方案既不是“好的做法”,也不是有效的。
你会怎么做 ?判断里面的控件在X ms后绘制完毕,设置定时器为X ms?


此屏幕截图显示了关闭 WS_CLIPSIBLINGS 和 WS_CLIPCHILDREN 的面板。
蓝线在 Panel 的 OnPaint 处绘制,并由文本框和标签简单地绘制。
红线绘制在顶部只是因为它不是从面板的 OnPaint 绘制的(实际上是由于单击了按钮而绘制的)


第三个:创建一个透明图层并在该图层上绘图。
我使用以下方法创建了一个透明控件:

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

问题仍然存在,将透明控件置于面板及其所有控件之上。
我曾尝试使用: "BringToFront()" 将其置于最前面,但似乎没有帮助。
我已将它放在 Line 控件的 OnPaint() 处理程序中。
我应该尝试将它放在其他地方吗??
- 这也会造成在面板顶部有另一个控件的问题。 (捕捉鼠标点击等)

任何帮助将不胜感激!

**编辑: 黑线是我尝试做的一个示例。 (用窗户漆画的)

【问题讨论】:

  • 我要问:为什么? (抱歉,我讨厌人们这么问——我不是说这是个坏主意,但我很好奇)
  • 好吧,我猜:你有一个控件供用户在表单周围拖动,而这里的拖动控件和其他控件之间划了一条线?
  • 我添加了一张照片来展示我正在尝试做的事情。我现在正在尝试您的标签解决方案。这是一个黑客,但现在可能就足够了。为了讨论,如果线不是水平或垂直的怎么办?有什么想法吗?
  • 这篇文章可能对这个主题有所帮助,但不确定它是否是解决方案:http://www.codeproject.com/KB/GDI-plus/WindowGraphics.aspx 我想我会添加它以防它帮助其他人。
  • Here 是另一种解决方案..

标签: c# .net winforms


【解决方案1】:

解决方案 #1(获取桌面 DC 并在屏幕上绘制)怎么样:

  1. 获取桌面 DC 和该 DC 的图形对象 [Graphics.fromHDC(...)]
  2. 将生成的 Graphics 对象的 Clip 属性设置为表单的当前可见区域。 (我还没有研究如何找到表单的可见区域)
  3. 进行图形渲染。

【讨论】:

    【解决方案2】:

    事实证明,这比我想象的要容易得多。感谢您不接受我的任何其他答案。以下是创建 Fline 的两步过程(floating line - 抱歉,来晚了):

    第 1 步:将 UserControl 添加到您的项目中,并将其命名为“Fline”。在 using 语句中添加以下内容:

    using System.Drawing.Drawing2D;
    

    第 2 步:将以下内容添加到 Fline 的 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);
    

    编译,然后将 Fline 拖到您的表单或面板上。重要提示:默认 BackColor 与表单相同,因此将 Fline 的 BackColor 更改为 Red 或其他明显的(在设计器中)。一个奇怪的怪癖是,当您在设计器中拖动它时,它会显示为一个实心块,直到您释放它 - 没什么大不了的。

    此控件可以出现在任何其他控件的前面或后面。如果将 Enabled 设置为 false,它仍然可见,但不会干扰下方控件上的鼠标事件。

    当然,您需要根据自己的目的来增强它,但这显示了基本原则。您可以使用相同的技术来创建您喜欢的任何形状的控件(我对此的初步测试制作了一个三角形)。

    更新:这也是一个很好的密集单线。只需将它放在您的 UserControl 的 Resize 事件中:

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

    【讨论】:

    • MusiGenesis,感谢您的回答。它有很大帮助。但是..我今天又在玩它,我试图创建一条 1 像素宽的虚线,类似于您上面答案中的红线。由于我无法打开 GraphicsPath,因此我试图使控件透明
    • 继续... 然后在上面画一条线。但是我找不到一种可以与它下面的所有控件一起工作的方法等等。你有什么想法吗?谢谢!
    • “漂亮的密集单线”总是一件坏事。
    【解决方案3】:

    我能想到的唯一简单的解决方案是为要在其上绘制的每个控件创建 Paint 事件处理程序。然后协调这些处理程序之间的线条绘制。这不是最方便的解决方案,但是这将使您能够在控件的 top 上进行绘制。

    假设按钮是面板的子控件:

    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
    }
    

    【讨论】:

      【解决方案4】:

      是的,这是可以做到的。问题是面板和它上面的控件都是独立的窗口(在 API 意义上),因此都是独立的绘图表面。没有一个绘图表面可以绘制来获得这种效果(除了顶级屏幕表面,在上面绘制被认为是不礼貌的)。

      (cough-hack-cough) 技巧是在控件下方的面板上画线,并在每个控件本身上画线,从而导致这种情况(即使当您单击按钮并移动鼠标时):

      创建一个 winforms 项目(默认情况下应与 Form1 一起提供)。如图所示,在面板上添加一个面板(名为“panel1”)和两个按钮(“button1”和“button2”)。在表单的构造函数中添加这段代码:

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

      然后将此方法添加到表单的代码中:

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

      需要在每个控件的 Paint 事件中绘制类似的内容,以使线条持续存在。直接在 .NET 中的控件上绘图很容易,但是当有人单击按钮或将鼠标移到它上面时,您绘制的任何内容都会被擦除(除非它在 ​​Paint 事件中永久重绘,如此处所示)。

      请注意,要使其正常工作,任何绘制的控件都必须具有 Paint 事件。我相信您将不得不修改此示例以实现您所需要的。如果你为此想出了一个很好的泛化函数,请发布它。

      更新:此方法不适用于滚动条、文本框、组合框、列表视图或基本上任何带有文本框类型的东西作为它的一部分(并不是因为它只偏移上面示例中的按钮 - 你只是可以'根本不会在文本框上绘制,至少不是从它的 Paint 事件中绘制,至少如果你是我的话)。希望这不会是个问题。

      【讨论】:

        【解决方案5】:

        如果您希望线条只是一条简单的水平线或垂直线,请在主面板上放置另一个面板(禁用,因此它不会拾取任何鼠标事件),将其高度(或宽度)设置为 3 或 4像素(或任何你想要的),并将其放在前面。如果您需要在运行时更改线条的位置,您只需移动面板并使其可见和不可见。这是它的外观:

        你甚至可以点击任何你喜欢的地方,而且线条完全不会干扰。这条线是在任何类型的控件上绘制的(尽管 ComboBox 或 DatePicker 的下拉部分仍然显示在这条线的上方,这无论如何都很好)。蓝线是同样的东西,但被发送到后面。

        【讨论】:

        • 似乎必须先将标签添加到面板才能最后绘制。玩 BringToFront() 似乎没有多大帮助
        • 您可以使用另一个面板(1 或 2 像素宽)来代替标签。如果你把它放在前面,无论如何它都会在面板上的所有其他东西前面。
        • 我在玩它时注意到的一点:如果线条位于禁用“WS_CLIPSIBLINGS”的控件上,则线条不会绘制在它上面。否则,似乎还可以。但是如果我们想要一条对角线,我们会怎么做呢?
        • 顺便说一句,为什么我的其他答案不起作用? (按钮上方有对角红线的那个)
        • 不是没用,只是不想钩住控件的事件处理程序。我不知道我正在绘制的表面上会有多少控件。如果我们画一些比线条更复杂的东西,比如移动的形状等等。你明白我的意思吗?
        【解决方案6】:

        制作一个新的 LineControl :像这样控制:

        然后在 InitializeComponent 之后调用 BringToFront()

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

        编辑:添加对角线支持

        我添加了一些对获得焦点时重绘的控件的支持。

        文本框和组合框无法正常工作,您需要自己制作并在其中挂上类似这样的油漆命令:

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

        它没有经过测试,我相信你可以改进它

        【讨论】:

        • Hath,这可能是一个不错的解决方案,但是.. 看看(见下图)当您将鼠标悬停在该行下方的按钮下方或该行下方的控件获取时会发生什么重点。 i73.photobucket.com/albums/i201/sdjc1/temp/screen4.png
        • @dtroy - 我添加了代码,当 parents.controls 集合中的另一个控件绘制时会失效。它不适用于文本框或组合框,但你可以看到我的内容已经完成了覆盖它们。不确定这是否能满足您的需求,但您可以让它满足您的需求。
        • 只是对代码的快速评论(还没有机会运行它):8465=0x02111,即 OCM_COMMAND。那是你想要得到的吗?另外,我注意到在“OnSimpleLineParentChanged”中,您可能希望在 (Parent!=null)&&(parentHooked!=null) 10x 时将事件与 parentHooked 分离
        • @dtroy - 添加了 parentHooked 的分离。只是注意到如果你做对角线会有一些问题..说如果你想选择一个被线覆盖的控件你不能因为线控件覆盖它..我想你可以以某种方式中继鼠标消息但我不知道..有点脏。
        • 这可能是有史以来绘制单行代码最多的一次。 :)
        【解决方案7】:

        Windows 窗体面板是控件的容器。如果您想在面板中的其他控件之上绘制一些东西,那么您需要的是另一个控件(在 z 顺序的顶部)。

        幸运的是,您可以创建具有非矩形边框的窗体控件。看看这个技巧:http://msdn.microsoft.com/en-us/library/aa289517(VS.71).aspx

        要在屏幕上绘制一些东西,请使用标签控件,然后关闭 AutoSize。然后附加到 Paint 事件并设置 Size 和 Region 属性。

        这是一个代码示例:

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

        【讨论】:

          【解决方案8】:

          编辑找到了摆脱递归绘画问题的方法。所以,现在,对我来说,这看起来非常、非常、非常接近您想要实现的目标。

          这是我能想到的。它使用原始问题中概述的方法#3。代码有点长,因为涉及到三个类:

          1. 一个名为DecorationCanvas 的私有类。这源自 Panel 并使用 WS_EX_TRANSPARENT 提供透明画布来绘制我们的东西
          2. 面板类本身,我称之为DecoratedPanel,它派生自Panel
          3. 一个名为 DecoratedPanelDesigner 的面板设计器类,以确保在设计期间可以保留 ZOrder。

          基本做法是:

          • 在 DecoratedPanel 的构造函数中,创建DecoratedCanvas 实例并将其添加到DecoratedPanel Controls 集合中。
          • 覆盖 OnControlAdded 和 OnControlRemoved,以自动挂钩/取消挂钩子控件的绘制事件,并确保 DecorationCanvas 保持在 ZOrder 之上。
          • 每当包含的控件绘制时,都会使相应的DecorationCanvas 矩形无效。
          • 覆盖 OnResize 和 OnSizeChanged 以确保 DecorationCanvas 与 DecoratedPanel 具有相同的大小。 (我尝试使用 Anchor 属性来完成此操作,但不知何故失败了)。
          • 提供从 DecoratedPanelDesigner 中重置 DecorationCanvas ZOrder 的内部方法。

          在我的系统(VS2010 / .net4 / Windows XP SP3)上运行良好。代码如下:

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

          让我知道你的想法......

          【讨论】:

            【解决方案9】:

            我认为最好的方法是继承您要在其上画线的控件。覆盖 OnPaint 方法,从内部调用 base.Paint(),然后使用相同的图形实例绘制线条。同时,你也可以有一个参数来指定在哪一点画线,这样你就可以直接从你的主窗体中控制线。

            【讨论】:

            • 做到了。不好。正如我在问题中提到的,它顶部的控件在面板的“OnPaint()”返回后进行绘制。
            • 您正在覆盖面板的 OnPaint,但我指的是控件本身的 OnPaint(显示“波浪”的那个)。如果还是不行,那你用什么控件来显示“wave”?
            • 图片中的控件是继承自UserControl,但同样适用于Label、TextBox等。问题是..图片中有8个控件,我的4个+ 4个Splitter。你是在建议我在每个人身上画一段线吗?
            • 如果面板上有其他控件(不同类型)怎么办?覆盖他们所有的 OnPaint()s 吗?虽然我知道这是一种可能性,但我希望有更好的解决方案。顺便说一句,感谢您的帮助
            • 抱歉回复晚了。是的,我指的是为它们中的每一个绘制片段,并为它们中的每一个正确添加一个,以便您可以轻松地设置何时希望该行来自您的用户控件。喜欢: ControlA.LinePosition = new Point(100, 0); ControlB.LinePosition = new Point(100, 0);
            【解决方案10】:

            原代码应该是:

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

            这行得通!

            【讨论】:

            • 约翰,感谢您的回复。您对问题中的错误是正确的,我已经修复了它。这将允许您在其他控件之上进行绘制,但由于面板中的控件是在面板的 OnPaint 之后绘制的,因此它们将简单地绘制在您所绘制的之上。
            猜你喜欢
            • 2018-01-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-23
            • 1970-01-01
            • 2013-07-07
            • 2011-07-27
            • 1970-01-01
            相关资源
            最近更新 更多