[c#] 在面板内部的控件上绘图(C#WinForms)



Answers

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

alt text http://www.freeimagehosting.net/uploads/832018002a.jpg

你甚至可以点击你喜欢的任何地方,线条根本不会干涉。 这条线完全被绘制在任何类型的控件上(虽然ComboBox或DatePicker的下拉部分仍然显示在该行的上方,这无论如何都是好的)。 蓝线是同样的东西,但发送回来。

Question

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

我有一个其他控件的面板。
我想在它上面和在面板中的所有控件上绘制一条线

我遇到了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上绘制(它实际上是由于点击了按钮而绘制的)

第3步:创建透明图层并在该图层的顶部绘图。
我使用以下方法创建了一个透明控件:

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

问题仍然存在,将透明控件置于Panel及其所有控件之上。
我尝试使用“BringToFront()”把它带到前面,但它似乎没有帮助。
我把它放在Line控件的OnPaint()处理程序中。
我应该尝试把它放在其他地方吗?
- 这也会在面板顶部设置另一个控件时产生问题。 (抓住鼠标点击等..)

任何帮助将不胜感激!

**编辑:黑线是我试图做的样本。 (用过的窗户油漆来涂漆)




创建一个新的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);
        }
    }
}

编辑:添加对角线支持

我已经为他们获得焦点时重新绘制的控件添加了一些支持。

文本框和组合框不会工作,因为你需要自己制作并挂钩那样的paintish命令:

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

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




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

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

要在屏幕上绘制内容,请使用标签控件,然后关闭AutoSize。 然后附加到Paint事件并设置Size和Region Properties。

这是一个代码示例:

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



我认为最好的方法是继承你想要绘制线条的控件。 重写OnPaint方法,从内部调用base.Paint(),然后使用相同的图形实例绘制线条。 同时,您还可以使用一个特定参数来绘制线条,以便您可以直接从主窗体控制线条。




这对解决方案#1(获取桌面DC和Draw在屏幕上)有何看法:

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