Laden...

SplashScreen funktioniert nicht (richtig)

Erstellt von Froggie vor 14 Jahren Letzter Beitrag vor 14 Jahren 4.918 Views
F
Froggie Themenstarter:in
323 Beiträge seit 2007
vor 14 Jahren
SplashScreen funktioniert nicht (richtig)

Ich habe dieses Forum durchsucht (nach SplashScreen, Splash und Screen) und auch einige verlinkte Threads durchgelesen und eigentlich (dachte ich zumindest) auch verstanden (bloss kein Application.DoEvents einsetzen).
Nun habe ich mal ein kleines Testprogramm erstellt und festgestellt, dass ich wohl doch einiges nicht verstanden habe.
Hier mal ein paar Auszüge meiner kreativen Ergüsse:

Program.cs


[STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            using (frmSplash splash = new frmSplash())
            {
                splash.Text = String.Empty;
                splash.ControlBox = false;
                splash.StartPosition = FormStartPosition.CenterScreen;
                splash.ShowInTaskbar = false;
                splash.TopMost = true;

                splash.Show();
                splash.Update();
                //Application.DoEvents();

                //Simulation einer längeren Initialisierung
                for (int i = 0; i < 299; i++)
                {
                    Thread.Sleep(1);
                }

                //Thread.Sleep(5000);
                splash.Close();
            }


            using (Form1 frmHaupt = new Form1())
            {
                frmHaupt.StartPosition = FormStartPosition.CenterScreen;

                Application.Run(frmHaupt);
            }
        }

frmSplash.cs


public partial class frmSplash : Form
    {
        private double myOpacity = 0.1;

        public frmSplash()
        {
            InitializeComponent();
        }

        private void timerFadeIn_Tick(object sender, EventArgs e)
        {
            myOpacity += 0.10;
            if (myOpacity >= 1)
            {
                this.Opacity = 1;
                timerFadeIn.Enabled = false;
                return;
            }
            this.Opacity = myOpacity;
            this.Update();
            //Application.DoEvents();
        }

        private void timerFadeOut_Tick(object sender, EventArgs e)
        {
            myOpacity-=0.10;
            if (myOpacity<=0.01)
            {
                timerFadeOut.Enabled = false;
                return;
            }
            this.Opacity = myOpacity;
        }

        private void frmSplash_Load(object sender, EventArgs e)
        {
            if (!SystemInformation.TerminalServerSession)
            {
                timerFadeIn.Interval = 5;
                timerFadeOut.Interval = 5;
                this.Opacity = myOpacity;
                timerFadeIn.Enabled = true;
                timerFadeOut.Enabled = false;
            }
        }

        private void frmSplash_FormClosing(object sender, FormClosingEventArgs e)
        {
            timerFadeOut.Enabled = true;
            timerFadeIn.Enabled = false;
        }
    }

Problem:
Der Splash wird angezeigt, aber nicht weiter aktualisiert. Konkret heißt das: die .Update Methoden zeigen keine Wirkung.
Nach ein wenig debugging ist mir aufgefallen, dass die Timer-Ticks gar nicht ausgeführt werden.

Ich habe das ganze auch noch mal als Anhang gepostet, damit ihr auch versteht was ich eigentlich meine/will. Das lässt sich halt schwer beschreiben.

Frage:
Welchen Hinweis oder Wink (mit dem Zaun) könnt ihr mir geben, so dass sich der SplashScreen richtig aufbaut bzw. was kann ich an meinem Splash besser/eleganter machen?

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo Froggie,

die langlaufende Aktion muss in einen extra Thread. Siehe [FAQ] Warum blockiert mein GUI?.

herbivore

F
Froggie Themenstarter:in
323 Beiträge seit 2007
vor 14 Jahren

**:::

**:::

1.Ich habe mir das zwar durchgelesen gehabt, aber irgendwie habe ich es erst jetzt verstanden. 1.Ich habe das nun in einen anderen Thread ausgelagert, aber der Splash wird immer noch nicht aktualisiert.

Der SplashScreen ist immer noch wie oben implementiert.

Ich hatte versucht einen Screenshot zu erstellen, aber da der Splash durchsichtig ist, sieht man nix (oder Cropper kommt damit nicht klar).

Program.cs


