如何在画布上快速重绘。 C#WINFORM

时间:2015-12-10 03:53:12

标签: c# multithreading user-interface drawing

对于我的软件,我使用的是来自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()时:

  • UI线程清除屏幕。
  • 然后UI线程重绘旧区域
  • 然后UI线程绘制加载圈
  • 然后UI线程绘制新区域。

当我调用我的两个方法OnPaintLoadingCircle()和OnPaintReadyToBePaintedArea()时:

  • 计时器线程绘制加载圈
  • 然后计时器线程绘制新区域

问题2:

我想知道是否存在一种让控制器在不清除它的情况下绘制表面的方法。 (我试过this.Parent.Update(),this.Parent.Refresh(),它们都首先清除屏幕。)

非常感谢你帮助我。

1 个答案:

答案 0 :(得分:3)

  

为什么它更快?

出于最简单的原因:因为当您在Invalidate()方法中调用OnPaint()时,会强制立即重新绘制窗口 ,这比计时器可以。

.NET中的计时器不适合高频操作。它们只保证间隔之间的时间至少您指定的时间。实际间隔可以且通常比您指定的长,特别是如果您使用非常短的间隔(例如,小于10-20ms的数量级)。这必然会限制您在使用计时器时重新绘制窗口的频率,其程度远远超过仅仅重新绘制窗口的程度。

  

我想知道是否存在一种让控制器在不清除它的情况下绘制表面的方法。

不容易,不。在最基本的级别,您可以覆盖OnPaintBackground()而不是调用基本实现。但是这种方法只有在您准备重绘所有内容时才有效,因为系统会指望您在绘制时使用正确的像素覆盖过时的像素。

事实上,更常见的方法是使用双缓冲。最基本的形式是在控件构造函数中设置DoubleBuffered属性。但是,您也可以将不清除窗口与维护自己的屏幕内Bitmap对象组合在一起。然后,当Paint事件发生时,您只需将Bitmap复制到窗口即可。

更复杂的方法是在窗口中托管Direct2D曲面。不适合胆小的人,但应该在Winforms计划中提供最佳性能。