Laden...

Ladebildschirm UI blockiert

Erstellt von LittleTester vor einem Jahr Letzter Beitrag vor einem Jahr 833 Views
L
LittleTester Themenstarter:in
158 Beiträge seit 2019
vor einem Jahr
Ladebildschirm UI blockiert

Ich habe mir eine Anwendung erstellt wo man mittels GridView alle Drucker eintragen kann und beim Start wird direkt geprüft, ob diese Online sind.
Das Problem ist, dass nach dem Doppelklick der EXE das Programm im Hintergrund loslegt, aber nichts darauf hindeutet, dass das Programm bereits arbeitet, weil die GUI erst geladen wird nachdem die komplette Liste durch ist und für alle Einträge das Ergebnis vor liegt. Mit zunehmender Größe der Datenbank dauert das halt immer länger. Ich brauche also einen Splashscreen / Ladebildschirm.

Auf YouTube habe ich leider nur Beispiele gefunden, wo das mit einem timer() gelöst wird, aber so ein "Fakeladebalken" bringt mir ja nichts.

Nun würde ich gerne eine der folgenden Möglichkeiten umsetzen, weis aber nicht wie:

  1. Ladebalken der Durchläuft und den Fortschritt des Scanvorgangs anzeigt. Im laufenden Programm habe ich sowas bereits und wenn man dort auf "Aktualisieren" klickt oder ein festgelegtes Intervall abgelaufen ist klappt das auch.

  2. Momentan ist es noch so, dass das Programm scheinbar einfriert während der Scanvorgang läuft (Egal ob beim Start oder im laufenden Programm getriggert) und dann mit einem Schlag das Ergebnis in allen Zeilen des DataGridView anzeigt. Schöner wäre es, wenn das Ergebnis Zeile für Zeile angezeigt wird sobald es vor liegt. Dann würde das Programm auch nicht "einfrieren".

Anbei mal der Code, wie er jetzt ist und der für die Angelegenheit wohl relevant ist. Ich hoffe, dass nicht alles furchtbar ist und es mit dezenten Verfeinerungen getan ist.


private void Form1_Load(object sender, EventArgs e)
{
    // Prüfen ob die Datenbankdatei existiert.
    if (File.Exists(dataSource))
    {

        // Timer starten. Intervall (in Minuten) wird in den Einstellungen festgelegt.
        timer1.Interval = Convert.ToInt32(Settings.Default["settingIntervall"]) * 60000;
        timer1.Enabled = true;
        timer1.Start();

        // Konfiguration einlesen
        foreach (string? line in deviceFile)
        {
            string[] tokens = line.Split(',');
            comboDevice.Items.Add(tokens[0]);
        }

        foreach (string? line in departmentFile)
        {
            string[] tokens = line.Split(',');
            comboDepartment.Items.Add(tokens[0]);
        }

        // Datagrid-Spaltenüberschriften generieren
        dataGridView1.Columns.Add("Column", "ID");
        dataGridView1.Columns.Add("Column", "Breich");
        dataGridView1.Columns.Add("Column", "Raum-Nr");
        dataGridView1.Columns.Add("Column", "Raum-Name");
        dataGridView1.Columns.Add("Column", "Gerät");
        dataGridView1.Columns.Add("Column", "IP");
        dataGridView1.Columns.Add("Column", "Bemerkungen");
        dataGridView1.Columns.Add("Column", "Status");

        // Datenbank auslesen.
        ReadDB();
        // Funktion ping() ausführen.
        Ping();

        }
        else
        {
                // Datenbankdatei anlegen
        }
}

