Comment exécuter du code après le chargement d'un formulaire?

126

Dans .NET, Windows Forms a un événement qui se déclenche avant le chargement du formulaire (Form.Load), mais il n'y a aucun événement correspondant qui est déclenché APRÈS le chargement du formulaire. Je voudrais exécuter une logique après le chargement du formulaire.

Quelqu'un peut-il conseiller sur une solution?

adeel825
la source
Bien que cette question ait de très bonnes réponses, il pourrait être utile de la mentionner: docs.microsoft.com/en-us/dotnet/framework/winforms
...

Réponses:

192

Vous pouvez utiliser l'événement " Shown ": MSDN - Form.Shown

"L'événement Shown n'est déclenché que la première fois qu'un formulaire est affiché; par la suite, réduire, maximiser, restaurer, masquer, afficher ou invalider et repeindre ne déclenchera pas cet événement."

Matthias Schippling
la source
10
Pour moi, il semble que le gestionnaire indiqué est exécuté PENDANT le chargement du formulaire ... ai-je tort?
ckonig le
3
Vieux mais or ... Oui, vous vous trompez. L'interface graphique ne peut pas exécuter des tâches parallèles, ce qui est important de faire quelque chose PENDANT qu'une autre exécution est effectuée.
Dennis Ziolkowski
2
Si le gestionnaire d'événements Load contient un code qui appelle Application.DoEvents (), l'événement Shown se déclenche avant que les gestionnaires d'événements Load aient terminé leur exécution. Cela est dû au fait que l'événement Shown est en fait placé dans une file d'attente de messages à l'aide de Form.BeginInvoke (ShownEvent) et DoEvents () le force à se déclencher avant la fin du chargement.
Artemix
1
Ce n'était pas assez de ne pas fonctionner pour moi, en C #. J'ai dû ajouter Shown += Form1_Shown;comme suggéré dans un autre fil
ocramot
11
vous devez ajouter This.Refresh (); à l'intérieur de l'événement Shown avant votre logique et il contiendra et actualisera le formulaire pour qu'il soit complètement chargé avant que votre logique ne commence à s'exécuter
Aylian Craspa
49

J'utilise parfois (en charge)

this.BeginInvoke((MethodInvoker) delegate {
  // some code
});

ou

this.BeginInvoke((MethodInvoker) this.SomeMethod);

