[C#] Опираясь на элементы управления внутри панели (C # WinForms)


Answers

Если вы хотите, чтобы линия была простой горизонтальной или вертикальной линией, положите другую панель (отключен, чтобы она не забирала какие-либо события мыши) на главной панели, установите ее высоту (или ширину) на 3 или 4 пикселя (или что бы вы ни пожелали), и довести его до фронта. Если вам нужно изменить, где находится строка во время выполнения, вы можете просто перемещать панель и делать ее видимой и невидимой. Вот как это выглядит:

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

Вы даже можете щелкнуть в любом месте, где хотите, и линии не мешают вообще. Линия нарисована над любым типом управления вообще (хотя выпадающая часть ComboBox или DatePicker все еще отображается над строкой, что хорошо в любом случае). Синяя линия - это одно и то же, но отправлено обратно.

Question

Я знаю, что этот вопрос задавали более чем несколько раз, но пока я не смог найти для этого хорошего решения.

У меня есть панель с другим контролем.
Я хочу нарисовать линию на ней и поверх всех элементов управления на панели

Я столкнулся с тремя типами решений (без них работал так, как я хотел):

  1. Получите рабочий стол DC и Draw на экране.
    Это будет использоваться для других приложений, если они перекрывают форму.

  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.
Синяя линия окрашена в OnPaint панели и просто нарисована текстовыми полями и ярлыками.
Красная линия окрашена сверху только потому, что она не окрашивается из панели OnPaint панели (она фактически окрашена в результате нажатия кнопки)

3. Создание прозрачного слоя и рисование поверх этого слоя.
Я создал прозрачный элемент управления, используя:

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

Проблема по-прежнему заключается в том, что прозрачный контроль над панелью и всеми ее элементами управления.
Я попытался вывести его на передний план, используя: «BringToFront ()», но, похоже, это не помогло.
Я поместил его в обработчик OnPaint () управления линией.
Должен ли я попытаться положить его в другое место?
- Это также создает проблему с другим контролем над панелью. (улавливание щелчков мыши и т. д.)

Любая помощь будет принята с благодарностью!

** EDIT: черная линия - образец того, что я пытался сделать. (используемая краска для окраски)




Я думаю, что лучший способ - наследовать контроль, на который вы хотите нарисовать линию. Переопределите метод OnPaint, вызовите base.Paint () изнутри, после этого нарисуйте линию, используя один и тот же графический экземпляр. В то же время вы также можете иметь параметр, который определен в тот момент, когда линия должна быть нарисована, чтобы вы могли контролировать линию непосредственно из вашей основной формы.




Создайте новый элемент управления LineControl: Control следующим образом:

затем вызовите BringToFront () после 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);
        }
    }
}

Изменить: добавлена ​​поддержка диагонали

Я добавил некоторую поддержку элементов управления, которые перерисовываются, когда они получают фокус.

текстовые поля и комбобокс не будут работать, так как вам нужно будет сделать свой собственный и закрепить там красящие команды следующим образом:

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

Его не тестировали, и я уверен, что вы можете улучшить его




Как насчет этого решения # 1 (Получить рабочий стол DC и рисовать на экране):

  1. Получите рабочий стол DC и графический объект для этого DC [Graphics.fromHDC (...)]
  2. Задайте свойство Clip для полученного объекта Graphics в качестве видимого в данный момент региона вашей формы. (Я еще не исследовал, как найти видимый регион формы)
  3. Сделайте графический рендеринг.



Панель форм окон представляет собой контейнер для элементов управления. Если вы хотите нарисовать что-то поверх других элементов управления внутри панели, то вам нужен другой элемент управления (в верхней части 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);
}