Заготовка Window Progress. Использование BackgroundWorker. Пример использования BeginInvoke при решении проблем с кросспотоковостью

И немного рекламы: магазин подарков . спортивное питание

В этой статье я расскажу о:


Я уже когда-то писал статью о создании формы, которая бы показывала информацию о ходе выполнения некоторых действий. Тот пример нельзя было назвать удачным, потому что там не использовалась многопоточность и это окно изредка тормозило при определенных условий. Поэтому я решил усовершенствовать то окно и сделать его более функциональным. Далее я буду использовать такое понятие, как шаблон окна, т.к. планировалось создать именно шаблон для использования его в разработке различных программ. Перед началом работы были поставлены следующие задачи:

После часа работы у меня получило с следующий код:

namespace ProgressW
{
    public delegate void CancelProgress();
    public delegate void ProgressChange();
    public delegate void Complete();

    public partial class frmProgress : Form
    {
        /// <summary>
        /// При отмене действия
        /// </summary>
        public event CancelProgress OnCancelWork;
        /// <summary>
        /// При изменении значения прогресс бара
        /// </summary>
        public event ProgressChange OnProgressChange;
        /// <summary>
        /// Событие возникающее по завершению
        /// </summary>
        public event Complete OnComplete;

        #region Переменные
        private bool _ifCancel = true;
        string _onClosingQuestion = "Прекратить работу?";
        ProgressBarStyle _style = ProgressBarStyle.Blocks;
        bool ifCancelClose = true;
        #endregion

        /// <summary>
        /// Возможна ли отмена
        /// </summary>
        public bool IfCancel
        {
            get { return _ifCancel; }
            set { btnCancel.Enabled = _ifCancel = value; }
        }

        /// <summary>
        /// Вопрос во время отмены
        /// </summary>
        public string OnClosingQuestion
        {
            get { return _onClosingQuestion; }
            set { _onClosingQuestion = value; }
        }

        /// <summary>
        /// Стиль прогресс бара
        /// </summary>
        public ProgressBarStyle Style
        {
            get { return _style; }
            set { prgBar.Style = _style = value; }
        }

        #region Методы

        #region Публичные
        public frmProgress()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Установить значение прогресс бара
        /// </summary>
        /// <param name="value">Значение</param>
        public void SetValue(int value)
        {
            if (value > -1 && value <= prgBar.Maximum)
                prgBar.Value = value;
        }

        /// <summary>
        /// Задать максимальное значение прогресс бара
        /// </summary>
        /// <param name="value"></param>
        public void SetMaxValue(int value)
        {
            if (value < prgBar.Value)
                prgBar.Maximum = prgBar.Value = value;
            else
                prgBar.Maximum = value;
        }

        /// <summary>
        /// Увеличение значения прогресс бара
        /// </summary>
        /// <param name="value">Значение</param>
        public void Increment(int value)
        {
            if (prgBar.Style == ProgressBarStyle.Marquee) return;

            prgBar.Increment(value);
            if (OnProgressChange != null)
                OnProgressChange();
            checkIfComplete();
        }

        /// <summary>
        /// Увеличение значения прогресс бара
        /// </summary>
        public void Increment()
        {
            Increment(1);
        }

        /// <summary>
        /// Задать заголовок
        /// </summary>
        /// <param name="text">Текст заголовка</param>
        public void SetTitle(string text)
        {
            if (this.InvokeRequired)
                BeginInvoke(new MethodInvoker(delegate
                   {
                       Text = text;
                   }));
            else
                Text = text;
        }

        /// <summary>
        /// Задать текущую информацию
        /// </summary>
        /// <param name="text">Содержание</param>
        public void SetInfo(string text)
        {
            if (this.InvokeRequired)
                BeginInvoke(new MethodInvoker(delegate
                {
                    lblInfo.Text = text;
                }));
            else
                lblInfo.Text = text;
        }

        /// <summary>
        /// Закрыть окно
        /// </summary>
        public new void Close()
        {
            ifCancelClose = false;
            base.Close();
        }
        #endregion

        #region Приватные
        private void checkIfComplete()
        {
            if (prgBar.Value == prgBar.Maximum)
                if (OnComplete != null)
                    OnComplete();
        }

        private void frmProgress_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = ifCancelClose;
            if (ifCancelClose)
                cancel();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            cancel();
        }

        private void cancel()
        {
            if (_ifCancel)
                if (MessageBox.Show(this, _onClosingQuestion, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    if (OnCancelWork != null)
                        OnCancelWork();
                }
        }
        #endregion
        #endregion
    }
}

В самом начале описаны делегаты для использования событий. Одно из них OnCancelWork, которое уведомляет если нажата кнопка Отмена. Доступ к основным переменным описан через аксессоры:
IfCancel — задает возможность прервать работу.
OnClosingQuestion — задает вопрос при нажатии на кнопку Отмена.
Style — задает стиль прогресс бара.

Также описаны публичные методы для управления элементами на форме:
SetValue — задает текущее значение для прогресс бара.
SetMaxValue — задает максимальное значение для прогресс бара.
Перегружаемый Increment — увеличивает на заданное число текущее значение прогресс бара.
SetTitle и SetInfo — задают текст для заголовка окна и текущей информации на форме.
Переназначенный метод Close — корректно закрывает окно.

