Zum Inhalt springen
  • 0

C# GUI früher anzeigen


Tician

Frage

Hallöööchen mit 3 öchen,

Ich habe ein Programm mit grafischer Oberfläche (Form) und ner Menge code der losrennt sobald das Programm gestartet wurde - unter anderem 3 SQL-Abfragen die jeweils über 100 Zeilen lang sind.

An sich läuft alles durch, aber vom "Klick" auf die exe-Datei bis hin zur sichtbaren Oberfläche dauert es 20 Sekunden - das passt mir nicht und ein Anwender denkt dann natürlich das es nicht funktioniert und klickt dementsprechend noch ein paarmal auf die ausführbare Datei.

Meine Frage ist nun: Gibt es eine Möglichkeit die grafische Oberfläche anzuzeigen und ohne Zwischenaktion (wie Button-Click oder so) erst den Rest des Codes rennen zu laufen wenn die GUI angezeigt wird?

Link zu diesem Kommentar
Auf anderen Seiten teilen

22 Antworten auf diese Frage

Empfohlene Beiträge

  • 1
vor 3 Minuten schrieb Tician:

@Chief Wiggum Der Konstruktor meiner Form1 (stinknormales .NET btw) ist das Problem, da wird die Verbindung zum Oracle-SQL hergestellt und es werden die SQL-Abfragen gemacht und je nach Ergebnis werden labels mit unterschiedlicher Hintergrundfarbe versehen. Ich weiß es zwar nicht genau, aber ich denke es werden die SQL-Abfragen sein die so lange dauern, wie gesagt die gehen über 100 Zeilen und die DB ist rießig.

Wenn du eine einfache Lösung haben möchtest - pack den Code der die SQL Abfragen auslöst in das Form.Shown() Event. Dann wird es erst nach Anzeige gestartet.

Unabhängig davon solltest du dir über das optimieren der SQL Abfragen Gedanken machen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 1

Du kannst eine ganz normale Methode schreiben, die du durch das Event auslösen lässt:

form.Shown += LoadSql;

public void LoadSql(object sender, EventArgs e)
{
	//...
}

Ich weiß gerade nicht genau, welche EventArgs du bei Shown nutzen musst, aber das sagt VS dir schon. Um diesen EventHandler nun asynchron auszuführen*, reicht es, die Signatur anzupassen:

public async void LoadSql(...)

Jetzt kannst du innerhalb der Methode await nutzen.

Ich weiß jetzt nicht genau, was du machst, aber die Methode könnte (konzeptionell) so aussehen:

public async void LoadSql(...)
{
	var firstResult = database.ExecuteSqlAsync(...);
	var secondResult = database.ExecuteSqlAsync(...);
	var thirdResult = database.ExecuteSqlAsync(...);

	await Task.WhenAll(new[]{ firstResult, secondResult, thirdResult });

	ProcessFirstResult(firstResult.Result);
	ProcessSecondResult(secondResult.Result);
	ProcessThirdResult(thirdResult.Result);
}

Ich habe jetzt mal angenommen, dass dir asynchrone Methoden zur Verfügung stehen, mit denen du auf deine Datenbank zugreifen kannst. Die erkennst du daran, dass sie häufig mit ...Async enden und als Rückgabewert einen Task<xyz> haben.

In den ersten drei Zeilen startest du alle drei Anfragen "gleichzeitig" und in der vierten wartest du dann darauf, dass alle fertig sind. Das Warten an dieser Stelle hat den Vorteil, dass die Oberfläche währenddessen nicht hängt. Wenn du das Ergebnis der ersten Abfrage für die zweite (usw.) brauchst, kannst du das auch folgendermaßen machen:

...
var firstResult = await database.ExecuteSqlAsync(...);
//mache irgendwas mit firstResult
var secondResult = await database.ExecuteSqlAsync(xyz);

Hier würdest du zuerst auf das Ergebnis der ersten Abfrage warten (ohne die Oberfläche zu blockieren) und damit dann weiterarbeiten.

Ich hoffe, das hilft erstmal für die nächsten Schritte.

Noch ein Hinweis, falls du irgendwo beim Lesen drauf stößt: Normalerweise sollte man async void Methoden vermeiden (das hat was damit zu tun, wie Exceptions, die in diesen auftreten, behandelt werden) - das geht aber leider nicht, wenn man die als EventHandler nutzen möchte.

 