public void Ping()
{

    int online = 0; // Zählt Online
    int offline = 0; // Zählt Offline
    int notDetermined = 0; // Zählt nicht ermittelte Geräte (Wenn IP nicht gültig)
    progressBar1.Value = 0; // Fortschrittsbalken

    try
    {
        // Füllen des Datagrid mit Daten aus der Datenbank.
        foreach (DataGridViewRow row in dataGridView1.Rows)
        {
            // Prüft ob eine IP-Adresse hinterlegt ist und versucht diese an zu pingen.
            try
            {
                if (row.IsNewRow)
                {
                    break; // Die letzte Zeile (die Leer sein wird) im DataGrid nicht durchlaufen.
                }

                // Ping ausführen. Warte 1,5 Sekunden pro Zeile auf Antwort.
                Ping ping = new Ping();
                PingReply reply = ping.Send(row.Cells[5].Value.ToString(), 1500);

                // Fortschrittsbalken.
                progressBar1.Maximum = dataGridView1.Rows.Count;
                progressBar1.Value++;

                // Wird ausgeführt, wenn die angepingte IP eine Antwort liefert.
                if (reply.Status.ToString() == "Success")
                {

                    // Verbindung herstellen
                    SQLiteConnection dbConnection = new SQLiteConnection("Data Source=" + dataSource + ";Version=3;"); //Create the connection with database
                    dbConnection.Open();

                    // Einfügen der Uhrzeit in die Datenbank
                    string myQuery = "UPDATE printerlist SET online = @_online WHERE id='" + row.Cells[0].Value + "'";
                    SQLiteCommand command = new SQLiteCommand(myQuery, dbConnection);

                    command.Parameters.AddWithValue("@_online", DateTime.Now.ToString("yyyy.MM.dd, HH:mm")); // Aktuelles Datum und Uhrzeit.
                    command.ExecuteNonQuery(); // Update-Befehl ausführen.
                    dbConnection.Close(); // Verbindung schließen.

                    // Schriftzug "Online" in Spalte "Online" anzeigen
                    row.Cells[7].Style = new DataGridViewCellStyle { ForeColor = Color.Green };
                    row.Cells[7].Value = "Online";

                    online++; // Zählen wie viele Geräte online sind.
                }
                else
                {

                    // Datum + Uhrzeit aus der Datenbank auslesen und unter "Online" anzeigen lassen
                    // SQlite-Verbindung herstellen
                    SQLiteConnection dbConnection = new SQLiteConnection("Data Source=" + dataSource + ";Version=3;"); //Create the connection with database
                    dbConnection.Open();

                    // SQlite-Datenbank auslesen
                    SQLiteCommand comm = new SQLiteCommand("SELECT online FROM printerlist WHERE id='" + row.Cells[0].Value + "'", dbConnection);
                    using (SQLiteDataReader read = comm.ExecuteReader())
                    {
                        while (read.Read())
                        {
                            row.Cells[7].Value = read[0].ToString();
                        }
                    }

                    dbConnection.Close(); // Verbindung schließen.
                    offline++; // Zählen wie viele Geräte offline sind.
                }
            }
            catch
            {

                // Fortschrittsbalken.
                progressBar1.Maximum = dataGridView1.Rows.Count;
                progressBar1.Value++;

                // Schriftzug "Nicht ermittelt" in Spalte "Online" anzeigen
                row.Cells[7].Value = "Nicht ermittelt";
                notDetermined++;
                continue;
            }

            lblOnline.Text = "Online: " + online;
            lblOffline.Text = "Offline: " + offline;
            lblNotDetermined.Text = "Nicht ermittelt: " + notDetermined;

        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

IDE: Visual Studio 2022
Sofern nicht anders genannt basieren meine Projekte auf C# und .net 6

16.806 Beiträge seit 2008
vor einem Jahr

Mehrere Fehler im Code.

Dein GUI Thread blockiert
=> [FAQ] Warum blockiert mein GUI?
Gleiches Prinzip nimmt man für jede Art von Hintergrundaktion.
Ergo: mit async/await und Tasks arbeiten.

Du mischt Logik mit UI Code und DB Code
=> [Artikel] Drei-Schichten-Architektur
Das Vermischen führt schnell - wie hier - zum falschen bzw. ungültigen Umsetzen von UI-Zugriffen.

Dein DB Zugriff ist unsicher
=> [Artikelserie] SQL: Parameter von Befehlen
Hinzu kommt die fehlerhafte Umsetzung der Verbindungsverwaltung: bei einer Exception bleibt die Sql Connection offen (und blockiert Ressourcen).
Daher arbeitet man hier mit using().
Aber auch hier solltest Du immer mit async/await arbeiten.

Zeiten in Datenbanken sollten standardisiert abgelegt werden (wie an jeder anderen Stelle auch).
[FAQ] DateTime vs. DateTimeOffset und der Umgang mit Zeiten in .NET
Sqlite hat aber die Einschränkung Zeiten nur mit einer Genauigkeit von .001 abspeichern zu können, was weniger ist als DateTimeOffset.
Willst Du die Zeit als String speichern, verwende ISO8601. Standards haben ihren Grund.

4.931 Beiträge seit 2008
vor einem Jahr

Für einen Ladebildschirm suche einfach mal nach "Splash" hier im Forum bzw. schau dir die beiden von mir verlinkten Komponenten in Splash Screen - MainForm im Hintergrund an.

L
LittleTester Themenstarter:in
158 Beiträge seit 2019
vor einem Jahr

Danke euch und Danke Abt für die Verlinkungen.
Ja, in PHP habe ich das MVC-Prinzip bereits umgesetzt. (Damit war ich die letzten Monate beschäftigt) Für die ergänzende Anwendung (ihr erinnert euch an mein Projekt zur Inventarisierung?) will ich das gerade richtig lernen, weil es nun ohne Rest-API nicht mehr gehen wird...

Das hier ist ein Programm, dass ich vor Jahren mal angefangen hatte, dann aber aufgehört habe, weil ich nicht weiter kam und es nun doch vollendet habe.
Es ist nur diese eine Sache übrig geblieben die mich nun stört und beheben will.

Was haltet ihr vom Ansatz in diesem YouTube-Video? (https://www.youtube.com/watch?v=JuIINT41Uf4) Kann man das AUCH so machen, oder sollte ich streng dem Beispielcode im verlinkten Thread folgen?

IDE: Visual Studio 2022
Sofern nicht anders genannt basieren meine Projekte auf C# und .net 6

C
2.121 Beiträge seit 2010
vor einem Jahr

Du guckst dir echt erst Werbung und dann ein Video an wo du jemandem beim tippen zu siehst? Du liebe Güte!
Mir wurds schnell zu blöd, aber viel kann dabei ja nicht rausgekommen sein. Vielleicht kannst du hier ein Beispiel posten das daraus entstanden ist.

16.806 Beiträge seit 2008
vor einem Jahr

Ich hab ehrlich gesagt auch wenig Lust irgendwelche Videos anzuschauen um irgendwann die richtige Stelle zu finden.
Videos sind einfach kein geeignetes Mittel (meiner Meinung nach für solch ein Wissenstransfer).

Aber stupide durch geskippt macht der nichts anderes als im Link steht: asynchrone Programmierung mit Tasks - genau das, was ich geschrieben hab.

F
10.010 Beiträge seit 2004
vor einem Jahr

Nur das er es nicht zuende gebracht hat.
In einem Task dann Thread.Sleep() aufrufen zeigt von ......