C#多线程throbber表单

3
我正在处理一个C#项目,想在单独的窗体中实现“等待”指示符(throbber)。经过大量的研究和试错,似乎建议的方法是使用一个不同于当前窗体/线程的线程加载一个窗体。
我采用这种方法的原因是,最初使用Show()方法在throbber窗体上产生了一个透明的窗体。我不能使用ShowDialog,因为在throbber显示后我需要运行一些代码,并在完成后关闭throbber窗体。
无论如何……尝试许多不同的方法在单独的线程中加载throbber窗体,我仍然会得到一个关于尝试从与创建它的线程不同的线程访问它的错误。这是项目代码的一个框架版本,应该阐明我的问题:
我正在使用的多线程示例是一个受欢迎的链接,用于在单独的线程中创建自己的闪屏界面:http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
    public void CheckData()
    {
        try
        {
            ProgressBar pb = new ProgressBar();
            pb.ShowProgressBar();
            //do data checking here
            pb.CloseForm()
        }   
        catch(Exception e)
        {
        }
   }
}

public partial class ProgressBar : Form
{
    static Thread ms_oThread = null;
    public bool shouldStop = false;
    static ProgressBar ms_ProgBar = null;
    public ProgressBar()
    {
        InitializeComponent();
        //DoWork();
    }

    public void ShowForm()
    {
        ms_ProgBar = new ProgressBar();
        Application.Run(ms_ProgBar);
    }
    public void CloseForm()
    {
        ms_ProgBar.Close();
    }

    public void ShowProgressBar()
    {
        // Make sure it is only launched once.
        if (ms_ProgBar != null)
            return;
        ms_oThread = new Thread(new ThreadStart(ShowForm));
        ms_oThread.IsBackground = true;
        ms_oThread.SetApartmentState(ApartmentState.STA);
        ms_oThread.Start();
        while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
        {
            System.Threading.Thread.Sleep(1000);
        }
    }
}

可能是重复的问题:如何在C#中从另一个线程更新GUI? - mbeckish
我实际上并不想在加载后更改或编辑表单上的任何控件,只是在完成工作时关闭表单。我是否应该使用Invoke方法来访问CloseForm()方法? - user981017
1
不应该创建多个UI线程。这几乎肯定不是解决问题的正确方法,只会给您带来痛苦。与其创建第二个UI线程使您的主UI线程被阻塞;从而无法执行任何操作,请在主UI线程中显示进度条。 - Servy
1个回答

0

你正在创建两个ProgressBar。一个在主函数中,另一个在新线程中。你还从主函数中调用了CloseWindow方法(并且在从未显示的窗口上),而不是在新线程窗口上。

你只需要使用新线程创建ProgressBar并显示它。将静态ProgressBar字段设置为public,这样你就可以直接从Main函数中调用close方法,但一定要使用Invoke来执行,因为它不在该窗口的GUI线程上。

此外,ShowProgressBar应该是静态的。

以下是重写的尝试:

public class Main
{
    public void CheckData()
    {
        try
        {
            ProgressBar.ShowProgressBar();
            //do data checking here
            ProgressBar.CloseForm();
        }   
        catch(Exception e)
        {
        }
   }

}

public partial class ProgressBar : Form
{
    static ProgressBar _progressBarInstance;

    public ProgressBar()
    {
        InitializeComponent();
        //DoWork();
    }

    static void ShowForm()
    {
        _progressBarInstance = new ProgressBar();
        Application.Run(ms_ProgressBar);
    }

    public static void CloseForm()
    {
         _progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
         _progressBarInstance= null;
    }

    public static void ShowProgressBar()
    {
        // Make sure it is only launched once.
        if (_progressBarInstance != null)
           return;

        var ms_oThread = new Thread(new ThreadStart(ShowForm));
        ms_oThread.IsBackground = true;
        ms_oThread.SetApartmentState(ApartmentState.STA);
        ms_oThread.Start();
    }
}

VS在进度栏类中的get和set、public ProgressBar()、void ShowForm()、void CloseForm()和Action等项目上给我“Expected class, delegate, enum, interface或struct”的错误,对此我感到很抱歉无法解决问题。更多背景信息是,我尝试使用静态方法而不是使用实例变量,但得到了相同的问题,并且同意它似乎会打开两次,但不确定如何引用该类并在其自己的线程中运行。 - user981017
抱歉,可能是命名问题。尝试将属性重命名为其他名称。我会更新我的示例。 - Nathan A
获取以下内容...需要对象引用,以便为Invoke提供非静态字段、方法或属性'system.Windows.forms.Control.Invoke(System.Delegate)'。如果我将其更改为_ progressBarInstance.Close(),我会收到关于尝试访问存在于不同线程中的某些内容的原始错误。 - user981017
抱歉,应该是 _progressBarInstance.Invoke(new Action(_progressBarInstance.Close)); 更新示例。 - Nathan A
啊,成功了!谢谢。现在是难点。你能解释一下 _progressBarInstance.Invoke(new Action(_progressBarInstance.Close)); 和 .Close() 有什么不同吗? - user981017
不得不更新这个,因为CloseForm是一个静态方法,而Form.Invoke()Form.Close()不是。所以我们必须在_progressBarInstance实例上引用那些方法。 - Nathan A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接