2009-12-07 4 views
0

Je dois pouvoir laisser plusieurs instances du même formulaire ouvertes car mon application peut être utilisée à différents endroits à la fois. D'un autre côté, je dois être en mesure de traiter les opérations pendant l'événement "OK" une à la fois pour assurer que les données sont stockées en toute sécurité et ne sont pas écrasées par une autre instance de formulaire par accident.Fournir plusieurs instances d'un formulaire mais traiter les événements un à la fois

Je montre ma forme en utilisant la méthode .Show() que j'utilise quelques délégués en elle:

 private void newToolStripMenuItem_Click(object sender, EventArgs e) 
    { 
     bookingForm = new BookingForm(AddMemberBooking, AddUserBooking, CloseBooking); 
     bookingForm.Show(); 
    } 

J'ai essayé d'utiliser le mutex pour permettre un seul événement du bouton OK est appuyé arriver à la fois, j'ai combiné cela avec un fil pour répondre aux critères dont j'ai besoin.

Lorsque je clique sur le bouton « OK » on me donne l'erreur suivante:

Cross-thread operation not valid: Control 'comboBoxDay' accessed from a thread other than the thread it was created on.

C'est le code pour mon formulaire de réservation classe:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 

namespace Collection 
{ 
    //Allows the class to be serialized 
    [Serializable()] 

    public delegate void AddMemberBookingMethod(int date, int time, int mNo); 
    public delegate void AddUserBookingMethod(int date, int time, string fName, string lName, string pCode); 
    public delegate void CloseBookingFormMethod(); 


    public partial class BookingForm : Form 
    { 
     public CloseBookingFormMethod CloseBookingForm; 
     public AddMemberBookingMethod AddMemberBooking; 
     public AddUserBookingMethod AddUserBooking; 
     private Mutex bookingMut = new Mutex(); 
     private Thread thread; 

     public bool IsUser; 

     public BookingForm(AddMemberBookingMethod ambm, AddUserBookingMethod aubm, CloseBookingFormMethod cbfm) 
     { 
      InitializeComponent(); 
      AddMemberBooking = ambm; 
      AddUserBooking = aubm; 
      CloseBookingForm = cbfm; 

      checkBoxMember.Checked = true; 
      //Control.CheckForIllegalCrossThreadCalls = false; 
     } 

     private void checkBoxUser_CheckedChanged(object sender, EventArgs e) 
     { 
      if (checkBoxUser.Checked) 
      { 
       IsUser = true; 
       checkBoxMember.CheckState = CheckState.Unchecked; 
       textBoxMNo.Enabled = false; 
       textBoxFName.Enabled = true; 
       textBoxLName.Enabled = true; 
       textBoxPCode.Enabled = true; 
      } 
      else 
      { 
       IsUser = false; 
       checkBoxMember.CheckState = CheckState.Checked; 
       textBoxMNo.Enabled = true; 
       textBoxFName.Enabled = false; 
       textBoxLName.Enabled = false; 
       textBoxPCode.Enabled = false; 
      } 

     } 

     private void checkBoxMember_CheckedChanged(object sender, EventArgs e) 
     { 
      if (checkBoxMember.Checked) 
      { 
       IsUser = false; 
       checkBoxUser.CheckState = CheckState.Unchecked; 
       textBoxFName.Enabled = false; 
       textBoxLName.Enabled = false; 
       textBoxPCode.Enabled = false; 
      } 
      else 
      { 
       IsUser = true; 
       checkBoxUser.CheckState = CheckState.Checked; 
       textBoxMNo.Enabled = false; 
       textBoxFName.Enabled = true; 
       textBoxLName.Enabled = true; 
       textBoxPCode.Enabled = true; 
      } 

     } 

     private void buttonOK_Click(object sender, EventArgs e) 
     { 
      this.thread = new Thread(new ThreadStart(MakeBooking)); 
      this.thread.Name = "bookingThread"; 
      this.thread.Start(); 
     } 

     private void MakeBooking() 
     { 
      this.bookingMut.WaitOne(); 

      int date = this.comboBoxDay.SelectedIndex; 
      int time = this.comboBoxTime.SelectedIndex; 

      if (IsUser) 
      { 
       string fName = textBoxFName.Text; 
       string lName = textBoxLName.Text; 
       string pCode = textBoxPCode.Text; 

       AddUserBooking(date, time, fName, lName, pCode); 
      } 
      else 
      { 
       int mNo = int.Parse(textBoxMNo.Text); 

       AddMemberBooking(date, time, mNo); 
      } 

      this.bookingMut.ReleaseMutex(); 

      CloseBookingForm(); 
     } 

     private void buttonClose_Click(object sender, EventArgs e) 
     { 
      CloseBookingForm(); 
     } 
    } 
} 

Je me rends compte que je ne peux pas être le faire de la manière la plus efficace, mais le temps est un facteur. J'ai étudié l'erreur et j'ai entendu parler de l'utilisation de délégués et .Invoke() mais je ne suis toujours pas tout à fait sûr de savoir comment y remédier.

EDIT:

J'ai trouvé cet extrait de code lors de la recherche d'une solution à mon problème. Je ne comprends pas où/comment je l'utiliserais.

