当侧控件无效时,DrawReversibleFrame不需要的行为(消失,乱画)

时间:2013-12-27 19:48:06

标签: c# winforms drawing

我正在使用DrawReversibleFrame根据名为SelectionTest的控件上的鼠标位置绘制一个反色的矩形。使用DrawReversibleFrame的基本代码取自此MSDN sample

如果在选择区域期间重新绘制控件(在无效或刷新之后),矩形可能会消失或具有不同大小的多个副本。你可能会问我,你为什么要刷新控制?其实,我不是!也不会引发控件Paint事件。

我发现重绘只发生在两个其他控件之间绘制矩形时非常快速失效(例如:鼠标移动事件或定时器)。

为了重现这个话题,我创建了两个示例类:

SelectionTest ,处理DrawReversibleFrame。复选框This.Invalidate计时器,启用/禁用调用SelectionTest的Invalidate的计时器。如果激活它,则可以使用easyl重现不需要的DrawReversibleFrame行为。请注意,这个(Invalidade调用)只是重现错误的“欺骗”。在我的应用程序上,我没有调用Invalidate或要求以任何方式重绘控件。正如我所说,在我的应用程序中,不会引发Paint事件。

Form1 ,拥有两个面板之间的SelectionTest。它拥有两个主要的复选框,用于控制鼠标移动时的无效以及使计时器无效。如果两者都被选中,则不需要的行为会更加显着。

以下是不受欢迎行为的屏幕截图。请注意中心(较暗区域)控件右上角的选择:

Unwanted behavior DrawReversibleFrame

