经过2个小时的研究,我仍然找不到解决问题的方法。
我所做的任务是处理BackGroundWorker线程中的一些文件。但是,有时我需要使用ShowDialog让用户选择SaveFile位置,但我收到STA / MTA错误。
MainForm代码:
private void button2_Click(object sender, EventArgs e)
{
button1.Enabled = false;
ProcessReportWorker.RunWorkerAsync();
}
DoWork代码:
void ProcessReportWorker_DoWork(object sender, DoWorkEventArgs e)
{
int ReportCount = Reports.Count();
foreach (string Report in Reports)
{
ProcessReport NewReport = new ProcessReport(Report);
string result = NewReport.Start();
}
}
ProcessReport.Start()代码:
class ProcessReport
{
public string Start()
{
if(File.Exists(ResultPath))
{
SaveFileDialog SaveReport = new SaveFileDialog();
SaveReport.InitialDirectory = "c:\somepath";
SaveReport.CheckPathExists = true;
SaveReport.DefaultExt = ".xls";
SaveReport.OverwritePrompt = true;
SaveReport.ValidateNames = true;
if (SaveReport.ShowDialog() == DialogResult.OK)
{
ResultPath = SaveReport.FileName;
if (File.Exists(ResultPath)) File.Delete(ResultPath);
}
}
}
}
如您所见,在某些情况下需要ShowDialog。 我相信这可以通过代表来完成,但我对代表并不熟悉。我确实在Calling ShowDialog in BackgroundWorker尝试了Jon的解决方案,但我无法让它工作。 (也许我对代表做错了什么?)
有人请帮帮我。如果需要,请提供给代表的代码。谢谢!
编辑: PoweredByOrange提供的解决方案有效。 HOwever,我不得不做一点改变:
this.Invoke((MethodInvoker)委托{....}); 不起作用,因为 - 目的是引用MainForm实例,但此代码存在于ProcessReport类中。所以这里的“ this ”指的是ProcessReport类实例,但它必须引用GUI实例(MainForm实例)才能工作。
我的修复: 我将MainForm的一个实例发送到ProcessReport类并进行了如下所述的更改:
IN DoWork:
ProcessReport NewReport = new ProcessReport(Report, this); //CHANGE: Sending 'this'
//this sends reference of MainForm(GUI) to the ProcessReport Class
在ProcessReport类中:
class ProcessReport
{
MainForm MainFormInstance;
public ProcessReport(string report, MainForm x)
{
MainFormInstance = x;
}
public string Start()
{
MainFormInstance.Invoke((MethodInvoker)delegate //changed this.Invoke to MainFormInstance.Invoke
{
SaveFileDialog SaveReport = new SaveFileDialog();
SaveReport.InitialDirectory = "c:\somepath";
SaveReport.CheckPathExists = true;
SaveReport.DefaultExt = ".xls";
SaveReport.OverwritePrompt = true;
SaveReport.ValidateNames = true;
if (SaveReport.ShowDialog() == DialogResult.OK)
{
ResultPath = SaveReport.FileName;
if (File.Exists(ResultPath)) File.Delete(ResultPath);
}
});
}
}
所以上面的事情终于奏效了。感谢PoweredByOrange,我理解得很清楚。
答案 0 :(得分:4)
您获得异常的原因是因为只允许拥有控件的线程修改/访问它。在这种情况下,SaveFileDialog
属于您的主线程,但Start()
方法在不同的(即后台)线程中运行。因此,在这种情况下,后台线程需要要求主线程打开它的SaveFileDialog 。
public string Start()
{
if(File.Exists(ResultPath))
{
this.Invoke((MethodInvoker)delegate
{
SaveFileDialog SaveReport = new SaveFileDialog();
SaveReport.InitialDirectory = "c:\somepath";
SaveReport.CheckPathExists = true;
SaveReport.DefaultExt = ".xls";
SaveReport.OverwritePrompt = true;
SaveReport.ValidateNames = true;
if (SaveReport.ShowDialog() == DialogResult.OK)
{
ResultPath = SaveReport.FileName;
if (File.Exists(ResultPath)) File.Delete(ResultPath);
}
});
}
}
为了更清楚,假设您希望您的朋友给您一个他的教科书。你不能去你朋友的房间偷书。您可以做的是打电话给您的朋友(调用)并寻求帮助(代表)。
答案 1 :(得分:0)
不确定这是否有帮助,但这里是我能为您提供的最简单的委托/事件代码;
public static class CacheManager
{
private static CacheEntryRemovedCallback callback = null;
public delegate void CacheExpiredHandler(string key);
public static event CacheExpiredHandler CacheExpired;
static CacheManager()
{
// create the callback when the cache expires.
callback = new CacheEntryRemovedCallback(MyCachedItemRemovedCallback);
}
private static void MyCachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
{
if (CacheExpired != null)
CacheExpired(arguments.CacheItem.Key);
}
public static class DataManager
{
static DataManager()
{
// when a cached list expires, notify me with the key of the list.
CacheManager.CacheExpired += new CacheManager.CacheExpiredHandler(CacheManager_CacheExpired);
}
/// <summary>
/// When a chached list expires, this is the callback method that is called.
/// </summary>
/// <param name="key">The key of the list that just expired.</param>
static void CacheManager_CacheExpired(string key)
{
// Do something now because the cache manager has raised an event that it has expired.
}