[c#] WinForms 진행률 표시 줄을 사용하는 방법?

외부 라이브러리에서 수행되는 계산의 진행 상황을 보여주고 싶습니다.

예를 들어 계산 메서드가 있고 Form 클래스의 100000 값에 사용하려면 다음과 같이 작성할 수 있습니다.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Caluculate(int i)
    {
        double pow = Math.Pow(i, i);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        progressBar1.Maximum = 100000;
        progressBar1.Step = 1;

        for(int j = 0; j < 100000; j++)
        {
            Caluculate(j);
            progressBar1.PerformStep();
        }
    }
}

계산 후 단계를 수행해야합니다. 그러나 외부 방법으로 100000 개의 계산을 모두 수행하면 어떨까요? 이 방법이 진행률 표시 줄에 종속되지 않도록하려면 언제 “단계를 수행”해야합니까? 예를 들어 다음과 같이 쓸 수 있습니다.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void CaluculateAll(System.Windows.Forms.ProgressBar progressBar)
    {
        progressBar.Maximum = 100000;
        progressBar.Step = 1;

        for(int j = 0; j < 100000; j++)
        {
            double pow = Math.Pow(j, j); //Calculation
            progressBar.PerformStep();
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        CaluculateAll(progressBar1);
    }
}

하지만 그렇게하고 싶지 않습니다.



답변

BackgroundWorker를 살펴보실 것을 제안합니다 . WinForm에 큰 루프가 있으면 차단되고 앱이 중단 된 것처럼 보입니다.

BackgroundWorker.ReportProgress()UI 스레드로 진행 등을보고하는 방법을 볼 수 있습니다.

예를 들면 :

private void Calculate(int i)
{
    double pow = Math.Pow(i, i);
}

private void button1_Click(object sender, EventArgs e)
{
    progressBar1.Maximum = 100;
    progressBar1.Step = 1;
    progressBar1.Value = 0;
    backgroundWorker.RunWorkerAsync();
}

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    var backgroundWorker = sender as BackgroundWorker;
    for (int j = 0; j < 100000; j++)
    {
        Calculate(j);
        backgroundWorker.ReportProgress((j * 100) / 100000);
    }
}

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // TODO: do something with final calculation.
}


답변

.NET 4.5부터는 UI 스레드에 업데이트를 보내기 위해 Progress 와 함께 asyncawait 조합을 사용할 수 있습니다 .

private void Calculate(int i)
{
    double pow = Math.Pow(i, i);
}

public void DoWork(IProgress<int> progress)
{
    // This method is executed in the context of
    // another thread (different than the main UI thread),
    // so use only thread-safe code
    for (int j = 0; j < 100000; j++)
    {
        Calculate(j);

        // Use progress to notify UI thread that progress has
        // changed
        if (progress != null)
            progress.Report((j + 1) * 100 / 100000);
    }
}

private async void button1_Click(object sender, EventArgs e)
{
    progressBar1.Maximum = 100;
    progressBar1.Step = 1;

    var progress = new Progress<int>(v =>
    {
        // This lambda is executed in context of UI thread,
        // so it can safely update form controls
        progressBar1.Value = v;
    });

    // Run operation in another thread
    await Task.Run(() => DoWork(progress));

    // TODO: Do something after all calculations
}

작업은 현재 수행하는 작업을 구현하는 데 선호되는 방법 BackgroundWorker입니다.

작업 Progress은 여기에 자세히 설명되어 있습니다.


답변

Dot Net pearls에 대한 유용한 튜토리얼이 있습니다 : http://www.dotnetperls.com/progressbar

Peter와 동의하여 약간의 스레딩을 사용해야합니다. 그렇지 않으면 프로그램이 중단되어 목적이 다소 무너집니다.

ProgressBar 및 BackgroundWorker를 사용하는 예 : C #

using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            // Start the BackgroundWorker.
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 100; i++)
            {
                // Wait 100 milliseconds.
                Thread.Sleep(100);
                // Report progress.
                backgroundWorker1.ReportProgress(i);
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        }
    }
} //closing here


답변

거기에 Task그것은 사용 unnesscery이며, 존재 BackgroundWorker, Task더 간단합니다. 예를 들면 :

ProgressDialog.cs :

   public partial class ProgressDialog : Form
    {
        public System.Windows.Forms.ProgressBar Progressbar { get { return this.progressBar1; } }

        public ProgressDialog()
        {
            InitializeComponent();
        }

        public void RunAsync(Action action)
        {
            Task.Run(action);
        }
    }

끝난! 그런 다음 어디에서나 ProgressDialog를 다시 사용할 수 있습니다.

var progressDialog = new ProgressDialog();
progressDialog.Progressbar.Value = 0;
progressDialog.Progressbar.Maximum = 100;

progressDialog.RunAsync(() =>
{
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(1000)
        this.progressDialog.Progressbar.BeginInvoke((MethodInvoker)(() => {
            this.progressDialog.Progressbar.Value += 1;
        }));
    }
});

progressDialog.ShowDialog();


답변