[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: черная линия - образец того, что я пытался сделать. (используемая краска для окраски)




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



Создайте новый элемент управления 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);
    }
}

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




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




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

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



Links