[c#] الرسم فوق عناصر التحكم داخل لوحة (C # WinForms)



Answers

إذا كنت تريد أن يكون الخط مجرد خط أفقي أو رأسي ، ضع لوحة أخرى (تم تعطيلها بحيث لا تلتقط أي أحداث ماوس) على اللوحة الرئيسية ، قم بتعيين ارتفاعها (أو عرضها) إلى 3 أو 4 بكسل (أو كل ما تريد) ، وإحضاره إلى الأمام. إذا كنت بحاجة إلى تغيير مكان الخط أثناء وقت التشغيل ، فيمكنك فقط تحريك اللوحة وجعلها مرئية وغير مرئية. هنا هو كيف يبدو:

نص بديل http://www.freeimagehosting.net/uploads/832018002a.jpg

يمكنك حتى النقر في أي مكان تريد ، والخطوط لا تتدخل على الإطلاق. يتم رسم الخط على أي نوع من التحكم على الإطلاق (على الرغم من أن الجزء المنسدل من ComboBox أو DatePicker لا يزال يظهر أعلى الخط ، وهو أمر جيد على أي حال). الخط الأزرق هو نفس الشيء ولكن يتم إرساله إلى الخلف.

Question

أعلم أن هذا السؤال قد تم طرحه أكثر من عدة مرات ، لكني لم أتمكن حتى الآن من إيجاد حل جيد له.

لدي لوحة تحكم أخرى عليها.
أرغب في رسم خط عليه وعلى رأس جميع عناصر التحكم في اللوحة

جئت عبر ثلاثة أنواع من الحلول (لم يعملوا بالطريقة التي أردتها):

  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 من عناصر التحكم فيه ، فإن رسم عناصر التحكم داخل ببساطة يرسم فوق الخط.
لقد رأيت أحدهم يقترح استخدام عامل تصفية رسائل للاستماع إلى marsages WM_PAINT ، واستخدام جهاز توقيت ، ولكن لا أعتقد أن هذا الحل هو "ممارسة جيدة" أو فعالة.
ماذا كنت ستفعل ؟ تقرر أن عناصر التحكم في الداخل قد انتهى الرسم بعد X MS ، وتعيين الموقت إلى X مللي ثانية؟

تعرض لقطة الشاشة هذه اللوحة مع WS_CLIPSIBLINGS و WS_CLIPCHILDREN متوقفة.
تم رسم الخط الأزرق في OnPaint للوحة ، وببساطة يتم رسمه بواسطة مربعات النص والتسمية.
يرسم الخط الأحمر على القمة فقط لأنه لا يتم رسمه من OnPaint الخاص باللوحة (تم رسمه في الواقع نتيجة النقر على زر)

ثالثًا: إنشاء طبقة شفافة والاعتماد على تلك الطبقة.
لقد قمت بإنشاء عنصر تحكم شفاف باستخدام:

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

لا تزال المشكلة تكمن في وضع السيطرة الشفافة على رأس الفريق وجميع ضوابطه.
لقد حاولت جلبها إلى الأمام باستخدام: "BringToFront ()" ، ولكن لا يبدو أنها تساعد.
لقد وضعته في معالج OnPaint () عنصر تحكم الخط.
يجب أن أحاول وضعه في مكان آخر؟
- هذا أيضًا يخلق مشكلة مع وجود عنصر تحكم آخر أعلى اللوحة. (اصطياد الفأرة نقرات الخ ..)

أي مساعدة سيكون موضع تقدير كبير!

** EDIT: الخط الأسود هو عينة مما كنت أحاول القيام به. (تستخدم ويندوز الطلاء لرسمه)




ماذا عن هذا الأمر على الحل رقم 1 (احصل على سطح المكتب DC وارسم على الشاشة):

  1. الحصول على سطح المكتب DC وكائن الرسومات لذلك DC [Graphics.fromHDC (...)]
  2. قم بتعيين الخاصية Clip للكائن الرسومات الناتجة لتكون المنطقة المرئية حالياً من النموذج الخاص بك. (لم أبحث حتى الآن عن كيفية العثور على المنطقة المرئية للنموذج)
  3. هل تقديم الرسومات الخاصة بك.



جعل 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);
        }
    }
}

تحرير: أضيفت دعم قطري

لقد أضفت بعض الدعم لعناصر التحكم التي تعيد الرسم عند الحصول على التركيز.

لن تعمل مربعات النص و comboboxs كما هو بحاجة إلى جعل بنفسك وربط هناك ترسم أوامر مثل:

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 base.Paint () من الداخل ، بعد ذلك رسم الخط باستخدام نفس نسخة الرسومات. في الوقت نفسه ، يمكنك أيضًا الحصول على معلمة محددة عند هذه النقطة يجب رسم الخط ، بحيث يمكنك التحكم في الخط مباشرةً من النموذج الرئيسي.




لوحة نماذج النوافذ عبارة عن حاوية لعناصر التحكم. إذا كنت ترغب في رسم شيء ما فوق عناصر التحكم الأخرى داخل لوحة ، فإن ما تحتاجه هو عنصر تحكم آخر (أعلى ترتيب z).

لحسن الحظ ، يمكنك إنشاء عناصر تحكم نماذج windows لها حدود غير مستطيلة. انظر إلى هذه التقنية: http://msdn.microsoft.com/en-us/library/aa289517(VS.71).aspx

لرسم شيء ما على الشاشة ، استخدم عنصر تحكم تسمية ، وقم بإيقاف تشغيل AutoSize. ثم قم بإرفاق حدث الرسام وتعيين خصائص المساحة والمنطقة.

فيما يلي نموذج الكود:

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



Links