如何正确地清除或更新屏幕上绘制的矩形 [英] How to properly clear or update a drawn rectangle on the screen

查看:340
本文介绍了如何正确地清除或更新屏幕上绘制的矩形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用一个半透明形式捕捉鼠标事件,如 LeftButtonDown LeftButtonUp 的MouseMove 以能够选择屏幕上的一个区域上绘制该区域的矩形,问题是,一个新的矩形,是我移动鼠标产生恼人的结果,这样每一次得出:

我只是想更新绘制的矩形,当我把鼠标移动到新的鼠标位置,以期待这样的结果别人:

我已经试过处置,清除和重新实例图形对象,而运气,也是我见过的<一个href="http://stackoverflow.com/questions/15682543/drawing-and-clearing-on-screen-with-graphics-fromhwnd">this所以。问题是谈论这个。

这是我使用的code中的相关部分:

 '''&LT;总结&gt;
'''Graphics对象绘制在屏幕上。
'''&LT; /总结&gt;
昏暗ScreenGraphic作为图形= Graphics.FromHwnd(IntPtr.Zero)

私人小组MouseEvents_MouseMove(BYVAL MouseLocation作为点)处理MouseEvents.MouseMove

    如果鼠标左键在HOLD然后设置矩形区域...
    如果IsMouseLeftDown然后

        ' ... 等等等等等等
        '...更多code在这里

        绘制矩形区域。
        Me.DrawRectangle()

    结束如果

'''&LT;总结&gt;
'''绘制所选区域的矩形。
'''&LT; /总结&gt;
私人小组的DrawRectangle()

    这里重新绘制之前调用EraseRectanglehere的方法?
    Me.EraseRectangle

    使用笔作为新笔(Me.BorderColor,Me.BorderSize)
        ScreenGraphic.DrawRectangle(笔,SelectionRectangle)
    结束使用

结束小组

'''&LT;总结&gt;
'''擦除最后绘制矩形。
'''&LT; /总结&gt;
私人小组EraseRectangle()

结束小组
 

这里是如果有人需要更好地inspectionate它充分code:

  

注:我已经更新了,我现在用我的最后一个问题编辑code

 导入了System.Runtime.InteropServices

公共类RangeSelector:继承表

#REGION属性

    '''&LT;总结&gt;
    '''获取或设置范围选择的边框大小。
    '''&LT; /总结&gt;
    '''&LT;价值&gt;在边框的大小和LT; /值GT;
    公共属性BorderSize为整数= 2

    '''&LT;总结&gt;
    '''获取或设置范围选择的边框颜色。
    '''&LT; /总结&gt;
    '''&LT;价值&gt;在边框的颜色&LT; /值GT;
    公共属性BorderColor由于颜色= Color.Red

#END地区

#REGION对象

    '''&LT;总结&gt;
    '''表示当单击鼠标左键时的初始位置。
    '''&LT; /总结&gt;
    私人InitialLocation作为点= Point.Empty

    '''&LT;总结&gt;
    '''指示包含所选择的区域的矩形。
    '''&LT; /总结&gt;
    私人SelectionRectangle作为矩形= Rectangle.Empty

    '''&LT;总结&gt;
    '''Graphics对象绘制在屏幕上。
    '''&LT; /总结&gt;
    私人ScreenGraphic作为图形= Graphics.FromHwnd(IntPtr.Zero)

#END地区

#REGION构造

    '''&LT;总结&gt;
    '''初始化℃的新的实例;参见CREF =RangeSelector/&GT;类。
    '''&LT; /总结&gt;
    公共子新()

        的InitializeComponent()

    结束小组

    '''&LT;总结&gt;
    '''初始化℃的新的实例;参见CREF =RangeSelector/&GT;类。
    '''&LT; /总结&gt;
    '''&LT; PARAM NAME =BorderSize&gt;表示的范围内选择的边框大小&LT; /参数&GT;
    '''&LT; PARAM NAME =边框颜色&gt;表示的范围内选择的边框颜色&LT; /参数&GT;
    公共子新(BYVAL BorderSize为整数,BYVAL边框颜色为彩色)

        Me.BorderSize = BorderSize
        Me.BorderColor =边框颜色

        的InitializeComponent()

    结束小组

#END地区

