Графический интерфейс Windows Forms зависает при вызове OpenFileDialog.ShowDialog()
Мой проект - трехуровневая архитектура с поддержкой WCF-сервиса в бэкэнде. Когда бэкэнд может извлечь данные из службы, он уведомляет бизнес-уровень с помощью публикации-подписки, которая, в свою очередь, уведомляет уровень GUI.
Я добавил OpenFileDialog в свой дизайн пользовательского интерфейса с помощью дизайнера Visual Studios. Обработчик события кнопки вызывает сообщение ShowDialog. Однако, как только я нажимаю кнопку, весь пользовательский интерфейс зависает.
Немного погуглив, я обнаружил, что использование делегатов является предпочтительным способом решения подобных задач. Однако ни с делегатом проблема сохраняется.
В настоящее время мой код выглядит так:
private void bOpen_Click(object sender, EventArgs e)
{
Func<Image> del = delegate
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
return Image.FromFile(d.FileName);
}
return null;
};
Invoke(del);
}
Я родом из мира Java, поэтому я не очень знаком со сложностями программирования на C# UI.
Что-то мне здесь не хватает?
5 ответов
Кажется, я решил проблему с добавлением атрибута [STAThread] в метод main. Мне сказали сделать это, как только я запустил программу в отладчике, чего раньше не делал, потому что я регулярно запускал службу из Visual Studio, а клиент - из Windows.
[STAThread]
public static void Main(string[] args)
{
GUI gui = new GUI();
gui.ShowDialog();
}
Кто-нибудь может объяснить, что именно происходит, хотя
openFileDialog1->ShowHelp = true;
Я вставил эту строку в мой код, после чего проблема была решена.
Это, как правило, является проблемой окружающей среды, когда вы используете OpenFileDialog, многие расширения оболочки загружаются в ваш процесс. Непослушный человек может легко испортить вашу программу. Там много плохих.
Отладить это сложно, вам нужен неуправляемый отладчик, так как эти расширения оболочки являются неуправляемым кодом. Вы можете быть в состоянии отличить что-либо от стека вызовов, когда входите в систему после тупика. Требуются символы отладки Windows, включите сервер символов Microsoft. Но самый эффективный подход - использовать утилиту SysInternals AutoRuns. Начните с отключения всех расширений оболочки, которые не были созданы Microsoft. Затем начните заново включать тех, без которых вы не можете жить один за другим.
И, как вы узнали, эти расширения оболочки ожидают запуска в потоке STA и с треском проваливаются, когда не получают его. Поток пользовательского интерфейса программы всегда должен быть STA, также для поддержки буфера обмена и перетаскивания и различных видов элементов управления, таких как WebBrowser. Обычно об этом всегда заботится автоматически с помощью атрибута [STAThread] метода Main(), который помещается туда шаблоном проекта. И вызов Application.Run(), необходимый для реализации контракта STA. Тупик, когда ты не.
Я считаю, что предпочтительный способ "делегата" на самом деле относится к использованию отдельного потока. Я приведу пример использования BackgroundWorker.
Это будет выглядеть так:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
m_Worker.DoWork += new DoWorkEventHandler(m_Worker_DoWork);
m_Worker.ProgressChanged += new ProgressChangedEventHandler(m_Worker_ProgressChanged);
m_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_Worker_RunWorkerCompleted);
}
void m_Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Usually, used to update a progress bar
}
void m_Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Usually, used to add some code to notify the user that the job is done.
}
void m_Worker_DoWork(object sender, DoWorkEventArgs e)
{
//e.Argument.ToString() contains the path to the file
//Do what you want with the file returned.
}
private void bOpen_Click(object sender, EventArgs e)
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
m_Worker.RunWorkerAsync(d.FileName);
}
}
BackgroundWorker m_Worker = new BackgroundWorker();
}
Теперь, по той причине, что ваш пользовательский интерфейс "зависает", это потому, что по умолчанию ваша операция выполняется в потоке пользовательского интерфейса, поэтому, если вы запустите что-то тяжелое, пользовательский интерфейс не будет отвечать.
Я тоже встречал эту проблему. И я попробовал все решения здесь, и никто не может решить его. Затем я меняю целевой фреймворк с.Net Framework 4.7 на 4.6.2, проблема решена...
Я думаю, что моя проблема в другом, поскольку ни одно из вышеперечисленных решений не помогло мне.
Я написал временный код для установки
OpenFileDialog.FileName
свойство на что-то не пустую или пустую строку (это была пустая строка, когда произошло зависание), и я перезапустил свой компьютер. Когда я снова запустил Visual Studio и запустил ее, она снова заработала без зависания.