如何在C#中创建一个线程安全的进度条?我究竟做错了什么?

时间:2011-01-23 06:35:35

标签: c# .net winforms

嘿大家,有人能让我知道他们看错了这段代码吗?

上抛出“跨线程操作无效”异常
_DialogueThread.Start();

但如果我从

中删除“所有者”
_progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};

不会抛出异常,但会显示progressDialouge,然后立即隐藏。

现在我理解为什么如果我将progressDialouge.Owner设置为在不同线程上创建的父窗体,则会抛出此错误。但为什么剂量表格消失时,我不?我做错了什么?

感谢

class Sampleer : BackgroundWorker
{
    private Progresser _progressDialogue;
    private Thread _DialogueThread;
    private Form _owner;
    private bool _SampleSuccess;

    public Sampleer(Form owner)
    {
        _owner = owner;
        _progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};
        _progressDialogue.Closed += ProgressDialogueClosed;
        WorkerReportsProgress = true;
        WorkerSupportsCancellation = true;
        DoWork += Sampleer_DoWork;
        RunWorkerCompleted += Sampleer_RunWorkerCompleted;
        ProgressChanged += Sampleer_ProgressChanged;
    }

    private void Sampleer_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {

       //UPDATE STATUS CODE IS HERE

    }

    void ProgressDialogueClosed(object sender, EventArgs e)
    {
        CancelAsync();
        Dispose();
    }

    void Sampleer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //FINISH PROCESS
    }


    void Sampleer_DoWork(object sender, DoWorkEventArgs e)
    {

        _DialogueThread = new Thread(_progressDialogue.Show);
        _DialogueThread.Start();


        //DO LONG PROCESS HERE
    }
}

4 个答案:

答案 0 :(得分:1)

在您的操作(按钮单击)中,我将创建进度对话框,然后关闭后台工作程序。然后,后台工作程序将报告回ProgressChanged事件中的对话框。

public partial class MainWindow : Window {

    private void btnDoSomething_Click(object sender0, RoutedEventArgs e0) {

        _progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};
        _progressDialogue.Closed += ProgressDialogueClosed;
        _progressDialogue.Show();

        BackgroundWorker worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;
        worker.DoWork += delegate(object sender, DoWorkEventArgs e) {
            DoSomething();

            e.Result = result;
        };
        worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e) {
            progressDialogue.Update()
        };
        worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
            progressDialogue.Close()
        };
        worker.RunWorkerAsync(new CustomArgs() {
            SomeValue = txtValue.Text,
        });
    }
}

答案 1 :(得分:1)

你的方法有一些错误。让我一个一个地指出它们。

  • 您继承BackgroundWorker。那样就好。但是你在里面创建了另一个线程(_DialogueThread)。没有必要。 DoWork()方法在一个单独的线程中运行。

  • 您在另一个线程中创建/使用/操作UI元素。现在,永远记住。 Thread永远不会创建UI元素。它的另一种方式。 UI元素会创建Thread。在您的情况下,Progresser应该创建新的Thread或使用BackgroundWorker来执行您需要的任何后台工作。

`

答案 2 :(得分:0)

是的,decyclone是对的,代码和解决方法中存在许多错误。

您是否正在使用BackgroundWorker类型但订阅自己的事件? 而是覆盖负责在类中引发事件的方法。 例如:不是订阅DoWork,而是覆盖OnDoWork方法。

我已经创建了一个示例应用程序,其中一个Form(执行后台任务时)显示另一个Form(BackgroundWorkUINotification)并在BackgroundWorker线程中启动后台任务。单击表单的CancelButton时,BackgroundWorkUINotification会通知主窗体。

通知主表单时,关闭通知程序并取消后台任务。

以下代码:BackgroundWorkUINotification表格

public partial class BackgroundWorkUINotification : Form
    {
        public event EventHandler CancelClicked;

    public BackgroundWorkUINotification()
    {
        InitializeComponent();

        // call code to animate progress bar..
        // probably in another BackgroundWorker that reports progress..            

        this.cancelButton.Click += HandleCancelButtonOnClick;
    }


    protected virtual void OnCancelClicked()
    {
        if (CancelClicked != null)
            this.CancelClicked(this, EventArgs.Empty);
    }

    private void HandleCancelButtonOnClick(Object sender, EventArgs e)
    {
        this.OnCancelClicked();
    }
}

主要表格

public partial class Form1 : Form
{
    private BackgroundWorker backgroundWorker;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        this.backgroundWorker = new BackgroundWorker();
        this.backgroundWorker.DoWork += HandleBackgroundWorkerOnDoWork;
        this.backgroundWorker.RunWorkerCompleted += HandleBackgroundWorkerOnRunWorkerCompleted;
        this.backgroundWorker.WorkerSupportsCancellation = true;
    }

    private void HandleDataRequest()
    {
        // show UI notification...
        BackgroundWorkUINotification backgroundWorkUINotification = new BackgroundWorkUINotification();
        backgroundWorkUINotification.CancelClicked += HandleBackgroundWorkUINotificationOnCancelClicked;
        backgroundWorkUINotification.Show(this);

        // start the background worker 
        this.backgroundWorker.RunWorkerAsync();
    }

    private void HandleBackgroundWorkUINotificationOnCancelClicked(Object sender, EventArgs e)
    {
        // UI notification Form, Cancelled
        // close the form...
        BackgroundWorkUINotification backgroundWorkUINotification = (BackgroundWorkUINotification)sender;
        backgroundWorkUINotification.Close();

        // stop the background worker thread...
        if (backgroundWorker.IsBusy)
            backgroundWorker.CancelAsync();
    }

    private void HandleBackgroundWorkerOnRunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
    {

    }

    private void HandleBackgroundWorkerOnDoWork(Object sender, DoWorkEventArgs e)
    {
        // do some work here..
        // also check for CancellationPending property on BackgroundWorker
    }
}

答案 3 :(得分:0)

前几天我遇到了同样的麻烦。 这对我很有帮助:"MSDN. How to: Make Thread-Safe Calls to Windows Forms Controls" 我使用了第一个aproach(检查InvokeRequired)因为它是最简单的方法。

希望有用的建议!