简体   繁体   English

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

[英]how can i make a threadsafe progress bar in C#? what am i doing wrong?

Hey everyone, can someone let me know what they see wrong with this code ? 大家好,有人可以让我知道他们认为这段代码有什么问题吗?

it throws "Cross-thread operation not valid" exception, on 它会引发“跨线程操作无效”异常

_DialogueThread.Start();

but if i remove "owner" from 但是如果我从中删除“所有者”

_progressDialogue = new Progresser{Owner = _owner, StartPosition = FormStartPosition.CenterParent};

the exception wont be thrown but the progressDialouge will be shown then hidden right away . 不会抛出异常,但是将显示progressDialouge然后立即隐藏。

now i understand why this the error is thrown if i set the progressDialouge.Owner to a parent form that was created on a different thread. 现在我明白了为什么如果我将progressDialouge.Owner设置为在另一个线程上创建的父窗体,则会引发此错误。 but why dose the form disappears when i dont ? 但是为什么当我不剂量形式消失了? what am i doing wrong ? 我究竟做错了什么 ?

thanks 谢谢

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

In your action (button click), i would create the progress dialog, and then fire off the background worker. 在您的操作(单击按钮)中,我将创建进度对话框,然后触发后台工作人员。 The background worker then reports back to the dialog in the ProgressChanged event. 然后,后台工作程序将在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,
        });
    }
}

There are a few mistakes in your approach. 您的方法中有一些错误。 Let me point them out one by one. 让我一一指出。

  • You inherit BackgroundWorker . 您继承BackgroundWorker That is fine. 那也行。 But you create another thread inside ( _DialogueThread ). 但是,您在( _DialogueThread )中创建了另一个线程。 There is no need. 没有必要。 DoWork() method runs in a separate thread. DoWork()方法在单独的线程中运行。

  • You create/use/manipulate a UI element in another thread. 您在另一个线程中创建/使用/操作UI元素。 Now, always remember. 现在,永远记住。 A Thread never creates a UI element. Thread永远不会创建UI元素。 Its the other way around. 相反。 A UI element creates a Thread . UI元素创建一个Thread Progresser in your case should be creating a new Thread or using BackgroundWorker to do any background work you require. 在您的情况下, Progresser应该创建一个新的Thread或使用BackgroundWorker进行所需的任何后台工作。

` `

Some days ago I had the same trouble. 几天前,我遇到了同样的麻烦。 This was helped me: "MSDN. How to: Make Thread-Safe Calls to Windows Forms Controls" I used first aproach (checking InvokeRequired) because it is easiest way. 这对我有所帮助: “ MSDN。如何:对Windows窗体控件进行线程安全的调用”我首先使用了一种方法(检查InvokeRequired),因为这是最简单的方法。

Hope it is helpful advise! 希望对您有所帮助!

Yes decyclone is right, there are many mistakes in the code and in your approach to solution. 是的,decyclone是正确的,在代码和解决方案中有很多错误。

You are inherting BackgroundWorker type but subscribing to its own events? 您要继承BackgroundWorker类型,但要订阅它自己的事件? Instead override the methods that are responsible for raising the event in your class. 而是重写负责引发类中的事件的方法。 ex: Instead of subscribing to DoWork, override OnDoWork method. 例如:取代订阅DoWork,请重写OnDoWork方法。

I've created a sample application, in which a Form (when performing a background task) shows another Form (BackgroundWorkUINotification) and starts the background task in BackgroundWorker thread. 我创建了一个示例应用程序,其中一个Form(执行后台任务时)显示另一个Form(BackgroundWorkUINotification),并在BackgroundWorker线程中启动后台任务。 The BackgroundWorkUINotification notifies the main form when the Form's CancelButton is clicked. 单击窗体的“取消按钮”时,BackgroundWorkUINotification通知主窗体。

The main Form when notified, closes the notifier and cancels the background task. 主窗体在收到通知后,将关闭通知程序并取消后台任务。

Code below: BackgroundWorkUINotification Form 下面的代码: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();
    }
}

Main Form 主要形式

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

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

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