Это все возможности которые доступны на данный момент. Чтобы подключить шаблон к проекту достаточно нажать Shift+Alt+A и выбрать файла frmProgress.cs. В обозревателе проекта должна появиться новая форма. Вы также можете доработать шаблон «под себя». На форме есть PictureBox, в котором вы можете отображать выбранную картинку. На сегодняшний день это тестовая версия.

Использование шаблона

Теперь давайте рассмотрим пример использования шаблона. Я создал проект и добавил два компонента BackgroundWorker. Один для работы без возможности отмены, другой с этой возможностью. Компонент BackgroundWorker очень удобно использовать при работе с многопоточностью. Он позволяет выполнять заданный код в отдельном потоке и корректно его завершать. Для быстрой разработки программ — вещь просто незаменимая.

При настройке компонента вам необходимо задать свойства WorkerReportsProgress и WorkerSupportCancellation. Первое позволяется использовать событие, которое должно возникнуть при изменении состояния, а второе — возможность прервать работу BackgroundWorker. В настройках событий следует задать все три событие, которые там расположены.

Для выполнения действий в коде пишем:

private void btnRun_Click(object sender, EventArgs e)
{
    frmPrg = new frmProgress();
    frmPrg.SetTitle("Проверка работы");
    frmPrg.Owner = this;
    frmPrg.Show();
    frmPrg.IfCancel = false;
    bgWorker.RunWorkerAsync();
}

private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i <= 100; i++)
    {
        frmPrg.SetInfo(i.ToString());
        Thread.Sleep(100);
        bgWorker.ReportProgress(1);
    }
}

private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    frmPrg.Increment();
}

private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (frmPrg != null)
        frmPrg.Close();
}

private void btnRunCancel_Click(object sender, EventArgs e)
{
    frmPrg = new frmProgress();
    frmPrg.SetTitle("Проверка работы");
    frmPrg.Owner = this;
    frmPrg.Show();
    cancel = false;
    frmPrg.OnCancelWork += new CancelProgress(frmPrg_OnCancelWork);
    bgWorkerCancel.RunWorkerAsync();
}

protected void frmPrg_OnCancelWork()
{
    cancel = true;
    frmPrg.SetInfo("Ожидание отмены");
}

bool cancel = false;
private void bgWorkerCancel_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i <= 100; i++)
    {
        if (cancel)
        {
            bgWorkerCancel.CancelAsync();
            break;
        }
        frmPrg.SetInfo(i.ToString());
        Thread.Sleep(100);
        bgWorkerCancel.ReportProgress(1);
    }
}

В первом методе мы описываем создание формы прогресса и задаем необходимые свойства. Обратите внимание, что задается свойство Owner, чтобы форма прогресса была поверх родительской. Ей не требуется при этом задавать свойство TopMost. Далее выполняем bgWorker.RunWorkerAsync(), который выполнит метод bgWorker_DoWork в отдельном потоке. Т.к. код выполняем в отдельном потоке, то нам необходимо реализовать кросспотоковость, т.к. объект frmPrg (форма прогресса) создан в потоке главной формы. Для доступа к элементу можно использовать следующую конструкцию:

if (this.InvokeRequired)
    BeginInvoke(new MethodInvoker(delegate
    {
           Text = text;
    }));
else
    Text = text;

if (this.InvokeRequired) — сначала проверяет нужно ли использовать BeginInvoke. BeginInvoke асинхронный вариант вызова Invoke и позволяет получить доступ к элементу из другого потока.

Если требуется изменить состояние, то вызывается метод bgWorker_ProgressChanged с помощью bgWorkerCancel.ReportProgress(1). В самом bgWorker_ProgressChanged описывается, что нужно сделать в этот момент. В данном случает увеличивается значение прогресс бара.

После того, как метод bgWorker_DoWork закончил работы (неважно успешно или нет), всегда выполняется метод bgWorker_RunWorkerCompleted. В данном случае у нас описан код, который закрывает форму с прогресс баром.
Рабочий проект

Заключение

Считаю, что с задачей создания шаблона формы прогресса мы справились. Надеюсь, что это форма поможет вам быстрее и качественнее разрабатывать свои программы. На момент написания статьи была выложена бета версия, которая требует глубокого тестирования. Если у вас будут дополнения и пожелания к выполненной работе, пишите.

Шаблон формы в разделе проектов
Исходники обучающего проекта

Популярность: 25%

Еще по этой теме:

  1. C#. Простой пример использования логирования log4net
  2. Альтернативные потоки данных NTFS с примером использования на C#
  3. C#. Использование System.Console для создания игр в текстовом режиме. Часть 3
  4. Использование Reflection для получения доступа к приватным переменным
  5. Создаем форму с информацией о ходе выполнения действий

Теги: , ,

Если у вас возникли вопросы, вы можете оставить их в комментариях

купить бассейн . китайские телефоны киев . антицеллюлитный массаж

Оставить комментарий

(обязательно)

(обязательно)