*wichtig: Allein durch das Nutzen von async/await wird der Code nicht asynchron (und schon gar nicht multi-threaded). Das ganze hier hilft dir nur, wenn das, was du tust auch tatsächlich asynchron abgearbeitet wird (Dateien lesen und Netzwerk-Zugriffe sind hier so die klassischen Beispiele).

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Hi,
ein einfacher Ansatz anhand einer neu erstellten Forms-Anwendung wäre so etwas hier:

namespace WindowsFormsApplication1
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            
            Task.WhenAll(
            Task.Run(() => 
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
                
            }),
            Task.Run(() => 
            {
                var x = 1;
                do
                {
                    Thread.Sleep(x * 1000);
                    Debug.WriteLine(x);
                    x++;

                } while (x <= 10);
            })).Wait();
        }
    }
}

Die Anwendung ist erst beendet, wenn beide Threads beendet sind.

Können die SQL-Abfragen auch parallel gestartet werden? Ist das SQL per Hand geschrieben, oder nutzt du einen ORM-Mapper für den Zugriff auf die Datenbank?

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Wie/wann wird den die SQL Anfrage ausgeführt? Normalerweise hast du ja Systemevents, die deinem Programm mitteilen wann etwas passiert. Da hätte ich einfach den Code für die Abfragen erst aufgerufen, wenn die GUI soweit ist. Der ganze Spaß ist natürlich Frameworkabhänging (WPF, .NET, ...) aber hier ein Beispiel: https://msdn.microsoft.com/en-us/library/system.windows.application.loadcompleted(v=vs.110).aspx

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Es geht ja schon damit los, dass eine 20 sekündige Datenbankabfrage nach großen konzeptionellen Problemen klingt. 

Poste am besten mal Codefragmente, dass man sich einen Eindruck verschaffen kann. 

Ansonsten klingt es vor allem nach optimierter Abfrage und asynchronem absetzen der query, bei einblendung einer Warteanimation

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Ohje, das sah mir bis jetzt zu hoch aus.

 

@Chief Wiggum Der Konstruktor meiner Form1 (stinknormales .NET btw) ist das Problem, da wird die Verbindung zum Oracle-SQL hergestellt und es werden die SQL-Abfragen gemacht und je nach Ergebnis werden labels mit unterschiedlicher Hintergrundfarbe versehen. Ich weiß es zwar nicht genau, aber ich denke es werden die SQL-Abfragen sein die so lange dauern, wie gesagt die gehen über 100 Zeilen und die DB ist rießig.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Ob die Db riesig ist oder nicht ist eigentlich erst einmal egal. 
Wenn im SQL viele Joins, Cursor, GroubBy oder Subselects verwendet werden, sollte man sich das sql nochmal angucken. 
Unglücklich designte Tabellen, Indicies, Trigger oder (Materialized)Views können hier auch schon ein Grund sein.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Aber selbst bei Form.Shown hast du dann eine 20 sekunden blockierte Anwendung.

ich würde dringend zu einer asynchronen Query raten.

(außerdem einem exception handling, dass Fehler dort fängt, wo sie passieren, nich einen globalen Exception Handler, das ist unsauber :X)

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 1 Minute schrieb SilentDemise:

Aber selbst bei Form.Shown hast du dann eine 20 sekunden blockierte Anwendung.

löst aber dieses

vor einer Stunde schrieb Tician:

An sich läuft alles durch, aber vom "Klick" auf die exe-Datei bis hin zur sichtbaren Oberfläche dauert es 20 Sekunden - das passt mir nicht und ein Anwender denkt dann natürlich das es nicht funktioniert und klickt dementsprechend noch ein paarmal auf die ausführbare Datei.

Meine Frage ist nun: Gibt es eine Möglichkeit die grafische Oberfläche anzuzeigen und ohne Zwischenaktion (wie Button-Click oder so) erst den Rest des Codes rennen zu laufen wenn die GUI angezeigt wird?

Problem. :)

Eine asynchrone Query ist natürlich schöner und löst auch das Problem der Blockade.

Bearbeitet von Gottlike
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Jo - darum würd ichs kombinieren. ;)

Also die asynchrone Query nach laden des Forms feuern, nicht währenddessen.

