web-dev-qa-db-ja.com

C#WinformProgressBarおよびBackgroundWorker

私は次の問題を抱えています:

MainFormという名前のフォームがあります。私はこのフォームで行われる長い操作があります。

この長い操作が行われている間、MainFormの上にProgressFormという名前の別のものを表示する必要があります。

ProgressFormには、長い操作が行われている間に更新する必要があるプログレスバーが含まれています。

長い操作が完了すると、ProgressFormは自動的に閉じられます。

私は次のコードを書きました:

_using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ClassLibrary
{
    public class MyClass
    {
        public static string LongOperation()
        {
            Thread.Sleep(new TimeSpan(0,0,30));

            return "HelloWorld";
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BackgroungWorker__HelloWorld
{
    public partial class ProgressForm : Form
    {
        public ProgressForm()
        {
            InitializeComponent();
        }

        public ProgressBar ProgressBar
        {
            get { return this.progressBar1; }
            set { this.progressBar1 = value; }
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using ClassLibrary;

namespace BackgroungWorker__HelloWorld
{
    public partial class MainForm : Form
    {
        ProgressForm f = new ProgressForm();

        public MainForm()
        {
            InitializeComponent();  
        }

        int count = 0;
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (f != null)
            {
                f.ProgressBar.Value = e.ProgressPercentage;
            }

            ++count;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled) 
            {  
                MessageBox.Show("The task has been cancelled");  
            }  
            else if (e.Error != null)  
            {                  
                MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());  
            }  
            else 
            {  
                MessageBox.Show("The task has been completed. Results: " + e.Result.ToString());  
            }
        }


        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            if (f == null)
            {
                f = new ProgressForm();
            }

            f.ShowDialog();

            //backgroundWorker1.ReportProgress(100);

            MyClass.LongOperation();

            f.Close();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();

            this.Close();
        }
    }
}
_

progressBarを更新する方法が見つかりません。

backgroundWorker1.ReportProgress()をどこに配置すればよいですか?また、これをどのように呼び出す必要がありますか?

アプリケーションのこのレイヤーで操作が完了するまでに何が起こるか、またはどのくらいの時間がかかるかわからないため、MyClassを変更してはなりません。

誰か助けてもらえますか?

15
user366312

1つの問題はあなたが30秒間眠っているということです。通常、長時間実行されるタスク内のさまざまなポイントでReportProgressを呼び出します。したがって、これを示すために、コードを1秒間スリープするように変更することもできますが、30回、スリープが終了するたびにReportProgressを呼び出します。

もう1つの問題は、ProgressFormfromバックグラウンドスレッドを表示していることです。 [〜#〜] ui [〜#〜]スレッドで開始する必要がありますが、バックグラウンドワーカーのProgressChangedイベントをフックします。その後、バックグラウンドワーカーが進捗状況を報告すると、進捗状況フォームが更新されます。

12
Jon Skeet

設定する必要があることに注意してください

backgroundWorker1.WorkerReportsProgress = true;

バックグラウンドワーカーにProgressChangedイベントを発生させたい場合

backgroundWorker1.WorkerSupportsCancellation = true;

ワーカースレッドをキャンセルできるようにしたい場合。

他の人が言っているように、UIスレッドでf.ShowDialog()を実行し、ProgressChangedを使用してProgressWindowを更新します。

ProgressFormを自動的に閉じるには、backgroundWorker1_RunWorkerCompletedでf.Close();を呼び出します。これは、長い操作が完了し、ウィンドウを閉じることができることを意味します。

2
Alastair Pitts

ReportProgressは、「working」メソッドで呼び出す必要のあるメソッドです。このメソッドは「ProgressChanged」イベントを発生させます。

フォームでは、イベントハンドラーをProgressChangedイベントにアタッチできます。そのイベントハンドラー内で、プログレスバーの位置を変更できます。イベントハンドラーをRunWorkerCompletedイベントにアタッチすることもでき、そのイベントハンドラー内で、プログレスバーを含むフォームを閉じることができます。

2

ここで必要なのは、メインフォーム全体をクラスに渡すのではなく、単にBackgroundWorkerのインスタンスに渡すことです。次のように、送信者をバックグラウンドワーカーとしてキャストする必要があります。

_private void bgWorker_DoWork(object sender DoWorkEventArgs e)
{
    // Get the BackgroundWorker that raised this event
    BackgroundWorker worker = sender as BackgroundWorker;

    // And now you just send worker to whatever class you need like so:
    bgArgs args = e.Argument as bgArgs;
    MyClass objMyClass = new MyClass();

    MyClass.MyMethod(strValue, args.Option, worker);

    // Do something based on return value.
}
_

次に、MyClass.MyMethod()で進行状況の計算を行い、worker.ReportProgress(int percentage)イベントを発生させて、メインUIフォームの進行状況バーなどを更新します。

これは完全に機能するはずです!

詳細については、このMSDNの記事を確認してください。フィボナッチの例を参照してください。CalculateFibonacciはメインフォームのUIに更新を送信するカスタムクラスであるため、これが実行されます。

詳細については、 MSDN BackgroundWorker Class を参照してください。

0
linnx88