IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Dotnet Discussion :

Interface graphique et BackgroundWorker


Sujet :

Dotnet

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite Avatar de ctxnop
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2007
    Messages
    858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2007
    Messages : 858
    Par défaut Interface graphique et BackgroundWorker
    Bien le bonjour,
    C'est un sujet qui à été maintes et maintes fois discutés, mais j'aimerai quand même en reparler un peu.
    je suis contraint, par la hiérarchie (et non pas techniquement), d'essayer de trouver un moyen de faire l'instanciation de fenêtres dans un thread en arrière plan. En fait, le constat est que la méthode "InitializeComponent" générée par le designer peut être assez longue. En conséquence on souhaite qu'elle se fasse dans un thread d'arrière plan pour ne pas figer l'interface graphique.

    Un BackgroundWorker donc.

    J'entends déjà "Oula mais non on ne peut pas, tout être fait dans le thread de l'interface graphique".

    Mais si on peut le faire. En réalité, ce qui empêche de le faire c'est le fait que les BackgroundWorker fonctionnent avec un ThreadPool, et les threads contenus sont forcément des MTA, alors qu'il faut que l'instance soit créée sur un thread STA. Mais si on utilise un thread créé manuellement, en spécifiant qu'il est en STA, on arrive à instancier un formulaire (et donc appeler le InitializeComponents).

    Mon problème se pose alors dans le dispose du formulaire et non plus dans la création.
    En effet, certains composants (pas tous) se plaignent que le dispose est fait dans un thread autre que celui qui à créé le composant.
    Une idée de comment palier à ce problème ?
    Il est évident que le thread qui à servi à créé le formulaire n'existe plus depuis longtemps lorsque le dispose est fait.

    En l'occurence, le contrôle qui aime pas pour le moment c'est un RichTextControl. Mais si je prend un formulaire qui ne contient que 2 boutons et un textbox, pas de problème.

    Donc voila, si quelqu'un à une idée de comment résoudre ce problème, ou à défaut une explication détaillée de pourquoi ce n'est pas possible, etc...

  2. #2
    Membre émérite Avatar de worm83
    Homme Profil pro
    Architecte logiciel
    Inscrit en
    Février 2010
    Messages
    459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Architecte logiciel
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2010
    Messages : 459
    Par défaut
    Salut,

    As tu essayé de surcharger la méthode dispose pour republier le base.Dispose() dans le Thread qui à créer le composant ?

  3. #3
    Membre émérite Avatar de ctxnop
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2007
    Messages
    858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2007
    Messages : 858
    Par défaut
    Bonjour et merci de t'intéresser au problème.

    J'ai deux problème avec ton conseil
    - Le principe est qu'on veut continuer à utiliser le Designer de Visual Studio, donc si on doit reprendre le code à chaque formulaire, c'est pas maintenable. On ne peut donc agir que sur les appels externe.
    Pour l'exemple, avant, quand l'utilisateur cliquais sur le bouton d'une interface et que ce bouton ouvrait une autre fenêtre, j'écrivais :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    private void Button1_Click(object sender, EventArgs e)
    {
        MonFormulaire frm = new MonFormulaire();
        frm.Show();
    }

    Maintenant j'écris
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    private void Button1_Click(object sender, EventArgs e)
    {
        FormLauncher<MonFormulaire> launcher = new FormLauncher<MonFormulaire>();
        launcher.Show();
    }

    - Mon second problème est que, comme expliqué dans le premier post, le thread qui à créé l'interface est un thread créé pour l'occasion, il n'a fait que cette création. Une fois la création finie, alors le thread est terminé et n'existe plus. Je ne peux donc pas le retrouver pour le dispose.

  4. #4
    Membre émérite Avatar de ctxnop
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2007
    Messages
    858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2007
    Messages : 858
    Par défaut
    Au cas où ça pourrait être utile pour quelqu'un, voici comment je fais pour lancer la création d'une fenêtre dans un thread séparé, et donc également comment je fais pour avoir un BackgroundWorker en STA.

    Attention cependant, c'est un code tapé à la va-vite dans la matinée, il n'est pas intégralement testé et peut souffrir de divers problèmes (d'ailleurs, n'hésitez pas à critiquer, les améliorations sont toujours bonnes à prendre).

    En parlant de problème, il y en a un qui est déjà identifié. Dans le "Main" d'un programme, le SynchronizationContext n'est pas encore définit. En conséquence, les évènements completed et progress s'exécutent dans le thread du BackgroundWorker et non dans le thread qui l'a créé. J'aimerai bien corriger ça mais je n'ai pas trouvé.

    Le code du STABackgroundWorker :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    #region Références
    using System;
    using System.ComponentModel;
    using System.Threading;
    #endregion
     
    /// <summary>
    /// Représente un BackgroundWorker qui fonctionne dans un thread STA.
    /// </summary>
    public class STABackgroundWorker
    	: Component
    {
    	#region Evènements
    	/// <summary>
    	/// Cet evènement se produit lorsque le thread d'arrière plan démarre, placez votre code de traitement ici.
    	/// Handler exécuté dans le thread d'arrière plan.
    	/// </summary>
    	public event DoWorkEventHandler DoWork;
     
    	/// <summary>
    	/// Cet évènement se produit lorsque le thread d'arrière plan notifie de sa progression.
    	/// Handler exécuté dans le thread d'interface graphique.
    	/// </summary>
    	public event ProgressChangedEventHandler ProgressChanged;
     
    	/// <summary>
    	/// Cet évènement se produit lorsque le thread d'arrière plan est terminé.
    	/// Handler exécuté dans le thread d'arrière plan
    	/// </summary>
    	public event RunWorkerCompletedEventHandler RunWorkerCompleted;
    	#endregion
     
    	#region Variables
    	private bool					_IsRunning;
    	private bool					_CanCancel;
    	private bool					_ReportProgress;
    	private int						_Progress;
    	private Thread					_Thread;
     
    	private SynchronizationContext	_SyncContext;
    	#endregion
     
    	#region Propriétés
    	/// <summary>
    	/// Obtient une valeur indiquant si le thread est en cours d'exécution ou non.
    	/// </summary>
    	public bool IsBusy
    	{
    		get { return _IsRunning; }
    	}
     
    	/// <summary>
    	/// Obtient ou définit une valeur indiquant si le thread notifie de l'avancement.
    	/// </summary>
    	public bool WorkerReportsProgress
    	{
    		get { return _ReportProgress; }
    		set { _ReportProgress = value; }
    	}
     
    	/// <summary>
    	/// Obtient ou définit une valeur indiquant si le thread supporte l'annulation.
    	/// </summary>
    	public bool WorkerSupportsCancellation
    	{
    		get { return _CanCancel; }
    		set { _CanCancel = value; }
    	}
    	#endregion
     
    	#region Constructeur
    	/// <summary>
    	/// Construit un STABackgroundWorker.
    	/// </summary>
    	public STABackgroundWorker()
    	{
    		Reset();
    		_CanCancel		= true;
    		_ReportProgress	= true;
    		_SyncContext	= SynchronizationContext.Current ?? new SynchronizationContext();
    	}
    	#endregion
     
    	#region Méthodes
    	/// <summary>
    	/// Ré-initialise les variables locales.
    	/// </summary>
    	private void Reset()
    	{
    		_IsRunning				= false;
    		_Progress				= 0;
    		_Thread					= null;
    	}
     
    	/// <summary>
    	/// Démarre le traitement.
    	/// </summary>
    	public void RunWorkerAsync()
    	{
    		RunWorkerAsync(null);
    	}
     
    	/// <summary>
    	/// Démarre le traitement.
    	/// </summary>
    	/// <param name="argument">Argument passé au traitement</param>
    	public void RunWorkerAsync(object argument)
    	{
    		if (_IsRunning) throw new InvalidOperationException("Le BackgroundWorker est déjà en cours d'exécution.");
    		Reset();
     
    		_Thread = new Thread(ThreadStart);
    		_Thread.SetApartmentState(ApartmentState.STA);
    		_Thread.Start();
    	}
     
    	/// <summary>
    	/// Point d'entré du thread.
    	/// </summary>
    	/// <param name="argument">Argument du thread.</param>
    	private void ThreadStart(object argument)
    	{
    		bool cancelled = false;
    		Exception error = null;
    		DoWorkEventArgs e = new DoWorkEventArgs(argument);
     
    		try
    		{
    			if(DoWork != null) DoWork(this, e);
    		}
    		catch(ThreadAbortException)
    		{
    			cancelled = true;
    		}
    		catch (Exception ex)
    		{
    			error = ex;
    		}
     
    		_SyncContext.Send(ThreadCompleted, new Tuple<Object, Exception, bool>(e.Result, error, cancelled || e.Cancel));
    	}
     
    	/// <summary>
    	/// Cette méthode est appelée lorsque le thread est terminé.
    	/// </summary>
    	/// <param name="result">Objet résultat du traitement.</param>
    	/// <param name="error">Erreur éventuelle. <c>null</c> si pas d'erreur.</param>
    	/// <param name="cancel">Indique si le thread à été annulé ou non.</param>
    	private void ThreadCompleted(object args)
    	{
    		if (RunWorkerCompleted != null)
    		{
    			Tuple<object, Exception, bool> r = args as Tuple<object, Exception, bool>;
    			RunWorkerCompletedEventArgs e = new RunWorkerCompletedEventArgs(r.Item1, r.Item2, r.Item3);
    			RunWorkerCompleted(this, e);
    		}
    	}
     
    	/// <summary>
    	/// Annule le traitement en cours.
    	/// </summary>
    	public void CancelAsync()
    	{
    		if(!_CanCancel) throw new InvalidOperationException("Ce traitement ne supporte pas l'annulation.");
    		if (_Thread != null && _IsRunning) _Thread.Abort();
    	}
     
    	/// <summary>
    	/// Délégué pour rapporter la progression.
    	/// </summary>
    	/// <param name="args">Argument de l'évènement.</param>
    	private void OnProgressChanged(object args)
    	{
    		if (ProgressChanged != null)
    			ProgressChanged(this, args as ProgressChangedEventArgs);
    	}
     
    	/// <summary>
    	/// Notifier de la progession.
    	/// </summary>
    	/// <param name="percentProgress">Pourcentage de progression.</param>
    	public void ReportProgress(int percentProgress)
    	{
    		ReportProgress(percentProgress, null);
    	}
     
    	/// <summary>
    	/// Notifier de la pogression.
    	/// </summary>
    	/// <param name="percentProgress">Pourcentage de progression.</param>
    	/// <param name="userState">Objet utilisateur.</param>
    	public void ReportProgress(int percentProgress, object userState)
    	{
    		if (!WorkerReportsProgress)
    			throw new InvalidOperationException("Ce BackgroundWorker ne permet pas de notifier de la progression.");
     
    		_Progress = percentProgress;
    		if (ProgressChanged != null)
    		{
    			ProgressChangedEventArgs e = new ProgressChangedEventArgs(_Progress, userState);
    			_SyncContext.Send(OnProgressChanged, e);
    		}
    	}
    	#endregion
    }

    Et maintenant celui du FormLauncher :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    #region Références
    using System.ComponentModel;
    using System.Windows.Forms;
    #endregion
     
    /// <summary>
    /// Cette classe permet de lancer un formulaire, en l'affichant quand il à fini de charger.
    /// L'initialisation du formulaire est faite dans un thread séparé permettant ainsi
    /// de ne pas figer le thread appelant.
    /// </summary>
    /// <typeparam name="T">Type du formulaire à appeller.</typeparam>
    public class FormLauncher<T>
    	where T : Form, new()
    {
    	#region Evènements
    	/// <summary>
    	/// Délégué pour créer une instance.
    	/// </summary>
    	/// <returns></returns>
    	public delegate T CreateFormInstanceHandler();
     
    	/// <summary>
    	/// Délégué pour les évènements avant/après affichage.
    	/// </summary>
    	/// <param name="instance">Instance du formulaire</param>
    	public delegate void ShowEventHandler(T instance);
     
    	/// <summary>
    	/// Abonnez-vous à cet évènement pour créer l'instance vous-même.
    	/// Ceci permet de faire des constructions beaucoup plus complexes.
    	/// </summary>
    	public event CreateFormInstanceHandler CreateFormInstance;
     
    	/// <summary>
    	/// Abonnez-vous à cet évènement pour intervenir sur l'instance du formulaire
    	/// avant qu'il ne soit affiché
    	/// </summary>
    	public event ShowEventHandler BeforeShow;
     
    	/// <summary>
    	/// Abonnez-vous à cet évènement pour intervenir sur l'instance du formulaire
    	/// aussitot après qu'il soit affiché. Attention, cet évènement n'est pas
    	/// lancé dans le cas d'un affichage modal.
    	/// </summary>
    	public event ShowEventHandler AfterShow;
    	#endregion
     
    	#region Variables locales
    	private STABackgroundWorker		_worker;
    	private IWin32Window			_owner;
    	private bool					_Modal;
    	#endregion
     
    	#region Propriétés
    	#endregion
     
    	#region Constructeur
    	/// <summary>
    	/// Constructeur du lanceur.
    	/// </summary>
    	public FormLauncher()
    	{
    		_worker		= new STABackgroundWorker();
    		_owner		= null;
    		_Modal		= false;
     
    		_worker.DoWork				+= CreateInstance;
    		_worker.RunWorkerCompleted	+= ShowInstance;
    	}
    	#endregion
     
    	#region Méthodes
     
    	/// <summary>
    	/// Affiche le formulaire.
    	/// </summary>
    	/// <param name="owner">Propriétaire du formulaire lancé.</param>
    	public void Show(IWin32Window owner = null)
    	{
    		ShowForm(owner, false);
    	}
     
    	/// <summary>
    	/// Affiche le formulaire en mode modal.
    	/// </summary>
    	/// <param name="owner">Propriétaire du formulaire lancé.</param>
    	public void ShowDialog(IWin32Window owner = null)
    	{
    		ShowForm(owner, true);
    	}
     
    	/// <summary>
    	/// Affiche le formulair avec les paramètres souhaités.
    	/// </summary>
    	/// <param name="owner">Propriétaire du formulaire lancé.</param>
    	/// <param name="modal">Indique si le formulair lancé sera en mode modal ou non.</param>
    	private void ShowForm(IWin32Window owner, bool modal)
    	{
    		_owner = owner;
    		_Modal = modal;
    		_worker.RunWorkerAsync();
    	}
     
    	/// <summary>
    	/// Cette méthode est exécutée dans le thread d'arrière plan.
    	/// On y crée l'instance du formulaire.
    	/// </summary>
    	/// <param name="sender">Instance du worker.</param>
    	/// <param name="e">Paramètres de l'évènement.</param>
    	private void CreateInstance(object sender, DoWorkEventArgs e)
    	{
    		e.Result = (CreateFormInstance == null) ? new T() : CreateFormInstance();
    	}
     
    	/// <summary>
    	/// Cette méthode est exécutée lorsque le thread d'arrière plan est terminé.
    	/// Affiche le formulaire une fois que celui-ci à finit de chargé.
    	/// </summary>
    	/// <param name="sender">Instance du worker.</param>
    	/// <param name="e">Paramètres de l'évènement.</param>
    	private void ShowInstance(object sender, RunWorkerCompletedEventArgs e)
    	{
    		if (e.Error != null) throw e.Error;
    		if (e.Result != null)
    		{
    			T frm = e.Result as T; // e.Result est affecté par CreateInstance avec une instance de T, donc le cast ne peut pas retourner null.
     
    			if (BeforeShow != null) BeforeShow(frm);
     
    			if (_Modal) frm.ShowDialog(_owner);
    			else
    			{
    				frm.Show(_owner);
    				if (AfterShow != null) AfterShow(frm);
    			}
    		}
    	}
    	#endregion
    }

  5. #5
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    La semaine dernière, j'ai fait une petite classe pour lancer une form dans un thread séparé.

    Je posterai le code demain.

  6. #6
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    La classe outil pour créer une forme dans un thread secondaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
     
    public class HmiFormTools 
      {
        //--------------- Create HmiForm -----------------------
        private  static Form                       HmiFormAtCreation        = null ;
        private  static Type                       HmiFormTypeAtCreation    = null ;
        private  static object[]                   HmiFormInitObjects       = null ;
        private  static string                     HmiFormInitMethodName    = null ;
     
        private static void InvokeMethodByName(System.Windows.Forms.Form TheForm,string MethodName,object[] TheObjects)
        {
          System.Reflection.MethodInfo m = TheForm.GetType().GetMethod(MethodName,System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) ;
          if (m!=null) m.Invoke(TheForm,new object[] { TheObjects }) ;
          else throw new ArgumentException("Internal error : Cannot find method "+MethodName+" for form "+TheForm.GetType().ToString()) ;
        }
        public static Form Create_HmiForm(Type FormType,int TimeOutInSeconds,string InitMethodName,object[] InitObjects)
        {// If form is killed by user action, calling process shall set returned object to null
          HmiFormAtCreation      = null           ;
          HmiFormTypeAtCreation  = FormType       ;
          HmiFormInitMethodName  = InitMethodName ;
          HmiFormInitObjects     = InitObjects    ;
          // Create a new thread from which to start the splash screen form
          System.Threading.Thread HmiFormThread = new System.Threading.Thread(new System.Threading.ThreadStart(HmiForm_Start));
          HmiFormThread.SetApartmentState(System.Threading.ApartmentState.STA) ;
          HmiFormThread.Start();
          for (int i=0;i<TimeOutInSeconds*4 && HmiFormTypeAtCreation!=null;i++) System.Threading.Thread.Sleep(250) ;
          return HmiFormAtCreation ;
        }
     
        static public void HmiForm_Start()
        { 
          HmiFormAtCreation = (Form)Activator.CreateInstance(HmiFormTypeAtCreation,true) ;
          InvokeMethodByName(HmiFormAtCreation,HmiFormInitMethodName,HmiFormInitObjects) ;
          HmiFormTypeAtCreation=null ; // in order to indicate the end of init
          Application.Run(HmiFormAtCreation);
        }
     
        static public bool HmiForm_Close(Form HmiForm,EventHandler KillTheFormEventHandler)
        { // use that routine, when calling process wants to close the form
          // example : HmiForm_Close(ref HmiTestForm,new EventHandler(HmiTestForm.KillMe)) ;
          //           HmiTestForm = null ; 
          bool Result=true ;
          if (HmiForm!=null) try { HmiForm.Invoke(KillTheFormEventHandler) ; HmiForm.Dispose() ; }
            catch { Result=false ; } ;
          return Result ;
        }
    }
    Exemple d'utilisation / thread principal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        HmiTestForm HmiTestFrm = (HmiTestForm)HmiFormTools.Create_HmiForm(typeof(HmiTestForm),5,"InitAsHmiForm",new object[] {"AAA","BBBB"}) ;
        ...
        HmiFormTools.HmiForm_Close(HmiTestFrm,new EventHandler(HmiTestFrm.KillHmiForm)) ;
    Exemple d'utilisation / Forme à lancer dans un thread secondaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
        public partial class HmiTestForm : Form
        {
          public HmiTestForm() { InitializeComponent(); }
          public void InitAsHmiForm(object[] InitObjects) 
          {
            for (int i=0;i<InitObjects.Length;i++) textBox1.Text=textBox1.Text+Environment.NewLine+(string)InitObjects[i] ;
          }
     
          public void KillHmiForm(object o, EventArgs e) 
          {
     // Inserer ici le code au close de la forme
          }
     
          private void button1_Click(object sender, EventArgs e) { label1.Text=DateTime.Now.ToLongTimeString() ; }
        } // HmiTestForm
    Si vous voyez des améliorations ou des problèmes, merci de les signaler

Discussions similaires

  1. Réponses: 2
    Dernier message: 29/03/2004, 18h29
  2. interface graphique utilisateur, que faut-il utiliser?
    Par Missvan dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 01/03/2004, 12h18
  3. Application multiplateforme avec interface graphique
    Par TNorth dans le forum Choisir un environnement de développement
    Réponses: 2
    Dernier message: 31/01/2004, 18h55
  4. [Kylix] Interface graphique pour lognes de commande linux
    Par lecharcutierdelinux dans le forum EDI
    Réponses: 6
    Dernier message: 29/08/2003, 10h20
  5. plugin interface graphique
    Par jocelyn dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 13/08/2003, 09h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo