BackgroundWorker 클래스를 사용한 진행률 표시

BackgroundWorker 클래스를 이용한 비동기 작업 샘플 코드입니다.

BackgroundWorker Class: Microsoft Doc 페이지에서 자세한 설명을 확인하실 수 있어요.

코드

전체코드는 [icon name=”github” class=”” unprefixed_class=””] sample-backgroundworker: GitHub 저장소에서 확인하세요.

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

        this.Load += Form1_Load;
        this.btnStart.Click += Click_btnStart;
        this.btnCancel.Click += Click_btnCancel;
        this.btnError.Click += Click_btnError;

        // BackgroundWorker 
        this.bgWorker.DoWork += bgWorker_DoWork;
        this.bgWorker.ProgressChanged += bgWorker_ProgressChanged;
        this.bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        try
        {
            // 작업 취소를 사용
            this.bgWorker.WorkerSupportsCancellation = true;

            // 작업 진행률 업데이트 보고를 사용
            this.bgWorker.WorkerReportsProgress = true;
            this.btnStart.Enabled = true;
            this.btnCancel.Enabled = false;

            this.lblWorkCount.Text = "0";
        }
        catch (Exception ex)
        {
            throw new Exception("오류가 발생했습니다", ex);
        }
    }

    #region 메서드

    /// <summary>
    /// 처리할 작업입니다.
    /// </summary>
    private void DoWork(BackgroundWorker worker, DoWorkEventArgs e)
    {
        /*
            * 실행중인 Form 과 다른 쓰레드에서 동작하므로 
            * 처리할 메서드에서는 UI 객체의 속성값(Value, Text 등..)을 사용하지 못합니다.
            * 
            * 작업에 필요한 값은 매개변수로 전달받아야 하고 UI객체의 상태를 변화시킬 필요가 있는 경우
            * ProgressChanged
            * RunWorkerCompleted
            * 이벤트를 사용해야 합니다.
            */

        double nMax = 100.0;
        int nExe = 0;

        while (nMax > nExe)
        {
            // 작업이 취소 요청이 되었는지 검사
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
            else
            {
                if (ErrorFlag)
                {
                    ErrorFlag = false;
                    throw new Exception("예외 발생 시뮬레이션");
                }

                // 시간이 걸리는 작업을 실행합니다.
                System.Threading.Thread.Sleep(200);
                nExe++;

                // 진행률 업데이트하기 위해 ProgressChanged 이벤트를 발생시킵니다.
                worker.ReportProgress((int)((nExe / nMax) * 100));
            }
        }
    }

    private void AppendMessage(string message)
    {
        textBox1.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}]: {message}{Environment.NewLine}");
        textBox1.ScrollToCaret();
    }

    #endregion

    #region 이벤트 핸들러

    /// <summary>
    /// 시작버튼 클릭
    /// </summary>
    private void Click_btnStart(object sender, EventArgs e)
    {
        try
        {
            this.progressBar.Value = 0;
            this.progressBar.Maximum = 100;
            this.progressBar.Step = 1;

            this.lblWorkCount.Text = "0";

            this.btnStart.Enabled = false;
            this.btnCancel.Enabled = true;

            this.Cursor = Cursors.WaitCursor;

            AppendMessage("작업을 시작합니다.");

            // 비동기로 작업을 시작합니다. DoWork 이벤트를 발생
            this.bgWorker.RunWorkerAsync(null);
        }
        catch (Exception ex)
        {
            throw new Exception("오류가 발생했습니다", ex);
        }
    }

    /// <summary>
    /// 취소 버튼 클릭
    /// </summary>
    private void Click_btnCancel(object sender, EventArgs e)
    {
        try
        {
            // 작업 취소요청
            this.bgWorker.CancelAsync();
        }
        catch (Exception ex)
        {
            throw new Exception("오류가 발생했습니다", ex);
        }
    }

    /// <summary>
    /// 예외 버튼 클릭
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Click_btnError(object sender, EventArgs e)
    {
        ErrorFlag = true;
    }

    /// <summary>
    /// 작업을 시작
    /// </summary>
    private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        this.DoWork((BackgroundWorker)sender, e);
        e.Result = null;

        //try
        //{
        //    this.DoWork((BackgroundWorker)sender, e);
        //    e.Result = null;
        //}
        //catch (Exception ex)
        //{
        //    throw new Exception("오류가 발생했습니다", ex);
        //}
    }

    /// <summary>
    /// 작업 진행률을 업데이트
    /// </summary>
    private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        try
        {
            this.progressBar.Value = e.ProgressPercentage;
            int nTmp = 0;
            int.TryParse(this.lblWorkCount.Text, out nTmp);
            nTmp++;
            this.lblWorkCount.Text = nTmp.ToString();
        }
        catch (Exception ex)
        {
            throw new Exception("오류가 발생했습니다", ex);
        }
    }

    /// <summary>
    /// 작업이 완료상태(예외, 취소, 완료)가 되면 발생하는 이벤트입니다
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        try
        {
            if (e.Error != null)    // 예외 발생
            {
                AppendMessage(String.Format("{0} ==> {1}", "예외가 발생했습니다.", e.Error.Message));
            }
            else if (e.Cancelled)   // 작업취소
            {
                AppendMessage("작업이 취소되었습니다.");
            }
            else                    // 완료
            {
                AppendMessage("작업이 완료되었습니다.");
            }
        }
        catch (Exception ex)
        {
            throw new Exception("오류가 발생했습니다", ex);
        }
        finally
        {
            this.btnStart.Enabled = true;
            this.btnCancel.Enabled = false;
            this.Cursor = Cursors.Default;
        }
    }

    #endregion

    private static bool ErrorFlag = false;
}

실행 영상

정상 종료

취소

예외

이 사이트는 광고를 포함하고 있습니다.
광고로 발생한 수익금은 서버 유지 관리에 사용되고 있습니다.

This site contains advertisements.
Revenue generated by the ad servers are being used for maintenance.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다