DataGridView: langsames OnPaint

28/09/2009 - 15:05 von Armin Zingler | Report spam
Servus,

ich kann mir nicht erklàren, warum der Bildschirmaufbau beim
DataGridView (DGV) so lange dauert.

Gegeben ist ein DGV im ungebundenen Modus mit 1000 Zeilen und 6 Spalten.
40 Zeilen sind sichtbar. Die Typen der Spalten (.ValueType-Eigenschaft
der DataGridViewTextBoxColumn-Objekte) sind 1 x DateTime und 5 x
Decimal. DGV.Readonly ist True. Es gibt keinen benutzerdefinierten
Zeichen- oder Formatierungsvorgang. Das Problem ist unabhàngig davon, ob
sich 1.000 oder 10.000 Zeilen im Grid befinden. Es laufen keine anderen
CPU-lastigen Prozesse. CPU-Takt ist stabil.

Der Bildschirmaufbau dauert bis zu 1,6 Sekunden. Man kann also zusehen,
wie die Zeilen einzeln gezeichnet werden. Das ist bei jedem Neuzeichnen
so, z.B. beim seitenweisen Scrollen oder beim Minimieren+Wiederherstellen.

Wenn ich wàhrend dieser 1,6 Sekunden Ctrl+Break drücke, um zu
analysieren, welcher Code derzeit ausgeführt wird, dann reagiert der
Debugger erst, wenn der Bildschirmaufbau beendet ist; also zu spàt. Ich
muss es schaffen, wàhrend der 1,6s per Alt+Tab zum Debugger zu schalten,
um den Prozess zu unterbrechen. Dann steht er eigentlich wie erwartet im
OnPaint bzw in irgendeiner darin aufgerufenen Funktion. Also nichts
erkennbar langsames. Ich kann also kaum feststellen, was so lange dauert.

Kurioserweise war es gestern noch so, dass nach einigen Sekunden nach
dem Programmstart die "Blockade" plötzlich gelöst war! Der
Zeichenvorgang ging also ratz-fatz. Allerdings nur, wenn ich den Prozess
aus der IDE gestartet habe. Ohne angehàngtem Debugger hat sich die
Blockade gar nicht gelöst. Strange. Heute bekomme ich überhaupt keinen
schnellen Zeichenvorgang mehr hin. :(

Habe in OnPaint die Zeiten mitprotokolliert:
(jeweils Dauer und e.ClipRectangle.ToString)

00:00:00.9452533 {X=0,Y=0,Width‡2,Height•6}
00:00:00.0000692 {X=0,Y=0,Width=0,Height=0}
00:00:01.6120948 {X=0,Y=0,Width‡2,Height•6}
00:00:01.0695191 {X=0,Y=0,Width‡2,Height•6}
00:00:00.0000723 {X=0,Y=0,Width=0,Height=0}
00:00:00.9292692 {X=0,Y=0,Width‡2,Height•6}

Woher das leere Cliprectangle kommt hat mich dann auch interessiert:
Wenn die Anwendung den Fokus verliert, wird auch WM_PAINT gesendet also
OnPaint aufgerufen. Das ist zwar auch seltsam, aber nicht das primàre
Problem.

Interessant ist evtl noch, dass der Task-Manager wàhrenddessen v.a. viel
"rot" anzeigt, also Kernelzeiten. Außerdem: Wenn keine anderen Fenster
geöffnet sind und das Programm nach dem Minimieren wiederhergestellt
wird, sieht man, wie der Desktop kurz flackert, also neu aufgebaut wird.
Normal ist das auch nicht. Dachte zuerst, vllt liegt es daran, dass das
durch die Protokollierung der Messungen in einer Logdatei ausgelöst
wird, und da der Desktop ja zum explorer.exe gehört, er diese
Dateioperation erkennt und sich neu zeichnet (wobei die Protokolldatei
aber nicht auf dem Desktop liegt). Das konnte ich durch Entfernung der
Protokollierung aber ausschließen.

Kennt jemand dieses seltsame Problem und hat vllt sogar ne Lösung dazu?

Graka-Treiber ist übrigens Catalyst 9.9 (HD 3850).


Armin
 

Lesen sie die antworten

#1 Armin Zingler
28/09/2009 - 19:24 | Warnen spam
Hi,

ich hab mal das Projekt abgespeckt. Hier der komplette Code zum nachvollziehen:
(in leeres VB Winforms-Projekt einfügen und Sub Main als Startobjekt einstellen)
("umstàndlichen" Code einfach ignorieren; ist ja aus dem Zusammenhang gerissen)

Würde mich schon interessieren, ob jemand dasselbe Problem hat.


Public Class Main

Shared Sub Main()

Dim linesList As List(Of String)
Dim f As Form1

linesList = GetLines()

f = New Form1

With f.DatagridView1
Dim CultInfo = System.Globalization.CultureInfo.InvariantCulture

Dim lines = From items In (From line In linesList _
Let Items = line.Split(","c) _
Select Items = ( _
From item In Items _
Select item = item.Trim _
) _
Where Items.Count > 0 AndAlso Items(0) <> "Date") _
Select Datum = DateTime.ParseExact(items(0), "yyyy-MM-dd HH:mm:ss", Nothing), _
Wert1 = Decimal.Parse(items(1), CultInfo), _
Wert2 = Decimal.Parse(items(2), CultInfo), _
Wert3 = Decimal.Parse(items(3), CultInfo), _
Wert4 = Decimal.Parse(items(4), CultInfo), _
Wert5 = If(items(5) = "-", 0, Decimal.Parse(items(5), CultInfo))



Dim RowCount = lines.Count

For Each line In lines
Dim Index = .Rows.Add( _
line.Datum, line.Wert1, line.Wert2, line.Wert3, line.Wert4, line.Wert5 _
)

Dim Row = .Rows(Index)

With Row.HeaderCell
.ValueType = GetType(String)
.Value = (Index + 1).ToString("#,##0")
.Style.Alignment = DataGridViewContentAlignment.MiddleRight
End With
Next

.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells)
End With

