Попробуйте это:
void btnClick(object sender, EventArgs e)
{
var t = new Thread(doStuff);
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
void doStuff()
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.ShowDialog();
}
SaveFileDialog запускается в отдельном потоке (он должен иметь однопоточную квартиру), и она не блокирует поток пользовательского интерфейса приложения. Однако будьте очень осторожны во всем, что вы делаете, это может быть очень нестабильно.
Основная проблема заключается в том, что в вашем приложении работает цикл обмена сообщениями Windows, который представляет собой цикл, который выполняет итерацию по сообщениям Windows, поступающим из ОС (и других приложений). Если по какой-то причине петля застряла, приложение перестает отвечать на запросы (когда вы нажимаете на мышь, ОС отправляет WM_MOUSEDOWN и еще много методов в очередь сообщений, которые должны быть депопулированы контуром сообщений, чтобы что-либо сделать). Быть в методе ShowDialog
- это один из способов заклинивания цикла - ваша форма больше не обрабатывает никаких сообщений, потому что у нее никогда не будет шанса.
Теперь, что делает Invoke
, он добавляет метод, который вы хотите вызвать в другую очередь в целевой форме. Следующий шанс, который форма получает во время цикла обмена сообщениями Windows, выполняет все элементы очереди вызова - опять же, цикл обмена сообщениями застревает.
Теперь, как диалоговое окно получает сообщения Windows? На практике он создает собственный WM-контур. Это очень маленький, но еще один цикл while
, который заканчивается только при закрытии модального диалога - блокирование цикла обмена сообщениями родительской формы (или, скорее, приложения).
Проблема с приведенным выше кодом заключается в том, что он может украсть цикл обмена сообщениями из нашего родительского окна. Решение состоит в том, чтобы явно создать новое окно с новым контуром обмена сообщениями, явным образом передавая владельцу ShowDialog
:
void doStuff()
{
NativeWindow nw = null;
try
{
nw = new NativeWindow();
nw.CreateHandle(new CreateParams());
SaveFileDialog sfd = new SaveFileDialog();
sfd.ShowDialog(nw);
}
finally
{
if (nw != null)
nw.DestroyHandle();
}
}
Не-пользовательские потоки и модальные диалоги не работают хорошо ... – James
Использование потоков напрямую для большинства задач фактически устарело. Предлагаемой технологией является TPL (http://msdn.microsoft.com/en-us/library/dd537609%28v=vs.110%29.aspx) с точки зрения использования средства «Задача». Он не решает вашу проблему, хотя, как @James сказал – Samuel
* Я делаю это через новый поток, так что графический интерфейс остается отзывчивым * Я ничего не вижу! вы просто делаете все в потоке пользовательского интерфейса. –