und es sollten auch dringend using statements um die Datenelemente verwendet werden, damit sie sauber wieder freigegeben werden, das fehlt in dem sample code oben auch.

Bearbeitet von SilentDemise
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Hab noch nie von asynchronem blablubb gehört.

Ich schau erstmal das die Funktionen fertig sind, das Ding soll auch noch Mails verschicken und heute noch fertig werden. Bis es produktiv ist schau ich mal das ich das using-Zeug auf die Datei-verarbeitung bekomme, das Event richtig hinbastel und schau mal etwas verständliches über asynchrone Abfragen zu finden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Man sollte niemals die GUI durch einen Thread blockieren. Um das zu bewerkstelligen, kann man Threads, Tasks oder etwas in die Richtung verwenden. Ein Blick wert sind in dieser Richtung auch Reactive Extensions (kurz, rx), damit könnte man die Datenbankabfragen in einen Service auslagern, diesen von der GUI heraus subscriben und sobald das Resultat zurück ist, weiter verarbeiten. Der Vorteil hierbei ist, dass man durch asynchrone Programmierung schnell in Race Conditions, Deadlocks oder andere Probleme der asynchronen Programmierung geraten kann. Observables lassen sich zB verketten, die Daten verändern usw. Gerade eine Verkettung wäre bei mehreren Datenbankabfragen hintereinander sinnvoll, um zum Beispiel darauf zu warten, bis alle fertig sind oder die eine Abfrage Resultate aus der anderen benötigt. 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

So, Programm funktioniert, bis mir jemand sagt das es sofort gebraucht wird habe ich also Zeit das ganze zu verbessern.

Ich habe mir mal die Seite von SilentDemise angesehen und wenn ich das richtig sehe funktioniert das nur mit Events. Jetzt wurde natürlich vorher gesagt ich soll die SQL-Abfragen in das Form.Shown()-Event packen.

Allerdings habe ich die Microsoft-Seite schon immer gehasst, die hat nichts was ich irgendwie verstehe.

Ich weiß das meine Form1 automatisch in den Designer und meinen eigentlichen Code aufgeteilt ist. Mache ich einen doppelklick auf das Event von der Form1-GUI (so hatte ich es gelernt) wird mein Event im Designer erstellt und im Code-Teil kann ich sagen was passieren soll. Wie funktioniert das mit dem async-Ding? Ich habe etwas rumptobiert (weiß auch nicht was "=>" heißen soll, habe ich noch nie benutzt)

Ich hätte es ja irgendwie so gemacht:

this.Shown += async (o, e) =>{}

Aber da schmeißt mich VS mit genau 68 Fehlermeldungen zu.

 

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Hi,

ich würde die Abfragen auch nicht direkt in die Events packen. Schau, dass du die Abfragen etwas kapselst und den Aufruf dieser in die Events legst.

Die async-Methoden müssen nicht events sein. Im Artikel von Microsoft, den SilentDemise gepostet hat sind auch entsprechende Beispiele hinterlegt (z.B.: async Task<User[]> GetUsers(IEnumerable<int> userIds) ).

Welche Fehlermeldungen erscheinen denn genau? Folgendes async Beispiel funktioniert z.B.

this.Shown += async (s,e)=> 
{
  await Task.Run(()=>Debug.WriteLine("Test"));
};

Dann musst du die einzelnen Fehlermeldungen mal auseinander nehmen. Wahrscheinlich versuchst du Variablen über Threadgrenzen direkt zu setzen.Einfach blind alles async machen klappt leider nicht.

 

Zum Thema:

this.shown += async (o, e) =>{ };

ist gleichzusetzen mit

...
this.shown += Form1_Shown;
}

private async void Form1_Shown(object sender, EventArgs e) { }

Das sind sogenannte Lambda-Ausdrücke (https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions)

 

OffTopic: Bei den ganzen Windows-Forms gefrickel merke ich, dass ich mich in WPF mit einem MVVM-Pattern viel wohler fühle. :-)

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Oh arlegermi <3

Mein Kopf war Hohl, mein Magen auch, jetzt nach dem Mittagessen kann ich wieder denken.

Ich hatte versucht mein Event (bzw die Erstellung dessen) außerhalb von Methoden, also direkt in die Klasse zu packen, deswegen die tausend Fehlermeldungen.

