- Подробности
-
Опубликовано 24.12.2015 21:47
-
Просмотров: 1094
В этой статье я расскажу о:
- создании шаблона Window Progress
- работе с компонентом BackgroundWorker
- использовании BeginInvoke и InvokeRequired
Я уже когда-то писал статью о создании формы, которая бы показывала информацию о ходе выполнения некоторых действий. Тот пример нельзя было назвать удачным, потому что там не использовалась многопоточность и это окно изредка тормозило при определенных условий. Поэтому я решил усовершенствовать то окно и сделать его более функциональным. Далее я буду использовать такое понятие, как шаблон окна, т.к. планировалось создать именно шаблон для использования его в разработке различных программ. Перед началом работы были поставлены следующие задачи:
- возможность работы отдельном потоке (со всеми вытекающими отсюда последствиями), чтобы выполнение действий не «замораживало» окно
- возможность отменить выполнение действий в любой момент
- удобный доступ к основным функциям шаблона окна
После часа работы у меня получило с следующий код:
3 |
public delegate void CancelProgress(); |
4 |
public delegate void ProgressChange(); |
5 |
public delegate void Complete(); |
7 |
public partial class frmProgress : Form |
10 |
/// При отмене действия |
12 |
public event CancelProgress OnCancelWork; |
14 |
/// При изменении значения прогресс бара |
16 |
public event ProgressChange OnProgressChange; |
18 |
/// Событие возникающее по завершению |
20 |
public event Complete OnComplete; |
23 |
private bool _ifCancel = true ; |
24 |
string _onClosingQuestion = "Прекратить работу?" ; |
25 |
ProgressBarStyle _style = ProgressBarStyle.Blocks; |
26 |
bool ifCancelClose = true ; |
30 |
/// Возможна ли отмена |
34 |
get { return _ifCancel; } |
35 |
set { btnCancel.Enabled = _ifCancel = value; } |
39 |
/// Вопрос во время отмены |
41 |
public string OnClosingQuestion |
43 |
get { return _onClosingQuestion; } |
44 |
set { _onClosingQuestion = value; } |
48 |
/// Стиль прогресс бара |
50 |
public ProgressBarStyle Style |
52 |
get { return _style; } |
53 |
set { prgBar.Style = _style = value; } |
61 |
InitializeComponent(); |
65 |
/// Установить значение прогресс бара |
67 |
/// <param name="value">Значение</param> |
68 |
public void SetValue( int value) |
70 |
if (value > -1 && value <= prgBar.Maximum) |
75 |
/// Задать максимальное значение прогресс бара |
77 |
/// <param name="value"></param> |
78 |
public void SetMaxValue( int value) |
80 |
if (value < prgBar.Value) |
81 |
prgBar.Maximum = prgBar.Value = value; |
83 |
prgBar.Maximum = value; |
87 |
/// Увеличение значения прогресс бара |
89 |
/// <param name="value">Значение</param> |
90 |
public void Increment( int value) |
92 |
if (prgBar.Style == ProgressBarStyle.Marquee) return ; |
94 |
prgBar.Increment(value); |
95 |
if (OnProgressChange != null ) |
101 |
/// Увеличение значения прогресс бара |
103 |
public void Increment() |
111 |
/// <param name="text">Текст заголовка</param> |
112 |
public void SetTitle( string text) |
114 |
if ( this .InvokeRequired) |
115 |
BeginInvoke( new MethodInvoker( delegate |
124 |
/// Задать текущую информацию |
126 |
/// <param name="text">Содержание</param> |
127 |
public void SetInfo( string text) |
129 |
if ( this .InvokeRequired) |
130 |
BeginInvoke( new MethodInvoker( delegate |
141 |
public new void Close() |
143 |
ifCancelClose = false ; |
149 |
private void checkIfComplete() |
151 |
if (prgBar.Value == prgBar.Maximum) |
152 |
if (OnComplete != null ) |
156 |
private void frmProgress_FormClosing( object sender, FormClosingEventArgs e) |
158 |
e.Cancel = ifCancelClose; |
163 |
private void btnCancel_Click( object sender, EventArgs e) |
168 |
private void cancel() |
171 |
if (MessageBox.Show( this , _onClosingQuestion, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) |
173 |
if (OnCancelWork != null ) |
В самом начале описаны делегаты для использования событий. Одно из них OnCancelWork, которое уведомляет если нажата кнопка Отмена. Доступ к основным переменным описан через аксессоры:
IfCancel – задает возможность прервать работу.
OnClosingQuestion – задает вопрос при нажатии на кнопку Отмена.
Style – задает стиль прогресс бара.
Также описаны публичные методы для управления элементами на форме:
SetValue – задает текущее значение для прогресс бара.
SetMaxValue – задает максимальное значение для прогресс бара.
Перегружаемый Increment – увеличивает на заданное число текущее значение прогресс бара.
SetTitle и SetInfo – задают текст для заголовка окна и текущей информации на форме.
Переназначенный метод Close – корректно закрывает окно.
Это все возможности которые доступны на данный момент. Чтобы подключить шаблон к проекту достаточно нажать Shift+Alt+A и выбрать файла frmProgress.cs. В обозревателе проекта должна появиться новая форма. Вы также можете доработать шаблон «под себя». На форме есть PictureBox, в котором вы можете отображать выбранную картинку. На сегодняшний день это тестовая версия.
Использование шаблона
Теперь давайте рассмотрим пример использования шаблона. Я создал проект и добавил два компонента BackgroundWorker. Один для работы без возможности отмены, другой с этой возможностью. Компонент BackgroundWorker очень удобно использовать при работе с многопоточностью. Он позволяет выполнять заданный код в отдельном потоке и корректно его завершать. Для быстрой разработки программ – вещь просто незаменимая.
При настройке компонента вам необходимо задать свойства WorkerReportsProgress и WorkerSupportCancellation. Первое позволяется использовать событие, которое должно возникнуть при изменении состояния, а второе – возможность прервать работу BackgroundWorker. В настройках событий следует задать все три событие, которые там расположены.
Для выполнения действий в коде пишем:
1 |
private void btnRun_Click( object sender, EventArgs e) |
3 |
frmPrg = new frmProgress(); |
4 |
frmPrg.SetTitle( "Проверка работы" ); |
7 |
frmPrg.IfCancel = false ; |
8 |
bgWorker.RunWorkerAsync(); |
11 |
private void bgWorker_DoWork( object sender, DoWorkEventArgs e) |
13 |
for ( int i = 0; i <= 100; i++) |
15 |
frmPrg.SetInfo(i.ToString()); |
17 |
bgWorker.ReportProgress(1); |
21 |
private void bgWorker_ProgressChanged( object sender, ProgressChangedEventArgs e) |
26 |
private void bgWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) |
32 |
private void btnRunCancel_Click( object sender, EventArgs e) |
34 |
frmPrg = new frmProgress(); |
35 |
frmPrg.SetTitle( "Проверка работы" ); |
39 |
frmPrg.OnCancelWork += new CancelProgress(frmPrg_OnCancelWork); |
40 |
bgWorkerCancel.RunWorkerAsync(); |
43 |
protected void frmPrg_OnCancelWork() |
46 |
frmPrg.SetInfo( "Ожидание отмены" ); |
50 |
private void bgWorkerCancel_DoWork( object sender, DoWorkEventArgs e) |
52 |
for ( int i = 0; i <= 100; i++) |
56 |
bgWorkerCancel.CancelAsync(); |
59 |
frmPrg.SetInfo(i.ToString()); |
61 |
bgWorkerCancel.ReportProgress(1); |
В первом методе мы описываем создание формы прогресса и задаем необходимые свойства. Обратите внимание, что задается свойство Owner, чтобы форма прогресса была поверх родительской. Ей не требуется при этом задавать свойство TopMost. Далее выполняем bgWorker.RunWorkerAsync(), который выполнит метод bgWorker_DoWork в отдельном потоке. Т.к. код выполняем в отдельном потоке, то нам необходимо реализовать кросспотоковость, т.к. объект frmPrg (форма прогресса) создан в потоке главной формы. Для доступа к элементу можно использовать следующую конструкцию:
1 |
if ( this .InvokeRequired) |
2 |
BeginInvoke( new MethodInvoker( delegate |
if (this.InvokeRequired) – сначала проверяет нужно ли использовать BeginInvoke. BeginInvoke асинхронный вариант вызова Invoke и позволяет получить доступ к элементу из другого потока.
Если требуется изменить состояние, то вызывается метод bgWorker_ProgressChanged с помощью bgWorkerCancel.ReportProgress(1). В самом bgWorker_ProgressChanged описывается, что нужно сделать в этот момент. В данном случает увеличивается значение прогресс бара.
После того, как метод bgWorker_DoWork закончил работы (неважно успешно или нет), всегда выполняется метод bgWorker_RunWorkerCompleted. В данном случае у нас описан код, который закрывает форму с прогресс баром.
Заключение
Считаю, что с задачей создания шаблона формы прогресса мы справились. Надеюсь, что это форма поможет вам быстрее и качественнее разрабатывать свои программы. На момент написания статьи была выложена бета версия, которая требует глубокого тестирования. Если у вас будут дополнения и пожелания к выполненной работе, пишите.