if(this.InvokeRequired) 
{ 
    this.Invoke(new MyEventHandler(this.CreateAForm())); 
    return; 
} 

EDIT2:

Semble le gars a vu enfin le sens, en créant le mot de la new passe apparemment les critères. J'aurais aimé le savoir avant d'essayer de réinventer la roue.

Répondre

0

Vous recevez cette exception car votre thread accède aux contrôles. Ce n'est pas légal, les propriétés de contrôle ne doivent être accessibles qu'à partir du thread de l'interface utilisateur. Vous êtes d'accord sur la propriété TextBox.Text, celle qui se trouve être mise en cache. Mais pas ComboBox.SelectedIndex. Et fermer le formulaire d'un autre fil va aussi bombarder.

Votre mutex n'a rien à voir avec cela, mais conservez-le si vous voulez empêcher le chevauchement des fils. Utiliser la méthode Invoke d'un délégué ne va pas le résoudre, cela démarre aussi un thread. Vous aurez besoin de collecter les informations dont le thread aura besoin dans une petite classe d'aide et le passer comme argument de la méthode Thread.Start().

La fermeture du formulaire est également un peu délicate, l'utilisateur pourrait bien l'avoir déjà fermé pendant que le thread était en cours d'exécution. Cela va provoquer une exception ObjectDisposed. Une solution rapide consiste à définir la propriété Enabled du formulaire sur false afin que l'utilisateur ne puisse pas le fermer. Vous devrez utiliser la méthode Invoke() du formulaire pour vous assurer que la fermeture est effectuée sur le thread d'interface utilisateur. Last but not least, si ces threads ne prennent pas beaucoup de temps (une seconde ou deux), pensez à ne pas utiliser de threads du tout et affichez un curseur d'attente à la place.

+0

Je ne veux pas utiliser de thread du tout, je sais que les avantages dépendent fortement de savoir si l'application est adaptée pour utiliser le filetage, mais malheureusement, c'est une partie des critères que je dois respecter. Je dois pouvoir autoriser l'ouverture de plusieurs formulaires de réservation, mais un seul peut être traité à la fois. Comment l'opération .Invoke() fonctionne-t-elle exactement? –

+0

Il n'est pas obligatoire d'avoir plusieurs formulaires en même temps. La boucle de messages Windows garantit que tous restent réactifs. La sérialisation du traitement est automatique. –

+0

Donc, est-il sûr de supposer que ce que j'essaie de faire simplement "ne devrait pas" être fait? –

0

Je pense que vous pourriez simplement désactiver les boutons OK sur d'autres formes ouvertes pour donner aux utilisateurs un repère visuel. Ensuite, vous ne devriez même pas avoir le problème. Fournissez un délégué de rappel à quelque chose dans le contrôleur d'application qui sait quels formulaires sont ouverts. Chaque formulaire peut fournir une méthode publique pour désactiver le bouton OK. Désactivez le bouton OK sur tous les autres formulaires.

Je ne suis pas vraiment votre code trop bien. Je pense que le mutex pourrait être en dehors du code de formulaire en premier lieu (c'est-à-dire dans les délégués qui effectuent le travail), et si c'est dans une seule application, vous pouvez simplement utiliser la méthode lock (objet) un thread exécute un bit de code donné.

Je voudrais aussi ajouter qu'un mutex n'empêchera pas plusieurs utilisateurs sur différents machiens de pouvoir cliquer sur OK en même temps. Je ne suis pas sûr si c'est ce que vous vouliez dire dans votre question par un formulaire qui se déroule à différents endroits.

Je pense que AddUserBooking et l'autre délégué devraient s'assurer qu'ils sont threadsafe et que cela ne devrait pas faire partie de l'interface utilisateur. Si elles ne sont pas sûres, pourquoi ne le sont-elles pas? Il est relativement facile de faire en sorte que les fonctions de validation de base de données aient chacune leur propre connexion à la base de données au cours de leurs opérations et que la sécurité des threads ne soit pas un problème.

+0

On m'a donné l'impression que le mutex ne permettrait qu'une seule forme à traiter à la fois. Verrouillerait-il réparer l'erreur que je reçois? Quelle partie du code ne suivez-vous pas? –

+0

Que démarrez-vous un thread au lieu de simplement appeler MakeBooking directement? –

+0

Je pensais qu'en créant un fil séparé cela me permettrait d'avoir différents utilisateurs en appuyant sur "ok" en même temps. –

0

Une méthode simple pour ce faire consiste à utiliser la surcharge de la méthode Thread.Start qui accepte un objet: Thread.Start Method (Object). Dans cet objet, vous allez stocker toutes les données/l'état nécessaires pour effectuer la mise à jour.

Tout le code qui fait référence au formulaire et à ses contrôles doit être déplacé dans la méthode d'événement OK click ou refacturé à une méthode qui renvoie simplement un objet de données. Puis passez cet objet dans la méthode de démarrage du thread.

Certains pseudo-code:

on_click_event() 
{ 
    object data=getFormData(); 
    thread.start(data); 
} 

Il existe de meilleures façons de le faire, mais cela est une solution rapide pour votre code.

Questions connexes