So, also mein Plan ist die Oberfläche zu haben, die soll ein kleines Element für Statusmeldungen haben zum Beispiel "Abfrage 1 wird ausgeführt", dann "Anfrage 2 wird ausgeführt", etc. Gleichzeitig soll sich nach jeder Abfrage ein label verändern. Gott sei Dank hat das Oracle-zeug das ich runtergeladen habe jeweils eine async-Methode zum ausführen dabei.

Mein Konzelt wäre also jetzt so:

        public Form1()
        {
          //load some settings
          this.Shown += LoadSQL;
        }


private async void LoadSQL(object sender, EventArgs e)
        {
  			label.Text = "Abfrage 1 ist in bearbeitung";
  			DataTable anfrage1 = await SQL-Anfrage1.ExecuteReaderAsync();
              //blabla paar Berechnungen
              label2.BackgroundColor = green;
  
  			DataTable anfrage2 = await SQL-Anfrage2.ExecuteReaderAsync();
  			...
              
        }

Sorry für die scheiß Formatierung^^

Ich muss mir das nochmal durchlesen, ich habe es nur halbherzig verstanden, vielleicht auch mal etwas rumprobieren mit Breakpoints (geht das überhaupt?) um zu sehen was genau gleichzeitig abläuft und was wann wartet.

Edit: DataTable enthält keine Definition für "GetAwaiter" ...

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Wenn ich das richtig in Erinnerung habe, bekommst du aus ExecuteReaderAsync einen Reader zurück, den du mit DataTable.Load laden kannst.

Aber Oracle ist da eh so 'ne Sache... guck dir mal diese Frage auf StackOverflow an, da macht jemand async mit der Oracle .NET Library: https://stackoverflow.com/questions/29016698/can-the-oracle-managed-driver-use-async-wait-properly/29034291#29034291

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Jetzt funktioniert es gar nicht mehr, es läuft und läuft und gibt mir meine GUI überhaupt nicht mehr - und der RAM wird immer voller. Oh man bin ich genervt, also erstmal debuggen mit Breakpoints...

Das wird immer seltsamer, mit Breakpoint zeigt es mir die Oberfläche an, aber die Elemente sind alle nur weiße Vierecke und blockiert ist sie trotzdem...

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Code wie ich ihn habe (ich weiß das man tausend Sachen besser machen kann, aber bitte eins nach dem anderen ._.):

using Oracle.ManagedDataAccess.Client;
...

OracleConnection con = new OracleConnection();
DataTable dataRollkarte = new DataTable();

int sumRollkarte = 0;
		
		public Form1()
        {
			InitializeComponent();
			this.Shown += LoadSQL; //Oberfläche wird angezeigt, aber blockiert, sämtliche Elemente werden nur als weiße Rechtecke angezeigt
		}
		
		private async void LoadSQL(object sender, EventArgs e)
        {
            //Rollkarte   
            label7.Text = "Processing";
            sumRollkarte = 0;
            dataRollkarte = await GetRollkarte();
            foreach (DataRow dr in dataRollkarte.Rows)
            {
                sumRollkarte += Convert.ToInt32(dr[11]); //Endlosschleife
            }
            label2.Text = Convert.ToString(sumRollkarte);
            label1.BackColor = Color.LimeGreen;
            label2.BackColor = Color.LimeGreen;
            label7.Text = "Fertig";
        }
		
        private async Task<DataTable> GetRollkarte()
        {
            dbConnect();
            OracleCommand cmd = new OracleCommand();
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = @"Viel zu lange...";
            cmd.Connection = con;

            DataTable dt = new DataTable();
            using (DbDataReader dr = await cmd.ExecuteReaderAsync())
            {                
                dt.Load(dr);
            }
            dbDisconnect();

            return dt;
        }

Ich hab die komische weiße Oberfläche und eine Schleife die endlos läuft - das hat sie vor dem async noch nicht getan. Ich musste auch meine OracleDataReader-Klasse gegen die NET-eigene DbDataReader-Klasse austauschen sonst hab ich Fehlermeldungen bekommen.

Unbenannt.PNG

Edit: Endlosschleife hat sich erledigt, es läuft alles durch, aber das async-Zeug funktioniert null^^

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Diese Frage beantworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...