Performance Problem mit DataSet / und Programmvorstellung "DasProgramm" - Achtung umfangreich

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    Japp, sobald AutoSize angewendet wird dauerts...
    Im Designer habe ich die Spalten 1 bis 8 auf none, Spalte 9 auf Fill und 10 auf none.
    Wenn ich die TabPage nun ganz normal anzeigen lasse und folgendes Makro über einen Button starte (also definitv nachdem die Daten vollständig im DGV sind):

    VB.NET-Quellcode

    1. For i = 0 To 7
    2. ArticleDataGridView.Columns(i).AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
    3. Next
    4. ArticleDataGridView.Columns(9).AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells

    Dauert es 8 Sekunden, bis meine Spaltenbreiten verändert werden.

    Also ists wohl so, wie @ErfinderDesRades oben beschreibt - bei größeren Tabellen, ist AutoSize von Arsch :(
    Schlauerweise habe ich alle Spalten in allen DGVs auf AutoFill :) - dann hab ich was zum klicken...

    Ich dachte mir ich schnappe mir das DGV_ColumnWidthChanged Event und beschreibe entsprechend in diesem Event für jede Spalte eine Variable in den Einstellungen, die ich dann beim Starten des Programmes lade.
    So kann der User die Spalten so ziehen wie er will, und die Breiten bleiben gespeichert.
    Wie ich das anders lösen soll weiß ich nicht.

    Jedoch triggert dieses Event auch beim Starten des Programmes und auch beim anzeigen der entsprechenden TabPage.
    Das wird dann wohl zu Problemen führen :(

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „DerSmurf“ ()

    Dann mach ne klassenweite Boolean-Variable RecalculateColumnWidth, die Du standardmäßig auf False setzt und wenn der Tab zuende geladen hat, setzt Du sie auf True. Und im genannten EventHandler verlässt Du die Prozedur, wenn die Variable auf False steht. Das ginge zwar auch mit An- und Abmelden beim/vom Event, aber so geht's etwas einfacher. Ein Anfang.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @Maffi
    Die Zeitverzögerung entsteht ja erdt, wenn autofill genutzt wird.

    @ErfinderDesRades
    Hmm. Ich hatte eine ähnliche Idee wie du, aber deutlich einfacher.
    Ich würde für jedes DGV in meinem Programm einen String Wert in den settings anlegen.
    Dann wie von @VaporiZed erklärt das DGV.columnwidthchanged Event verwenden, um die Spaltenbreiten wir folgt zu speichern 100;20;40;50;60 .
    Dann kann ich im Form.load der Hauptform mittels Split die Spaltenbreiten meiner DGVs entsprechend setzen.

    Findest du deine Methode nur für DGV Spaltenbreiten nicht ein bisschen "Kanonen auf Spatzen"?
    (Damit möchte ich deinen gezeigten Code natürlich in keiner Weise setzen oder gar angreifen)
    Wir immer möchte ich nur verstehen, warum mir was geraten wird.
    Hallo ihr lieben
    Ich habe jetzt den "SpaltenbreitenSpeicherCode" für ein anderes Projekt fertig gestellt.
    In diesem gibt es nur ein einziges DGV, daher habe ich erstmal hier angefangen.
    Das DGV hat insgesamt 10 Spalten (ich nenne sie im folgenden 0 - 9).
    Hier habe ich die Spalten 0 - 8 im DGV im Designer auf AutoSizeMode None und die Spalte 9 auf Fill.
    In den Settings habe ich "DGVColWidth" vom Typ String erstellt.

    Mein Ladecode:

    VB.NET-Quellcode

    1. Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. 'setzen der Spaltenbreiten im DGV
    3. Dim widths() As String = My.Settings.DGVColWidth.Split(";"c)
    4. For i = 0 To 8
    5. DTParcelDataGridView.Columns(i).Width = Integer.Parse(widths(i))
    6. Next
    7. End Sub


    und mein Speichercode:

    VB.NET-Quellcode

    1. Private Sub frmMain_Closed(sender As Object, e As EventArgs) Handles Me.Closed
    2. Dim DGVColwidths As String = ""
    3. For i = 0 To 8
    4. DGVColwidths = DGVColwidths & DTParcelDataGridView.Columns.Item(i).Width & ";"
    5. Next
    6. My.Settings.DGVColWidth = DGVColwidths
    7. End Sub


    Ich habe den Speichercode (erstmal) ins _Closed Event geschmissen, da ich kein geeignetes Event gefunden habe, welches nur triggert, wenn die TabPage "Parcels" angezeit wird.
    TPParcels.Validate funktioniert prinzipiell, allerdings triggert dieses nicht, wenn ich mein Programm starte und die TabPage Parcels ausgewählt ist.
    Also welches Event verwende ich, damit ich meinen Speichercode ins DGVColumnWidthChanged Event verschieben kann?

    Edit:
    Der letzte Abschnitt liest sich konfus - also hier die Erklärung in Kurzform:
    speichern der DGV Spaltenbreite soll eigentlich ins DGV_ColumnWidthChanged Event
    das triggert aber z.B. auch beim Laden der Form
    also Klassenweite Boolean mit standart = False, welche auf True gesetzt wird, wenn die DGV angezeigt wird, wenn ich also auch tatsächlich mit der Maus die Spaltenbreite änder.
    In welches Event kommt das Boolean = True ?

    und noch ein Edit zum speichern:
    So ists doch hübsch, oder nicht?
    Immer wenn ich ins DGV Klicke, was ich ja muss, wenn ich die Spaltenbreite ändere, aber nur kann, wenn ich das DGV auch sehe, setzte ich meine boolean auf True und mein Programm ist empfänglich für das ColumnWidthChanged Event.
    Das läuft alles - einzig doof ist, dass meine Boolean ja auf True bleibt, wenn ich "nur so" ins DGV klicke.
    Aber mir fällt kein Scenario ein, wo das schlimm wäre.
    Also hab ichs selbst gelöst? (oder hab ich doch wieder was vergessen?)

    VB.NET-Quellcode

    1. Private RecalculateColumnWidth as boolean = False
    2. Private Sub DTParcelDataGridView_Click(sender As Object, e As EventArgs) Handles DTParcelDataGridView.Click
    3. RecalculateColumnWidth = True
    4. End Sub
    5. Private Sub DTParcelDataGridView_ColumnWidthChanged(sender As Object, e As DataGridViewColumnEventArgs) Handles DTParcelDataGridView.ColumnWidthChanged
    6. If RecalculateColumnWidth Then
    7. Dim DGVColwidths As String = ""
    8. For i = 0 To 8
    9. DGVColwidths = DGVColwidths & DTParcelDataGridView.Columns.Item(i).Width & ";"
    10. Next
    11. My.Settings.DGVColWidth = DGVColwidths
    12. RecalculateColumnWidth = False
    13. End If
    14. End Sub


    ​RecalculateColumnWidth = False könnte ich ja auch in das LostFocus Event der DGV packen.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „DerSmurf“ ()

    So, ich überflieg mal Deinen Programm-Code.
    Da ich inzwischen ziemlich CCD-verseucht bin, tu ich mich etwas schwer, da ich sehr derb/krass mit meinen Codeaufteilungen geworden bin. Aber ich reiß mich zusammen :P . Trotzdem werden ein paar Verweise dabei sein.
    My.Settings.WrongPWCount = My.Settings.WrongPWCount + 1 -> My.Settings.WrongPWCount += 1
    My.Settings.Save(): überflüssig
    Application.Exit() =O : hier und hier meine aktuellen Kommentare dazu.
    Catch ex As Exception: Fange nur Exceptions ab, die Du kennst und sinnvoll bearbeiten kannst. Anders ausgedrückt: Werd konkret mit dem Exceptiontyp.
    Private Declare Function LockWorkStation Lib "user32.dll" () As Long - n bisken VB6-Old-Style, aber es funktioniert ja.
    In Private Sub FrmMainForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown kannst Du Code einsparen, indem Du die Keys in Zeichen umwandelst und diese dann an die TextBox weitergibst:

    VB.NET-Quellcode

    1. Dim CharacterToWrite As Char = Nothing
    2. Case Keys.D0 To Keys.D9 'Zahlentasten 0-9
    3. CharacterToWrite = Convert.ToChar(e.KeyCode)
    4. Case Keys.NumPad0 To Keys.NumPad9 'Zehnertastatur 0-9
    5. CharacterToWrite = Convert.ToChar(e.KeyCode - 48)
    6. '...
    7. If TCMain.SelectedTab Is TPAddressbook Then TBSearch.AppendText(CharacterToWrite)
    8. If TCMain.SelectedTab Is TPArticles Then TBSearchArticles.AppendText(CharacterToWrite)
    9. If TCMain.SelectedTab Is TPCustomerOrders Then TBSearchCorder.AppendText(CharacterToWrite)
    10. If TCMain.SelectedTab Is TPPasswords Then TBSearchPW.AppendText(CharacterToWrite)

    Nutze Extensions. So z.B. den Doppelcast vom EdR
    Nutze Option Infer: Dim TheDate As String = getDate() -> Dim TheDate = getDate()
    Nutze Verallgemeinerung, um das DRY-Prinzip zu üben. In der Region "Change Events" passiert mit den 4 TextBoxen das gleiche. In eine Prozedur auslagern und als Parameter die jeweilige TextBox übergeben.
    In der Sub SaveOrder() sind zahlreiche Abbruchbedingungen, die das Speichern ggf. verhindern. Lagere sie ggf. in eine Funktion namens DataAreValidForSaving aus. Das verschlankt den Code der SaveOrder-Prozedur (aber nicht des Codes insgesamt, das ist richtig), was die Übersicht verbessert und mit dem Du SRP und SLA üben kannst.

    Ich werd erstmal nicht weiterschauen, sonst zieht sich meine Antwort noch ewig in die Nacht rein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Wie gesagt, keine Eile. Bin froh u d überaus dankbar, dass du dir die Arbeit machst :o)
    Bei deiner bisherigen "Kritik" komm ich ja sogar deutlich besser weg, als ich gedacht hab :)

    Soweit erstmal ein großes DANKE! - auch wenn ich ein wenig wacher sein muss, als ich es gerade bin, um alles zu verstehen.
    Also schau ich mir das ganze in Ruhe sährend der Feiertage an.

    @VaporiZed
    eins noch.
    Wie findest du meine Lösung zum speichern der DGV Spalten?
    Das Beispiel hier ist ein DGV mit 10 Spalten, Index 0 - 8 per Designer auf AutoSize None - Index9 auf Fill.

    VB.NET-Quellcode

    1. Private RecalculateColumnWidth as boolean = False
    2. Private Sub DTParcelDataGridView_Click(sender As Object, e As EventArgs) Handles DTParcelDataGridView.Click
    3. RecalculateColumnWidth = True
    4. End Sub
    5. Private Sub DTParcelDataGridView_ColumnWidthChanged(sender As Object, e As DataGridViewColumnEventArgs) Handles DTParcelDataGridView.ColumnWidthChanged
    6. If RecalculateColumnWidth Then
    7. Dim DGVColwidths As String = ""
    8. For i = 0 To 8
    9. DGVColwidths = DGVColwidths & DTParcelDataGridView.Columns.Item(i).Width & ";"
    10. Next
    11. My.Settings.DGVColWidth = DGVColwidths
    12. RecalculateColumnWidth = False
    13. End If
    14. End Sub
    15. RecalculateColumnWidth = False könnte ich ja auch in das LostFocus Event der DGV packen.

    Wenn das so klar geht, kann ich das ja gleich in meine "Codeoptimierung" integrieren, um all die AutoSize Columns los zu werden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DerSmurf“ ()

    Ja, wenn's seinen Dienst tut, ist doch ok. Ich hätte es etwas anders geschrieben (Stichwort If-Umkehrung) und LINQ, aber inhaltlich sonst nicht verändert:

    VB.NET-Quellcode

    1. Private Sub DTParcelDataGridView_ColumnWidthChanged(sender As Object, e As DataGridViewColumnEventArgs) Handles DTParcelDataGridView.ColumnWidthChanged
    2. If Not RecalculateColumnWidth Then Return
    3. Dim DGVColwidths = String.Join(";", DTParcelDataGridView.Columns.Cast(Of DataGridViewColumn).Take(9).Select(Function(x) x.Width)) '*
    4. My.Settings.DGVColWidth = DGVColwidths
    5. RecalculateColumnWidth = False
    6. End Sub

    * Es gibt dann aber kein abschließendes ;!
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    DerSmurf schrieb:

    Findest du deine Methode nur für DGV Spaltenbreiten nicht ein bisschen "Kanonen auf Spatzen"?
    Jo, klar ist das KanoneSpatz.
    Aber Code geschrieben hättest du weniger - meinen hätteste ja nur einbinden und richtig verwenden müssen - letzteres ist zugegebenerweise nicht trivial, du müsstest dir ja ein Verständnis meines ich finde originellen Ansatzes erarbeiten.
    Dann kann man damit aber auch alles andere an User-Layout mit minimalem Aufwand persistieren (je 1 Zeile): Fenster-Grösse, Position, Stellung von SplitContainern, sowas.

    Aber die GrundIdee haste jetzt ja selbst umgesetzt, sogar meim Prinzip sehr ähnlich: das Layout in einen String konvertiert und in die Settings damit. :thumbsup:
    OK - noch mehr Literatur für die Arbeitsfreien Tage xD

    Sind denn die verwendeten Events für das setzen der Klassenweiten boolean, also DGV_Click = True und DGV_LostFocus = False.

    Edit: @ErfinderDesRades - schon wieder überschnitten unsere Posts...
    Mit deinem Code werde ich mich auch noch beschäftigen - aber zur Zeit mache ich eh zuviel gleichzeitig glaube ich.
    Aber den werd ich bestimmt irgendwann mal brauchen, wenn es darum geht, mehr als nur Spaltenbreiten in Settings zu schmeißen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DerSmurf“ ()

    So @VaporiZed
    Mit ein bisschen Verspätung gehe ich nun mal deine Kommentare zu meinem Code durch.
    Das was ich hier ignoriere, ist entweder klar, oder wir hatten das schon öfters (also x += 1 z.B.).
    Wobei ich jetzt schon öfters mal dran denke.
    1. Application.Exit:
    Ich habe zuerst Me.Close versucht. Allerdings springt der Code nach dieser Zeile in die SaveSettings Sub, aufgerufen durch FrmMainForm_Closed. In der Save Settings kommt es dann zum Fehler, weil auf die xml Datei nicht zugegriffen werden kann.
    Außerdem soll ja garnichts gespeichert werden, sondern das Programm wieder beendet werden (weil es wurde ja ein falsches PW eingegeben). Da ist eigentlich tu nix und beende sofort, genau das was beabsichtigt war.
    Die Anweisung End würde ebenso funktionieren. Allerdings widersprichst du dir in deinen beiden geposteten Links, welche Anweisung schlimmer ist.

    2. Catch Exeption:
    Der Post vom EdR leuchtet ein. Allerdings leuchtet mir gerade nicht ein, wofür der Try Catch Block überhaupt gut sein soll.
    Ich prüfe obs den Ordner gibt, wenn nicht erstellen. Zur Exception kommt es doch dann nur, wenn ich keine Schreibrechte im Programmordner habe - das ist ja höchst unwahrscheinlich. Hab ich also einfach weggelöscht.

    3- KeyDown Event:
    Die ganze Sub hab ich wegelöscht und durch das KeyPress Event ersetzt. Dort fange ich Excape und Enter Taste ab, und übergebe sonst den Wert an meine Textboxen mittels, Textbox.AppendText(e.Keychar), je nachdem welche TabPage ausgewählt ist.
    Also so ähnlich wie in meinem Parcel Programm, wo du mir mal geholfen hast.

    4. Option Infer:
    In VBA Zeiten habe ich gelernt, dass das nutzen von Variant eine Sünde ist. Daher tue ich mich mit Option Infer schwer.
    Ich verstehe nicht, wo der Vorteil ist. Ob ich nun Dim x = 2, oder Dim x as integer = 2 schreibe.
    Und ich habe Sorge, dass evtl. mal ein falscher, oder eben ein anderer als der erwartete Datentyp verwendet wird.

    DerSmurf schrieb:

    In VBA Zeiten habe ich gelernt, dass das nutzen von Variant eine Sünde ist. Daher tue ich mich mit Option Infer schwer.
    Das Pendant zu Variant wäre Object in Verbindung mit Option Strict Off.

    Option Infer vermeidet nur überflüssige Deklarationen.
    Dim s As String = "abc" vs. Dim s = "abc"
    Die erste Definition ist doppelt gemoppelt.
    Der Compiler weiß schon anhand der Zuweisung, dass es ein String ist.
    Die Typprüfung ist dennoch aktiv.

    VB.NET-Quellcode

    1. Dim s = "abc"
    2. s = 123 'hier wird die IDE einen Fehler werfen, weil einem String keine Zahl zugewiesen werden kann.


    Es ist aber auch nicht falsch, ohne Infer zu arbeiten, nur gesprächiger und dadurch ggf. etwas unübersichtlicher.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „petaod“ ()

    Ich widerspreche mir?
    Einmal sag ich: »Application.Exit ist derb. Nicht so derb wie End, aber unnötig«. Also End böser als Application.Exit()
    In dem anderen Thread sag ich: »End ist der Todesschuss für eine App: [...] Noch ein Zacken schärfer als Application.Exit.« Also End böser als Application.Exit()
    -> das gleiche.

    DerSmurf schrieb:

    Der Post vom EdR leuchtet ein. Allerdings leuchtet mir gerade nicht ein, wofür der Try Catch Block überhaupt gut sein soll.
    Das ist ein Widerspruch in sich. Eine gute Zusammenfassung ist m.E. die Verlinkungsbeschreibung von mir: »Fange nur Exceptions ab, die Du kennst und sinnvoll bearbeiten kannst.« Ich benutze auch Try-Blöcke. Und zwar dort, wo ich Exceptions nicht verhindern kann. z.B. eben bei dem Versuch, auf ein Verzeichnis und dessen Unterverzeichnisse rekursiv zuzugreifen (irgendwann komm ich ggf. an eine Du-hast-hier-nix-zu-suchen-Exception) oder beim Versuch, auf einen anderen Computer zuzugreifen (Time-Out). Die Fälle sind selten, aber gezielt. Und wenn ich eine Möglichkeit finde, es ohne Exception zu lösen, dann schreib ich es um.

    Das mit Option Infer. Naja, Geschmackssache. Aber wenn ich solche Dinger lese wie

    C#-Quellcode

    1. MyClass MyInstance = New MyClass

    oder eben in VB.Net

    VB.NET-Quellcode

    1. Dim MyInstance As MyClass = New MyClass 'nein, eben nicht Dim MyInstance As New MyClass

    dann krieg ich Gähnanfälle.

    Bzgl. Me.Close/Application.Exit nochmal: Alles eine Frage der Programmgestaltung.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Ich widerspreche mir?

    Ah, das nehme ich natürlich zurück. Ich bin nur zu doof zum lesen.

    DerSmurf schrieb:

    Der Post vom EdR leuchtet ein. Allerdings leuchtet mir gerade nicht ein, wofür der Try Catch Block überhaupt gut sein soll.

    Damit meine ich, warum ich an dieser Stelle überhaupt einen Try Catch eingebaut habe.
    Und verstanden, warum ich Try Catch nicht "einfach so" verwenden sollte, habe ich auch.

    Das mit Option Infer habe ich auch kapiert.
    Danke @petaod . Mein Vergleich mit VBA und Variant ist ja dann doof, weils nix mit Variant zu tun hat.
    Letztlich spare ich mir ja nur die Angabe des Datentyps, wenn dieser ohnehin klar ist.
    Das wiederum finde ich dann gar nicht so doof :)