Предоставление нескольких экземпляров формы и обработка событий по одному - PullRequest
0 голосов
/ 08 декабря 2009

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

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

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

Я пытался использовать мьютекс, чтобы за один раз происходило только одно нажатие кнопки ОК, я скомбинировал это с потоком, чтобы соответствовать необходимым критериям.

Когда я нажимаю кнопку «ОК», я получаю следующую ошибку:

Операция между потоками недопустима: элемент управления 'comboBoxDay' доступен из потока, отличного от потока, в котором он был создан.

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

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:

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

Ответы [ 3 ]

0 голосов
/ 08 декабря 2009

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

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

Какой-то псевдокод:

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

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

0 голосов
/ 08 декабря 2009

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

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

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

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

0 голосов
/ 08 декабря 2009

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

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

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

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

...