Question

Je souhaite qu'un écran de démarrage s'affiche pendant le chargement de l'application.J'ai un formulaire auquel est lié un contrôle de la barre d'état système.Je souhaite que l'écran de démarrage s'affiche pendant le chargement de ce formulaire, ce qui prend un peu de temps car il accède à une API de service Web pour remplir certaines listes déroulantes.Je souhaite également effectuer quelques tests de base pour les dépendances avant le chargement (c'est-à-dire que le service Web est disponible, le fichier de configuration est lisible).Au fur et à mesure de chaque phase du démarrage, je souhaite mettre à jour l'écran de démarrage avec la progression.

J'ai beaucoup lu sur le threading, mais je ne sais plus d'où cela devrait être contrôlé (le main() méthode?).Il me manque aussi comment Application.Run() fonctionne, est-ce à partir de là que les fils de discussion doivent être créés ?Maintenant, si le formulaire avec le contrôle de la barre d’état système est le formulaire « vivant », le splash devrait-il provenir de là ?De toute façon, cela ne se chargerait-il pas tant que le formulaire ne serait pas rempli ?

Je ne cherche pas un document de code, mais plutôt un algorithme/une approche pour pouvoir comprendre cela une fois pour toutes :)

Était-ce utile?

La solution

Eh bien, pour une application ClickOnce que j'ai déployée dans le passé, nous avons utilisé le Microsoft.VisualBasic espace de noms pour gérer le thread de l’écran de démarrage.Vous pouvez référencer et utiliser le Microsoft.VisualBasic assembly à partir de C# dans .NET 2.0 et il fournit de nombreux services intéressants.

  1. Faire en sorte que le formulaire principal hérite de Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. Remplacez la méthode "OnCreateSplashScreen" comme ceci :

    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

Très simple, il affiche votre SplashForm (que vous devez créer) pendant le chargement, puis le ferme automatiquement une fois le chargement du formulaire principal terminé.

Cela simplifie vraiment les choses, et le VisualBasic.WindowsFormsApplicationBase est bien sûr bien testé par Microsoft et possède de nombreuses fonctionnalités qui peuvent vous faciliter la vie dans Winforms, même dans une application 100% C#.

En fin de compte, c'est tout IL et bytecode de toute façon, alors pourquoi ne pas l'utiliser ?

Autres conseils

L'astuce consiste à créer un thread séparé responsable de l'affichage de l'écran de démarrage.
Lorsque vous exécutez votre application .net, elle crée le thread principal et charge le formulaire spécifié (principal).Pour masquer le travail acharné, vous pouvez masquer le formulaire principal jusqu'à ce que le chargement soit terminé.

En supposant que Form1 - est votre formulaire principal et que SplashForm est de niveau supérieur, ce formulaire de démarrage est agréable :

private void Form1_Load(object sender, EventArgs e)
{
    Hide();
    bool done = false;
    ThreadPool.QueueUserWorkItem((x) =>
    {
        using (var splashForm = new SplashForm())
        {
            splashForm.Show();
            while (!done)
                Application.DoEvents();
            splashForm.Close();
        }
    });

    Thread.Sleep(3000); // Emulate hardwork
    done = true;
    Show();
}

Après avoir cherché des solutions partout sur Google et SO, voici ma préférée :http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs :

public partial class FormSplash : Form
{
    private static Thread _splashThread;
    private static FormSplash _splashForm;

    public FormSplash() {
        InitializeComponent();
    }

    /// <summary>
    /// Show the Splash Screen (Loading...)
    /// </summary>
    public static void ShowSplash()
    {
        if (_splashThread == null)
        {
            // show the form in a new thread
            _splashThread = new Thread(new ThreadStart(DoShowSplash));
            _splashThread.IsBackground = true;
            _splashThread.Start();
        }
    }

    // called by the thread
    private static void DoShowSplash()
    {
        if (_splashForm == null)
            _splashForm = new FormSplash();

        // create a new message pump on this thread (started from ShowSplash)
        Application.Run(_splashForm);
    }

    /// <summary>
    /// Close the splash (Loading...) screen
    /// </summary>
    public static void CloseSplash()
    {
        // need to call on the thread that launched this splash
        if (_splashForm.InvokeRequired)
            _splashForm.Invoke(new MethodInvoker(CloseSplash));

        else
            Application.ExitThread();
    }
}

Programme.cs :

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // splash screen, which is terminated in FormMain
        FormSplash.ShowSplash();

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        // this is probably where your heavy lifting is:
        Application.Run(new FormMain());
    }
}

FormMain.cs

    ...

    public FormMain()
    {
        InitializeComponent();            

        // bunch of database access, form loading, etc
        // this is where you could do the heavy lifting of "loading" the app
        PullDataFromDatabase();
        DoLoadingWork();            

        // ready to go, now close the splash
        FormSplash.CloseSplash();
    }

