我正在写一个WinForms应用程序。NET 4.8),你可以用它加载图片到一个图片框,移动它,缩放它,最后在它上面画画。按照计划,这幅画将先被移动,然后再在上面作画。因此,只有当相关的radio button
被选中时,图片才会分配给PictureBox。不幸的是,图像的分配一次又一次地引起paint event
。有一个无限循环时触发一次。我该如何预防呢?我已经尝试从PictureBox
继承并设置不同的设置。
Public Class PictBoxEx : Inherits PictureBox
Public Sub New()
SetStyle(ControlStyles.Selectable Or ControlStyles.UserMouse, True) ' Important so that the PictureBox does not throw events on its own!
End Sub
End Class
Private Sub PictureBoxEx1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBoxEx1.Paint
If LoadedImage Is Nothing Then
Return
End If
If RadioButton_verschieben.Checked Then
Dim the_rectangle_to_be_zoomed_in_and_drawn As RectangleF = calculate_zoomed_and_or_moved_rect()
e.Graphics.DrawImage(LoadedImage, the_rectangle_to_be_zoomed_in_and_drawn)
ElseIf RadioButton_freihand.Checked Then
PictureBoxEx1.Image = Nothing
GC.Collect()
PictureBoxEx1.Image = LoadedImage
FunctionsToDraw.DrawTheUsersPath(e.Graphics)
End If
End Sub
这是有问题的程序。↑
如果你需要更多的信息,我将源代码放在这里。
Form1.vb
Imports System.Drawing.Drawing2D
Imports Microsoft.VisualBasic.ControlChars
Imports Microsoft.WindowsAPICodePack.Dialogs
Public NotInheritable Class FormMain
'— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
'these variables are used for moving and zooming
Private Shared LoadedImage As System.Drawing.Bitmap
Private Shared current_Zoom_factor As Single = 1.0F
Private mouse_location As PointF = Point.Empty
Private image_location As PointF = Point.Empty
Private image_rect As RectangleF = RectangleF.Empty
Private mouse_up_must_follow As Boolean
Private moving As Boolean
'— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
'these variables are used for drawing
Private Shared _manuallydrawnpathonscreen As New System.Drawing.Drawing2D.GraphicsPath
Private last_mouse_location_on_screen As Point
Public Shared Property Manuallydrawnpathonscreen As GraphicsPath
Get
Return _manuallydrawnpathonscreen
End Get
Set(value As GraphicsPath)
_manuallydrawnpathonscreen = value
End Set
End Property
Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.BackColor = Color.FromArgb(0, 0, 31) ' very dark blue
For Each Bu As Button In Me.Controls.OfType(Of Button)
Bu.BackColor = Color.FromArgb(201, 201, 189)
Next
RadioButton_verschieben.Text = $"verschieben{NewLine}und{NewLine}zoomen"
RadioButton_verschieben.Checked = True
End Sub
Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
Using OFD As New CommonOpenFileDialog
OFD.Title = "Bild zum Öffnen auswählen"
OFD.Filters.Add(New CommonFileDialogFilter("images", ".jpg;.jpeg;.bmp;.png"))
OFD.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
If OFD.ShowDialog() = CommonFileDialogResult.Ok Then
LoadedImage = Nothing
PictureBoxEx1.Image = Nothing
GC.Collect()
LoadedImage = New System.Drawing.Bitmap(OFD.FileName)
current_Zoom_factor = 1.0F
mouse_location = Point.Empty
image_location = Point.Empty
image_rect = RectangleF.Empty
Manuallydrawnpathonscreen.ClearMarkers()
'PictureBoxEx1.Image = LoadedImage
resize_PictureBox()
Else
Return
End If
End Using
End Sub
Private Sub resize_PictureBox()
If LoadedImage Is Nothing Then Return
'Width of the current picture
Dim Aktuelle_Breite As Integer = LoadedImage.Size.Width
'Height of the current picture
Dim Aktuelle_Hoehe As Integer = LoadedImage.Size.Height
' possible width on Form
Dim Moegliche_Breite As Integer = 1762
' possible height on Form
Dim Moegliche_Hoehe As Integer = 1000
If Aktuelle_Breite > Moegliche_Breite OrElse Aktuelle_Hoehe > Moegliche_Hoehe Then
PictureBoxEx1.SizeMode = PictureBoxSizeMode.StretchImage
PictureBoxEx1.Size = If(CInt(Math.Round(Aktuelle_Breite * Moegliche_Hoehe / Aktuelle_Hoehe, 0)) > Moegliche_Breite,
New Size(Moegliche_Breite, CInt(Math.Round(Aktuelle_Hoehe * Moegliche_Breite / Aktuelle_Breite, 0))),
New Size(CInt(Math.Round(Aktuelle_Breite * Moegliche_Hoehe / Aktuelle_Hoehe, 0)), Moegliche_Hoehe))
Else
PictureBoxEx1.SizeMode = PictureBoxSizeMode.Normal
PictureBoxEx1.Size = New Size(Aktuelle_Breite, Aktuelle_Hoehe)
End If
End Sub
Private Sub PictureBoxEx1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseDown
Select Case e.Button
Case MouseButtons.Left
If RadioButton_verschieben.Checked Then
mouse_location = e.Location
image_location = image_rect.Location ' ist (0|0) wenn Bild frisch geladen
Me.Cursor = Cursors.NoMove2D
mouse_up_must_follow = True
moving = True
PictureBoxEx1.Invalidate()
Return
ElseIf RadioButton_freihand.Checked Then
Manuallydrawnpathonscreen.AddLine(last_mouse_location_on_screen, e.Location)
End If
Case MouseButtons.Right
If RadioButton_freihand.Checked Then
Manuallydrawnpathonscreen.ClearMarkers()
PictureBoxEx1.Invalidate()
End If
Case Else
Exit Select
End Select
End Sub
Private Sub PictureBoxEx1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseMove
If e.Button = MouseButtons.Left AndAlso mouse_up_must_follow Then
If RadioButton_verschieben.Checked Then
'for Zoom
image_rect.Location = New PointF(image_location.X + (e.Location.X - mouse_location.X),
image_location.Y + (e.Location.Y - mouse_location.Y))
mouse_up_must_follow = True
moving = True
PictureBoxEx1.Invalidate()
Return
' end Zoom region
ElseIf RadioButton_freihand.Checked Then
mouse_up_must_follow = True
moving = True
Manuallydrawnpathonscreen.AddLine(last_mouse_location_on_screen, e.Location)
PictureBoxEx1.Invalidate()
End If
End If
last_mouse_location_on_screen = e.Location
End Sub
Private Sub PictureBoxEx1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseUp
If e.Button = MouseButtons.Left AndAlso mouse_up_must_follow Then
If RadioButton_verschieben.Checked Then
Me.Cursor = Cursors.Default
mouse_up_must_follow = False
moving = False
Return
ElseIf RadioButton_freihand.Checked Then
Manuallydrawnpathonscreen.CloseFigure()
mouse_up_must_follow = False
End If
End If
last_mouse_location_on_screen = e.Location
End Sub
Private Sub PictBox1_MouseWheel(sender As System.Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseWheel
If RadioButton_verschieben.Checked Then
If e.Delta > 0 Then
current_Zoom_factor += (1.0F / 30.0F)
PictureBoxEx1.Invalidate()
Else
If current_Zoom_factor > 0.0F Then
current_Zoom_factor -= (1.0F / 30.0F)
PictureBoxEx1.Invalidate()
End If
End If
End If
End Sub
Private Sub PictureBoxEx1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBoxEx1.Paint
If LoadedImage Is Nothing Then
Return
End If
If RadioButton_verschieben.Checked Then
Dim the_rectangle_to_be_zoomed_in_and_drawn As RectangleF = calculate_zoomed_and_or_moved_rect()
e.Graphics.DrawImage(LoadedImage, the_rectangle_to_be_zoomed_in_and_drawn)
ElseIf RadioButton_freihand.Checked Then
PictureBoxEx1.Image = Nothing
GC.Collect()
PictureBoxEx1.Image = LoadedImage
FunctionsToDraw.DrawTheUsersPath(e.Graphics)
End If
End Sub
Private Function calculate_zoomed_and_or_moved_rect() As RectangleF
image_rect.Width = PictureBoxEx1.Width
image_rect.Height = PictureBoxEx1.Height
Dim newLocation As PointF
If moving Then
newLocation = image_rect.Location
Else
newLocation = New PointF(
PictureBoxEx1.Location.X + (PictureBoxEx1.Size.Width / 2.0F) - image_rect.Width * current_Zoom_factor / 2.0F,
PictureBoxEx1.Location.Y + (PictureBoxEx1.Size.Height / 2.0F) - image_rect.Height * current_Zoom_factor / 2.0F)
End If
Dim scaled As New RectangleF(newLocation,
New SizeF(image_rect.Width * current_Zoom_factor, image_rect.Height * current_Zoom_factor))
Return scaled
End Function
End Class
FunctionsToDraw.vb
Public NotInheritable Class FunctionsToDraw
Public Shared Sub DrawTheUsersPath(ByVal g As Drawing.Graphics)
If g Is Nothing Then
Return
End If
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear
Using Pen_green As New Pen(Color.FromArgb(0, 210, 0), 2.0F)
g.DrawPath(Pen_green, FormMain.Manuallydrawnpathonscreen)
End Using
End Sub
End Class
我现在已经解决了这个问题。你们都是对的-在paint事件处理程序中将图片分配给Picturebox是不明智的,特别是因为我还想在缩放的矩形上绘制。我想我必须先解决这个问题,但后来发现这实际上就是问题所在。
我现在已经做了。我可以在缩放和移动的矩形上绘制。
我创建了第二个表单,显示鼠标和矩形的当前值。在不久的将来,当我在缩放和移动的图像上绘制GraphicsPath时,我将测试计算原始图像上的坐标位置。
我把整个代码放到网上,因为我已经重建了很多。欢迎您提出改进建议。
Form1.vb
#Disable Warning CA1707
Imports Microsoft.VisualBasic.ControlChars
Imports Microsoft.WindowsAPICodePack.Dialogs
Public NotInheritable Class FormMain
Private Shared _lF As LoggingForm
Private Shared _deu As New System.Globalization.CultureInfo("de-DE")
'— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
'these variables are used for moving and zooming
Private Shared _loadedImage As System.Drawing.Bitmap
Private Shared current_Zoom_factor As Single = 1.0F
Private mouse_location As PointF = Point.Empty
Private image_location As PointF = Point.Empty
Private image_rect As RectangleF = RectangleF.Empty
Private mouse_up_must_follow As Boolean
Private moving As Boolean
'— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
'these variables are used for drawing
Private Shared _manuallydrawnpathonscreen As New System.Drawing.Drawing2D.GraphicsPath
Private Shared _lastMouseLocationOnScreen As Point = Point.Empty
Public Shared Property Manuallydrawnpathonscreen As Drawing2D.GraphicsPath
Get
Return _manuallydrawnpathonscreen
End Get
Set(value As Drawing2D.GraphicsPath)
_manuallydrawnpathonscreen = value
End Set
End Property
Public Shared Property LoadedImage As Bitmap
Get
Return _loadedImage
End Get
Set(value As Bitmap)
_loadedImage = value
End Set
End Property
Public Shared Property LastMouseLocationOnScreen As Point
Get
Return _lastMouseLocationOnScreen
End Get
Set(value As Point)
_lastMouseLocationOnScreen = value
End Set
End Property
Public Shared Property Deu As System.Globalization.CultureInfo
Get
Return _deu
End Get
Set(value As System.Globalization.CultureInfo)
_deu = value
End Set
End Property
Public Shared Property LF As LoggingForm
Get
Return _lF
End Get
Set(value As LoggingForm)
_lF = value
End Set
End Property
'— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.BackColor = Color.FromArgb(0, 0, 31) ' very dark blue
For Each Bu As Button In Me.Controls.OfType(Of Button)
Bu.BackColor = Color.FromArgb(201, 201, 189)
Next
RadioButton_verschieben.Text = $"verschieben{NewLine}und{NewLine}zoomen"
RadioButton_verschieben.Checked = True
'––––––––––––––––––––––––––––––––––––––––––––––
' Find a second screen if possible.
Dim allScreens As Screen() = Screen.AllScreens
If allScreens.Length = 2 Then
Me.Location = New Point(allScreens(1).Bounds.X, allScreens(1).Bounds.Y)
End If
'––––––––––––––––––––––––––––––––––––––––––––––
End Sub
Private Sub FormMain_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
LF = New LoggingForm
LF.Show()
End Sub
Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
Using OFD As New CommonOpenFileDialog
OFD.Title = "Bild zum Öffnen auswählen"
OFD.Filters.Add(New CommonFileDialogFilter("images", ".jpg;.jpeg;.bmp;.png"))
OFD.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
If OFD.ShowDialog() = CommonFileDialogResult.Ok Then
LoadedImage = Nothing
PictureBoxEx1.Image = Nothing
LoadedImage = New System.Drawing.Bitmap(OFD.FileName)
current_Zoom_factor = 1.0F
mouse_location = Point.Empty
image_location = Point.Empty
image_rect = RectangleF.Empty
PictureBoxEx1.Image = LoadedImage ' must be assigned once so that the PictureBox assumes the correct dimensions with ‘resize_PictureBox()’. This is also important later for the rectangle.
resize_PictureBox()
PictureBoxEx1.Image = Nothing
GC.Collect()
Else
Return
End If
End Using
End Sub
Private Sub resize_PictureBox()
If LoadedImage Is Nothing Then Return
'Width of the current picture
Dim Aktuelle_Breite As Integer = LoadedImage.Size.Width
'Height of the current picture
Dim Aktuelle_Hoehe As Integer = LoadedImage.Size.Height
' possible width on Form
Dim Moegliche_Breite As Integer = 1762
' possible height on Form
Dim Moegliche_Hoehe As Integer = 1000
If Aktuelle_Breite > Moegliche_Breite OrElse Aktuelle_Hoehe > Moegliche_Hoehe Then
PictureBoxEx1.SizeMode = PictureBoxSizeMode.StretchImage
PictureBoxEx1.Size = If(CInt(Math.Round(Aktuelle_Breite * Moegliche_Hoehe / Aktuelle_Hoehe, 0)) > Moegliche_Breite,
New Size(Moegliche_Breite, CInt(Math.Round(Aktuelle_Hoehe * Moegliche_Breite / Aktuelle_Breite, 0))),
New Size(CInt(Math.Round(Aktuelle_Breite * Moegliche_Hoehe / Aktuelle_Hoehe, 0)), Moegliche_Hoehe))
Else
PictureBoxEx1.SizeMode = PictureBoxSizeMode.Normal
PictureBoxEx1.Size = New Size(Aktuelle_Breite, Aktuelle_Hoehe)
End If
End Sub
Private Sub PictureBoxEx1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseDown
Select Case e.Button
Case MouseButtons.Left
If RadioButton_verschieben.Checked Then
mouse_location = e.Location
image_location = image_rect.Location ' is (0 | 0) if the picture is freshly loaded
Me.Cursor = Cursors.NoMove2D
mouse_up_must_follow = True
moving = True
Return
ElseIf RadioButton_freihand.Checked Then
mouse_up_must_follow = True
Manuallydrawnpathonscreen.AddLine(LastMouseLocationOnScreen, e.Location)
PictureBoxEx1.Invalidate()
End If
Case MouseButtons.Right
If RadioButton_freihand.Checked Then
Manuallydrawnpathonscreen = New Drawing2D.GraphicsPath
PictureBoxEx1.Invalidate()
End If
Case Else
Exit Select
End Select
End Sub
Private Sub PictureBoxEx1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseMove
If e.Button = MouseButtons.Left AndAlso mouse_up_must_follow Then
If RadioButton_verschieben.Checked Then
'for Zoom
image_rect.Location = New PointF(image_location.X + (e.Location.X - mouse_location.X),
image_location.Y + (e.Location.Y - mouse_location.Y))
mouse_up_must_follow = True
moving = True
PictureBoxEx1.Invalidate()
Return
' end Zoom region
ElseIf RadioButton_freihand.Checked Then
mouse_up_must_follow = True
moving = True
Manuallydrawnpathonscreen.AddLine(LastMouseLocationOnScreen, e.Location)
PictureBoxEx1.Invalidate()
End If
End If
LastMouseLocationOnScreen = e.Location
End Sub
Private Sub PictureBoxEx1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseUp
If e.Button = MouseButtons.Left AndAlso mouse_up_must_follow Then
If RadioButton_verschieben.Checked Then
Me.Cursor = Cursors.Default
mouse_up_must_follow = False
moving = False
Return
ElseIf RadioButton_freihand.Checked Then
Manuallydrawnpathonscreen.CloseFigure()
mouse_up_must_follow = False
End If
End If
LastMouseLocationOnScreen = e.Location
End Sub
Private Sub PictBox1_MouseWheel(sender As System.Object, e As MouseEventArgs) Handles PictureBoxEx1.MouseWheel
If RadioButton_verschieben.Checked Then
If e.Delta > 0 Then
current_Zoom_factor += (1.0F / 30.0F)
PictureBoxEx1.Invalidate()
Else
If current_Zoom_factor > 0.0F Then
current_Zoom_factor -= (1.0F / 30.0F)
PictureBoxEx1.Invalidate()
End If
End If
End If
End Sub
Private Sub PictureBoxEx1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBoxEx1.Paint
If LoadedImage Is Nothing Then
Return
End If
If RadioButton_verschieben.Checked Then
Dim the_rectangle_to_be_zoomed_in_and_drawn As RectangleF = calculate_zoomed_and_or_moved_rect()
e.Graphics.DrawImage(LoadedImage, the_rectangle_to_be_zoomed_in_and_drawn)
LF.updateListBox1(the_rectangle_to_be_zoomed_in_and_drawn.X.ToString(Deu),
the_rectangle_to_be_zoomed_in_and_drawn.Y.ToString(Deu),
the_rectangle_to_be_zoomed_in_and_drawn.Width.ToString(Deu),
the_rectangle_to_be_zoomed_in_and_drawn.Height.ToString(Deu),
current_Zoom_factor.ToString(Deu))
ElseIf RadioButton_freihand.Checked Then
Dim the_rectangle_to_be_zoomed_in_and_drawn As RectangleF = calculate_zoomed_and_or_moved_rect()
FunctionsToDraw.DrawTheUsersPath(e.Graphics, the_rectangle_to_be_zoomed_in_and_drawn)
End If
End Sub
Private Function calculate_zoomed_and_or_moved_rect() As RectangleF
image_rect.Width = PictureBoxEx1.Width
image_rect.Height = PictureBoxEx1.Height
Dim newLocation As PointF
If moving Then
newLocation = image_rect.Location
Else
newLocation = New PointF(
PictureBoxEx1.Location.X + (PictureBoxEx1.Size.Width / 2.0F) - image_rect.Width * current_Zoom_factor / 2.0F,
PictureBoxEx1.Location.Y + (PictureBoxEx1.Size.Height / 2.0F) - image_rect.Height * current_Zoom_factor / 2.0F)
End If
Dim scaled As New RectangleF(newLocation,
New SizeF(image_rect.Width * current_Zoom_factor, image_rect.Height * current_Zoom_factor))
Return scaled
End Function
End Class
#Enable Warning CA1707
FunctionsToDraw.vb
Public NotInheritable Class FunctionsToDraw
Public Shared Sub DrawTheUsersPath(g As Graphics, ByVal r As RectangleF)
If g Is Nothing OrElse r.Width = 0.0F Then
Return
End If
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear
Using Pen_green As New Pen(Color.FromArgb(0, 210, 0), 2.0F)
g.DrawImage(FormMain.LoadedImage, r)
g.DrawPath(Pen_green, FormMain.Manuallydrawnpathonscreen)
FormMain.LF.updateListBox2(FormMain.LastMouseLocationOnScreen.X.ToString(FormMain.Deu),
FormMain.LastMouseLocationOnScreen.Y.ToString(FormMain.Deu))
End Using
End Sub
End Class
PictureBoxEx.vb
Public NotInheritable Class PictureBoxEx : Inherits PictureBox
Public Sub New()
SetStyle(ControlStyles.Selectable Or ControlStyles.UserMouse, True)
End Sub
End Class