2008-12-16 7 views
2

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

Я достигаю этого в начале приложения, начиная экземпляр класса DeviceMonitor. Этот класс создает System.Threading.Timer и каждый Tick (обычно 1 секунду) вместе с несколькими другими вещами, которые он пытается извлечь данные из базы данных. Если ничья завершилась неудачей, и причина определяется из-за отсутствия возможности подключения, исключение обрабатывается, выбирая вышеупомянутый диалог. Аналогично, если выборка данных завершается успешно, и в настоящее время диалог закрыт.

Проблема в том, что, хотя все это прекрасно работает, диалог ConnectionLost не блокирует взаимодействие пользователя с пользовательским интерфейсом. Это имеет смысл, поскольку событие Timer.Elapsed создается в пределах собственного потока, и noConnectionDialog.ShowDialog() вызывается из обратного вызова, который блокирует поток, на котором он включен, но не является потоком пользовательского интерфейса.

По моему мнению, мне нужно либо заставить noConnectionDialog.ShowDialog() работать в потоке пользовательского интерфейса, либо блокировать поток пользовательского интерфейса до тех пор, пока не вызывается noConnectionDialog.Hide(), но я не знаю, как это сделать.

Возможно, есть какое-то другое средство или я что-то упустил. Любые советы приветствуются.

EDIT: Дополнительная информация - это стилизованное диалоговое окно, а не только сообщение. Он создается, когда мое приложение запускается Castle Windsor и вводится в класс DialogFactory, который передается. Диалог поэтому доступ

var d = _dialogFactory.GetNoConnectionDialog(); 
d.ShowDialog(); 

Я экспериментировал с введением этого кода вне таймера истечения обратного вызова - при нажатии кнопки на интерфейсе UI щёлкнули, например, - и он блокирует пользовательский интерфейс просто отлично оттуда, так что это а не вопрос создания формы.

ответ

1

Если у вас есть доступ к элементу пользовательского интерфейса, вы можете нажать на поток пользовательского интерфейса, используя такие вещи, как:

someControl.Invoke((Action)delegate { 
    MessageBox.Show(someControl, "Foo"); 
    // could also show a form here, etc 
}); 

(someControl в MessageBox.Show помогает родителю сообщение коробками)

Если у вас нет доступа к элементу управления пользовательского интерфейса, вы можете также использовать для синхронизации контекста:

SynchronizationContext.Current.Post(delegate { 
    MessageBox.Show("Foo"); 
}, null); 

Но это легче держать управления ;-p

+0

Это не сообщение. Вернее, это стилизованный ящик для сообщений, поэтому он должен быть полной формой. У меня есть декоратор, обертывающий диалог, который вызывает Invoke(), если это необходимо, поэтому я думал, что он будет работать в потоке пользовательского интерфейса, но, видимо, это не так. –

+0

Форма будет работать в потоке, который ее создает. Итак, создайте форму внутри Control.Invoke –

+0

Форма создана Castle Windsor и вступает в класс фабрики диалога, поэтому я не думаю, что это проблема. Когда я вызываю его из другого места, кроме монитора устройства, он блокирует поток пользовательского интерфейса только отлично –

2

Я уверен, что Марк предложил работать.Это, как я хотел бы написать это использовать диалог вместо MessageBox:

someControl.Invoke((Action)delegate { 
    var d = _dialogFactory.GetNoConnectionDialog(); 
    d.ShowDialog(); 
}, null); 

Если это на самом деле не работает, я имел успех в прошлом, с помощью элемента управления Timer (System.Windows.Forms.Timer) на моей форме и очереди действий с функцией Tick, которая выглядит следующим образом:

void timer_Tick(object sender, System.EventArgs e) 
{ 
    lock(queue) 
    { 
     while(queue.Count > 0) 
     { 
      Action a = queue.Dequeue(); 
      a(); 
     } 
    } 
} 

И когда ваш класс DeviceMonitor должен показать пользовательский интерфейс он будет делать это:

lock(queue) 
{ 
    queue.Enqueue((Action)delegate 
    { 
     var d = _dialogFactory.GetNoConnectionDialog(); 
     d.ShowDialog(); 
    }); 
} 

При этом я действительно хочу повторить, что, по-моему, метод Marc должен работать правильно, и я бы использовал только метод Timer + queue, если вы абсолютно уверены, что Control.Invoke не сработает для вас.

0

Это называется маршалинг и является очень простой концепцией, как только вы прочтете хороший материал (Google - ваш друг).

Если в фоновом потоке есть делегат, который вызывает объект, принадлежащий потоку пользовательского интерфейса, то этот метод (на вызываемом конце делегата) который будет потоком пользовательского интерфейса), а затем блокировать. Это очень простой код (IsInvokeRequired), вам просто нужно понять, как это сделать. (Это в основном повторяет то, что сказал Марк, но с более высокого уровня.)

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