“显示对话框"方法无法正常工作(设置 Form 父级时?) [英] "ShowDialog" method is not working properly (when setting the Form parent?)
问题描述
我正在尝试编写一个应在屏幕上绘制矩形以选择屏幕区域的表单,问题和解决方案可以在其他 StackOverflow 帖子中看到,此处.我已经根据我的需要调整了@ZeroWorks 的标记解决方案,但现在的问题是我找不到像我需要的那样使用表单的方法.
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
我只想使用 ShowDialog
方法来显示表单并停止执行,直到用户选择该区域,因为我关闭了 OnMouseUp
事件表单,但是如果我尝试使用 ShowDialog
方法,我将无法绘制/查看矩形,顺便说一下,如果我使用 Show
方法,它可以正常工作但就像我说的,我需要使用 ShowDialog
方法来等待表单的响应"以继续执行下一个指令,我不明白为什么会发生这个问题,我是什么我失踪了?,我该如何解决这个问题?.
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
推荐答案
我不会用 formdialog 方法来做,你会遇到很多麻烦让它工作,表单对话框会阻塞执行并等待它关闭.但是有一个解决方案......我会使用回调.所以,首先定义一个委托:
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:
Public Delegate Sub RegionSelectedDelegate(Region As Rectangle)
然后在调用表单中,在子或函数中,因为使用会破坏表单改变它:
Then in the calling form, in a sub or function, since using will destroy form change it:
Dim RegionRect As Rectangle = Rectangle.Empty
Dim Working As Boolean = False
Public Sub GetRectangle()
Dim Callback As RegionSelectedDelegate
Dim Selector As New RegionSelector
If Working Then Exit Sub 'Only one selection at once!
Working = True
Callback = New RegionSelectedDelegate(AddressOf RectangleDrawn)
With Selector
.Callback = Callback
.Show()
End With
' Don't do any stuff here... do it in Rectangle Drawn...
End Sub
...
Public Sub RectangleDrawn(Region as Rectangle)
Working = false 'Allow draw again.
Me.RegionRect=Region
MessageBox.Show("Do next steps!")
' Some stuff Here
End Sub
该委托将从 onmouseup 上的绘图表单(绘制矩形时)调用,并由 RegionSelector 类接收.因此,将在 RegionSelector 类中定义一个新的属性回调,并在 onmouseup 事件处理程序中调用此委托:
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:
''' <summary>
''' Selects a region on the Screen.
''' </summary>
Public Class RegionSelector : Inherits Form
#Region " Properties "
''' <summary>
''' Callback to be invoked when drawing is done...
''' </summary>
''' <value>Delegate of Region Selected</value>
Public Property Callback As RegionSelectedDelegate = Nothing
''' <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)
'Do the callback here!
Me.IsDrawing = False
Callback.Invoke(SelectedRegion)
Me.Close() 'Must be called last.
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
就是这样,没有显示任何阻塞的模态形式,流程保持其逻辑并且可以正常工作.希望有帮助.
And that's all, no blocking modal forms are shown, the flow stills its logic and it works. Hope it helps.
这篇关于“显示对话框"方法无法正常工作(设置 Form 父级时?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!