无法在表单关闭时访问已处置的对象
Cannot access a disposed object on form close
我正在尝试为具有网格和滴答计数器的游戏编写一些代码,但网格只是试图填充 window 而不是停止并且不显示滴答计数器。不断出现的错误是:
A first chance exception of type 'System.ObjectDisposedException' occurred in System.Windows.Forms.dll
我不知道这是什么意思,也不知道如何解决。
这是我的代码:
Public Class Form1
Dim G As Graphics
Dim BBG As Graphics
Dim BB As Bitmap
Dim r As Rectangle
Dim tSec As Integer = TimeOfDay.Second
Dim tTicks As Integer = 0
Dim MaxTicks As Integer = 0
Dim IsRunning As Boolean = True
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Show()
Me.Focus()
G = Me.CreateGraphics
BB = New Bitmap(Me.Width, Me.Height)
StartGameLoop()
End Sub
Private Sub DrawGraphics()
For X = 0 To 19
For Y = 0 To 14
r = New Rectangle(X * 32, Y * 32, 32, 32)
G.FillRectangle(Brushes.BurlyWood, r)
G.DrawRectangle(Pens.Black, r)
Next
Next
G.DrawString("Ticks: " & tTicks & vbCrLf & _
"TPS: " & MaxTicks, Me.Font, Brushes.Black, 650, 0)
G = Graphics.FromImage(BB)
BBG = Me.CreateGraphics
BBG.DrawImage(BB, 0, 0, Me.Width, Me.Height)
G.Clear(Color.Wheat)
End Sub
Private Sub StartGameLoop()
Do While IsRunning = True
Application.DoEvents()
DrawGraphics()
TickCounter()
Loop
End Sub
Private Sub TickCounter()
If tSec = TimeOfDay.Second And IsRunning = True Then
tTicks = tTicks + 1
Else
MaxTicks = tTicks
tTicks = 0
tSec = TimeOfDay.Second
End If
End Sub
End Class
你在这里使用了很多不好的做法...
首先,使用Me.CreateGraphics()
是不好的,生成的对象只能用一次绘制,这意味着你被迫多次调用它。连续调用它只会创建越来越多的图形对象,从而增加内存使用量。即使您每次完成绘图后都将它们处理掉,这仍然是一个巨大的瓶颈,因为它会减慢处理速度。
其次,使用 Application.DoEvents()
是 非常糟糕的做法 并且会在循环中燃烧你的 CPU那。除非正确使用(你没有正确使用),否则它会导致意外和不可预测的行为。您遇到的错误就是此类意外行为的一个很好的例子。
我建议您阅读这篇 MSDN 博客,它准确地解释了为什么 不应该 使用 Application.DoEvents()
:Keeping your UI Responsive and the Dangers of Application.DoEvents.
相反,为了正确地做到这一点:
将 Me.CreateGraphics()
替换为您的表单的 Paint
event 并通过 e.Graphics
对象在其中绘制所有内容。
将您的游戏循环替换为 Timer
that continuously calls Me.Invalidate()
以重绘表单。
例如:
Dim WithEvents GameTimer As New Timer() With {.Interval = 1}
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
GameTimer.Start()
End Sub
Private GameTimer_Tick(sender As System.Object, e As System.EventArgs) Handles GameTimer.Tick
Me.Invalidate() 'Redraw the form.
TickCounter()
End Sub
Private Sub Form1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
e.Graphics.Clear(Color.Wheat)
For X = 0 To 19
For Y = 0 To 14
Dim r As New Rectangle(X * 32, Y * 32, 32, 32)
e.Graphics.FillRectangle(Brushes.BurlyWood, r)
e.Graphics.DrawRectangle(Pens.Black, r)
Next
Next
e.Graphics.DrawString("Ticks: " & tTicks & vbCrLf & _
"TPS: " & MaxTicks, Me.Font, Brushes.Black, 650, 0)
End Sub
我正在尝试为具有网格和滴答计数器的游戏编写一些代码,但网格只是试图填充 window 而不是停止并且不显示滴答计数器。不断出现的错误是:
A first chance exception of type 'System.ObjectDisposedException' occurred in System.Windows.Forms.dll
我不知道这是什么意思,也不知道如何解决。
这是我的代码:
Public Class Form1
Dim G As Graphics
Dim BBG As Graphics
Dim BB As Bitmap
Dim r As Rectangle
Dim tSec As Integer = TimeOfDay.Second
Dim tTicks As Integer = 0
Dim MaxTicks As Integer = 0
Dim IsRunning As Boolean = True
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Show()
Me.Focus()
G = Me.CreateGraphics
BB = New Bitmap(Me.Width, Me.Height)
StartGameLoop()
End Sub
Private Sub DrawGraphics()
For X = 0 To 19
For Y = 0 To 14
r = New Rectangle(X * 32, Y * 32, 32, 32)
G.FillRectangle(Brushes.BurlyWood, r)
G.DrawRectangle(Pens.Black, r)
Next
Next
G.DrawString("Ticks: " & tTicks & vbCrLf & _
"TPS: " & MaxTicks, Me.Font, Brushes.Black, 650, 0)
G = Graphics.FromImage(BB)
BBG = Me.CreateGraphics
BBG.DrawImage(BB, 0, 0, Me.Width, Me.Height)
G.Clear(Color.Wheat)
End Sub
Private Sub StartGameLoop()
Do While IsRunning = True
Application.DoEvents()
DrawGraphics()
TickCounter()
Loop
End Sub
Private Sub TickCounter()
If tSec = TimeOfDay.Second And IsRunning = True Then
tTicks = tTicks + 1
Else
MaxTicks = tTicks
tTicks = 0
tSec = TimeOfDay.Second
End If
End Sub
End Class
你在这里使用了很多不好的做法...
首先,使用Me.CreateGraphics()
是不好的,生成的对象只能用一次绘制,这意味着你被迫多次调用它。连续调用它只会创建越来越多的图形对象,从而增加内存使用量。即使您每次完成绘图后都将它们处理掉,这仍然是一个巨大的瓶颈,因为它会减慢处理速度。
其次,使用 Application.DoEvents()
是 非常糟糕的做法 并且会在循环中燃烧你的 CPU那。除非正确使用(你没有正确使用),否则它会导致意外和不可预测的行为。您遇到的错误就是此类意外行为的一个很好的例子。
我建议您阅读这篇 MSDN 博客,它准确地解释了为什么 不应该 使用 Application.DoEvents()
:Keeping your UI Responsive and the Dangers of Application.DoEvents.
相反,为了正确地做到这一点:
将
Me.CreateGraphics()
替换为您的表单的Paint
event 并通过e.Graphics
对象在其中绘制所有内容。将您的游戏循环替换为
Timer
that continuously callsMe.Invalidate()
以重绘表单。
例如:
Dim WithEvents GameTimer As New Timer() With {.Interval = 1}
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
GameTimer.Start()
End Sub
Private GameTimer_Tick(sender As System.Object, e As System.EventArgs) Handles GameTimer.Tick
Me.Invalidate() 'Redraw the form.
TickCounter()
End Sub
Private Sub Form1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
e.Graphics.Clear(Color.Wheat)
For X = 0 To 19
For Y = 0 To 14
Dim r As New Rectangle(X * 32, Y * 32, 32, 32)
e.Graphics.FillRectangle(Brushes.BurlyWood, r)
e.Graphics.DrawRectangle(Pens.Black, r)
Next
Next
e.Graphics.DrawString("Ticks: " & tTicks & vbCrLf & _
"TPS: " & MaxTicks, Me.Font, Brushes.Black, 650, 0)
End Sub