#REGION事件处理程序

    受保护的覆盖子onmousedown事件(E上MouseEventArgs)

        MyBase.OnMouseDown(五)
        InitialLocation = e.Location
        SelectionRectangle =新的Rectangle(InitialLocation.X,InitialLocation.Y,0,0)

    结束小组

    受保护的覆盖子OnMouseUp(E上MouseEventArgs)

        '使窗体透明采取区域截图。
        Me.Opacity = 0.0R

        ' 去做:
        采取的屏幕截图。
        返回所选的矩形区域,并将其保存。

        Me.Close()

    结束小组

    受保护的覆盖子的OnMouseMove(E上MouseEventArgs)

        如果鼠标左键在HOLD然后设置矩形区域...
        如果e.Button = MouseButtons.Left然后

            如果(e.Location.X&LT; Me.InitialLocation.X)_
            AndAlso(e.Location.Y&LT; Me.InitialLocation.Y)然后'左上角

                Me.SelectionRectangle =新的Rectangle(e.Location.X,
                                                      e.Location.Y,
                                                      Me.InitialLocation.X  -  e.Location.X,
                                                      Me.InitialLocation.Y  -  e.Location.Y)

            elseif的(e.Location.X&GT; Me.InitialLocation.X)_
            AndAlso(e.Location.Y&LT; Me.InitialLocation.Y)然后右上

                Me.SelectionRectangle =新的Rectangle(Me.InitialLocation.X,
                                                      e.Location.Y,
                                                      e.Location.X  -  Me.InitialLocation.X,
                                                      Me.InitialLocation.Y  -  e.Location.Y)

            elseif的(e.Location.X&LT; Me.InitialLocation.X)_
            AndAlso(e.Location.Y&GT; Me.InitialLocation.Y)然后左下

                Me.SelectionRectangle =新的Rectangle(e.Location.X,
                                                      Me.InitialLocation.Y,
                                                      Me.InitialLocation.X  -  e.Location.X,
                                                      e.Location.Y  -  Me.InitialLocation.Y)

            elseif的(e.Location.X&GT; Me.InitialLocation.X)_
            AndAlso(e.Location.Y&GT; Me.InitialLocation.Y)然后右下

                Me.SelectionRectangle =新的Rectangle(Me.InitialLocation.X,
                                                      Me.InitialLocation.Y,
                                                      e.Location.X  -  Me.InitialLocation.X,
                                                      e.Location.Y  -  Me.InitialLocation.Y)
            结束如果

            绘制矩形区域。
            Me.DrawRectangle()

        结束如果

    结束小组

#END地区

