I'm trying to write a Form that should draw a rectangle over the screen to select an screen region, the question and the solution(s) can be seen in other StackOverflow post, here. I've adapted the marked solution of @ZeroWorks to my needs, but the problem now is that I can't find the way to use the Form like I require.
This is how I would like to use it:
Dim RegionRect As Rectangle = Rectangle.Empty
Using Selector As New RegionSelector
Selector.ShowDialog()
RegionRect = Selector.SelectedRegion
End Using
I just would like to use the ShowDialog
method to show the Form and stop the execution until the region is selected by the user 'cause on the OnMouseUp
event I close the form, but If I try to use the ShowDialog
method I'm not able to draw/see the rectangle, by the way if I use the Show
method it works properly but like I've said I need to use instead the ShowDialog
method to wait a "response" from the Form to proceed with the next instructions, I don't understand why happens this problem, what I'm missing?, how I could fix this?.
This is the (full) code of the custom region selector:
''' <summary>
''' Selects a region on the Screen.
''' </summary>
Public Class RegionSelector : Inherits Form
#Region " Properties "
''' <summary>
''' Gets or sets the border size of the region selector.
''' </summary>
''' <value>The size of the border.</value>
Public Property BorderSize As Integer = 2
''' <summary>
''' Gets or sets the border color of the region selector.
''' </summary>
''' <value>The color of the border.</value>
Public Property BorderColor As Color = Color.Red
''' <summary>
''' Gets the rectangle that contains the selected region.
''' </summary>
Public ReadOnly Property SelectedRegion As Rectangle
Get
Return Me.DrawRect
End Get
End Property
#End Region
#Region " Objects "
''' <summary>
''' Indicates the initial location when the mouse left button is clicked.
''' </summary>
Private InitialLocation As Point = Point.Empty
''' <summary>
''' The rectangle where to draw the region.
''' </summary>
Public DrawRect As Rectangle = Rectangle.Empty
''' <summary>
''' The Graphics object to draw on the screen.
''' </summary>
Private ScreenGraphic As Graphics = Graphics.FromHwnd(IntPtr.Zero)
Public IsDrawing As Boolean = False
Dim DrawSize As Size
Dim DrawForm As Form
#End Region
#Region " Constructors "
''' <summary>
''' Initializes a new instance of the <see cref="RegionSelector"/> class.
''' </summary>
Public Sub New()
End Sub
''' <summary>
''' Initializes a new instance of the <see cref="RegionSelector" /> class.
''' </summary>
''' <param name="BorderSize">Indicates the border size of the region selector.</param>
''' <param name="BorderColor">Indicates the border color of the region selector.</param>
Public Sub New(ByVal BorderSize As Integer,
ByVal BorderColor As Color)
Me.BorderSize = BorderSize
Me.BorderColor = BorderColor
End Sub
#End Region
#Region " Event Handlers "
''' <summary>
''' Handles the Load event of the RegionSelector.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
Private Sub RegionSelector_Load(sender As Object, e As EventArgs) Handles Me.Load
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(0, 0)
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 = "RegionSelector"
Me.Opacity = 0.15R
Me.ShowIcon = False
Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
Me.TopMost = False
Me.WindowState = System.Windows.Forms.FormWindowState.Maximized
Me.ResumeLayout(True)
Me.DrawForm = New DrawingRegionClass(Me)
With DrawForm
.BackColor = Color.Tomato
.TopLevel = True
.TransparencyKey = Color.Tomato
.TopMost = False
.FormBorderStyle = Windows.Forms.FormBorderStyle.None
.ControlBox = False
.WindowState = FormWindowState.Maximized
End With
Me.AddOwnedForm(Me.DrawForm)
Me.DrawForm.Show()
End Sub
''' <summary>
''' Raises the <see cref="E:System.Windows.Forms.Control.MouseDown" /> event.
''' </summary>
''' <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs" /> that contains the event data.</param>
Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Left Then
Me.InitialLocation = e.Location
Me.IsDrawing = True
End If
End Sub
''' <summary>
''' Raises the <see cref="E:System.Windows.Forms.Control.MouseUp" /> event.
''' </summary>
''' <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs" /> that contains the event data.</param>
Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
Me.IsDrawing = False
Me.Close()
End Sub
''' <summary>
''' Raises the <see cref="E:System.Windows.Forms.Control.MouseMove" /> event.
''' </summary>
''' <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs" /> that contains the event data.</param>
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
If Me.IsDrawing Then
Me.DrawSize = New Size(e.X - Me.InitialLocation.X, e.Y - Me.InitialLocation.Y)
Me.DrawRect = New Rectangle(Me.InitialLocation, Me.DrawSize)
If Me.DrawRect.Height < 0 Then
Me.DrawRect.Height = Math.Abs(Me.DrawRect.Height)
Me.DrawRect.Y -= Me.DrawRect.Height
End If
If Me.DrawRect.Width < 0 Then
Me.DrawRect.Width = Math.Abs(Me.DrawRect.Width)
Me.DrawRect.X -= Me.DrawRect.Width
End If
Me.DrawForm.Invalidate()
End If
End Sub
#End Region
End Class
Public Class DrawingRegionClass : Inherits Form
Private DrawParent As RegionSelector
Public Sub New(ByVal Parent As Form)
Me.DrawParent = Parent
End Sub
Protected Overrides Sub OnPaintBackground(ByVal e As PaintEventArgs)
Dim Bg As Bitmap
Dim Canvas As Graphics
If Me.DrawParent.IsDrawing Then
Bg = New Bitmap(Width, Height)
Canvas = Graphics.FromImage(Bg)
Canvas.Clear(Color.Tomato)
Canvas.DrawRectangle(Pens.Red, Me.DrawParent.DrawRect)
Canvas.Dispose()
e.Graphics.DrawImage(Bg, 0, 0, Width, Height)
Bg.Dispose()
Else
MyBase.OnPaintBackground(e)
End If
End Sub
End Class
I wouldn't do it with formdialog method, you will have a lot of troubles to get it work, form dialog blocks execution and waits until its closed. But there's is a solution instead... I would use a Callback. So, first define a Delegate:
Then in the calling form, in a sub or function, since using will destroy form change it:
...
That delegate will be called from drawing form on onmouseup (when the rectangle is drawn) and will be received by RegionSelector Class. So, will define a new property Callback in RegionSelector Class, and invoke this delegate in onmouseup event handler:
And that's all, no blocking modal forms are shown, the flow stills its logic and it works. Hope it helps.