如何异步使用SaveFileDialog?

发布于 2024-07-30 08:49:08 字数 1020 浏览 4 评论 0原文

我有一个 Windows 窗体应用程序,带有一个按钮 - 在按钮的事件处理程序上,我需要使用 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()。

有谁知道我缺少什么?

谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

江湖正好 2024-08-06 08:49:08

您可以使用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)
}

请注意,在非 UI 线程上执行的所有操作都必须仅影响非 UI 元素。 如果要修改 UI 元素,则应使用 Control.Invoke(例如 this.Invoke)将回调编组回 UI 线程。 请参阅这篇文章了解更多详情。

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.

花之痕靓丽 2024-08-06 08:49:08

在我的例子中,调试器 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 秒后才会出现一个消息框被展示

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

彼岸花似海 2024-08-06 08:49:08

伯恩霍夫也许是对的,但要小心。 所有 UI 元素应在同一线程上执行。 因此,如果您为 SFD 创建新线程,请确保不要更新主窗口上的任何控件。

亲切的问候,
纪尧姆·哈尼克

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

暗喜 2024-08-06 08:49:08

我知道这是一个非常古老的问题,但由于我遇到了同样的问题,我想分享我的现代解决方案:
修复方法只是在“await Task.Run(()”之前调用对话框:

public async Task<string> DownloadFile(string pFilter = "MyNotes document|*.note|All Files|*.*", string pDefaultExt = "note")
{
    string _ret = string.Empty;
    try
    {
        if (FilesRequest != null)
        {
            SaveFileDialog SFDialog = new SaveFileDialog()
            {
                Filter = pFilter,
                FilterIndex = 0,
                DefaultExt = pDefaultExt,
                AddExtension = true,
                CheckPathExists = true,
                OverwritePrompt = true,
                FileName = FileData.Name,
                InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
            };

            if (SFDialog.ShowDialog() == DialogResult.OK)
            {
                await Task.Run(() =>
                {
                    DownloadedFilePath = SFDialog.FileName;
                    using (var download_stream = new System.IO.FileStream(SFDialog.FileName, System.IO.FileMode.Create))
                    {
                        var DS = FilesRequest.DownloadWithStatus(download_stream);
                        if (DS.Status == Google.Apis.Download.DownloadStatus.Completed)
                        {
                            _ret = SFDialog.FileName;
                        }
                        else
                        {
                            _ret = DS.Status.ToString();
                        }
                    }
                });
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return _ret;
}

然后像这样调用它:

this.FilePath = await API.DownloadFile();

I know this is a very old question, but since i had this same problem, i want to share my solution for modern times:
The fix is simply to call the Dialog before the 'await Task.Run(()':

public async Task<string> DownloadFile(string pFilter = "MyNotes document|*.note|All Files|*.*", string pDefaultExt = "note")
{
    string _ret = string.Empty;
    try
    {
        if (FilesRequest != null)
        {
            SaveFileDialog SFDialog = new SaveFileDialog()
            {
                Filter = pFilter,
                FilterIndex = 0,
                DefaultExt = pDefaultExt,
                AddExtension = true,
                CheckPathExists = true,
                OverwritePrompt = true,
                FileName = FileData.Name,
                InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
            };

            if (SFDialog.ShowDialog() == DialogResult.OK)
            {
                await Task.Run(() =>
                {
                    DownloadedFilePath = SFDialog.FileName;
                    using (var download_stream = new System.IO.FileStream(SFDialog.FileName, System.IO.FileMode.Create))
                    {
                        var DS = FilesRequest.DownloadWithStatus(download_stream);
                        if (DS.Status == Google.Apis.Download.DownloadStatus.Completed)
                        {
                            _ret = SFDialog.FileName;
                        }
                        else
                        {
                            _ret = DS.Status.ToString();
                        }
                    }
                });
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return _ret;
}

Then call it like this:

this.FilePath = await API.DownloadFile();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文