#REGION私有方法

    私人小组的InitializeComponent()

        Me.SuspendLayout()
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
        Me.BackColor = System.Drawing.Color.Black
        Me.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None
        Me.CausesValidation =假
        Me.ClientSize =新System.Drawing.Size(100,100)
        Me.ControlBox = FALSE
        Me.Cursor = System.Windows.Forms.Cursors.Cross
        Me.DoubleBuffered = TRUE
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
        Me.MaximizeBox = FALSE
        Me.MinimizeBox = FALSE
        Me.Name =RangeSelector
        Me.Opacity = 0.01R
        Me.ShowIcon =假
        Me.ShowInTaskbar = FALSE
        Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.TopMost = TRUE
        Me.WindowState = System.Windows.Forms.FormWindowState.Maximized
        Me.ResumeLayout(假)

    结束小组

    '''&LT;总结&gt;
    '''绘制所选区域的矩形。
    '''&LT; /总结&gt;
    私人小组的DrawRectangle()

        只是一个奇怪的把戏刷新绘画。
        Me.Opacity = 0.0R
        Me.Opacity = 0.01R

        将G作为图形= Graphics.FromHwnd(IntPtr.Zero)

        使用笔作为新笔(Me.BorderColor,Me.BorderSize)
            ScreenGraphic.DrawRectangle(笔,Me.SelectionRectangle)
        结束使用

        结束使用

    结束小组

#END地区

末级
 

  

更新1

我翻译所有的code使用它作为一个表格对话框中选择一个区域,当有更多的灵活性,我已经更换了整个code以上来更新我的问题,在code不会有太大变化,而不是只用LL钩捕获鼠标事件我处理一个半透明形式最大化的鼠标事件,我还在上绘制矩形在桌面屏幕图形(而不是在的OnPaint 表格事件)的code这部分是一样的,你可以在$见C $ C以上:

 私人ScreenGraphic作为图形= Graphics.FromHwnd(IntPtr.Zero)
 

...因为正如我已经说过的形式是半透明的,所以如果我画一个矩形中这将是半透明窗体太(或者至少是我不知道的方式,以避免)

然后我发现了一个奇怪的把戏,通过改变窗体的透明度绘制矩形的新坐标前解决矩形问题:

  Me.Opacity = 0.0R
    Me.Opacity = 0.01R

    使用笔作为新笔(Me.BorderColor,Me.BorderSize)
        ScreenGraphic.DrawRectangle(笔,Me.SelectionRectangle)
    结束使用
 

这个问题? ...是不完美的,它会产生一个非常恼人的效果,因为我得到了很多闪烁的绘图时矩形(是的,我有如下形式doubleBuffered,也是我使用了的CreateParams 诱骗要尽量避免闪烁,但没有)。

  

更新2

我试图用什么@ Plutonix 指出在他的评论中,的 InvalidateRect函数功能,这个API声明:

 &LT;的DllImport(user32.dll中)&GT;
私人共享功能InvalidateRect函数(
        BYVAL的hWnd作为整数,
        为ByRef升preCT作为矩形,
        BYVAL bErase由于布尔)作为布尔
端功能
 

我试图用它与两个 / 标记。

这个问题?问题是一样的,我指着我的第一次更新了一句:

是不完美的,它会产生一个非常恼人的效果,因为我得到了很多绘图时矩形(是的,我有如下形式doubleBuffered,也是我使用闪烁的的CreateParams 诱骗要尽量避免闪烁,但没有)。

  

更新3

我想使用的 RedrawWindow 功能,为我所看到的<一个href="http://stackoverflow.com/questions/2325894/difference-between-invalidaterect-and-redrawwindow">this SO回答该可以用来做相同的 InvalidateRect函数功能确实还具有更大的灵活性,也许没有,我开始使用的 InvalidateRect函数功能,我只需要尝试一下。

  

在RedrawWindow功能更新指定的矩形或区域   窗口的客户区。

这是API声明:

 &LT;的DllImport(user32.dll中)&GT;
私人共享功能RedrawWindow(
        BYVAL的hWnd作为IntPtr的,
        百分比抑制率以]≥的ByRef lprcUpdate如矩形,
        BYVAL hrgnUpdate作为IntPtr的,
        BYVAL旗帜RedrawWindowFlags)作为布尔
端功能

&LT;标志()&GT;
私人枚举RedrawWindowFlags作为UInteger
    '''&LT;总结&gt;
    '''失效你在lprcUpdate或hrgnUpdate指定的矩形或地区。
    '''你可以只设定一个这些参数为一个非NULL值的。如果两者都为NULL,RDW_INVALIDATE整个窗口无效。
    '''&LT; /总结&gt;
    无效=安培; H1

    '''&其中;总结&gt;使操作系统来发布WM_PAINT消息到窗口无论窗口的一部分是否无效&所述; /总结&gt;
    InternalPaint =安培; H2

    '''&LT;总结&gt;
    '''导致窗口收到WM_ERASEBKGND消息,当窗口被重新绘制。
    '''指定与RDW_INVALIDATE值相结合的设定值;否则,RDW_ERASE没有效果。
    '''&LT; /总结&gt;
    [删除] =&放大器; H4

    '''&LT;总结&gt;
    '''验证你在lprcUpdate或hrgnUpdate指定的矩形或地区。
    '''你可以只设定一个这些参数为一个非NULL值的。如果两者都为NULL,RDW_VALIDATE验证整个窗口。
    '''这个值不影响内部WM_PAINT消息。
    '''&LT; /总结&gt;
    验证=安培; H8

    NoInternalPaint =安培; H10

    '''&LT;总结&gt;燮presses任何未决的WM_ERASEBKGND消息&LT; /总结&gt;
    NoErase =安培; H20

    '''&其中;总结&gt;不包括子窗口,如果有的话,从重绘操作&所述; /总结&gt;
    NoChildren =安培; H40

    '''&其中;总结&gt;内含子窗口,如果有的话,在重画动作&所述; /总结&gt;
    AllChildren =安培; H80

    '''&LT;总结&gt;使受影响的Windows,它通过设置RDW_ALLCHILDREN和RDW_NOCHILDREN值指定的RedrawWindow返回之前收到WM_ERASEBKGND和WM_PAINT消息,如果有必要&LT; /总结&gt;
    updatenow更新=安培; H100

    '''&LT;总结&gt;
    '''导致受影响的Windows,它通过设置RDW_ALLCHILDREN和RDW_NOCHILDREN值指定,接收WM_ERASEBKGND消息RedrawWindow返回之前,如果有必要的。
    '''受影响的Windows收到WM_PAINT消息在平时。
    '''&LT; /总结&gt;
    EraseNow =安培; H200

    帧=&放大器; H400

    NOFRAME =安培; H800
结束枚举
 

我试图使用功能与这些参数:

  RedrawWindow(IntPtr.Zero,Me.SelectionRectangle,IntPtr.Zero,RedrawWindowFlags.Invalidate)
 

...这我想,作为MSDN文档描述,如果第一个参数为NULL,则意味着桌面屏幕,第二个参数是指以更新的长方形,第三个参数必须为空,如果我指定一个长方形中的第二个参数,并在最后一个参数是指一个标志,指示动作(S)以执行(在这种情况下,矩形作废,@的 Plutonix 所述?)

我试图使用isntruction绘制矩形后绘制它之前,我的意思是,在的OnMouseMove 事件,否则在我的 DrawRectangle的方法,在我的code,但我看不到屏幕上的任何区别,我仍然有我显示的图像上面同样的问题绘制矩形时,我的意思是多个矩形当我移动鼠标的任何矩形这个功能删除,也许我使用了错误的参数绘制?

解决方案

该解决方案更简单,不需要窗口API。只要创建从一个透明和绘制红色矩形就可以了。继code做到这一点,你只需要更换您的半透明形式。闪烁的情况,因为我们清洁的图形,然后绘制,以避免它做画一次最简单的方法,所以如果我们画上一个位图矩形,然后绘制位图,操作完成的一步,并闪烁不发生的情况。

图将被将需要完成的图纸形式的OnPaintBackground所以绘图的形式。这是主要的类,这里的事件被捕获

 公共类YourFormClass

    昏暗开始为点
    昏暗DrawSize由于尺寸
    公众的drawRect作为矩形
    公共图纸由于布尔= FALSE
    昏暗的信息作为标签
    昏暗DrawForm作为表

    私人小组YourFormClass_Load(发送者为对象,E作为EventArgs的)把手Me.Load
        在InitializeComponent()调用后'添加任何初始化。
        控制盒= FALSE
        的WindowState = FormWindowState.Maximized
        FormBorderStyle = Windows.Forms.FormBorderStyle.None
        背景色= Color.Gray
        不透明度= 0.2

        DrawForm =新DrawingFormClass(ME)
        随着DrawForm
            .BackColor = Color.Tomato
            .TopLevel = TRUE
            .TransparencyKey = Color.Tomato
            .TopMost = TRUE
            .FormBorderStyle = Windows.Forms.FormBorderStyle.None
            .ControlBox =假
            .WindowState = FormWindowState.Maximized
        结束与

        信息=新标签
        随着信息
            .TOP = 16
            。左= 16
            .ForeColor = Color.White
            .AutoSize = TRUE
            DrawForm.Controls.Add(信息)
        结束与

        Me.AddOwnedForm(DrawForm)
        DrawForm.Show()
    结束小组

    私人小组Form1_MouseDown(发送者为对象,E作为MouseEventArgs)处理Me.MouseDown
        绘图= TRUE
        启动= e.Location
    结束小组

    私人小组Form1_MouseMove(发送者为对象,E作为MouseEventArgs)处理Me.MouseMove
        如果绘制然后
            DrawSize =新的大小(e.X  -  Start.X,e.Y  -  Start.Y)
            drawRect的=新的Rectangle(开始,DrawSize)

            如果DrawRect.Height&LT; 0然后
                DrawRect.Height = Math.Abs​​(DrawRect.Height)
                DrawRect.Y  -  = DrawRect.Height
            结束如果

            如果DrawRect.Width&LT; 0然后
                DrawRect.Width = Math.Abs​​(DrawRect.Width)
                DrawRect.X  -  = DrawRect.Width
            结束如果

            Info.Text = DrawRect.ToString
            DrawForm.Invalidate()
        结束如果
    结束小组

    私人小组Form1_MouseUp(发送者为对象,E作为MouseEventArgs)处理Me.MouseUp
        绘图=假
    结束小组

末级
 

正如图将在OnPaintBackground完成,第二类是必要的:

 公共类DrawingFormClass

    私人DrawParent作为YourFormClass

    公共子新(母公司为YourFormClass)

        '该调用是由设计师。
        的InitializeComponent()

        在InitializeComponent()调用后'添加任何初始化。
        Me.DrawParent = YourFormClass
    结束小组

    受保护的覆盖子OnPaintBackground(五作为PaintEventArgs的)
        昏暗博伽梵歌作为位图
        昏暗帆布作为图形


        如果DrawParent.Drawing然后
            BG =新位图(宽,高)
            帆布= Graphics.FromImage(BG)
            Canvas.Clear(Color.Tomato)
            Canvas.DrawRectangle(Pens.Red,DrawParent.DrawRect)
            Canvas.Dispose()
            e.Graphics.DrawImage(BG,0,0,宽度,高度)

            Bg.Dispose()
        其他
            MyBase.OnPaintBackground(五)
        结束如果

    结束小组

末级
 

只需创建两种形式,粘贴......这将创建绘图格式,绘制红色矩形创建位图缓冲区,以便绘制时只需一人操作完成。这工作很细的无闪烁。希望它可以帮助!

I'm using a semi-transparent Form to capture the mouse events such as LeftButtonDown, LeftButtonUp and MouseMove to be able to select an area on the screen to draw a rectangle on that area, the problem is that a new rectangle is drawn every time that I move the mouse producing an annoying result like this:

I just would like to update the drawn rectangle when I move the mouse to the new mouse location to expect a result like this else:

I've tried to dispose, clear, and re-instance the Graphics object without luck, also I've seen this S.O. question that talks about this.

This is the relevant part of the code that I'm using:

''' <summary>
''' The Graphics object to draw on the screen.
''' </summary>
Dim ScreenGraphic As Graphics = Graphics.FromHwnd(IntPtr.Zero)

