Рисование поверх элементов управления внутри панели (C # WinForms) - PullRequest
39 голосов
/ 12 ноября 2008

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

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

Я столкнулся с 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 мс, и установите таймер на X мс?


На этом снимке экрана показана панель с выключенными WS_CLIPSIBLINGS и WS_CLIPCHILDREN.
Синяя линия закрашивается на панели OnPaint и просто наносится текстовыми полями и надписью.
Красная линия нарисована сверху только потому, что она не нарисована с помощью OnPaint панели (на самом деле она нарисована в результате нажатия кнопки)
alt text


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

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

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

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

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

alt text

Ответы [ 10 ]

18 голосов
/ 25 ноября 2008

Оказывается, это намного проще, чем я думал. Спасибо, что не приняли ни одного из моих других ответов. Вот двухэтапный процесс создания Fline ( f loating line - извините, уже поздно):

alt text

Шаг 1 : Добавьте UserControl в ваш проект и назовите его «Fline». Добавьте следующее в операторы using:

using System.Drawing.Drawing2D;

Шаг 2 : Добавьте следующее в событие изменения размера Fline:

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 по умолчанию совпадает с формой, поэтому измените BackColor Fline на Red или что-то очевидное (в конструкторе). Одна странная странность в том, что когда вы перетаскиваете его в конструкторе, он отображается как сплошной блок, пока вы его не отпустите - не так уж и много.

Этот элемент управления может находиться перед любым другим элементом управления или позади него. Если для параметра Enabled установлено значение false, оно все равно будет отображаться, но не будет мешать событиям мыши на элементах управления под ним.

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

Обновление : это также делает хороший плотный однострочный. Просто поместите это в событие Resize вашего 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}));
10 голосов
/ 12 ноября 2008

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

alt text

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

6 голосов
/ 18 ноября 2008

Да, это можно сделать. Проблема заключается в том, что панель и элементы управления на ней - это отдельные окна (в смысле API), и, следовательно, все отдельные поверхности рисования. Нет такой рисовальной поверхности, чтобы рисовать, чтобы получить этот эффект (кроме поверхности экрана верхнего уровня, и это считается невежливым, чтобы рисовать все это).

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

alt text

Создать проект 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 голосов
/ 12 ноября 2008

Панель форм Windows - это контейнер для элементов управления. Если вы хотите нарисовать что-то поверх других элементов управления на панели, то вам нужен другой элемент управления (в верхней части z-порядка).

К счастью, вы можете создавать элементы управления окнами, которые имеют непрямоугольные границы. Посмотрите на эту технику: http://msdn.microsoft.com/en-us/library/aa289517(VS.71).aspx

Чтобы просто нарисовать что-то на экране, используйте элемент управления меткой и выключите AutoSize. Затем присоедините к событию Paint и установите свойства размера и области.

Вот пример кода:

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);
}
3 голосов
/ 18 ноября 2008

Создайте новый LineControl: Элемент управления вот так:

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

Это не проверено, и я уверен, что вы можете улучшить его

3 голосов
/ 12 ноября 2008

Единственное простое решение, о котором я могу подумать, - это создать обработчики событий 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
}
2 голосов
/ 17 мая 2011

РЕДАКТИРОВАТЬ Нашел способ избавиться от проблемы с рекурсивной живописью, которая у меня была. Так что теперь для меня это выглядит очень, очень, очень близко к тому, чего вы хотите достичь.

Вот что я могу придумать. Он использует подход № 3, изложенный в первоначальном вопросе. Код несколько длинный, потому что задействованы три класса:

  1. Частный класс под названием DecorationCanvas. Это происходит от Panel и использует WS_EX_TRANSPARENT для обеспечения прозрачного холста для рисования нашего материала на
  2. Сам класс Panel, я назвал его DecoratedPanel, он происходит от Panel
  3. Класс конструктора DecoratedPanelDesigner для панели, обеспечивающий сохранение ZOrder во время разработки.

Основной подход:

  • В конструкторе DecoratedPanel создайте экземпляр DecorationCanvas и добавьте его в коллекцию элементов управления DecoratedPanel.
  • Переопределите OnControlAdded и OnControlRemoved, чтобы автоматически перехватывать / отцеплять события рисования для дочерних элементов управления и следить за тем, чтобы DecorationCanvas оставался поверх ZOrder.
  • Всякий раз, когда содержащийся элемент управления рисует, лишает законной силы соответствующий прямоугольник DecorationCanvas.
  • Переопределите OnResize и OnSizeChanged, чтобы удостовериться, что DecomentCanvas имеет тот же размер, что и DecoratedPanel. (Я пытался сделать это, используя свойство Anchor, но это как-то не получилось).
  • Предоставляет внутренний метод для сброса DecorationCanvas ZOrder из DecoratedPanelDesigner.

Прекрасно работает в моей системе (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);
        }
      }
    }
  }
}

Дайте мне знать, что вы думаете ...

1 голос
/ 30 декабря 2009

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

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

Исходный код должен быть:

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

Это работает !!

1 голос
/ 12 ноября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...