Laden...

SplashScreen-Komponente (noch eine ;-P)

Erstellt von TheBrainiac vor 14 Jahren Letzter Beitrag vor 14 Jahren 14.620 Views
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren
SplashScreen-Komponente (noch eine ;-P)

Beschreibung:

Auch ich habe wie einige andere (hier bzw. hier) eine eigene Splash-Screen Komponente. Da ich natürlich (wie vermutlich die beiden Anderen auch😉) finde, meine eigene ist die komfortabelste :evil:, will ich sie euch nicht vorenthalten.

Beispielaufruf:

public class Program
{
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(true);

        SplashScreen.ShowSplash((spl) =>
        {
            var rndm = new Random();

            spl.Title = "Updater";
            spl.Description = "Updating Application";
            spl.Product = "MyCustomUpdater";
            spl.ProductCopyright = "© 2009 Christian Weber";
            spl.ProductDescription = "This updater updates a dummy application. \n\nTHIS IS ONLY A DUMMY!\nIT DOES NO CHANGES TO ANYTHING!";
            spl.TopMost = true;
            spl.Image = Resources.Image;
            spl.Icon = Resources.Icon;

            var i = spl.AddTask(Resources.bullet_black, "Checking for updates");

            spl.Progress = 0;
            spl.Status = "Checking for updates...";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.UpdateTask(i, Resources.bullet_black, "Checking for updates (done)");
            i = spl.AddTask(Resources.bullet_black, "Downloading updates");

            spl.Progress += 10;
            spl.Status = "Downloading updates (0 of 2 at 120 kBit/s)";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.Progress += 10;
            spl.Status = "Downloading updates (1 of 2 at 140 kBit/s)";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.Progress += 10;
            spl.Status = "Downloading updates (2 of 2 at 112 kBit/s)";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.UpdateTask(i, Resources.bullet_black, "Downloading updates (done)");
            i = spl.AddTask(Resources.bullet_black, "Installation");

            spl.Progress += 10;
            spl.Status = "Running preinstallation tasks...";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.Progress += 10;
            spl.Status = "Performing installation (0 of 2 finished)";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.Progress += 10;
            spl.Status = "Performing installation (1 of 2 finished)";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.Progress += 10;
            spl.Status = "Performing installation (2 of 2 finished)";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.Progress += 10;
            spl.Status = "Running postinstallation tasks...";
            Thread.Sleep(rndm.Next(200, 2000));

            spl.UpdateTask(i, Resources.bullet_black, "Installation (done)");

            spl.Progress += 10;
            spl.Marquee = true;
            spl.Status = "Completing installation...";
            Thread.Sleep(rndm.Next(200, 2000));
        });

        Application.Run(new MainForm());
    }
}

Anbei ein ScreenShot, Source im nächsten Post.

Gruß, Christian.

Schlagwörter: splash screen

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hier nun der Source-Code mit einer kurzen Dokumentation:

Statische Methode zum Aufrufen des SplashScreens:

// Erzeugt eine neue Instanz von SplashScreen, zeigt die SplashScreen-Form an und ruft action in einem Worker-Thread mit der Instanz von SplashScreen als Parameter auf.
public static void ShowSplash(Action<SplashScreen> action);

// Erzeugt eine neue Instanz von SplashScreen, zeigt customSplash an und ruft action in einem Worker-Thread mit der Instanz von SplashScreen als Parameter auf.
public static void ShowSplash(ISplashScreenForm customSplash, Action<SplashScreen> action);

Aufrufbare Properties / Methoden innerhalb von action:


// Setzt den Statustext der aktuellen Operation
public String Status;

// Setzt den Titel der aktuellen Operation
public String Title;

// Setzt den Fortschritt der aktuellen Operation (Wert zwischen 0 und 100)
public Int32 Progress;

// Versetzt die Fortschrittsanzeige in den Marquee-Modus
public Boolean Marquee;

// Setzt den SplashScreen dauerhaft in den Vordergrund
public Boolean TopMost;