Private Sub MouseEvents_MouseMove(ByVal MouseLocation As Point) Handles MouseEvents.MouseMove

    ' If left mouse button is hold then set the rectangle area...
    If IsMouseLeftDown Then

        ' ... blah blah blah
        ' ... more code here

        ' Draw the rectangle area.
        Me.DrawRectangle()

    End If

''' <summary>
''' Draws the rectangle on the selected area.
''' </summary>
Private Sub DrawRectangle()

    ' Call the "EraseRectanglehere" method here before re-drawing ?
    ' Me.EraseRectangle

    Using pen As New Pen(Me.BorderColor, Me.BorderSize)
        ScreenGraphic.DrawRectangle(pen, SelectionRectangle)
    End Using

End Sub

''' <summary>
''' Erases the last drawn rectangle.
''' </summary>
Private Sub EraseRectangle()

End Sub

And here is the full code if someone need to inspectionate it better:

NOTE: I've updated the code that I'm using now in my last question edit.

Imports System.Runtime.InteropServices

Public Class RangeSelector : Inherits Form

#Region " Properties "

    ''' <summary>
    ''' Gets or sets the border size of the range selector.
    ''' </summary>
    ''' <value>The size of the border.</value>
    Public Property BorderSize As Integer = 2

    ''' <summary>
    ''' Gets or sets the border color of the range selector.
    ''' </summary>
    ''' <value>The color of the border.</value>
    Public Property BorderColor As Color = Color.Red

