2009-12-07 2 views
0

Мне нужно иметь возможность открывать несколько экземпляров одной и той же формы, так как мое приложение может быть использовано в разных местах одновременно. С другой стороны, мне нужно иметь возможность обрабатывать операции во время события «ОК» по одному за раз, чтобы гарантировать, что данные хранятся безопасно и не перезаписываются другим экземпляром формы случайно.Предоставление нескольких экземпляров формы, но обработка событий по одному

я показываю свою форму, используя метод .Show(), как я использую несколько делегатов в нем:

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

Я пытался использовать семафор, чтобы только одно событие кнопки OK нажата происходят одновременно, я объединил это с Thread для соответствия критериям, которые мне нужны.

Когда я нажимаю на кнопку «OK» Я даюсь следующее сообщение об ошибке:

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

Это код для моего бронирования класс формы:

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(); 
     } 
    } 
} 

Я понимаю, что не может быть делая это наиболее эффективным способом, но время - это немного фактор. Я исследовал ошибку и слышал об использовании делегатов и .Invoke(), но я все еще не совсем уверен, как это исправить.

EDIT:

Я нашел этот фрагмент кода при поиске исправления к моей проблеме. Я не понимаю, где/как я буду использовать его.

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

EDIT2:

Кажется, что парень наконец-то увидел смысл, путем создания из с new словами он явно проходит критерии. Хотел бы я знать это, прежде чем пытаться изобрести колесо.

ответ

0

Вы получаете это исключение, потому что ваш поток получает доступ к элементам управления. Это нелогично, свойства управления должны быть доступны только из потока пользовательского интерфейса. Вы полностью согласны с свойством TextBox.Text, что один из них кэшируется. Но не ComboBox.SelectedIndex. И закрытие формы из другого потока тоже будет бомбить.

Ваш мьютекс не имеет к этому никакого отношения, но сохраните его, если вы хотите, чтобы потоки не перекрывались. Использование метода Invoke для делегата не собирается его решать, это просто начинает поток. Вам нужно будет собрать информацию о том, что поток понадобится в небольшом вспомогательном классе и передать это как аргумент метода Thread.Start().

Закрытие формы немного сложно, пользователь, возможно, уже закрыл ее, пока поток работал. Это вызовет исключение ObjectDisposed. Быстрое исправление заключается в том, чтобы установить свойство Enabled формы на false, чтобы пользователь не мог его закрыть. Вам нужно будет использовать метод Invoke() формы, чтобы обеспечить закрытие в потоке пользовательского интерфейса.

И последнее, но не менее важное: если эти потоки не занимают много времени (вторые или около того), подумайте о том, чтобы не использовать потоки вообще, и вместо этого отображать курсор ожидания.

+0

Я вообще не хочу использовать нить, я знаю, что преимущества сильно зависят от того, подходит ли приложение для использования потоков, но, к сожалению, это часть критериев, которые мне нужно выполнить. Мне нужно разрешить открывать несколько форм бронирования, но только один может обрабатываться одновременно. Как работает операция .Invoke()? –

+0

Threading * not * требуется одновременное открытие нескольких форм. Цикл сообщений Windows гарантирует, что все они останутся отзывчивыми. Сериализация обработки производится автоматически. –

+0

Итак, можно ли предположить, что то, что я пытаюсь сделать просто «не должно»? –

0

Я думаю, вы могли бы просто отключить кнопки OK на других открытых формах, чтобы дать пользователям визуальный сигнал. Тогда у вас тоже не должно быть проблемы. Предоставьте делегату обратного вызова что-то в контроллере приложения, который знает, какие формы открыты. Каждая форма может предоставить общедоступный метод отключения кнопки OK. Отключите кнопку ОК на всех других формах.

Я не слишком хорошо слежу за вашим кодом. Я бы подумал, что мьютекс может быть вне кода формы в первую очередь (т. Е. У делегатов, выполняющих фактическую работу), и если он находится в одном приложении, вы можете просто использовать метод lock (object), чтобы обеспечить только один поток выполняет заданный бит кода.

Я также хотел бы добавить, что мьютекс не остановит нескольких пользователей на разных машинах, которые могут одновременно нажать «ОК». Я не уверен, что это то, что вы имели в виду в своем вопросе, с помощью формы, выполняемой в разных местах.

Я думаю, что AddUserBooking и другой делегат должны нести ответственность за то, что они являются потокобезопасными, и это не должно быть частью пользовательского интерфейса. Если они не являются потокобезопасными, почему бы и нет? Относительно легко сделать, что функции фиксации базы данных имеют свое собственное подключение к базе данных во время их операций, и безопасность потоков не должна быть проблемой.

+0

Мне дали впечатление, что мьютекс позволит обрабатывать только одну форму за раз. Будет ли фиксировать ошибку, которую я получаю? В какой части кода вы не следуете? –

+0

Что вы начинаете с темы, а не просто вызываете MakeBooking напрямую? –

+0

Я думал, создав отдельную нить, это позволит мне иметь разных пользователей, одновременно нажимая «ok». –

0

Один простой способ сделать это - использовать перегрузку метода Thread.Start, который принимает объект: Thread.Start Method (Object). В этом объекте вы сохраните все данные/состояние, необходимые для обновления.

Весь код, который ссылается на форму и ее элементы управления, необходимо перенести в метод события щелчка OK или реорганизовать метод, который просто возвращает объект данных. Затем передайте этот объект в метод запуска потока.

Некоторые псевдо-код:

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

Есть лучшие способы сделать это, но это быстро исправить для вашего кода.

Смежные вопросы