我有一个小程序,它以应用栏开头(停靠在桌面上的窗口,可以隐藏起来(不使用时位于桌面的顶部,右侧,底部或左侧)。用户将文件从任意给定位置拖到应用栏上,然后将其转换为PDF(通过将每个文件转换为PDF,然后将生成的PDF合并为一个PDF文件)。
使用后台工作程序在单独的线程上运行转换过程。后台工作人员获取文件列表后,我将弹出一个模式对话框,并在其中加载了相关文件的列表视图,并允许用户在最终合并过程之前对其进行重新排序。
我在模式对话框中遇到跨线程问题。我在高处和低处寻找解决方案,但感到困惑。由于使用关键字this导致出现问题。
我在后台工作人员中尝试了以下代码:
using (var md = new MergeDlg())
{
md.Files = (string[])files;
if (md.ShowDialog(this) == DialogResult.OK)
files = md.Files;
}
如果我删除关键字 this ,我不会出错,但是对话框的行为就像是在主线程上启动的,而backgroundworkerthread就像没有模式对话框一样继续运行-我理解< strong> 是 ,因为该对话框是在主UI线程上启动的。
我还尝试将对话框创建移出后台工作线程,并在线程中调用它 创建模式对话框的代码如下:
private string[] ShowMergeDlg(string[] files)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(() =>
{
MergeDlg md = new MergeDlg();
md.Files = (string[])files;
if (md.ShowDialog(this) == DialogResult.OK)
files = md.Files;
}
));
}
else
{
MergeDlg md = new MergeDlg();
md.Files = (string[])files;
if (md.ShowDialog(this) == DialogResult.OK)
files = md.Files;
}
return files;
}
在backgroundworker线程上,该函数称为:
files = ShowMergeDlg(files);
同样,该代码显然会以相同的结果在主UI线程上启动对话框。
我的问题是:
如何在backgroundworker线程上显示模式对话框,在线程对话框关闭之前暂停执行线程?
答案 0 :(得分:1)
您最好切换到异步/等待和任务。这里的样本非常有限
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click( object sender, EventArgs e )
{
button1.Enabled = false;
label1.Text = "acquire files ...";
ICollection<string> acquiredFiles = await AcquireFileAsync();
label1.Text = "select files ...";
ICollection<string> selectedFiles = SelectFilesDialog( acquiredFiles );
label1.Text = "process files ...";
await ProcessFilesAsync( selectedFiles );
label1.Text = "finished.";
button1.Enabled = true;
}
private async Task ProcessFilesAsync( ICollection<string> selectedFiles )
{
foreach (var item in selectedFiles)
{
await Task.Delay( 250 ).ConfigureAwait( false );
}
}
private ICollection<string> SelectFilesDialog( ICollection<string> acquiredFiles )
{
var dialog = new Form2();
dialog.ShowDialog();
return acquiredFiles;
}
private async Task<ICollection<string>> AcquireFileAsync()
{
await Task.Delay( 2500 ).ConfigureAwait( false );
return Enumerable.Range( 1, 20 ).Select( e => e.ToString() ).ToList();
}
}
答案 1 :(得分:0)
如果从后台工作人员调用ShowMergeDlg
,则InvokeRequired
为true,则对话框将在UI线程上创建。因此,只需删除它,让对话框在后台线程上创建即可。
private string[] ShowMergeDlg(string[] files)
{
/*
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(() =>
{
MergeDlg md = new MergeDlg();
md.Files = (string[])files;
if (md.ShowDialog(this) == DialogResult.OK)
files = md.Files;
}
));
}
else
*/
{
MergeDlg md = new MergeDlg();
md.Files = (string[])files;
if (md.ShowDialog(this) == DialogResult.OK)
files = md.Files;
}
return files;
}
我的测试代码
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 5; ++i)
{
this.Invoke(new Action(() => textBox1.AppendText("1")));
Thread.Sleep(500);
}
var f2 = new Form2();
if(f2.ShowDialog(this) == DialogResult.OK)
this.Invoke(new Action(() => textBox1.AppendText("2")));
else
this.Invoke(new Action(() => textBox1.AppendText("3")));
for (int i = 0; i < 100; ++i)
{
this.Invoke(new Action(() => textBox1.AppendText("1")));
Thread.Sleep(500);
}
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
}