1

经过2个小时的研究,我仍然找不到解决问题的方法。

我所做的任务是在 BackGroundWorker 线程中处理一些文件。但是,有时我需要使用 ShowDialog 让用户选择 SaveFile 位置,但我收到 STA/MTA 错误。

主窗体代码:

private void button2_Click(object sender, EventArgs e)
{
            button1.Enabled = false;
            ProcessReportWorker.RunWorkerAsync();
}

工作代码:

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。我相信这可以使用代表来完成,但我对代表不太熟悉。我确实尝试了 Jon 在BackgroundWorker 中调用 ShowDialog的解决方案,但我无法让它工作。(也许我对代表做错了什么?)

有人请帮我解决这个问题。如果需要,请向我提供代表的代码。谢谢!

编辑: PoweredByOrange 给出的解决方案有效。但是,我不得不对其进行一些小改动:

this.Invoke((MethodInvoker)delegate{....}); 没有工作,因为 - 目的是引用 MainForm 实例,但此代码存在于 ProcessReport 类中。所以这里的“ this ”是指ProcessReport类的实例,但它必须引用GUI实例(MainForm实例)才能工作。

我的修复:我将 MainForm 的一个实例发送到 ProcessReport 类并进行了如下所述的更改:

在 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,我对此非常了解。

4

2 回答 2

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);
                    }
                });
        }
    }

为了更清楚,假设您希望您的朋友给您他的一本教科书。你不能去你朋友的房间偷书。你能做的就是打电话给你的朋友(invoke)并请求帮助(delegate)。

于 2013-08-13T22:04:39.540 回答
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.
    }
于 2013-08-13T22:01:31.167 回答