Question

I have a windows forms application, with a button - on the button's event handler, I need to download a file with SaveFileDialog. But I need to do this asynchronously on a separate thread.

So far, I came up with this code, but I don't know if my approach is flawed or OK:

        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
            }
        }
}

My logic in the code above is: on button click create a new thread, pass the DoWorkDownload() method to the thread, and then start it; at that moment it is supposed to enter the work method - however, when debugging it never enters DoWorkDownload().

Does anyone know what I am missing?

Thank you.

Was it helpful?

Solution

You can use the BackgroundWorker, which is easy to use.

Also, I'm not sure it's completely safe (I could be wrong, though) to show the SaveFileDialog in the new thread. My recommendation would be a flow as such:

  1. Show SaveFileDialog on main thread.
  2. Pass file name to a method, which is then called asynchronously.

Here's an example implementation, without the use of 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)
}

Note that all actions performed on non-UI threads, must only affect non-UI elements. If you want to modify UI elements, you should marshal the call back to the UI thread, by using Control.Invoke (e.g. this.Invoke). See this post for further details.

OTHER TIPS

In my case debugger DO enters DoWorkDownload() It enters after the end of btnDownload_Click() Set breakpoint on SaveFileDialog sfd = new SaveFileDialog(); and it should work

In order to prove that it works asynchronously I even put the following code

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

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

and run without debugger and you will see that when current thread is going to sleep, SaveFileDialog will appear... and only after 10 seconds a message box will be shown

Bernhof might be right, however, be careful. All UI elements should execute on the same thread. So if you create a new thread for the SFD, make sure you don't update any control on your main window.

Kind regards, Guillaume Hanique

in your class

private object sync_temp = new object();

and in thread method

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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top