我有一个 Windows 窗体应用程序,带有一个按钮 - 在按钮的事件处理程序上,我需要使用 SaveFileDialog 下载文件。但我需要在单独的线程上异步执行此操作。
到目前为止,我想出了这段代码,但我不知道我的方法是否有缺陷或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
}
}
}
上面代码中我的逻辑是:单击按钮创建一个新线程,将 DoWorkDownload() 方法传递给该线程,然后启动它;在那一刻,它应该进入工作方法 - 然而,在调试时它永远不会进入 DoWorkDownload()。
有人知道我错过了什么吗?
谢谢你。
您可以使用BackgroundWorker,它很容易使用。
另外,我不确定在新线程中显示 SaveFileDialog 是否完全安全(不过我可能是错的)。我的建议是这样的流程:
这是一个示例实现,没有使用
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 线程。请参阅这篇文章了解更多详情。
在我的例子中,调试器 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 秒后才会显示消息框
伯恩霍夫可能是对的,但要小心。所有 UI 元素应在同一线程上执行。因此,如果您为 SFD 创建新线程,请确保不要更新主窗口上的任何控件。
亲切的问候, 纪尧姆·哈尼克
我知道这是一个非常古老的问题,但由于我也遇到了同样的问题,我想分享我的现代解决方案: 修复方法只是在“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();