f.Size = New Size(880, 990)
f.Show()
Application.Run(f)

End Sub

Shared Function GetLines() As List(Of String)

Dim result As New List(Of String)

For i = 1 To 5000
result.Add("2009-01-01 00:00:00, 123.0, 123.0, 123.0, 123.0, 123.0")
Next

Return result

End Function

End Class



Public Class MyDGV
Inherits DataGridView

Sub New()

Dim CellStyle As New DataGridViewCellStyle

CellStyle.Alignment = DataGridViewContentAlignment.MiddleRight

[ReadOnly] = True
RowHeadersVisible = True

DefaultCellStyle = CellStyle
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
AllowUserToAddRows = False
AllowUserToDeleteRows = False
AllowUserToResizeColumns = False
AllowUserToResizeRows = False
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize
RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders

ColumnHeadersDefaultCellStyle.Font = New Font( _
ColumnHeadersDefaultCellStyle.Font, FontStyle.Bold _
)

SelectionMode = DataGridViewSelectionMode.FullRowSelect

Columns.Add _
( _
New DataGridViewTextBoxColumn() With _
{ _
.HeaderText = "Date", _
.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells _
} _
)
Columns.Add(CreateDecimalColumn("GPU Core Clock [MHz]"))
Columns.Add(CreateDecimalColumn("GPU Memory Clock [MHz]"))
Columns.Add(CreateDecimalColumn("GPU Temperature [°C]"))
Columns.Add(CreateDecimalColumn("Fan Speed [%]"))
Columns.Add(CreateDecimalColumn("GPU Load [%]"))


End Sub

Private Shared Function CreateDecimalColumn(ByVal HeaderText As String) _
As DataGridViewTextBoxColumn

Dim Result As New DataGridViewTextBoxColumn

Result.HeaderText = HeaderText
Result.ValueType = GetType(Decimal)

Return Result

End Function

Public Overrides Sub Sort(ByVal comparer As System.Collections.IComparer)
Dim OldCursor = Cursor.Current
Cursor.Current = Cursors.WaitCursor
MyBase.Sort(comparer)
Cursor.Current = OldCursor
End Sub

Public Overrides Sub Sort( _
ByVal dataGridViewColumn As System.Windows.Forms.DataGridViewColumn, _
ByVal direction As System.ComponentModel.ListSortDirection)

Dim OldCursor = Cursor.Current
Cursor.Current = Cursors.WaitCursor
MyBase.Sort(dataGridViewColumn, direction)
Cursor.Current = OldCursor

End Sub

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

Dim watch = Stopwatch.StartNew
MyBase.OnPaint(e)
watch.Stop()

Dim DebugText = Now.ToString("o") & vbTab & watch.Elapsed.ToString _
& vbTab & e.ClipRectangle.ToString

Debug.Print(DebugText)

'IO.File.AppendAllText( _
' "e:\dgvpaint.txt", _
' DebugText & Environment.NewLine, _
' System.Text.Encoding.Default _
')

End Sub
End Class


Class Form1
Inherits Form

Public DataGridView1 As New MyDGV

Sub New()

DataGridView1.Dock = DockStyle.Fill
Controls.Add(DataGridView1)
StartPosition = FormStartPosition.CenterScreen

End Sub

End Class





Armin

Ähnliche fragen