public class Program
    {
        frmSplash splash;
        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Program program = new Program();
            program.splash = new frmSplash();

            program.splash.Text = String.Empty;
            program.splash.ControlBox = false;
            program.splash.StartPosition = FormStartPosition.CenterScreen;
            program.splash.ShowInTaskbar = false;
            program.splash.TopMost = true;

            program.splash.Show();
            program.splash.Update();

            //HintergrundThread erstellen
            ParameterizedThreadStart threadstart = new ParameterizedThreadStart(program.longRunning);
            Thread worker = new Thread(threadstart);
            worker.IsBackground = true;
            //Wie bekomme ich einen Thread aus dem ThreadPool?
            bool test = worker.IsThreadPoolThread;

            worker.Start(9999);

            //warten bis HintergrundThread erstellt wurde
            while (!worker.IsAlive)
            {
                Thread.Sleep(1);
            }

            //warten bis HintergrundThread fertig ist
            worker.Join();

            //SplashScreen schließen
            program.splash.Close();

            //Hauptformular anzeigen
            using (Form1 frmHaupt = new Form1())
            {
                frmHaupt.StartPosition = FormStartPosition.CenterScreen;

                Application.Run(frmHaupt);
            }
        }

        private void longRunning(object parameter)
        {
            for (int i = 0; i < (int)parameter; i++)
            {
                Debug.WriteLine("Bin zurzeit bei: " + i.ToString());
                Thread.Sleep(1);
            }
        }
    }

Frage(n):
Gibt es noch einen Hinweis warum der Splashscreen nicht richtig angezeigt wird? Nicht richtig angezeigt heißt, dass der FadeIn Effekt nur 1mal aufgrufen wird.

Der von mir erstellte Thread ist ja KEIN Thread aus dem ThreadPool. Ich muss allerdings warten bis der Thread fertig ist. Gibt es diese Möglichkeit auch mit Threads aus dem Pool?

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo Froggie,

warum Thread.Join nicht funktioniert, steht in [FAQ] Warum blockiert mein GUI?

Ich kann nur nochmal sagen, alles was du brauchst, steht in der FAQ. Mach es so und es wir funktionieren.

herbivore

A
266 Beiträge seit 2007
vor 14 Jahren

Du versuchst deinen Splash ein- und aus-faden zu lassen oder?

Ich habe schon einen Splash der ganau das tut... Ich werde den Code heute abend mal linken wenn du so lange warten kannst...

Ich habe das aber auch nur mit nem Timer gelöst, also ohne neuen Thread aufmachen zu müssen...

Bis dahin...

Von all den Sachen, die mir verloren gegangen, hab ich am meisten an meinem Verstand gehangen... MfG...

F
Froggie Themenstarter:in
323 Beiträge seit 2007
vor 14 Jahren

Erstmal vielen Dank für die Hilfe!

Ich bin gerade am Umbauen meines Testprojektes. Bis jetzt habe ich den Splashscreen immer normal über die .Show-Methode angezeigt. Werde das jetzt testen, wie das aussieht, wenn ich den SplashScreen mit Application.Run(Splash) aufrufe.
Das soll deutlich einfacher gehen.
ABER:
Meines Wissens nach wird so ein weiterer GUI-Thread erstellt. Und genau das wollte ich ja verhindern. Aber wenn es nicht anders bzw. das andere zu kompliziert ist, dann mache ich das halt mit 2 GUI-Threads.
Hat jemand berechtigte Einwände dagegen?

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo Froggie,

es ist nicht nötig und nicht sinnvoll zwei GUI-Thread zu verwenden.

Application.Run ist natürlich erforderlich, aber es gibt keinen Grund das Application.Run nicht im Haupt-Thread zu machen. Es muss ja nur die langlaufenden Aktion und nicht die Erzeugung und Anzeige des Splash-Screens im Worker-Thread erfolgen. Du solltest dazuwischen klar trennen.

Obwohl alles was ich jetzt schreibe, aus der FAQ hervorgeht, hier mal alles auf einen Blick: im Haupt-Thread das Splash-Fenster erzeugen, den Worker-Thread starten und Application.Run für das Splash aufrufen. An Ende des Worker-Threads, wie in der FAQ gezeigt, das Splash-Fenster schließen. Anschließend im Haupt-Thread ein zweites Application.Run für das eigentliche (Haupt-)Form. Fertig!

herbivore

A
266 Beiträge seit 2007
vor 14 Jahren

BTW: was soll das denn??



                //Simulation einer längeren Initialisierung
                for (int i = 0; i < 299; i++)
                {
                    Thread.Sleep(1);
                }

Das wäre doch der Teil den du in dan anderen Thread auslagern könntest...

Von all den Sachen, die mir verloren gegangen, hab ich am meisten an meinem Verstand gehangen... MfG...

F
10.010 Beiträge seit 2004
vor 14 Jahren

Warum schaut eigentlich niemand mal, was das Framework diesbezüglich anbietet?

Seit FW2 bietet WindowsFormsApplicationBase schon solche sachen an.
SplashScreenunterstützung, Abfangen von Mehrfachstart, Start und Stop Behandlung usw.

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo FZelle,

Warum schaut eigentlich niemand mal, was das Framework diesbezüglich anbietet?

der vollqualifiziere Klassenname lautet: Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.

