Private Sub LoadDataBgwDoWork(sender As System.Object, _
e As System.ComponentModel.DoWorkEventArgs) _
Handles LoadDataBGW.DoWork
_blv.DataSource.Clear()
'''...
' Fortschritt mitteilen
While (s < _designOrders.Count())
LoadDataBGW.ReportProgress(CInt((s * 1.0) / _designOrders.Count() * 100.0), _designOrders.Skip(s).Take(Pagesize).ToList())
s += Pagesize
End While
End Sub
Private Sub LoadDataBGWProgressChanged(sender As System.Object, _
e As System.ComponentModel.ProgressChangedEventArgs) _
Handles LoadDataBGW.ProgressChanged
Try
For Each o In DirectCast(e.UserState, List(Of OrderDataGridViewModelRow))
_blv.DataSource.Add(o)
Next
_waitDialog.Progress = e.ProgressPercentage
Catch ex As Exception
Console.WriteLine(ex)
End Try
End Sub
Public Property Progress As Integer
Get
Return ProgressBar1.Value
End Get
Set(value As Integer)
If ProgressBar1.Value <> value _
AndAlso value >= ProgressBar1.Minimum _
AndAlso value <= ProgressBar1.Maximum Then
ProgressBar1.Value = value
End If
End Set
End Property
|
News:
|
|
Hast du WorkerReportsProgress = true gesetzt?
– mrmee 09.05.2012
|
|
|
|
Ok ... Problem verstanden ... vielleicht bin ich es ja auch falsch angegangen. Den Progressbar habe ich eingebaut, weil ich ein DataGridView mit Hilfe von DataBinding befülle. Das reine Zeichnen des DataGrid dauert gefühlt eine Ewigkeit, obwohl vielleicht 100 Einträge drin sind. Das Abfragen und ViewModel erzeugen geht schnell, daß sagt zumindest der Profiler.
In dem DataGridView sind, neben Text und Datum, auch ein paar kleine Piktogramme. Könnten die es vielleicht sein? Gibt es irgendwo eine super Erklärung, wie man Databinding RICHTIG verwendet? – TiMeBaNDiT76 10.05.2012
|
||
| 2 |
Das hat so leider nicht geholfen ... ich habe aber inzwischen entdeckt, daß die LANGSAMKEIT nicht direkt aus der Bindung stammen, sondern durch meine Abfragen resultieren und einen Join mit Linq, der sich leider so nicht vermeiden läßt.
Ich möchte mich trotzdem sehr herzlich bei euch allen Bedanken. Durch eure Anregungen habe ich viele neue Ideen bekommen und auch umgesetzt. – TiMeBaNDiT76 11.05.2012
|
|
|
Dann schreib dir eine Stored Procedure und verlager das SQL dort hinein, statt das ganze SQL dynamisch zu erzeugen. Das hat zudem noch andere Vorteile.
1. die Compile-Zeit für das Statment fällt weg 2. du hast direkten Einfluss auf das Statment und kannst spezielle Optimierungen verwenden (z.B. Index Hints) – Floyd 11.05.2012
|
||
|
Ja, darüber habe ich auch schon nachgedacht ... mein Problem. Die Abfrage geht über mehrere Schemata auf einem MySql Server und das kann EF irgendwie nicht richtig. Zumindest nicht in Version 4.x
– TiMeBaNDiT76 11.05.2012
|
||
|
Aber eine SP sollte es können, und damit das Problem aus dem EF in die Datenbank verlagern.
Ich bin generell weniger einer Fan von Dynamischen SQL, als mehr einer davon SP und FUNCS als Datenzugriff zu nehmen damit man den Datenzugriff optimal an jeweilige Datenbank anpassen zu können und auch Änderungen am Datenmodel transparent für die Clients machen zu können. – Floyd 11.05.2012
|
||
|
Ja ...
um an dieser Stelle weiter zu diskutieren, sollten wir vielleicht einen neuen Thread aufmachen, wie z.B. Dyn.SQL vs. Stored Procedures oder so ;-) ... würde mich auch interessieren, wie man das geschickt umsetzen kann. – TiMeBaNDiT76 11.05.2012
|
||
|
Dann schau dir einmal http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx an und wenn du Fragen bzw. Diskussionstoff hast, stehen wir dir gern zur Seite ;)
– Floyd 11.05.2012
|
||
LoadDataBGW.WorkerReportsProgress = True
if(this.InvokeRequired) {
this.Invoke(new MethodInvoker(() => LoadDataBGWProgressChanged(sender, args)));
return;
}Private Sub LoadDataBgwDoWork(sender As System.Object, _
e As System.ComponentModel.DoWorkEventArgs) _
Handles LoadDataBGW.DoWork
_blv.DataSource.Clear()
'''...
' Fortschritt mitteilen
While (s < _designOrders.Count())
' \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
DirectCast(sender, System.ComponentModel.BackgroundWorker).ReportProgress(CInt((s * 1.0) / _designOrders.Count() * 100.0), _designOrders.Skip(s).Take(Pagesize).ToList())
s += Pagesize
End While
End Sub
Private Sub LoadDataBGWProgressChanged(sender As System.Object, _
e As System.ComponentModel.ProgressChangedEventArgs) _
Handles LoadDataBGW.ProgressChanged
'\/ C# Code den du in VB.Net übersetzten musst
'\/ sorgt dafür das du in den UI-Thread wechselst
if(this.InvokeRequired) {
this.Invoke(new MethodInvoker(() => LoadDataBGWProgressChanged(sender, args)));
return;
}
Try
For Each o In DirectCast(e.UserState, List(Of OrderDataGridViewModelRow))
_blv.DataSource.Add(o)
Next
_waitDialog.Progress = e.ProgressPercentage
Catch ex As Exception
Console.WriteLine(ex)
End Try
End Sub
Private Sub LoadDataBgwDoWork(sender As System.Object, _
e As System.ComponentModel.DoWorkEventArgs) _
Handles LoadDataBGW.DoWork
Dim lastUpdate as DateTime = DateTime.Min
_blv.DataSource.Clear()
'''...
' Fortschritt mitteilen
While (s < _designOrders.Count())
'ReportProgress nur einmal pro Sekunden auslösen
if (DateTime.Now - lastUpdate).TotalMilliseconds < 1000 then
DirectCast(sender, System.ComponentModel.BackgroundWorker).ReportProgress(CInt((s * 1.0) / _designOrders.Count() * 100.0), _designOrders.Skip(s).Take(Pagesize).ToList())
lastUpdate = DateTime.Now
Thread.Sleep(100); '100 Millisekunden warten um den UI-Thread die Möglichkeit zu geben das Event abzuarbeiten
end if
s += Pagesize
End While
End Sub
|
|
|
Hi,
beide angesprochene Themen sind es nicht. 1. WorkerReportsProgress Habe ich natürlich gesetzt. 2. Das Invoke brauche ich nur, wenn ich es von einer externen Klasse aufrufe. Da ich mich aber im eigenen Thread befinde funktioniert es so. Wenn es im Debugger durchgehe, funktioniert es auch. Wie schon gesagt, ich bekomme keine Fehlermeldung. Der Thread, welcher die Daten bearbeitet scheint alle Ressourcen des Rechners zu sperren. Wenn ich ein Thread.Sleep(1000) einbaue geht es. – TiMeBaNDiT76 09.05.2012
|
||
|
Ich will den Vorgang aber nicht unnötig verlängern, indem immer wieder ein Warten eingebaut wird. Es muss doch auch anders gehen.
– TiMeBaNDiT76 09.05.2012
|
||
| 2 |
Ich glaube auch nicht, dass es am Thread-Wechsel liegt. Der BGW soll es einem ja gerade abnehmen, über InvokeRequired und dergleichen nachdenken zu müssen, und ProgressChanged ist für das Aktualisieren des UI gedacht. Zitat MSDN:
Im DoWork-Ereignishandler dürfen keine Benutzeroberflächenobjekte bearbeitet werden. Verwenden Sie stattdessen zum Kommunizieren mit der Benutzeroberfläche das ProgressChanged-Ereignis und das RunWorkerCompleted-Ereignis. – Matthias Hlawatsch 09.05.2012
|
|
|
Die Idee ist natürlich nicht schlecht. Werde ich morgen einmal ausprobieren.
– TiMeBaNDiT76 09.05.2012
|
||
|
@Floyd: er befüllt doch die DataSource gerade *nicht* in DoWork, sondern in ProgressChanged?
– Matthias Hlawatsch 09.05.2012
|
||
|
Stimmt, ich schein heute nicht auf der Höhe zu sein, aber unabhängig davon, wissen wir beide nicht was er dort macht wo nur ein "''..." steht.
– Floyd 09.05.2012
|
||
|
Aber da stellt sich mir doch die Frage warum man das im ProgressChanged macht und nicht im DoWork ?
– Floyd 09.05.2012
|
||
| 1 |
@Floyd: Ich denke, dass ein Befüllen der DataSource in DoWork nicht funktioniert, weil das ein Aktualisieren des UI zur Folge hätte, was in DoWork nicht erlaubt ist (es sei denn, es wird mit Invoke() gekapselt, und das will man ja eigentlich mit dem BGW gerade vermeiden). Das ist halt die Crux beim Databinding :-(
Ansonsten: ja, mich interessiert auch, was an den ausdgeblendeten Stellen passiert ;-) – Matthias Hlawatsch 09.05.2012
|
|
|
@Matthias: Von der Logik her ist ja die DoWork Methode dafür da.
Einen echten Grund um auf Invoke in der DoWork-Methode zu verzichten sehe ich aber nicht. Aber ich muss auch zugeben das ich mit DataGridViews und DataSource nicht wirklich auskenne, da mein Schwerpunkt mehr im Bereich der Webentwicklung, BuisnessLogiken und API-Design liegt. – Floyd 09.05.2012
|
||
|
Kleine Anmerkung, ich hab TotalMilliseconds < 1000 geschrieben was natürlich falsch ist. TotalMilliseconds > 1000 ist richtiger.
– Floyd 10.05.2012
|
|
|
|
Hallo,
also Du hast schon recht, ich habe die ReportProgress Methode verwendet, weil sie nicht im Thread des Backgroundworkers läuft und damit ein Update der UI möglich ist. Ich aktualisiere den Progressbar und füge Elemente zur BindingSource hinzu. Das was ich mit "..." markiert hatte, war für den Ablauf nicht wichtig. Meint ihr, wenn ich mir die Mühe mache und das Hinzufügen von Elementen in eine Methode auslagere, welche ich dann mit INVOKE aufrufe wird es funktionieren? Dann würde ich im ReportProgress nur noch den Progressbar aktualisieren. Der Sleep war in der DoWork Methode. – TiMeBaNDiT76 09.05.2012
|
||
Private Sub LoadDataBgwDoWork(sender As System.Object, _
e As System.ComponentModel.DoWorkEventArgs) _
Handles LoadDataBGW.DoWork
Dim init = DirectCast(e.Argument, Boolean)
_blv.DataSource.Clear()
' Rechte prüfen
If Context.Instance.IsSuperVisor Then
_designOrders = New OrderDataGridViewModel(init)
Else
_designOrders = New OrderDataGridViewModel(init, Context.Instance.CurrentUser.PersonenID)
End If
'ListView erstellen
Dim s = 0
' Fortschritt mitteilen
While (s < _designOrders.Count())
FillData(_designOrders.Skip(s).Take(Pagesize).ToList())
LoadDataBGW.ReportProgress(CInt((s * 1.0) / _designOrders.Count() * 100.0), Nothing)
s += Pagesize
End While
End Sub
Delegate Sub DelegateFillData(list As IEnumerable(Of OrderDataGridViewModelRow))
''' <summary>
''' Füllt das DataGrid mit Elementen
''' </summary>
''' <param name="list"></param>
''' <remarks></remarks>
Private Sub FillData(list As IEnumerable(Of OrderDataGridViewModelRow))
If _dgvJobs.InvokeRequired Then
Dim fill As DelegateFillData = AddressOf FillData
Invoke(fill, New Object() {list})
Else
Try
For Each item In list
_blv.DataSource.Add(item)
Next
Catch ex As Exception
Throw ex
End Try
End If
End Sub
Private Sub LoadDataBGWProgressChanged(sender As System.Object, _
e As System.ComponentModel.ProgressChangedEventArgs) _
Handles LoadDataBGW.ProgressChanged
_waitDialog.Progress = e.ProgressPercentage
End Sub
Private Sub LoadDataBGWRunWorkerCompleted(sender As System.Object, _
e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles LoadDataBGW.RunWorkerCompleted
If Not Context.Instance.IsSuperVisor Then
EditReleasesToolStripMenuItem.Visible = False
End If
' Nur die offenen Aufträge anzeigen
'OpenOrdersCheckBox.Checked = True
If _onlyOpenOrdersLoaded Then
_blv.Refresh()
Else
_blv.ApplyFilter(AddressOf ApplyFilter)
End If
Cursor = Cursors.Arrow
_waitDialog.Hide()
End Sub
|
|
| 1 |
Ist OrderDataGridViewModel IQueryable oder woher hat das ViewModel die Count() Methode? Bei IEnumerable kann der Aufruf der Count() Methode nicht optimiert bzw. verzögert ausgeführt werden, was dazu führt, dass jeder Aufruf von Count die Items komplett iteriert. Das mag nicht zwingend mit Deinem Aktualisierungsproblem zusammenhängen, würde aber die Loadperformance bei der Häufigkeit der Aufrufe signifikant steigern.
– ffordermaier 10.05.2012
|
|
|
Nunja, wenn ich nochmal drüber nachdenke, ist IEnumerable eher unwahrscheinlich. War halt ein spontaner Gedanke...
– ffordermaier 10.05.2012
|
||
|
Es ist eine GenericList ... aber ich habe die Klammern mal weg gemacht, damit er das Property und nicht die Linq-Methode verwendet. Wobei es in VB ja wohl irgendwie egal ist, ob man Klammern setzt, wenn mann keine Parameter hat. Ich weiß also nicht genau, ob er das Property oder die Methode aufruft.
– TiMeBaNDiT76 10.05.2012
|
Dim lastUpdate as DateTime = DateTime.Min
While (s < _designOrders.Count())
FillData(_designOrders.Skip(s).Take(Pagesize).ToList())
'ReportProgress nur einmal pro Sekunden auslösen
if (DateTime.Now - lastUpdate).TotalMilliseconds > 1000 then '>1000 nicht <1000
DirectCast(sender, System.ComponentModel.BackgroundWorker).ReportProgress(CInt((s * 1.0) / _designOrders.Count() * 100.0), Nothing)
lastUpdate = DateTime.Now
Thread.Sleep(100)
end if
s += Pagesize
End While
|
|