#End Region

#Region " Objects "

    ''' <summary>
    ''' Indicates the initial location when the mouse left button is clicked.
    ''' </summary>
    Private InitialLocation As Point = Point.Empty

    ''' <summary>
    ''' Indicates the rectangle that contains the selected area.
    ''' </summary>
    Private SelectionRectangle As Rectangle = Rectangle.Empty

    ''' <summary>
    ''' The Graphics object to draw on the screen.
    ''' </summary>
    Private ScreenGraphic As Graphics = Graphics.FromHwnd(IntPtr.Zero)

#End Region

#Region " Constructors "

    ''' <summary>
    ''' Initializes a new instance of the <see cref="RangeSelector"/> class.
    ''' </summary>
    Public Sub New()

        InitializeComponent()

    End Sub

    ''' <summary>
    ''' Initializes a new instance of the <see cref="RangeSelector" /> class.
    ''' </summary>
    ''' <param name="BorderSize">Indicates the border size of the range selector.</param>
    ''' <param name="BorderColor">Indicates the border color of the range selector.</param>
    Public Sub New(ByVal BorderSize As Integer, ByVal BorderColor As Color)

        Me.BorderSize = BorderSize
        Me.BorderColor = BorderColor

        InitializeComponent()

    End Sub

#End Region

#Region " Event Handlers "

    Protected Overrides Sub OnMouseDown(e As MouseEventArgs)

        ' MyBase.OnMouseDown(e)
        InitialLocation = e.Location
        SelectionRectangle = New Rectangle(InitialLocation.X, InitialLocation.Y, 0, 0)

    End Sub

    Protected Overrides Sub OnMouseUp(e As MouseEventArgs)

        ' Make the Form transparent to take the region screenshot.
        Me.Opacity = 0.0R

        ' ToDo:
        ' take the screenshot.
        ' Return the selected rectangle area and save it.

        Me.Close()

    End Sub

    Protected Overrides Sub OnMouseMove(e As MouseEventArgs)

        ' If left mouse button is hold then set the rectangle area...
        If e.Button = MouseButtons.Left Then

            If (e.Location.X < Me.InitialLocation.X) _
            AndAlso (e.Location.Y < Me.InitialLocation.Y) Then ' Top-Left

                Me.SelectionRectangle = New Rectangle(e.Location.X,
                                                      e.Location.Y,
                                                      Me.InitialLocation.X - e.Location.X,
                                                      Me.InitialLocation.Y - e.Location.Y)

            ElseIf (e.Location.X > Me.InitialLocation.X) _
            AndAlso (e.Location.Y < Me.InitialLocation.Y) Then ' Top-Right

                Me.SelectionRectangle = New Rectangle(Me.InitialLocation.X,
                                                      e.Location.Y,
                                                      e.Location.X - Me.InitialLocation.X,
                                                      Me.InitialLocation.Y - e.Location.Y)

            ElseIf (e.Location.X < Me.InitialLocation.X) _
            AndAlso (e.Location.Y > Me.InitialLocation.Y) Then ' Bottom-Left

                Me.SelectionRectangle = New Rectangle(e.Location.X,
                                                      Me.InitialLocation.Y,
                                                      Me.InitialLocation.X - e.Location.X,
                                                      e.Location.Y - Me.InitialLocation.Y)

            ElseIf (e.Location.X > Me.InitialLocation.X) _
            AndAlso (e.Location.Y > Me.InitialLocation.Y) Then ' Bottom-Right

                Me.SelectionRectangle = New Rectangle(Me.InitialLocation.X,
                                                      Me.InitialLocation.Y,
                                                      e.Location.X - Me.InitialLocation.X,
                                                      e.Location.Y - Me.InitialLocation.Y)
            End If

            ' Draw the rectangle area.
            Me.DrawRectangle()

        End If

    End Sub

#End Region

#Region " Private Methods "

    Private Sub InitializeComponent()

        Me.SuspendLayout()
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
        Me.BackColor = System.Drawing.Color.Black
        Me.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None
        Me.CausesValidation = False
        Me.ClientSize = New System.Drawing.Size(100, 100)
        Me.ControlBox = False
        Me.Cursor = System.Windows.Forms.Cursors.Cross
        Me.DoubleBuffered = True
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
        Me.MaximizeBox = False
        Me.MinimizeBox = False
        Me.Name = "RangeSelector"
        Me.Opacity = 0.01R
        Me.ShowIcon = False
        Me.ShowInTaskbar = False
        Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.TopMost = True
        Me.WindowState = System.Windows.Forms.FormWindowState.Maximized
        Me.ResumeLayout(False)

    End Sub

    ''' <summary>
    ''' Draws the rectangle on the selected area.
    ''' </summary>
    Private Sub DrawRectangle()

        ' Just a weird trick to refresh the painting.
        ' Me.Opacity = 0.0R
        ' Me.Opacity = 0.01R

        ' Using g As Graphics = Graphics.FromHwnd(IntPtr.Zero)

        Using pen As New Pen(Me.BorderColor, Me.BorderSize)
            ScreenGraphic.DrawRectangle(pen, Me.SelectionRectangle)
        End Using

        ' End Using

    End Sub