J'ai eu des problèmes avec le Microsoft.VisualBasic solution -- La recherche a fonctionné sur XP, mais sur Windows 2003 Terminal Server, le formulaire de candidature principal apparaîtrait (après l'écran de démarrage) en arrière-plan et la barre des tâches clignoterait.Et mettre une fenêtre au premier plan/se concentrer dans le code est une toute autre boîte de Pandore pour laquelle vous pouvez utiliser Google/SO.

C'est une vieille question, mais je la rencontrais sans cesse en essayant de trouver une solution d'écran de démarrage threadé pour WPF qui pourrait inclure une animation.

Voici ce que j'ai finalement reconstitué :

Application.XAML :

<Application Startup="ApplicationStart" …

App.XAML.cs :

void ApplicationStart(object sender, StartupEventArgs e)
{
        var thread = new Thread(() =>
        {
            Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
            Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        // call synchronous configuration process
        // and declare/get reference to "main form"

        thread.Abort();

        mainForm.Show();
        mainForm.Activate();
  }

Je recommande d'appeler Activate(); directement après le dernier Show(); dans la réponse fournie par aku.

Citant MSDN :

L'activation d'un formulaire l'amène à l'avant s'il s'agit de l'application active, ou il clignote la légende de la fenêtre s'il ne s'agit pas de l'application active.Le formulaire doit être visible pour que cette méthode ait un effet.

Si vous n'activez pas votre formulaire principal, il peut s'afficher derrière toute autre fenêtre ouverte, ce qui donne un aspect un peu ridicule.

Je pense qu'en utilisant une méthode comme celui d'aku ou Les gars est la voie à suivre, mais quelques choses à retenir des exemples spécifiques :

  1. Le principe de base serait d'afficher votre splash sur un fil de discussion séparé dès que possible.C'est la façon dont je me pencherais, similaire à ce qu'illustre Aku, puisque c'est la façon dont je suis le plus familier.Je n'étais pas au courant de la fonction VB mentionnée par Guy.Et même si c'était un VB bibliothèque, il a raison - c'est tout IL à la fin.Donc, même si ça semble sale ce n'est pas si mal que ça !:) Je pense que vous voudrez être sûr que soit VB fournit un fil de discussion séparé pour ce remplacement, soit que vous en créez un vous-même - recherchez-le certainement.

  2. En supposant que vous créiez un autre fil de discussion pour afficher ce splash, vous devrez faire attention aux mises à jour de l'interface utilisateur entre threads.J'en parle parce que vous avez mentionné les progrès de la mise à jour.Fondamentalement, pour être sûr, vous devez appeler une fonction de mise à jour (que vous créez) sur le formulaire de démarrage à l'aide d'un délégué.Vous transmettez ce délégué au Invoquer fonction sur l'objet de formulaire de votre écran de démarrage.En fait, si vous appelez directement le formulaire de démarrage pour mettre à jour les éléments de progression/interface utilisateur, vous obtiendrez une exception à condition que vous exécutiez sur le CLR .Net 2.0.En règle générale, tout élément d'interface utilisateur d'un formulaire doit être mis à jour par le thread qui l'a créé - c'est ce que garantit Form.Invoke.

Enfin, j'opterais probablement pour créer le splash (si je n'utilise pas la surcharge VB) dans la méthode principale de votre code.Pour moi, c'est mieux que de laisser la forme principale effectuer la création de l'objet et d'y être si étroitement liée.Si vous adoptez cette approche, je suggère de créer une interface simple que l'écran de démarrage implémente - quelque chose comme IStartupProgressListener - qui reçoit les mises à jour de la progression du démarrage via une fonction membre.Cela vous permettra d'échanger facilement l'une ou l'autre classe selon vos besoins et de découpler joliment le code.Le formulaire de démarrage peut également savoir quand se fermer si vous le notifiez lorsque le démarrage est terminé.

Un moyen simple consiste à utiliser quelque chose comme ceci comme main() :

<STAThread()> Public Shared Sub Main()

    splash = New frmSplash
    splash.Show()

    ' Your startup code goes here...

    UpdateSplashAndLogMessage("Startup part 1 done...")

    ' ... and more as needed...

    splash.Hide()
    Application.Run(myMainForm)
End Sub

Lorsque le .NET CLR démarre votre application, il crée un thread « principal » et commence à exécuter votre main() sur ce thread.Le Application.Run(myMainForm) à la fin fait deux choses :

  1. Démarre la « pompe à messages » Windows, en utilisant le thread qui a exécuté main() comme thread GUI.
  2. Désigne votre « formulaire principal » comme « formulaire d’arrêt » pour l’application.Si l'utilisateur ferme ce formulaire, Application.Run() se termine et le contrôle revient à votre main(), où vous pouvez effectuer l'arrêt de votre choix.