(remplacez "this" par votre variable de formulaire si vous gérez l'événement sur une instance autre que "this").

Cela pousse l'appel sur la boucle windows-forms, de sorte qu'il est traité lorsque le formulaire traite la file d'attente de messages.

[mis à jour sur demande]

Les méthodes Control.Invoke / Control.BeginInvoke sont destinées à être utilisées avec le threading et sont un mécanisme pour pousser le travail sur le thread d'interface utilisateur. Normalement, cela est utilisé par les threads de travail, etc. Control.Invoke effectue un appel synchrone, alors que Control.BeginInvoke effectue un appel asynchrone.

Normalement, ceux-ci seraient utilisés comme:

SomeCodeOrEventHandlerOnAWorkerThread()
{
  // this code running on a worker thread...
  string newText = ExpensiveMethod(); // perhaps a DB/web call

  // now ask the UI thread to update itself
  this.Invoke((MethodInvoker) delegate {
      // this code runs on the UI thread!
      this.Text = newText;
  });
}

Il le fait en poussant un message dans la file d'attente de messages Windows; le thread de l'interface utilisateur (à un moment donné) met le message en file d'attente, traite le délégué et signale au travailleur qu'il s'est terminé ... jusqu'à présent tout va bien ;-p

D'ACCORD; alors que se passe-t-il si nous utilisons Control.Invoke / Control.BeginInvoke sur le thread d'interface utilisateur? Il fait face ... si vous appelez Control.Invoke, il est assez judicieux de savoir que le blocage de la file d'attente de messages entraînerait un blocage immédiat - donc si vous êtes déjà sur le thread de l'interface utilisateur, il exécute simplement le code immédiatement ... de sorte que ne nous aide pas ...

Mais Control.BeginInvoke fonctionne différemment: il pousse toujours le travail dans la file d'attente, même si nous sommes déjà sur le thread de l'interface utilisateur. Cela fait une façon vraiment simple de dire "dans un instant", mais sans les inconvénients des minuteries, etc. (qui devraient quand même faire la même chose de toute façon!).

Marc Gravell
la source
1
Je n'ai pas complètement compris celui-là. Pouvez-vous expliquer un peu plus?
Torbjørn
Salut Marc, est-il possible de rendre le formulaire réactif pendant que le processus se termine qui est appelé dans BeginInvoke ??
huMpty duMpty
qu'est-ce que c'est équivalent dans WPF?
mrid
6

La première fois qu'il ne démarrera PAS "AfterLoading",
il l'enregistrera simplement pour démarrer le chargement suivant.

private void Main_Load(object sender, System.EventArgs e)
{
    //Register it to Start in Load 
    //Starting from the Next time.
    this.Activated += AfterLoading;
}

private void AfterLoading(object sender, EventArgs e)
{
    this.Activated -= AfterLoading;
    //Write your code here.
}
Ahmed Sabry
la source
5

J'ai eu le même problème et je l'ai résolu comme suit:

En fait, je veux afficher le message et le fermer automatiquement après 2 secondes. Pour cela, j'ai dû générer un formulaire simple (dynamiquement) et une étiquette affichant le message, arrêter le message pendant 1500 ms pour que l'utilisateur le lise. Et fermez le formulaire créé dynamiquement. L'événement affiché se produit après l'événement de chargement. Donc, le code est

Form MessageForm = new Form();
MessageForm.Shown += (s, e1) => { 
    Thread t = new Thread(() => Thread.Sleep(1500)); 
    t.Start(); 
    t.Join(); 
    MessageForm.Close(); 
};
Sharmila
la source
2

Vous pouvez également essayer de placer votre code dans l'événement Activated du formulaire, si vous souhaitez qu'il se produise, juste lorsque le formulaire est activé. Vous auriez besoin de mettre une vérification booléenne «a exécuté» si elle n'est censée s'exécuter qu'à la première activation.

Vendeurs Mitchel
la source
1

C'est une vieille question et dépend davantage du moment où vous devez commencer vos routines. Puisque personne ne veut d'exception de référence nulle, il est toujours préférable de vérifier d'abord la valeur null, puis de l'utiliser au besoin; cela seul peut vous épargner beaucoup de chagrin.

La raison la plus courante de ce type de question est lorsqu'un conteneur ou un type de contrôle personnalisé tente d'accéder à des propriétés initialisées en dehors d'une classe personnalisée où ces propriétés n'ont pas encore été initialisées, ce qui peut entraîner le remplissage de valeurs nulles et peut même provoquer des exceptions de référence nulles sur les types d'objets. Cela signifie que votre classe est en cours d'exécution avant d'être complètement initialisée - avant que vous ayez fini de définir vos propriétés, etc. Une autre raison possible pour ce type de question est de savoir quand effectuer des graphiques personnalisés.

Pour mieux répondre à la question de savoir quand commencer à exécuter le code après l'événement de chargement de formulaire, il faut surveiller le message WM_Paint ou se connecter directement à l'événement de peinture lui-même. Pourquoi? L'événement paint se déclenche uniquement lorsque tous les modules sont complètement chargés par rapport à votre événement de chargement de formulaire. Remarque: This.visible == true n'est pas toujours vrai lorsqu'il est défini sur true, il n'est donc pas du tout utilisé à cette fin, sauf pour masquer un formulaire.

Voici un exemple complet de la façon de commencer à exécuter votre code après l'événement de chargement de formulaire. Il est recommandé de ne pas lier inutilement la boucle du message de peinture afin que nous créions un événement qui commencera à exécuter votre code en dehors de cette boucle.

using System.Windows.Forms;

namespace MyProgramStartingPlaceExample {

/// <summary>
/// Main UI form object
/// </summary>
public class Form1 : Form
{

    /// <summary>
    /// Main form load event handler
    /// </summary>
    public Form1()
    {
        // Initialize ONLY. Setup your controls and form parameters here. Custom controls should wait for "FormReady" before starting up too.
        this.Text = "My Program title before form loaded";
        // Size need to see text. lol
        this.Width = 420;

        // Setup the sub or fucntion that will handle your "start up" routine
        this.StartUpEvent += StartUPRoutine;

        // Optional: Custom control simulation startup sequence:
        // Define your class or control in variable. ie. var MyControlClass new CustomControl;
        // Setup your parameters only. ie. CustomControl.size = new size(420, 966); Do not validate during initialization wait until "FormReady" is set to avoid possible null values etc. 
        // Inside your control or class have a property and assign it as bool FormReady - do not validate anything until it is true and you'll be good! 
    }

    /// <summary>
    /// The main entry point for the application which sets security permissions when set.
    /// </summary>
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }


    #region "WM_Paint event hooking with StartUpEvent"            
    //
    // Create a delegate for our "StartUpEvent"
    public delegate void StartUpHandler();
    //
    // Create our event handle "StartUpEvent"
    public event StartUpHandler StartUpEvent;
    //
    // Our FormReady will only be set once just he way we intendded
    // Since it is a global variable we can poll it else where as well to determine if we should begin code execution !!
    bool FormReady;
    //
    // The WM_Paint message handler: Used mostly to paint nice things to controls and screen
    protected override void OnPaint(PaintEventArgs e)
    {
        // Check if Form is ready for our code ?
        if (FormReady == false) // Place a break point here to see the initialized version of the title on the form window
        {
            // We only want this to occur once for our purpose here.
            FormReady = true;
            //
            // Fire the start up event which then will call our "StartUPRoutine" below.
            StartUpEvent();
        }
        //
        // Always call base methods unless overriding the entire fucntion
        base.OnPaint(e);
    }
    #endregion


    #region "Your StartUp event Entry point"
    //
    // Begin executuing your code here to validate properties etc. and to run your program. Enjoy!
    // Entry point is just following the very first WM_Paint message - an ideal starting place following form load
    void StartUPRoutine()
    {
        // Replace the initialized text with the following
        this.Text = "Your Code has executed after the form's load event";
        //
        // Anyway this is the momment when the form is fully loaded and ready to go - you can also use these methods for your classes to synchronize excecution using easy modifications yet here is a good starting point. 
        // Option: Set FormReady to your controls manulaly ie. CustomControl.FormReady = true; or subscribe to the StartUpEvent event inside your class and use that as your entry point for validating and unleashing its code.
        //
        // Many options: The rest is up to you!
    }
    #endregion

}

}

Jamie
la source
Cela semble incroyablement long et présente-t-il des avantages par rapport à la simple capture de l'événement Shown?
Steve Smith le
0

Je sais que c'est un ancien message. Mais voici comment je l'ai fait:

    public Form1(string myFile)
    {
        InitializeComponent();
        this.Show();
        if (myFile != null)
        {
            OpenFile(myFile);
        }
    }

    private void OpenFile(string myFile = null)
    {
            MessageBox.Show(myFile);
    }
J. Fischlein
la source
-9

Vous pouvez fermer votre formulaire après une certaine exécution.

//YourForm.ActiveForm.Close ();

    LoadingForm.ActiveForm.Close();
Codeur E
la source