// Die Beschreibung unter dem Titel
public String Description;

// Der Name des Produkts
public String Product;

// Die Beschreibung des Produkts
public String ProductDescription;

// Copyright oder Version des Produkts
public String ProductCopyright;

// Bild oben links in der Ecke
public Image Image;

// Icon der Form
public Icon Icon;

// Setzt benutzerdefinierte Eigenschaften
public Object this[String key];

// Fügt einen Task hinzu, gibt die Task-ID zurück
public Int32 AddTask(Image image, String text);

// Ändert einen Task
public void UpdateTask(Int32 index, Image image, String text);

// Löscht einen Task
public void RemoveTask(Int32 index);

// Fügt einen neuen Job zu Queue hinzu.
public Guid EnqueueJob(Action<SplashScreen> action, JobMode jobMode, Guid waitFor);

// Führt alle Jobs aus.
public void RunJobs();

// Führt die Operation auf dem Gui-Thread aus. Ermöglicht es, Gui-Elemente innerhalb des SplashScreens zu erzeugen.
public Object InvokeOnGUIThread(Delegate method, params Object[] args)

Der SplashScreen schließt sich automatisch, sobald action durchgelaufen ist.

Weiterhin kann über ein Interface (ISplashScreenForm) eine eigene Form als SplashScreen verwendet werden:

using (var mySplash = new MyForm())
{
    SplashScreen.ShowSplash(mySplash, (spl) =>
    {
        // Do some work here ...
    });
}

Um das Disposing der eigenen Form muss sich selbst gekümmert werden, das Invoking auf dem UIThread wird vom SplashScreen übernommen.

Jobs

SplashScreen.ShowSplash((spl) =>
{
    spl.Title = "Updater";
    spl.Description = "Updating Application";
    spl.Product = "MyCustomUpdater";
    spl.ProductCopyright = "© 2009 Christian Weber";
    spl.ProductDescription = "This updater updates a dummy application. \n\nTHIS IS ONLY A DUMMY!\nIT DOES NO CHANGES TO ANYTHING!";
    spl.TopMost = true;
    spl.Image = Resources.Image;
    spl.Icon = Resources.Icon;

    spl.Status = "Working...";
    
    // Job wird synchron ausgeführt
    spl.EnqueueJob((s) =>
    {
        AddTaskAndWait(s, "Initializing", 10, 2000);
    }, JobMode.Synchronous, Guid.Empty);

    // Job wird asynchron ausgeführt
    spl.EnqueueJob((s) =>
    {
    AddTaskAndWait(s, "Downloading Updates", 10, 2 * f);
    }, JobMode.Asynchronous, Guid.Empty);

    // Job wird asynchron ausgeführt, läuft gleichzeitig mit "Downloading Updates"
    var guid1 = spl.EnqueueJob((s) =>
    {
        AddTaskAndWait(s, "Operation 1", 10, 3 * f);
    }, JobMode.Asynchronous, Guid.Empty);

    // Job hängt von "Operation 1" ab, beginnt erst mit der Arbeit, wenn "Operation 1" fertig ist
    // Downloading Updats ist hiervon unbeeinträchtigt
    spl.EnqueueJob((s) =>
    {
        AddTaskAndWait(s, "Depending Operation 1", 10, 5 * f);
    }, JobMode.Asynchronous, guid1);

    // Synchron, wartet bis alle vorherigen fertig sind
    spl.EnqueueJob((s) =>
    {
        AddTaskAndWait(s, "Finishing", 10, 2 * f);
    }, JobMode.Synchronous, Guid.Empty);

    // Führt alle Jobs in der Queue aus
    spl.RunJobs();

    spl.Status = "Jobs finished";
    spl.Marquee = true;
    Thread.Sleep(2000);
});

Aktuelle Builds und Dokumentation / Anleitungen ab sofort auf meiner Website.

Brainiac's Code / Projekte / SplashScreen

Gruß, Christian.