Il n'est pas nécessaire de générer un thread pour gérer la fenêtre de démarrage, et en fait c'est une mauvaise idée, car vous devrez alors utiliser des techniques thread-safe pour mettre à jour le contenu du splash à partir de main().

Si vous avez besoin d'autres threads pour effectuer des opérations en arrière-plan dans votre application, vous pouvez les générer à partir de main().N'oubliez pas de définir Thread.IsBackground sur True, afin qu'ils meurent à la fin du thread principal/GUI.Sinon, vous devrez prendre des dispositions pour mettre fin à tous vos autres threads vous-même, ou ils garderont votre application en vie (mais sans interface graphique) lorsque le thread principal se terminera.

J'ai publié un article sur l'incorporation d'un écran de démarrage dans l'application sur codeproject.Il est multithread et pourrait vous intéresser

Encore un autre écran de démarrage en C#

private void MainForm_Load(object sender, EventArgs e)
{
     FormSplash splash = new FormSplash();
     splash.Show();
     splash.Update();
     System.Threading.Thread.Sleep(3000);
     splash.Hide();
}

J'ai obtenu ceci quelque part sur Internet, mais je n'arrive pas à le retrouver.Simple mais pourtant efficace.

J'aime beaucoup la réponse d'Aku, mais le code est pour C# 3.0 et versions ultérieures car il utilise une fonction lambda.Pour les personnes ayant besoin d'utiliser le code en C# 2.0, voici le code utilisant un délégué anonyme au lieu de la fonction lambda.Vous avez besoin d'un formulaire Winform le plus élevé appelé formSplash avec FormBorderStyle = None.Le TopMost = True Le paramètre du formulaire est important car l'écran de démarrage peut sembler apparaître puis disparaître rapidement s'il n'est pas le plus haut.je choisis aussi StartPosition=CenterScreen cela ressemble donc à ce qu'une application professionnelle ferait avec un écran de démarrage.Si vous souhaitez un effet encore plus cool, vous pouvez utiliser le TrasparencyKey propriété de créer un écran de démarrage de forme irrégulière.

private void formMain_Load(object sender, EventArgs e)
  {

     Hide();
     bool done = false;
     ThreadPool.QueueUserWorkItem(delegate
     {
       using (formSplash splashForm = new formSplash())
       {
           splashForm.Show();
           while (!done)
              Application.DoEvents();
           splashForm.Close();
       }
     }, null);

     Thread.Sleep(2000);
     done = true;
     Show();
  }

Je ne suis pas d'accord avec les autres réponses recommandant WindowsFormsApplicationBase.D'après mon expérience, cela peut ralentir votre application.Pour être précis, pendant qu'il exécute le constructeur de votre formulaire en parallèle avec l'écran de démarrage, il reporte l'événement Shown de votre formulaire.

Considérons une application (sans écran de démarrage) avec un constructeur qui prend 1 seconde et un gestionnaire d'événements sur Shown qui prend 2 secondes.Cette application est utilisable après 3 secondes.

Mais supposons que vous installiez un écran de démarrage en utilisant WindowsFormsApplicationBase.Tu pourrais penser MinimumSplashScreenDisplayTime de 3 secondes est raisonnable et ne ralentira pas votre application.Mais essayez-le, votre application prendra désormais 5 secondes à charger.


class App : WindowsFormsApplicationBase
{
    protected override void OnCreateSplashScreen()
    {
        this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
        this.SplashScreen = new Splash();
    }

    protected override void OnCreateMainForm()
    {
        this.MainForm = new Form1();
    }
}

et

public Form1()
{
    InitializeComponent();
    Shown += Form1_Shown;
    Thread.Sleep(TimeSpan.FromSeconds(1));
}

void Form1_Shown(object sender, EventArgs e)
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
    Program.watch.Stop();
    this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}

Conclusion:ne pas utiliser WindowsFormsApplicationBase si votre application a un gestionnaire sur l'événement Slown.Vous pouvez écrire un meilleur code qui exécute le splash en parallèle au constructeur et à l'événement Shown.

En fait, la lecture multiple n'est pas nécessaire ici.

Laissez votre logique métier générer un événement chaque fois que vous souhaitez mettre à jour l'écran de démarrage.

Laissez ensuite votre formulaire mettre à jour l’écran de démarrage en conséquence dans la méthode liée au gestionnaire d’événements.

Pour différencier les mises à jour, vous pouvez soit déclencher différents événements, soit fournir des données dans une classe héritée de EventArgs.

De cette façon, vous pouvez avoir un écran de démarrage changeant sans aucun problème de multithreading.

En fait, avec cela, vous pouvez même prendre en charge, par exemple, une image GIF sur un formulaire de démarrage.Pour que cela fonctionne, appelez Application.DoEvents() dans votre handler :

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top