WinForms 진행률 표시 줄을 사용하는 방법?


101

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

예를 들어 계산 메서드가 있고 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);
    }
}

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


4
메서드에 대리자 개체를 전달합니다.
Hans Passant 2012-08-26

답변:


112

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

5
좋은 예이지만 코드에 작은 오류가 있습니다. backgroundWorker.WorkerReportsProgress를 true로 설정해야합니다. 내 편집 확인
Mana

1
@mana 가정은 BackgroundWorker디자이너를 통해 추가되고 구성 된다는 것입니다. 하지만 예,로 설정되도록 구성해야 WorkerReportsProgress합니다 true.
Peter Ritchie

아, 맙소사, 디자이너에서 설정할 수 있다는 것을 몰랐습니다
Mana

그래도 궁금한 점이 있다면 귀하의 예제는 99 개의 backgroundWorker.ReportProgress ((j * 100) / 100000); 100 % 카운트를 얻는 방법
Mana

1
@mana 진행률의 100 %를 표시하려면 처리기가 수행하지 않으면 RunWorkerCompleted이벤트 처리기에서 DoWork수행합니다.
Peter Ritchie

77

.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은 여기에 자세히 설명되어 있습니다.


2
이것은 선택된 대답, IMO입니다. 좋은 대답입니다!
JohnOpincar

거의 대부분 가장 좋은 해답은, 그 대신에 통상의 비동기 Task.Run 함수를 사용하는 것을 제외하고
KansaiRobot

@KansaiRobot 반대 말 await DoWorkAsync(progress);인가요? 추가 스레드가 실행되지 않기 때문에 이것은 매우 의도적 인 것입니다. 예를 들어 I / O 작업을 기다리기 위해 DoWorkAsync자체적으로 호출하는 경우에만 함수가 계속됩니다. 이 기간 동안 기본 UI 스레드가 차단됩니다. 경우 정말 비동기 그러나 동기 문 단지 많은, 당신은 아무것도 얻을 수 없습니다. awaitbutton1_ClickDoWorkAsync
Wolfzoon

System.Windows.Controls.ProgressBar에 "단계"필드가 없습니다. 예제에서 제거해야합니다. 특히 어쨌든 사용되지 않기 때문에.
Robert Tausig

2
@RobertTausig StepWinForms의 진행률 표시 줄에서만 사용할 수 있고 여기에 필요하지 않은 것은 사실이지만 질문의 예제 코드 (태그가 지정된 winforms)에 있었으므로 그대로있을 수 있습니다.
quasoft

4

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

1

거기에 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();

제발 여러 질문하지 후 동일한 답변을 . 하나의 좋은 답변을 게시 한 다음 투표 / 플래그하여 다른 질문을 중복으로 종료합니다. 질문이 중복되지 않는 경우 질문에 대한 답변을 수정합니다 .
Martijn Pieters
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.