UPDATE: Posts zusammengelegt.
UPDATE: Flackern beim Start beseitigt
UPDATE: Jobunterstützung hinzugefügt
UPDATE: Changelog, .Net 2.0, Div. kleine Änderungen
UPDATE: InvokeOnGuiThread hinzugefügt

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
1.130 Beiträge seit 2007
vor 14 Jahren

Ich finde, bis jetzt ist es
a)Die schönste Komponente
b)Die umfangreichtse

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

906 Beiträge seit 2005
vor 14 Jahren

Hallo, deine Komponente ist echt nett, aber ich habe ein Problem.

Die Darstellung des SplashScreens flackert am Anfang stark. Ich habe einfach dein Beispiel von deiner Homepage genommen:


SplashScreen.ShowSplash((spl) =>
            {
                spl.Title = "MyApp";
                spl.Description = "Meine Testwendung";
                spl.Product = "ich";
                spl.ProductCopyright = "© 2009 ich";
                spl.ProductDescription = "das ist ein Testanwendung";
                spl.TopMost = true;
                spl.Image = Resources.Image;
                spl.Icon = Resources.Icon;
                spl.Progress = 0;
                spl.Status = "Starte Anwendung";
 
                // Custom operation that takes a long, long time
 
                spl.Progress = 100;
                spl.Status = "Ok, alles ok";
                Thread.Sleep(1000);
            });

Hast du ne Idee woher das kommt?

Ich nutze Vista Ultimate Sp2 (x86).

TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi, MagicAndre1981.

Habe das Flackern beseitigt. Es lag wie ich vermutet habe an den TableLayoutPanels, die habe ich jetzt einfach weggelassen.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi @ All.

So. Heute habe ich noch Unterstützung für ein Job-System eingebaut.

Dokumentation und aktuelle Sources sowohl in den ersten zwei Posts als auch auf meiner Website.

Viel Spaß damit!

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
J
1.114 Beiträge seit 2007
vor 14 Jahren

Also Christian, ich bin wirklich begeistert von deiner Implementierung. Das scheint mir bis jetzt die flexibelste SplashScreen Komponente zu sein, die ich bislang gefunden hab...

Allerdings bin ich grossteils noch in .NET 2.0 unterwegs, und kenne mich deswegen überhaupt nicht mit diesen Lambda Expressions aus. Und nach 45 Minuten Sucherei im Web hab ich's jetzt aufgegeben. Ich krieg dein Beispiel einfach nicht umgebogen, damit es mit anonymen Methoden hinhaut.

Kanns einer mir bischen unter die Arme greifen, denn ich blick diese Lambda Expressions wirklich nicht.

z.B. hier:

spl.EnqueueJob((s) =>
{
    AddTaskAndWait(s, "Initializing", 10, 2 * f);
}, JobMode.Synchronous, Guid.Empty);
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi, Jelly.

spl.EnqueueJob((s) =>
{
    AddTaskAndWait(s, "Initializing", 10, 2 * f);
}, JobMode.Synchronous, Guid.Empty);

wäre in .Net 2.0:

spl.EnqueueJob(delegate(SplashScreen s)
{
    AddTaskAndWait(s, "Initializing", 10, 2 * f);
}, JobMode.Synchronous, Guid.Empty);

Gruß, Christian

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
906 Beiträge seit 2005
vor 14 Jahren

die Komponenten geht doch aber unter .net 2.0 gar nicht.

Btw, kannst du die eigentliche Komponente in ein DLL Projekt packen (damit man das nicht immer wieder selber machen muss), gescheit versionieren und ein Changelog bei jeder neuen Version schreiben?

J
1.114 Beiträge seit 2007
vor 14 Jahren

Danke Christian. Irgendwann kapier ich diese Lamda Expressions auch 😃

die Komponenten geht doch aber unter .net 2.0 gar nicht.

Warum sollte sie nicht gehen, wenn man die ganzen Lambda Ausdrücke durch anonyme Methoden ersetzt. Oder werden noch Klassen benutzt, die in 2.0 nicht enthalten sind?

TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi, Jelly.

Ich benutze einige Delegates (Action<W, U, R, S, T> etc.), aber die kann man leicht ersetzen.

Wenn du VS2008 hast, dann ist das var-Schlüsselwort kein Problem (mit VS2005 schon, leider).

@MagicAndre1981: Werd ich machen, sobald ich wieder zuhause bin.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
906 Beiträge seit 2005
vor 14 Jahren

über eine reine .net 2.0 Lösung würde ich mich auch freuen. Es kann auch eine 2008 Solution mit var sein, aber das Zielframework das 2.0.

TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi @ All.

Ich habe den SplashScreen jetzt in eine eigene Bibliothek ausgelagert und das Zielframework auf .Net 2.0 umgestellt. Weiterhin habe ich ein Changelog eingeführt und ein paar kosmetische Veränderungen vorgenommen. Dokumentiert habe ich das projekt auch.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
J
1.114 Beiträge seit 2007
vor 14 Jahren

Deine Vorgehen bei diesem Projekt ist echt lobenswert. Das Ding ist wirklich genial.

Danke auch für die Umstellung .NET 2.0

C
89 Beiträge seit 2005
vor 14 Jahren

Hej Schamese,

ich hab ein kleines Problem mit Deiner Komponente. Ich möchte als einen Job unteranderem das Laden der MainForm durchführen lassen. Das Laden an sich funktioniert auch, jedoch funktionieren Timer nicht mehr, die ich z.B. verwende um die aktuelle Uhrzeit anzuzeigen. Die Eigenschaft Timer.Enabled ist zwar true, aber das Tick-Event wird nicht mehr ausgelöst. Woran könnte das liegen?

Geladen wir die Form mit der SplashScreenLibrary so:


Form1 mainForm = null;
            using (JippoSplash splash = new JippoSplash())
            {
                SplashScreen.ShowSplash(splash, delegate(SplashScreen spl)
                {
                   //Properties von spl setzen

                    spl.EnqueueJob(delegate(SplashScreen s)
                    {
                        int n = s.AddTask(Resources.bullet_go, "Hauptanwendung wird initialisiert.");
                        mainForm = new Form1();
                        s.UpdateTask(n, Resources.bullet_green, "Hauptanwendung wurde geladen.");
                        s.Progress += 50;
                    }, JobMode.Synchronous, Guid.Empty);

                    spl.RunJobs();

                    spl.Progress = 100;
                    spl.Status = "Fertig...";
                    Thread.Sleep(1000);
                });
            }

    Application.Run(mainForm);

TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi, Calexico.

Ich konnte dein Problem reproduzieren.

Das Problem ist, dass alles, was in SplashScreen.ShowSplash(splash, delegate(SplashScreen spl) { ... }); steht, in einem Hintergrund-Thread läuft. Wenn man nun GUI-Komponenten nicht im GUI-Thread (also dem Haupt-Thread) erzeugt, kann es zu solchen Probelmen kommen. Ich weis nicht, warum deine MainForm so lange braucht, um zu laden, aber wenn es irgendwelche von der GUI unabhängige Operationen sind, dann lagere diese in den SplashScreen aus. Danach erzeugst du die GUI (also deine MainForm) im GUI-Thread und übergibst ihr die im SplashScreen geladenen Daten. So war das eigentlich meinerseits gedacht.

Ein kleines Beispiel:

// Hier sind wir (normalerweise) im GUI-Thread
var list = new List<String>();

SplashScreen.ShowSplash((spl) =>
{
    // Hier sind wir in einem Background-Worker (nennen wir ihn mal BG1)
    spl.EnqueueJob((s) =>
    {
        // Wenn JobMode.Synchronous, dann sind wir hier in BG1 (s.o.)
        // Wenn JobMode.Asynchronous, dann sind wir hier in einem weiteren Background-Worker
        int n = s.AddTask(Resources.bullet_go, "Daten laden");
        // Hier werden nur die Daten geladen. Dauert ziemlich lange ;-)
        list.AddRange(HoleDatenAusDateiOderInternetOderSonstWoHer());
        s.UpdateTask(n, Resources.bullet_green, "Daten geladen.");
        s.Progress += 50;
    }, JobMode.Synchronous, Guid.Empty);

    // Hier sind wir in BG1
    spl.RunJobs();

    spl.Progress = 100;
    spl.Status = "Fertig...";
    Thread.Sleep(1000);
});

