Как использовать SaveFileDialog асинхронно? - PullRequest
2 голосов
/ 05 августа 2009

У меня есть приложение Windows Form, с кнопкой - в обработчике события кнопки мне нужно скачать файл с помощью SaveFileDialog. Но мне нужно сделать это асинхронно в отдельном потоке.

Пока я придумал этот код, но я не знаю, ошибочен ли мой подход или все в порядке:

        private void btnDownload_Click(object sender, EventArgs e)
        {
                ThreadStart tStart = new ThreadStart(DoWorkDownload);
                Thread thread = new Thread(tStart);
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
        }

        private void DoWorkDownload()
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.InitialDirectory = "C:\\";
            sfd.Filter = "All files (*.*)|*.*";
            sfd.FilterIndex = 1;
            sfd.RestoreDirectory = true;

            if (sfd.ShowDialog() == DialogResult.OK)
            {
            //do file saving here
            }
        }
}

Моя логика в приведенном выше коде такова: при нажатии кнопки создайте новый поток, передайте в поток метод DoWorkDownload () и запустите его; в этот момент предполагается ввести метод работы - однако при отладке он никогда не входит в DoWorkDownload ().

Кто-нибудь знает, чего мне не хватает?

Спасибо.

Ответы [ 4 ]

2 голосов
/ 05 августа 2009

В моем случае отладчик DO входит в DoWorkDownload () Он входит после окончания btnDownload_Click () Установить точку останова на SaveFileDialog sfd = new SaveFileDialog (); и это должно работать

Чтобы доказать, что он работает асинхронно, я даже поставил следующий код

ThreadStart tStart = new ThreadStart(DoWorkDownload);
Thread thread = new Thread(tStart);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

Thread.Sleep(10000);
MessageBox.Show("qwe");

и запустите без отладчика, и вы увидите, что когда текущий поток перейдет в спящий режим, появится SaveFileDialog ... и только через 10 секунд появится сообщение

2 голосов
/ 05 августа 2009

Вы можете использовать BackgroundWorker , который прост в использовании.

Кроме того, я не уверен, что совершенно безопасно (хотя я могу ошибаться) показывать SaveFileDialog в новом потоке. Моя рекомендация будет такой:

  1. Показать SaveFileDialog в главном потоке.
  2. Передать имя файла методу, который затем вызывается асинхронно.

Вот пример реализации, без использования BackgroundWorker:

private void button1_Click(object sender, EventArgs e)
{
  SaveFileDialog sfd = new SaveFileDialog();
  sfd.InitialDirectory = "C:\\";
  sfd.Filter = "All files (*.*)|*.*";
  sfd.FilterIndex = 1;
  sfd.RestoreDirectory = true;
  if (sfd.ShowDialog() == DialogResult.OK)
  {
    // Invoke the SaveFile method on a new thread.
    Action<string> invoker = new Action<string>(SaveFile);
    invoker.BeginInvoke(sfd.FileName, OnSaveFileCompleted, invoker);
  }
}

protected void SaveFile(string fileName)
{
  // save file here (occurs on non-UI thread)
}

protected void OnSaveFileCompleted(IAsyncResult result)
{
  Action<string> invoker = (Action<string>) result.AsyncState;
  invoker.EndInvoke(result);
  // perform other actions after the file has been saved (also occurs on non-UI thread)
}

Обратите внимание, что все действия, выполняемые с потоками без пользовательского интерфейса, должны влиять только на элементы без пользовательского интерфейса. Если вы хотите изменить элементы пользовательского интерфейса, вы должны перенаправить обратный вызов в поток пользовательского интерфейса, используя Control.Invoke (например, this.Invoke). См. этот пост для получения дополнительной информации.

1 голос
/ 05 августа 2009

Бернхоф может быть прав, но будьте осторожны. Все элементы пользовательского интерфейса должны выполняться в одном потоке. Поэтому, если вы создаете новый поток для SFD, убедитесь, что вы не обновляете элементы управления в главном окне.

С уважением, Гийом Ханик

0 голосов
/ 26 марта 2016

в вашем классе

private object sync_temp = new object();

и в потоке метод

SaveFileDialog save = new SaveFileDialog();
// your code to do with "save"
Action ac = () => { lock (sync_temp) { save.ShowDialog(); } };
Invoke(ac);
//Thread.Sleep(10);
lock (sync_temp)
{
   string path = save.FileName;
}
...