简体   繁体   English

BackGroundWorker Thread中的ShowDialog和UI交互

[英]ShowDialog and UI interaction in BackGroundWorker Thread

After 2 hours of researching, i still couldn't find a solution to my problem. 经过2个小时的研究,我仍然无法找到解决问题的方法。

The task I do is process some files in the BackGroundWorker thread. 我所做的任务是处理BackGroundWorker线程中的一些文件。 However, sometimes I need to use ShowDialog to let the user choose the SaveFile location but i'm getting the STA/MTA error. 但是,有时我需要使用ShowDialog让用户选择SaveFile位置,但我收到STA / MTA错误。

MainForm code: MainForm代码:

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

DoWork Code: 工作代码:

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() Code: 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);
                    }
        }
    }
}

As you can see, the ShowDialog is needed in some cases. 如您所见,在某些情况下需要ShowDialog。 I believe this can be done using delegates but i'm not much familiar with delegates. 我相信这可以通过代表来完成,但我对代表并不熟悉。 I did try the solution by Jon in Calling ShowDialog in BackgroundWorker but i couldn't get it to work. 我确实尝试过Jon在CallWorker调用ShowDialog的解决方案,但我无法让它工作。 (maybe i'm doing something wrong with delegates?) (也许我对代表做错了什么?)

Someone please help me with this. 有人请帮帮我。 Please provide me the code for delegates if needed for this. 如果需要,请提供给代表的代码。 Thanks! 谢谢!

EDIT: Solution given by PoweredByOrange worked. 编辑: 由PoweredByOrange提供的解决方案。 HOwever, i had to make a small change to it: HOwever,我不得不对它做一个小改动:

this.Invoke((MethodInvoker)delegate{....}); this.Invoke((MethodInvoker)代表{....}); did not work because - the intention is to refer to the MainForm instance but this code exists in the ProcessReport Class. 不起作用,因为 - 意图是引用MainForm实例,但此代码存在于ProcessReport类中。 So the " this " here is referring to the ProcessReport class instance, but it must refer to the GUI instance (MainForm instance) to work. 所以这里的“ this ”指的是ProcessReport类实例,但它必须引用GUI实例(MainForm实例)才能工作。

My Fix: I sent an instance of the MainForm to the ProcessReport class and made the changes as mentioned below: 我的修复:我将一个MainForm实例发送到ProcessReport类并进行了如下所述的更改:

IN DoWork: 在DoWork中:

ProcessReport NewReport = new ProcessReport(Report, this); //CHANGE: Sending 'this'
//this sends reference of MainForm(GUI) to the ProcessReport Class

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

So the above thing finally worked. 所以上面的事情终于奏效了。 I understood this pretty well, thanks to PoweredByOrange. 感谢PoweredByOrange,我对此非常了解。

The reason you're getting the exception is because only the thread that owns a control is allowed to modify/access it. 您获得异常的原因是因为只允许拥有控件的线程修改/访问它。 In this case, the SaveFileDialog belongs to your main thread, but the Start() method is running in a different (ie background) thread. 在这种情况下, SaveFileDialog属于您的主线程,但Start()方法在不同的(即后台)线程中运行。 Therefore, the background thread in this case needs to ask the main thread to open up its SaveFileDialog . 因此,在这种情况下,后台线程需要请求主线程打开其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);
                    }
                });
        }
    }

To make it more clear, assume you want your friend to give you one of his textbooks. 为了使它更清楚,假设你希望你的朋友给你的教科书之一。 You are NOT allowed to go to your friend's room and steal the book. 你不能去你朋友的房间偷书。 What you could do, is call your friend ( invoke ) and ask for a favor ( delegate ). 你可以做的是打电话给你的朋友( 调用 )并寻求帮助( 代表 )。

Unsure if this will help but here is the simplest delegate / event code I can provide you; 不确定这是否有帮助,但这里是我能为您提供的最简单的委托/事件代码;

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.
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM