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

  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 : 41
    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 : 41
    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 : 41
    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

  7. #7
    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 : 41
    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
    Intéressant tout ça, merci d'avoir partagé.
    Je n'avais pas penser à faire un Application.Run sur mon formulaire, ça tiendrais mon thread en vie et je pourrais du coup peut être m'arranger pour que le dispose soit fait dans le bon thread.

    Je ne suis pas sur d'avoir saisi l'intérêt de la partie reflexion. Si j'ai bien compris tu impose que ta form possède une méthode d'initialisation dédiée au mode "hmi" (d'ailleurs hmi signifie quoi ? ).
    Si j'ai bien compris, c'est fait pour te permettre de construire la form avec uniquement le constructeur par défaut, puis de passer des paramètres, remplacant ainsi les constructeurs qui auraient été classiquement déclarés pour ce travail.

    Personnellement j'ai "résolu" ce problème via un handler. Si tu ne t'abonnes pas, alors le constructeur par défaut est utilisé, via un generic, par contre si tu t'abonnes, c'est toi qui crée l'objet, tu peux donc utiliser directement les bonnes classes sans avoir à faire de la reflexion.
    Je ne sais pas si c'est mieux ou plus performant (enfin je pense que generic+handler c'est plus rapide que reflexion, mais je n'ai pas testé).

    Par contre je vois que tout est static dans ton HmiFormTools (tous les membres sont static mais la classe elle même ne l'est pas ?), du coup ça signifie que tu ne peux lancer qu'une seule fenêtre de ce genre à la fois ou j'ai loupé un épisode ?

    Quoi qu'il en soit, encore merci d'avoir partager, je vais tester cette histoire de Application.Run, je ne sais pas si j'aurais le temps de le faire aujourd'hui.
    Je vous tiendrai au courant si ça marche.

  8. #8
    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
    Je ne suis pas sur d'avoir saisi l'intérêt de la partie reflexion. Si j'ai bien compris tu impose que ta form possède une méthode d'initialisation dédiée au mode "hmi" (d'ailleurs hmi signifie quoi ?).
    HMI = Human Machine Interface (IHM en français).
    La reflexion permet de généraliser la methode d'initialisation des forms à tout type de form.
    Si j'ai bien compris, c'est fait pour te permettre de construire la form avec uniquement le constructeur par défaut, puis de passer des paramètres, remplacant ainsi les constructeurs qui auraient été classiquement déclarés pour ce travail.
    C'est bien l'objectif.
    tous les membres sont static mais la classe elle même ne l'est pas ?
    J'ai pas pensé à déclarer la classe comme static .
    tout est static dans ton HmiFormTools. du coup ça signifie que tu ne peux lancer qu'une seule fenêtre de ce genre à la fois ou j'ai loupé un épisode ?
    Oui, si on veut lancer plusieurs fenêtres à partir de threads différents (thread1-form1 lance thread2-form2, puis thread1-form1 lance Thread3-form3 et thread2-form2 lance thread4-form4, il faudrait ajouter un mechanisme de vérouillage dans la fonction Create_HmiForm().

  9. #9
    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 : 41
    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
    Oula, pas réveillé moi, j'ai pas pensé une seconde à IHM, je cherchais un truc en rapport avec les threads... Du coup "mode hmi" ne veut rien dire

    J'ai un peu de mal à voir ce dont tu parles quand tu dis qu'il faut un mécanisme de verrouillage. Verrouiller quoi ? Quand ? Pourquoi ?
    Dans la mesure où les forms ne sont pas sensées communiquer entre-elles, je ne voit rien à verrouiller.

    Sinon, pour la partie reflexion, tu penses quoi de la méthode que j'utilise (un handler exécuté dans thread d'arrière plan dans lequel on peu créer l'objet comme on veut). Ca me permet de pouvoir créer le formulaire avec n'importe quel constructeur, passer toutes les valeurs que je veux, appeler plus qu'une simple simple méthode, etc...

    Le cas classique étant de mettre une lambda derrière :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    FormLauncher<TypeDeMonFormulaire> fl = new FormLauncher<TypeDeMonFormulaire>();
     
    fl.CreateFormInstanceHandler += delegate
    {
        TypeDeMonFormulaire frm = new TypeDeMonFormulaire("truc", 12);
        frm.Init("bouuuh");
        // ... etc, tu mets autant de code d'init que tu veux
        return frm;
    };
     
    fl.Show();

  10. #10
    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
    Verrouiller quoi ? Quand ? Pourquoi ?
    Une instruction telle que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     Lock("Create_HmiForm_LockString") 
    {
       // ici instructions de la procedure Create_HmiForm
    }
    évitera que l'on puisse exécuter simultanément cette procédure depuis 2 threads différents.

    Pour gérer les initialisations, un delegate est aussi une bonne solution si, opérant dans le nouveau thread, il est exécuté avant la fin de l'appel à la création de thread2-form2 dans thread1-form1. Sinon, les paramètres utilisés dans l'init de thread2-form2 pourraient avoir été modifiés dans thread1-form1.

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