Как открыть форму в ветке и заставить ее оставаться открытой
У меня все еще есть проблемы с выяснением того, как создавать winforms в отдельном потоке пользовательского интерфейса, который я обсуждал здесь.
Пытаясь понять это, я написал следующую простую тестовую программу. Я просто хочу, чтобы он открыл форму в отдельном потоке с именем "Поток пользовательского интерфейса" и продолжал работу потока до тех пор, пока форма открыта, в то же время позволяя пользователю взаимодействовать с формой (вращение обманывает). Я понимаю, почему ниже не удается и поток закрывается сразу, но я не уверен, что я должен сделать, чтобы это исправить.
using System;
using System.Windows.Forms;
using System.Threading;
namespace UIThreadMarshalling {
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var tt = new ThreadTest();
ThreadStart ts = new ThreadStart(tt.StartUiThread);
Thread t = new Thread(ts);
t.Name = "UI Thread";
t.Start();
Thread.Sleep(new TimeSpan(0, 0, 10));
}
}
public class ThreadTest {
Form _form;
public ThreadTest() {
}
public void StartUiThread() {
_form = new Form1();
_form.Show();
}
}
}
6 ответов
В новом потоке вызовите Application.Run, передав объект формы, это заставит поток запустить свой собственный цикл обработки сообщений, пока окно открыто.
Затем вы можете вызвать.Join в этом потоке, чтобы заставить ваш основной поток подождать, пока поток пользовательского интерфейса не завершится, или использовать аналогичный прием, чтобы дождаться завершения этого потока.
Пример:
public void StartUiThread()
{
using (Form1 _form = new Form1())
{
Application.Run(_form);
}
}
private void button1_Click(object sender, EventArgs e)
{
var t = new Thread(RunNewForm);
t.Start();
}
public static void RunNewForm()
{
Application.Run(new Form2());
}
Я думаю, что ваша проблема с этой мыслью: "открыть форму в отдельном потоке с именем" поток пользовательского интерфейса ""
Windows работает следующим образом (обратите внимание, Vista может изменить некоторые из этих реалий):
Существует один важный поток, который называется "Основной поток" или "Поток пользовательского интерфейса". Этот поток обрабатывает сообщения Windows, например, "эй, щелкнул мышкой по этому пикселю".
Эти сообщения попадают в очередь, и главный поток обрабатывает их, когда он не занят чем-то другим.
Поэтому, если вы выполняете вызов функции foo() в главном потоке, если это занимает много времени, сообщения Windows не обрабатываются в течение этого времени, и, следовательно, взаимодействие с пользователем не может произойти.
Основной поток также рисует пользовательский интерфейс на экране, поэтому длительное выполнение foo() также остановит рисование вашего приложения.
Все остальные потоки, кроме этого священного и специального основного потока, являются рабочими. Эти рабочие потоки могут что-то делать, но они никогда не могут напрямую взаимодействовать с пользовательским интерфейсом.
Эта реальность вызывает две проблемы:
ВЫХОД ИЗ ГЛАВНОЙ РЕЗЬБЫ: Поскольку вы не хотите, чтобы долго выполняющаяся функция foo() прекращала все взаимодействие с пользователем, вам необходимо перенести эту работу в рабочий поток.
ПОЛУЧЕНИЕ ВЕРНУТЬСЯ В ГЛАВНУЮ РЕЗЬБУ: Когда завершается долго выполняющаяся функция foo(), вы, вероятно, захотите уведомить пользователя, делая что-то в пользовательском интерфейсе, но вы не можете сделать это в рабочем потоке, поэтому вам нужно "вернуться" к Основная тема.
Поэтому я считаю, что ваша проблема в вышеприведенной программе очень общая: ваша цель неверна, поскольку предполагается, что нельзя вызывать _form.Show() в каком-либо потоке, кроме основного основного потока.
Вы не можете открыть форму GUI в каком-либо потоке, потому что в ней будет отсутствовать насос сообщений. Вы должны явно запустить насос сообщений в этом потоке, вызвав Application.Run() в методе потока. Другой вариант - вызвать DoEvents() в цикле, если вам нужно сделать что-то еще, потому что после Application.Run() этот поток будет ждать, когда пользователь закроет форму в этой точке выполнения.
Вместо вызова show() в форме, которая будет выполняться в форме и затем просто закрываться в конце выполнения потока внутри функции StartUiThread(), вы можете заблокировать поток до тех пор, пока форма не будет остановлена внутри метода, поскольку вы просто блокируете другая нить. Пример:
public void StartUiThread() {
_form = new Form1();
_form.ShowDialog(); //Change Show() to ShowDialog() to wait in thread
}
Это заставит новый поток ждать, пока диалог не закроется. Я не знаю, решит ли это ваши проблемы, но это решило мои.
Я думаю, что просто вызов ShowDialog вместо Show поможет. Похоже, проблема заключается в том, что поток завершает работу сразу после вызова Show, после чего сборщик мусора формы get. ShowDialog остановит поток, но все равно запустит в нем события формы, поэтому поток продолжит работать, пока форма не будет закрыта.
Обычно я делал бы это наоборот. Запустите форму в начальном потоке и запустите фоновые потоки, если вы хотите запустить длительные фоновые задачи.
Я также прочитал ваш второй вопрос, но не смог понять, что вы пытаетесь сделать. MVP-архитектура не требует от вас запуска бизнес-логики в разных потоках. Многопоточность трудно сделать правильно, поэтому я бы использовал несколько потоков, только если они мне действительно нужны.