#End Region

End Class

UPDATE 1

I've translated all the code to use it as a Form dialog to have more flexibility when selecting a region, I've replaced the entire code above to update my question, the code does not change too much just instead using a LL Hook to capture the mouse events I'm handling the mouse events of a semi-transparent maximized Form, I still drawing the rectangle on the Desktop Screen Graphics (not on the OnPaint Form event) that part of the code is the same as you can see in the code above:

Private ScreenGraphic As Graphics = Graphics.FromHwnd(IntPtr.Zero)

...'cause as I've said the Form is semi-transparent so if I draw a rectangle in the Form it will be semi-transparent too (or at least I don't know a way to avoid that).

Then I've discover a weird trick to solve the rectangle issue by changing the opacity of the Form before drawing the rectangle in the new coordinates:

    Me.Opacity = 0.0R
    Me.Opacity = 0.01R

    Using pen As New Pen(Me.BorderColor, Me.BorderSize)
        ScreenGraphic.DrawRectangle(pen, Me.SelectionRectangle)
    End Using

The problem? ...Is not perfect, it generates a very annoying effect 'cause I get a lot of flickering when drawing the rectangle (and yes I have the Form doubleBuffered and also I'm using the CreateParams trick to try to avoid flickering, but nothing).

UPDATE 2