// Hier sind wir wieder im GUI-Thread
// Hier wird die MainForm erstellt und die Daten werden an die MainForm übergeben.
Application.Run(new MainForm(list));

Zusammenfassend: **:::

Hoffe, ich konnte dir helfen.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi @ All.

Ich habe für das Problem (siehe vorherigen Post) jetzt eine Methode Object InvokeOnGuiThread(Delegate, Object[]) hinzugefügt. Diese führt den übergebenen Delegaten auf dem GUI-Thread aus.

Damit kann man auch GUI-Elemente im SplashScreen erzeugen.
**:::

Beispiel:

MainForm mainForm = null;

SplashScreen.ShowSplash((spl) =>
{
    spl.EnqueueJob(delegate(SplashScreen s)
    {
        int n = s.AddTask(Resources.bullet_go, "Anwendung wird initialisiert.");
        s.InvokeOnGUIThread(new MethodInvoker(() =>
        {
            // Hier sind wir jetzt im GUI-Thread.
            // Hier kann man also Gui-Elemente gefahrlos erzeugen.
            // Nachteil: Der SplashScreen ist blockiert und reagiert nicht mehr.
            mainForm = new MainForm();
        }));
        s.UpdateTask(n, Resources.bullet_green, "Anwendung wurde geladen.");
        s.Progress += 50;
    }, JobMode.Synchronous, Guid.Empty);

    spl.RunJobs();

    spl.Progress = 100;
    spl.Status = "Fertig...";
    Thread.Sleep(1000);
});

Application.Run(mainForm);

Neue Sources oben und auf meiner WebSite.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
906 Beiträge seit 2005
vor 14 Jahren

Hi, mir wurde gesagt, dass unter XP deine Komponente ein kleines Problem hat. Die Bereiche in denen die benutzerdefinierten Texte angezeigt werden sind nach dem Anzeigen des Splash-Screens kurz schwarz. Da ich nur Vista nutze, kann ich das nicht überprüfen. Kannst du mal nachsehen?

TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi, MagicAndre1981.

Auch das konnte ich reproduzieren (leider :evil: ).
Ich werde mich sobald es geht darum kümmern, allerdings geht es im Moment nicht so schnell. Habe an der Arbeit viel zu tun...

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
906 Beiträge seit 2005
vor 14 Jahren

Hi,

das ist kein Problem, ist ja nur ein kleines optisches Problemchen. So schlimm wird das Flackern ja nicht sein.

André

906 Beiträge seit 2005
vor 14 Jahren

ich wollte mal ganz vorsichtig nachfragen ob du das Flackern beseitigen konntest.

TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 14 Jahren

Hi, MagicAndre1981.

Ich bin dem Flackern auf den Grund gegangen. Es liegt an der Kombination BackgroundImage + Controls darüber. Ich habe dann versucht, die SplashScreenForm komplett selbst zu zeichnen, aber das Flackern war trotz ControlStyles.OptimizedDoubleBuffer nicht ganz weg und die Transparenz funktionierte unter XP trotz ControlStyles.SupportsTransparentBackColor nicht mehr.

Deswegen und weil ich zur Zeit keine Zeit für das SplashScreen-Projekt habe und da man ja (falls das Flackern wirklich so extrem ist und stört) eine eigene Form als SplashScreen benutzen kann, habe ich mich dazu entschlossen, das Problem vorerst nicht zu beseitigen.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
906 Beiträge seit 2005
vor 14 Jahren

Hallo,

ok, und danke für die Erklärung 👍