对于我的软件,我使用的是来自Systems.timer库的Timer,每次我的计时器滴答时,它都会调用重新绘制屏幕的方法。我不想清除屏幕,然后重新粉刷它。我只想直接在上面绘制新区域。
一开始,我这样做了:
Private Sub txtURThirdMolar_KeyDown(sender As Object, e As KeyEventArgs) Handles txtURThirdMolar.KeyDown
MySqlConn.open()
If e.KeyCode = Keys.Enter Then
query1 = "SELECT * FROM teethhistory WHERE Patient_ID_Number ='" & lblID.Text & "'"
cmd1 = New MySqlCommand(query1, MySqlConn)
reader = cmd1.ExecuteReader
If reader.HasRows Then
Dim i As Integer
With cmd
.Connection = MySqlConn
.CommandText = "UPDATE teethhistory SET Up_Right_3rd_Molar = concat('" & txtURThirdMolar.Text & Environment.NewLine & "',Up_Right_3rd_Molar) WHERE Patient_ID_Number = " & lblID.Text
reader.Close()
i = .ExecuteNonQuery
End With
If i > 0 Then
MsgBox("Updated!", MsgBoxStyle.Information, "Success")
txtURThirdMolar.Text = ""
Dim query2 As String
Dim reader1 As MySqlDataReader
Dim cmd2 As MySqlCommand
query2 = "SELECT * FROM teethhistory WHERE Patient_ID_Number ='" & lblID.Text & "'"
cmd2 = New MySqlCommand(query2, MySqlConn)
reader1 = cmd2.ExecuteReader
If reader1.Read() Then
rtbURThirdMolar.Text = reader1.Item("Up_Right_3rd_Molar") 'THIS IS WHERE THE ERROR OCCURS
End If
Else
MsgBox("Failed", MsgBoxStyle.Information, "Failed")
End If
Else
Dim cmd As MySqlCommand = MySqlConn.CreateCommand
cmd.CommandText = String.Format("INSERT INTO teethhistory (Patient_ID_Number, Fullname, Up_Right_3rd_Molar )" &
"VALUES ('{0}' ,'{1}' ,'{2}')",
lblID.Text,
lblFullname.Text,
txtURThirdMolar.Text)
reader.Close()
Dim affectedrows As Integer = cmd.ExecuteNonQuery()
If affectedrows > 0 Then
MsgBox("Saved!", MsgBoxStyle.Information, "Success")
txtURThirdMolar.Text = ""
Dim query2 As String
Dim reader2 As MySqlDataReader
Dim cmd2 As MySqlCommand
query2 = "SELECT * FROM teethhistory WHERE Patient_ID_Number ='" & lblID.Text & "'"
cmd2 = New MySqlCommand(query2, MySqlConn)
reader2 = cmd2.ExecuteReader
If reader2.Read() Then
rtbURThirdMolar.Text = reader2.Item("Up_Right_3rd_Molar")
End If
Else
MsgBox("Saving failed.", MsgBoxStyle.Critical, "Failed")
End If
End If
End If
MySqlConn.close()
End Sub
然后我注意到当OnPaint方法包含这个时,绘画要快得多:
Constructor{
...
this.timer = new Timer
{
Interval = 10,
};
this.timer.Elapsed += OnPaint;
this.timer.start();
}
public void OnPaint(Object sender, EventArgs e)
{
This.Parent.OnPaintLoadingCircle();
This.Parent.OnPaintReadyToBePaintedAreas();
}
所以我有两个问题:
问题1:
为什么它更快?
因为当我调用invalidate()时:
当我调用我的两个方法OnPaintLoadingCircle()和OnPaintReadyToBePaintedArea()时:
问题2:
我想知道是否存在一种让控制器在不清除它的情况下绘制表面的方法。 (我试过this.Parent.Update(),this.Parent.Refresh(),它们都首先清除屏幕。)
非常感谢你帮助我。
答案 0 :(得分:3)
为什么它更快?
出于最简单的原因:因为当您在Invalidate()
方法中调用OnPaint()
时,会强制立即重新绘制窗口 ,这比计时器可以。
.NET中的计时器不适合高频操作。它们只保证间隔之间的时间至少您指定的时间。实际间隔可以且通常比您指定的长,特别是如果您使用非常短的间隔(例如,小于10-20ms的数量级)。这必然会限制您在使用计时器时重新绘制窗口的频率,其程度远远超过仅仅重新绘制窗口的程度。
我想知道是否存在一种让控制器在不清除它的情况下绘制表面的方法。
不容易,不。在最基本的级别,您可以覆盖OnPaintBackground()
而不是调用基本实现。但是这种方法只有在您准备重绘所有内容时才有效,因为系统会指望您在绘制时使用正确的像素覆盖过时的像素。
事实上,更常见的方法是使用双缓冲。最基本的形式是在控件构造函数中设置DoubleBuffered
属性。但是,您也可以将不清除窗口与维护自己的屏幕内Bitmap
对象组合在一起。然后,当Paint
事件发生时,您只需将Bitmap
复制到窗口即可。
更复杂的方法是在窗口中托管Direct2D
曲面。不适合胆小的人,但应该在Winforms计划中提供最佳性能。