I've tried to use what @Plutonix pointed in his comment, the InvalidateRect function, with this API declaration:

<DllImport("user32.dll")>
Private Shared Function InvalidateRect(
        ByVal hWnd As Integer,
        ByRef lpRect As Rectangle,
        ByVal bErase As Boolean) As Boolean
End Function

I've tried to use it with both False/True Flags.

The problem? the problem is the same as the one that I pointed in my first update:

'Is not perfect, it generates a very annoying effect 'cause I get a lot of flickering when drawing the rectangle (and yes I have the Form doubleBuffered and also I'm using the CreateParams trick to try to avoid flickering, but nothing).'

UPDATE 3

I'm trying to fix this issue using the RedrawWindow function which as I've seen in this SO answer it can be used to do the same as InvalidateRect function does but also with more flexibility and maybe without the annoying effect that I get using the InvalidateRect function, I just needed to try it.

The RedrawWindow function updates the specified rectangle or region in a window's client area.

This is the API declaration:

<DllImport("user32.dll")>
Private Shared Function RedrawWindow(
        ByVal hWnd As IntPtr,
        <[In]> ByRef lprcUpdate As Rectangle,
        ByVal hrgnUpdate As IntPtr,
        ByVal flags As RedrawWindowFlags) As Boolean
End Function

<Flags()>
Private Enum RedrawWindowFlags As UInteger
    ''' <summary>
    ''' Invalidates the rectangle or region that you specify in lprcUpdate or hrgnUpdate.
    ''' You can set only one of these parameters to a non-NULL value. If both are NULL, RDW_INVALIDATE invalidates the entire window.
    ''' </summary>
    Invalidate = &H1

    ''' <summary>Causes the OS to post a WM_PAINT message to the window regardless of whether a portion of the window is invalid.</summary>
    InternalPaint = &H2

    ''' <summary>
    ''' Causes the window to receive a WM_ERASEBKGND message when the window is repainted.
    ''' Specify this value in combination with the RDW_INVALIDATE value; otherwise, RDW_ERASE has no effect.
    ''' </summary>
    [Erase] = &H4

    ''' <summary>
    ''' Validates the rectangle or region that you specify in lprcUpdate or hrgnUpdate.
    ''' You can set only one of these parameters to a non-NULL value. If both are NULL, RDW_VALIDATE validates the entire window.
    ''' This value does not affect internal WM_PAINT messages.
    ''' </summary>
    Validate = &H8

    NoInternalPaint = &H10

    ''' <summary>Suppresses any pending WM_ERASEBKGND messages.</summary>
    NoErase = &H20

    ''' <summary>Excludes child windows, if any, from the repainting operation.</summary>
    NoChildren = &H40

    ''' <summary>Includes child windows, if any, in the repainting operation.</summary>
    AllChildren = &H80

    ''' <summary>Causes the affected windows, which you specify by setting the RDW_ALLCHILDREN and RDW_NOCHILDREN values, to receive WM_ERASEBKGND and WM_PAINT messages before the RedrawWindow returns, if necessary.</summary>
    UpdateNow = &H100

    ''' <summary>
    ''' Causes the affected windows, which you specify by setting the RDW_ALLCHILDREN and RDW_NOCHILDREN values, to receive WM_ERASEBKGND messages before RedrawWindow returns, if necessary.
    ''' The affected windows receive WM_PAINT messages at the ordinary time.
    ''' </summary>
    EraseNow = &H200

    Frame = &H400

    NoFrame = &H800
End Enum

I've tried to use the function with these parameters:

RedrawWindow(IntPtr.Zero, Me.SelectionRectangle, IntPtr.Zero, RedrawWindowFlags.Invalidate)

...Which I suppose that as the MSDN documentation describes, if the first parameter is NULL it means the desktop screen, the second parameter means the rectangle to update, the third parameter need to be null if I've specified a rectangle in the second parameter, and the last parameter means a flag that indicates the action(s) to perform (in this case invalidate the rectangle as @Plutonix said?)

I've tried to use that isntruction after drawing the rectangle and before drawing it, I mean in the OnMouseMove event, and else inside my DrawRectangle method in my code, but I don't see any difference in the screen, I still have the same problem that I shown in the images above when drawing the rectangle I mean that multiple rectangles are drawn when I move the mouse and any rectangle is erased by this function, maybe I'm using the wrong parameters?.

解决方案

The solution is more simple and windows API isn't required. Just create a transparent from and draw red rectangle on it. Following code do that, you only need to replace in your semi transparent form. The flickering happens because we clean the graphics and then draw, the easiest way to avoid it is do painting at once, so if we paint the rectangle on a bitmap and then draw the bitmap, operation is done in one step and flickering doesn't happens.

Drawing will be done on OnPaintBackground of the drawing form so a drawing form with will be needed. This is the main class, where the events are captured:

Public Class YourFormClass

    Dim Start As Point
    Dim DrawSize As Size
    Public DrawRect As Rectangle
    Public Drawing As Boolean = False
    Dim Info As Label
    Dim DrawForm As Form

    Private Sub YourFormClass_Load(sender As Object, e As EventArgs) Handles Me.Load
        ' Add any initialization after the InitializeComponent() call.
        ControlBox = False
        WindowState = FormWindowState.Maximized
        FormBorderStyle = Windows.Forms.FormBorderStyle.None
        BackColor = Color.Gray
        Opacity = 0.2

        DrawForm = New DrawingFormClass(Me)
        With DrawForm
            .BackColor = Color.Tomato
            .TopLevel = True
            .TransparencyKey = Color.Tomato
            .TopMost = True
            .FormBorderStyle = Windows.Forms.FormBorderStyle.None
            .ControlBox = False
            .WindowState = FormWindowState.Maximized
        End With

        Info = New Label
        With Info
            .Top = 16
            .Left = 16
            .ForeColor = Color.White
            .AutoSize = True
            DrawForm.Controls.Add(Info)
        End With

        Me.AddOwnedForm(DrawForm)
        DrawForm.Show()
    End Sub

    Private Sub Form1_MouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
        Drawing = True
        Start = e.Location
    End Sub

    Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
        If Drawing Then
            DrawSize = New Size(e.X - Start.X, e.Y - Start.Y)
            DrawRect = New Rectangle(Start, DrawSize)

            If DrawRect.Height < 0 Then
                DrawRect.Height = Math.Abs(DrawRect.Height)
                DrawRect.Y -= DrawRect.Height
            End If

            If DrawRect.Width < 0 Then
                DrawRect.Width = Math.Abs(DrawRect.Width)
                DrawRect.X -= DrawRect.Width
            End If

            Info.Text = DrawRect.ToString
            DrawForm.Invalidate()
        End If
    End Sub

    Private Sub Form1_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
        Drawing = False
    End Sub

End Class

As drawing will be done in OnPaintBackground, a second class is needed:

Public Class DrawingFormClass

    Private DrawParent As YourFormClass

    Public Sub New(Parent As YourFormClass)

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.DrawParent = YourFormClass
    End Sub

    Protected Overrides Sub OnPaintBackground(e As PaintEventArgs)
        Dim Bg As Bitmap
        Dim Canvas As Graphics


        If DrawParent.Drawing Then
            Bg = New Bitmap(Width, Height)
            Canvas = Graphics.FromImage(Bg)
            Canvas.Clear(Color.Tomato)
            Canvas.DrawRectangle(Pens.Red, DrawParent.DrawRect)
            Canvas.Dispose()
            e.Graphics.DrawImage(Bg, 0, 0, Width, Height)

            Bg.Dispose()
        Else
            MyBase.OnPaintBackground(e)
        End If

    End Sub

End Class

Just create two forms and paste... It will create the drawing form and draw the red rectangle creating a bitmap buffer so only one operation is done when drawing. This works very fine without flickering. Hope it helps!

这篇关于如何正确地清除或更新屏幕上绘制的矩形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