我尝试构建将生成Crystal Reports文件的 .NET 4.0 应用程序。 我有一个工作版本,但一切都在同步工作 - 点击生成按钮应用程序冻结5秒后 而不是我想显示会说文件正在生成的进度指示器,但我的代码有问题。
我生成报告的方法如下所示:
public static Task<string> GenerateLetter()
{
const string destinationLocation = @"C:\Export";
const string source = @"C:\Test_Report.rpt";
return Task<string>.Factory.StartNew(() =>
{
if (File.Exists(source))
{
var crReportDocument = new ReportDocument();
crReportDocument.Load(source);
var destinationFolder = new DirectoryInfo(destinationLocation);
if (!destinationFolder.Exists)
destinationFolder.Create();
var timeStamp = DateTime.Now.ToString().Replace(":", "").Replace(" ", "").Replace("-", "");
var destination = Path.Combine(destinationFolder.FullName, timeStamp + ".pdf");
var crDiskFileDestinationOptions = new DiskFileDestinationOptions { DiskFileName = destination };
var crExportOptions = crReportDocument.ExportOptions;
{
crExportOptions.DestinationOptions = crDiskFileDestinationOptions;
crExportOptions.ExportDestinationType = ExportDestinationType.DiskFile;
crExportOptions.ExportFormatType = ExportFormatType.PortableDocFormat;
}
try
{
crReportDocument.Export();
return destination;
}
catch (Exception ex)
{
throw new SystemException("Error exporting!", ex);
}
}
throw new FileNotFoundException("Report file not found!", source);
});
}
Method返回生成文件的本地化,如果出现问题则抛出异常。
在我的表单中,我放置了一个按钮和一个选框进度条。我已将此处理程序附加到按钮的点击:
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
try
{
Task<string> xx = ReportGenerator.GenerateLetter();
MessageBox.Show(xx.Result);
progressBar1.Visible = false;
}
catch (AggregateException ae)
{
ae.Handle(x =>
{
if (x is FileNotFoundException)
{
var ex = x as FileNotFoundException;
MessageBox.Show(ex.Message,"File not found");
progressBar1.Visible = false;
}
else if (x is SystemException)
{
var ex = x as SystemException;
MessageBox.Show(ex.Message,"Other exception");
progressBar1.Visible = false;
}
return true;
});
}
}
我的问题:
答案 0 :(得分:3)
Task.Result
阻止调用,因此您的UI线程会冻结,直到任务完成。
解决此问题的方法是使用新的async
/ await
功能。您甚至可以在.NET 4.0中使用它,但您需要包含async targeting pack for .NET 4.0。
使您的事件处理程序方法异步并且&#34;等待&#34;任务。
private async void button1_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
try
{
string result =await ReportGenerator.GenerateLetter();
MessageBox.Show(result);
progressBar1.Visible = false;
}
catch{
........
}
答案 1 :(得分:3)
Task是开始时的好点。
您可以在4.0和4.5 .net框架中使用它们。
为了确保您的TPL任务在线程上运行,与UI线程不同,您应该使用TaskScheduler.Default(例如,查看this thread)。
所以你应该这样做:
Task<string>.Factory.StartNew(() =>
{
// Your logic here
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith((p) =>
{
// This will be UI thread
// p is the parent task
progressBar1.Visible = false;
// if parent task has faulted
if (p.IsFaulted)
{
// Do with p.Exception field what ever you want - log it, show it.
// for .net 4.0 you must read this property in order to prevent
// application failure
MessageBox.Show(p.Exception.Message);
return;
}
// Here you know all about the parent task, so you can do the logic you want:
MessageBox.Show(p.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
由于此TaskScheduler.FromCurrentSynchronizationContext()参数,将在UI线程上执行继续任务。
在.net 4.0中,您还必须处理延续任务中的异常。例如,你可以使用try-catch块来完成它。