Die Gründe könnten also sein:
*Microsoft statt System: sprich weniger portabel. *VisualBasic statt CSharp: Sicher kann man in .NET sprachübergreifend programmieren, aber wenn die Klassen tatsächlich von allgemeiner Bedeutung sind, dann sollte man das nicht durch ein VisualBasic im Namespace verschleiern. *Die Untersützung ist nicht oder wenig nützlich oder aber umständlich: Kann ich nicht beurteilen. *Die Unterstützung ist unbekannt: so war das bei mir. Vermutlich ist sie bei C#-Programmierer generell deutlich weniger bekannt als bei VB-Programmieren.

herbivore

3.511 Beiträge seit 2005
vor 14 Jahren

@FZelle:
Hi,

laut Doku befindet sich diese Klasse in der Assembly Microsoft.VisualBasic. Da ist sie aber nicht drin?! Wollte mir das Ding mal mittels Reflector anschauen. Ich referenziere auf "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=969db8053d3322ac". Liegt die doch woanders?

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

F
Froggie Themenstarter:in
323 Beiträge seit 2007
vor 14 Jahren

... Application.Run für das Splash aufrufen. ... Anschließend im Haupt-Thread ein zweites Application.Run für das eigentliche (Haupt-)Form...

Ich denke jedes Application.Run erzeugt einen neuen GUI-Thread. Dann würde ich hier ja wieder 2 GUI-Threads haben.
Habe ich das falsch verstanden mit dem Application.Run?

Nach meinem Wissensstand wären das dann insgesamt 3 Threads:

  • ein Thread wird sowieso vom Framework für jede Applikation berietgestellt
  • ein GUI-Thread durch Application.Run für den Splash
  • ein GUI-Thread durch Application.Run für das Hauptform
A
266 Beiträge seit 2007
vor 14 Jahren

Du brauchst keinen neuen Thread aufzumachen...
Ausser du willst im Hintergrund noch irgendwelche Sachen laden...

Nimm dir nen Timer mit nem Intervall von 50ms und tue rein:


private bool FadedIn = false;

// timerevent
if (this.Opacity < 1 && !FadedIn)
{
this.Opacty += 0.1;
}
else if (this.Opacity >= 1&& !FadedIn)
{
FadedIn = true;
// etl. wenn du auf ein ereignis warten willst bevor du raus fadest
Timer.enabled = true;
}
else if (this.Opacity > 0 && FadedIn)
{
this.Opacity -=0.1;
}

Von all den Sachen, die mir verloren gegangen, hab ich am meisten an meinem Verstand gehangen... MfG...

4.931 Beiträge seit 2008
vor 14 Jahren

Hallo Khalid,

die gesuchte Klasse findest sich unter 'Microsoft.VisualBasic.ApplicationServices' in der von dir genannten 'Microsoft.VisualBasic.dll'.

Ups: das hatte herbivore ja schon geschrieben...
Ich habe sie jedenfalls mit dem Reflektor anschauen können.

Und ich sehe es auch so wie herbivore, daß mir die VisualBasic-Klassen in C# eher befremdlich vorkommen -)

3.511 Beiträge seit 2005
vor 14 Jahren

Komisch. Nehme ich die MS.VB Assembly aus dem GAC, ist da fast nichts drin. Nehme ich die direkt aus dem Frameworkordner aus dem Windowsordner, ist alles drin. Naja, jetzt hab ichs jedenfalls.

Danke.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo Froggie,

Ich denke jedes Application.Run erzeugt einen neuen GUI-Thread.

nein, beide Application.Run würden ja - nacheinander - im selben Thread ausgeführt. Meinem Vorschlag folgend im Haupt-Thread. Application.Run erzeugt selbst keinen (weiteren/neuen) Thread, sondern führt alle Aktionen in dem Thread aus, in dem es aufgerufen wurde.

Habe ich das falsch verstanden mit dem Application.Run?

Anscheinend.

Hallo Atomroflman,

Du brauchst keinen neuen Thread aufzumachen...
Ausser du willst im Hintergrund noch irgendwelche Sachen laden...

für das reine Ein-/Ausblenden hättest du recht, aber es geht hier doch um langwierige Initialisierungen, die dann natürlich in einen extra Thread ausgelagert werden müssen.

herbivore

F
10.010 Beiträge seit 2004
vor 14 Jahren

@herbivore :
Die geschichte hinter WindowsFormsApplicationBase ist, das es schon in der IDE
bei VB.NET Projekten direkt erreichbar ist, und dann von VS direkt als die startklasse
erstellt wird.

Das hat überhaupt nichts mit VB Compatibility oder ähnlichem zu tun.
Genausowenig wie die BigInter klassen im J#.

A
266 Beiträge seit 2007
vor 14 Jahren

Du brauchst keinen neuen Thread aufzumachen...
Ausser du willst im Hintergrund noch irgendwelche Sachen laden...

Deswegen schrieb ich das...

Von all den Sachen, die mir verloren gegangen, hab ich am meisten an meinem Verstand gehangen... MfG...