问题:

  1. 如何将DrawReversibleFrame矩形消失,就像在Paint事件上消失一样,但是不会引发事件?
  2. 如何克服由侧边控制引起的屏幕重绘问题?
  3. 有没有办法使用Graphics绘制反色矩形? 示例代码:
  4. SelectionTest.cs - 基于MSDN sample使用DrawReversibleFrame的控件。

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace DrawReversibleFrameTest
    {
        public partial class SelectionTest : UserControl
        {
            Timer timer = new Timer();
    
            public SelectionTest()
            {
                InitializeComponent();
                this.MouseDown += SelectionTest_MouseDown;
                this.MouseMove += SelectionTest_MouseMove;
                this.MouseUp += SelectionTest_MouseUp;
    
                timer.Interval = 100;
                timer.Tick += timer_Tick;
                timer.Enabled = ckbTimerEnabled.Checked;
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                Console.WriteLine("SelectionTest OnPaint");
            }
    
            protected override void OnPaintBackground(PaintEventArgs e)
            {
                base.OnPaintBackground(e);
                Console.WriteLine("SelectionTest OnPaintBackground");
            }
    
            void timer_Tick(object sender, EventArgs e)
            {
                this.Invalidate();
            }
    
            // The following three methods will draw a rectangle and allow  
            // the user to use the mouse to resize the rectangle.  If the  
            // rectangle intersects a control's client rectangle, the  
            // control's color will change. 
    
            bool isDrag = false;
            Rectangle theRectangle = new Rectangle(new Point(0, 0), new Size(0, 0));
            Point startPoint;
    
            private void SelectionTest_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
            {
    
                // Set the isDrag variable to true and get the starting point  
                // by using the PointToScreen method to convert form  
                // coordinates to screen coordinates. 
                if (e.Button == MouseButtons.Left)
                {
                    isDrag = true;
                }
    
                Control control = (Control)sender;
    
                // Calculate the startPoint by using the PointToScreen  
                // method.
                startPoint = control.PointToScreen(new Point(e.X, e.Y));
            }
    
            private Point lastPosition = Point.Empty;
    
            private void SelectionTest_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
            {
                if (lastPosition == e.Location)
                    return;
    
                lastPosition = e.Location;
    
                // If the mouse is being dragged,  
                // undraw and redraw the rectangle as the mouse moves. 
                if (isDrag)
    
                // Hide the previous rectangle by calling the  
                // DrawReversibleFrame method with the same parameters.
                {
                    ControlPaint.DrawReversibleFrame(theRectangle, this.BackColor, FrameStyle.Dashed);
    
                    // Calculate the endpoint and dimensions for the new  
                    // rectangle, again using the PointToScreen method.
                    Point endPoint = ((Control)sender).PointToScreen(new Point(e.X, e.Y));
    
                    int width = endPoint.X - startPoint.X;
                    int height = endPoint.Y - startPoint.Y;
                    theRectangle = new Rectangle(startPoint.X, startPoint.Y, width, height);
    
                    // Draw the new rectangle by calling DrawReversibleFrame 
                    // again.  
                    ControlPaint.DrawReversibleFrame(theRectangle, this.BackColor, FrameStyle.Dashed);
                }
            }
    
            private void SelectionTest_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
            {
    
                // If the MouseUp event occurs, the user is not dragging.
                isDrag = false;
    
                // Draw the rectangle to be evaluated. Set a dashed frame style  
                // using the FrameStyle enumeration.
                ControlPaint.DrawReversibleFrame(theRectangle,
                    this.BackColor, FrameStyle.Dashed);
    
                // Find out which controls intersect the rectangle and  
                // change their color. The method uses the RectangleToScreen   
                // method to convert the Control's client coordinates  
                // to screen coordinates.
                Rectangle controlRectangle;
                for (int i = 0; i < Controls.Count; i++)
                {
                    controlRectangle = Controls[i].RectangleToScreen
                        (Controls[i].ClientRectangle);
                    if (controlRectangle.IntersectsWith(theRectangle))
                    {
                        Controls[i].BackColor = Color.BurlyWood;
                    }
                }
    
                // Reset the rectangle.
                theRectangle = new Rectangle(0, 0, 0, 0);
            }
    
            private void ckbTimerEnabled_CheckedChanged(object sender, EventArgs e)
            {
                timer.Enabled = ckbTimerEnabled.Checked;
            }
    
            #region  Designer
            /// <summary> 
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary> 
            /// Clean up any resources being used.
            /// </summary>
            /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Component Designer generated code
    
            /// <summary> 
            /// Required method for Designer support - do not modify 
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                this.ckbTimerEnabled = new System.Windows.Forms.CheckBox();
                this.comboBox1 = new System.Windows.Forms.ComboBox();
                this.button1 = new System.Windows.Forms.Button();
                this.label1 = new System.Windows.Forms.Label();
                this.label2 = new System.Windows.Forms.Label();
                this.label3 = new System.Windows.Forms.Label();
                this.label4 = new System.Windows.Forms.Label();
                this.SuspendLayout();
                // 
                // ckbTimerEnabled
                // 
                this.ckbTimerEnabled.AutoSize = true;
                this.ckbTimerEnabled.Location = new System.Drawing.Point(3, 114);
                this.ckbTimerEnabled.Name = "ckbTimerEnabled";
                this.ckbTimerEnabled.Size = new System.Drawing.Size(120, 17);
                this.ckbTimerEnabled.TabIndex = 7;
                this.ckbTimerEnabled.Text = "This.Invalidate timer";
                this.ckbTimerEnabled.UseVisualStyleBackColor = true;
                this.ckbTimerEnabled.CheckedChanged += new System.EventHandler(this.ckbTimerEnabled_CheckedChanged);
                // 
                // comboBox1
                // 
                this.comboBox1.FormattingEnabled = true;
                this.comboBox1.Location = new System.Drawing.Point(29, 28);
                this.comboBox1.Name = "comboBox1";
                this.comboBox1.Size = new System.Drawing.Size(121, 21);
                this.comboBox1.TabIndex = 6;
                // 
                // button1
                // 
                this.button1.Location = new System.Drawing.Point(29, 64);
                this.button1.Name = "button1";
                this.button1.Size = new System.Drawing.Size(75, 23);
                this.button1.TabIndex = 5;
                this.button1.Text = "button1";
                this.button1.UseVisualStyleBackColor = true;
                // 
                // label1
                // 
                this.label1.AutoSize = true;
                this.label1.Location = new System.Drawing.Point(165, 36);
                this.label1.Name = "label1";
                this.label1.Size = new System.Drawing.Size(35, 13);
                this.label1.TabIndex = 4;
                this.label1.Text = "label1";
                // 
                // label2
                // 
                this.label2.AutoSize = true;
                this.label2.Location = new System.Drawing.Point(130, 74);
                this.label2.Name = "label2";
                this.label2.Size = new System.Drawing.Size(35, 13);
                this.label2.TabIndex = 8;
                this.label2.Text = "label2";
                // 
                // label3
                // 
                this.label3.AutoSize = true;
                this.label3.Location = new System.Drawing.Point(130, 98);
                this.label3.Name = "label3";
                this.label3.Size = new System.Drawing.Size(35, 13);
                this.label3.TabIndex = 9;
                this.label3.Text = "label3";
                // 
                // label4
                // 
                this.label4.AutoSize = true;
                this.label4.Location = new System.Drawing.Point(171, 74);
                this.label4.Name = "label4";
                this.label4.Size = new System.Drawing.Size(35, 13);
                this.label4.TabIndex = 10;
                this.label4.Text = "label4";
                // 
                // SelectionTest
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.Controls.Add(this.label4);
                this.Controls.Add(this.label3);
                this.Controls.Add(this.label2);
                this.Controls.Add(this.ckbTimerEnabled);
                this.Controls.Add(this.comboBox1);
                this.Controls.Add(this.button1);
                this.Controls.Add(this.label1);
                this.Name = "SelectionTest";
                this.Size = new System.Drawing.Size(226, 134);
                this.ResumeLayout(false);
                this.PerformLayout();
    
            }
    
            #endregion
    
            private System.Windows.Forms.CheckBox ckbTimerEnabled;
            private System.Windows.Forms.ComboBox comboBox1;
            private System.Windows.Forms.Button button1;
            private System.Windows.Forms.Label label1;
            private System.Windows.Forms.Label label2;
            private System.Windows.Forms.Label label3;
            private System.Windows.Forms.Label label4;
            #endregion
        }
    }
    

    Form1.cs - 包含SelectionTest和两个侧面板

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace DrawReversibleFrameTest
    {
        public partial class Form1 : Form
        {
    
            Timer invalidateTimer = new Timer();
            public Form1()
            {
                InitializeComponent();
                invalidateTimer.Interval = 100;
                invalidateTimer.Tick += invalidateTimer_Tick;
                invalidateTimer.Enabled = ckbInvalidateTimer.Checked;
            }
    
            void invalidateTimer_Tick(object sender, EventArgs e)
            {
                this.panel1.Invalidate();
                this.panel2.Invalidate();
            }
    
            private void selectionTest1_MouseMove(object sender, MouseEventArgs e)
            {
                if (this.ckbInvalidatePanelsOnMove.Checked)
                {
                    this.panel1.Invalidate();
                    this.panel2.Invalidate();
                    //Console.WriteLine("Mouse move invalidate");
                }
            }
    
            private void Panel1_Paint(object sender, PaintEventArgs e)
            {
                if (ckbPanel1DrawRectangle.Checked)
                    e.Graphics.FillRectangle(Brushes.Red, new Rectangle(new Point(2, 2), new Size(20, 60)));
            }
    
            private void Panel2_Paint(object sender, PaintEventArgs e)
            {
                if (ckbPanel2DrawRectangle.Checked)
                    e.Graphics.FillRectangle(Brushes.Red, new Rectangle(new Point(2, 2), new Size(20, 60)));
            }
    
            private void ckbInvalidateTimer_CheckedChanged(object sender, EventArgs e)
            {
                invalidateTimer.Enabled = ckbInvalidateTimer.Checked;
            }
    
            #region Designer
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Windows Form Designer generated code
    
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                this.panel1 = new System.Windows.Forms.Panel();
                this.panel2 = new System.Windows.Forms.Panel();
                this.ckbInvalidatePanelsOnMove = new System.Windows.Forms.CheckBox();
                this.ckbInvalidateTimer = new System.Windows.Forms.CheckBox();
                this.ckbPanel1Visible = new System.Windows.Forms.CheckBox();
                this.ckbPanel2Visible = new System.Windows.Forms.CheckBox();
                this.ckbPanel1DrawRectangle = new System.Windows.Forms.CheckBox();
                this.ckbPanel2DrawRectangle = new System.Windows.Forms.CheckBox();
                this.selectionTest1 = new DrawReversibleFrameTest.SelectionTest();
                this.SuspendLayout();
                // 
                // panel1
                // 
                this.panel1.BackColor = System.Drawing.SystemColors.Highlight;
                this.panel1.Location = new System.Drawing.Point(12, 12);
                this.panel1.Name = "panel1";
                this.panel1.Size = new System.Drawing.Size(49, 184);
                this.panel1.TabIndex = 5;
                this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.Panel1_Paint);
                // 
                // panel2
                // 
                this.panel2.BackColor = System.Drawing.SystemColors.Highlight;
                this.panel2.Location = new System.Drawing.Point(350, 12);
                this.panel2.Name = "panel2";
                this.panel2.Size = new System.Drawing.Size(49, 130);
                this.panel2.TabIndex = 6;
                this.panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.Panel2_Paint);
                // 
                // ckbInvalidatePanelsOnMove
                // 
                this.ckbInvalidatePanelsOnMove.AutoSize = true;
                this.ckbInvalidatePanelsOnMove.Location = new System.Drawing.Point(12, 256);
                this.ckbInvalidatePanelsOnMove.Name = "ckbInvalidatePanelsOnMove";
                this.ckbInvalidatePanelsOnMove.Size = new System.Drawing.Size(150, 17);
                this.ckbInvalidatePanelsOnMove.TabIndex = 7;
                this.ckbInvalidatePanelsOnMove.Text = "Invalidate panels on move";
                this.ckbInvalidatePanelsOnMove.UseVisualStyleBackColor = true;
                // 
                // ckbInvalidateTimer
                // 
                this.ckbInvalidateTimer.AutoSize = true;
                this.ckbInvalidateTimer.Location = new System.Drawing.Point(12, 279);
                this.ckbInvalidateTimer.Name = "ckbInvalidateTimer";
                this.ckbInvalidateTimer.Size = new System.Drawing.Size(97, 17);
                this.ckbInvalidateTimer.TabIndex = 8;
                this.ckbInvalidateTimer.Text = "Invalidate timer";
                this.ckbInvalidateTimer.UseVisualStyleBackColor = true;
                this.ckbInvalidateTimer.CheckedChanged += new System.EventHandler(this.ckbInvalidateTimer_CheckedChanged);
                // 
                // ckbPanel1Visible
                // 
                this.ckbPanel1Visible.AutoSize = true;
                this.ckbPanel1Visible.Checked = true;
                this.ckbPanel1Visible.CheckState = System.Windows.Forms.CheckState.Checked;
                this.ckbPanel1Visible.Location = new System.Drawing.Point(168, 256);
                this.ckbPanel1Visible.Name = "ckbPanel1Visible";
                this.ckbPanel1Visible.Size = new System.Drawing.Size(94, 17);
                this.ckbPanel1Visible.TabIndex = 9;
                this.ckbPanel1Visible.Text = "Panel 1 visible";
                this.ckbPanel1Visible.UseVisualStyleBackColor = true;
                this.ckbPanel1Visible.CheckedChanged += new System.EventHandler(this.ckbPanel1Visible_CheckedChanged);
                // 
                // ckbPanel2Visible
                // 
                this.ckbPanel2Visible.AutoSize = true;
                this.ckbPanel2Visible.Checked = true;
                this.ckbPanel2Visible.CheckState = System.Windows.Forms.CheckState.Checked;
                this.ckbPanel2Visible.Location = new System.Drawing.Point(168, 279);
                this.ckbPanel2Visible.Name = "ckbPanel2Visible";
                this.ckbPanel2Visible.Size = new System.Drawing.Size(94, 17);
                this.ckbPanel2Visible.TabIndex = 10;
                this.ckbPanel2Visible.Text = "Panel 2 visible";
                this.ckbPanel2Visible.UseVisualStyleBackColor = true;
                this.ckbPanel2Visible.CheckedChanged += new System.EventHandler(this.ckbPanel2Visible_CheckedChanged);
                // 
                // ckbPanel1DrawRectangle
                // 
                this.ckbPanel1DrawRectangle.AutoSize = true;
                this.ckbPanel1DrawRectangle.Checked = true;
                this.ckbPanel1DrawRectangle.CheckState = System.Windows.Forms.CheckState.Checked;
                this.ckbPanel1DrawRectangle.Location = new System.Drawing.Point(268, 256);
                this.ckbPanel1DrawRectangle.Name = "ckbPanel1DrawRectangle";
                this.ckbPanel1DrawRectangle.Size = new System.Drawing.Size(135, 17);
                this.ckbPanel1DrawRectangle.TabIndex = 11;
                this.ckbPanel1DrawRectangle.Text = "Panel 1 draw rectangle";
                this.ckbPanel1DrawRectangle.UseVisualStyleBackColor = true;
                // 
                // ckbPanel2DrawRectangle
                // 
                this.ckbPanel2DrawRectangle.AutoSize = true;
                this.ckbPanel2DrawRectangle.Checked = true;
                this.ckbPanel2DrawRectangle.CheckState = System.Windows.Forms.CheckState.Checked;
                this.ckbPanel2DrawRectangle.Location = new System.Drawing.Point(268, 279);
                this.ckbPanel2DrawRectangle.Name = "ckbPanel2DrawRectangle";
                this.ckbPanel2DrawRectangle.Size = new System.Drawing.Size(135, 17);
                this.ckbPanel2DrawRectangle.TabIndex = 12;
                this.ckbPanel2DrawRectangle.Text = "Panel 2 draw rectangle";
                this.ckbPanel2DrawRectangle.UseVisualStyleBackColor = true;
                // 
                // selectionTest1
                // 
                this.selectionTest1.BackColor = System.Drawing.SystemColors.ControlDark;
                this.selectionTest1.Location = new System.Drawing.Point(67, 12);
                this.selectionTest1.Name = "selectionTest1";
                this.selectionTest1.Size = new System.Drawing.Size(277, 238);
                this.selectionTest1.TabIndex = 0;
                this.selectionTest1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.selectionTest1_MouseMove);
                // 
                // Form7_DrawReversibleRectangleTest
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(449, 313);
                this.Controls.Add(this.ckbPanel2DrawRectangle);
                this.Controls.Add(this.ckbPanel1DrawRectangle);
                this.Controls.Add(this.ckbPanel2Visible);
                this.Controls.Add(this.ckbPanel1Visible);
                this.Controls.Add(this.ckbInvalidateTimer);
                this.Controls.Add(this.ckbInvalidatePanelsOnMove);
                this.Controls.Add(this.panel2);
                this.Controls.Add(this.panel1);
                this.Controls.Add(this.selectionTest1);
                this.Name = "Form7_DrawReversibleRectangleTest";
                this.Text = "Form7_DrawReversibleRectangleTest";
                this.ResumeLayout(false);
                this.PerformLayout();
    
            }
    
            #endregion
    
            private DrawReversibleFrameTest.SelectionTest selectionTest1;
            private System.Windows.Forms.Panel panel1;
            private System.Windows.Forms.Panel panel2;
            private System.Windows.Forms.CheckBox ckbInvalidatePanelsOnMove;
            private System.Windows.Forms.CheckBox ckbInvalidateTimer;
            private System.Windows.Forms.CheckBox ckbPanel1Visible;
            private System.Windows.Forms.CheckBox ckbPanel2Visible;
            private System.Windows.Forms.CheckBox ckbPanel1DrawRectangle;
            private System.Windows.Forms.CheckBox ckbPanel2DrawRectangle;
            #endregion
    
            private void ckbPanel1Visible_CheckedChanged(object sender, EventArgs e)
            {
                this.panel1.Visible = ckbPanel1Visible.Checked;
            }
    
            private void ckbPanel2Visible_CheckedChanged(object sender, EventArgs e)
            {
                this.panel2.Visible = ckbPanel2Visible.Checked;
            }
        }
    }
    

1 个答案:

答案 0 :(得分:0)

您尝试做的伪逻辑是:

MouseDown:

  1. 绘制矩形。
  2. 保存矩形边界。
  3. MouseMove(带左按钮):

    1. 重绘以前的矩形以进行擦除。
    2. 计算下一个矩形。
    3. 绘制下一个矩形。
    4. 保存矩形边界。
    5. 冲洗/重复(无限期地)
    6. 的MouseUp:

      1. 重绘最后一个要擦除的矩形。
      2. 要排除故障:为DrawReversibleFrame()调用创建包装函数。绘制矩形后,将矩形边界打印到调试窗口。将鼠标拖过控件时,验证每个矩形是否被绘制两次。