Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

5_laba

.pdf
Скачиваний:
9
Добавлен:
27.05.2015
Размер:
666.37 Кб
Скачать

Лабораторная работа №5. Фоновый поток. Индикатор выполнения.

Многодокументный интерфейс. Меню.

Цель работы: изучить возможнос ти использования фонового потока. Познакомиться с MDI, научиться использовать ProgressBar и различные меню.

Порядок выполнения

1.Ознакомится с целью работы, и изучить теоретический материал,

2.Внимательно изучить задание,

3.Изучите дополнительный материал,

3.Изучить приведенные примеры и требования к отчету ,

4.Получить номер варианта,

5. Написать и отладить программы в соответствии с заданием, 6. Подготовить отчет о проделанной работе.

Многодокументный интерфейс

Существуют два основных стиля интерфейсов пользователя: интерфейс с одним документом (single-document interface, SDI) и интерфейс со многими документами (mu ltiple -document interface, M DI). Текстовый редактор с интерфейсом SDI позволяет открыть то лько о дин документ - чтобы открыть другой, следует закрыть предыдущий (notepad). В приложении MDI Вы можете открыть сразу несколько документов (Word).

В приложениях C# можно добавить MDI форму, которая будет служить контейнером для по дчиненных форм. Подчиненная форма - это обычная форма, у которой значение свойства MdiParent равно родительской форме. В режиме выполнения по дчиненные окна помещаются внутри родительского окна M DI формы. При закрытии формы-контейнера закрываю тся все подчиненные формы.

При переключении между дочерними окнами основное меню программы и панели инструментов (если они есть) меняются в соответствии с состоянием и параметрами активного дочернего окна. Например, в панели инструментов браузера всегда есть кнопки Назад и Вперед, но кнопка Назад активна то лько, если страница позволяет навигацию назад по истории. При переключении между дочерними окнами состояние этих кнопок до лжно меняться в зависимости о т доступности тех или иных действий в выбранном окне.

Форма приложения становится контейнером многодокументного интерфейса после инициации свойства IsM diContainer значением true. После этого создается элемента управления типа MdiClient, который заполняет клиентскую область и становится фактическим «родителем» всех дочерних документов. Вместе с тем, взаимодействовать с MdiClient напрямую не принято. Дочернее окно является экземпляром класса Form или его по томка. При создании дочерней формы се свойство MdiParent инициируется формой приложения и вызывается метод Show.

В приложении MDI можно открыть много окон построенных по одному образцу. При этом в режиме проектирования создается один экземпляр формы. Для того чтобы в режиме выпо лнения о ткрыть много окон в ко де программы создаются новые экземпляры подчиненной формы.

Если в программе используется элемент управления MenuStrip, а не использовавшийся ранее MainMenu, нужно инициализировать свойство MainMenuSirip формы объектом MenuStrip – только в этом случае все будет работать, как до лжно.

Свойство IsMdiCbild позво ляет дочернему окну выяснить, является ли оно частью MDI-интерфейса. Для получения набора потомков из родительского окна служит свойство Md iChildren.

Дочернее окно с подсвеченным заголовком, располагающееся повер х остальных потомков, называется активным потомком. Из формы приложения можно активизировать конкретно го потомка вызовом метода AclivateMdiCh ild. Форма приложения получает информации об активном в данный момент потомке путем считывания свойства ActiveMdiCh ild. Для выявления смены активного по томка в форме приложения устанавливают обработчик события MdiCh ildAclivate или (что делаю т чаще) переопределяю т метод

OnMdiCh ildActivate.

Нет ничего необычного, что в MDl-программе элемент меню верхнего уровня называется Window. Обычно это последний элемент меню перед элементом Help. Обычно в этом меню есть команды , позво ляющие свернуть или расположить дочерние окна каскадом или замостить ими клиентскую область родителя. Реализовать эти команды меню можно методом LayoutMdi.

В соответствии с принятой практикой меню Window также содержит список всех текущих дочерних окон, позволяя пользователю выбирать нужное. Если дочерних окон больше девяти, внизу меню появится команда, открывающая небольшое диалоговое окно, в котором можно выб рать нужное дочернее окно. Эту функцию специально реализовывать не нужно – достаточно приравнять свойство MdiWindowListItem элемента управления MenuStrip к

ToolStripMenuItem элемента Window.

В качестве примера рассмотрите следующий ко д: namespace MDIFormsApp

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent(); DoubleBuffered = true;

}

int childnum = 1;

private void NewToolStripMenuItem_Click(object sender,

EventArgs e)

{

Form frm = new Form();

frm.Text = "Текстовый документ " + childnum; frm.MdiParent = this;

RichTextBox tbx = new RichTextBox();

tbx.Name = "RichTB";

tbx.Font = new Font("Times New Roman", 12); tbx.Dock = DockStyle.Fill; frm.Controls.Add(tbx);

frm.Show();

}

private void SaveToolStripMenuItem_Click(object sender,

EventArgs e)

{

SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "RichText files|*.rtf"; sfd.DefaultExt = "rtf";

if (sfd.ShowDialog() ==

System.Windows.Forms.DialogResult.OK)

{

Form f1 = this.ActiveMdiChild; (f1.Controls["RichTB"] as

RichTextBox).SaveFile(sfd.FileName);

}

}

private void OpenToolStripMenuItem_Click(object sender,

EventArgs e)

{

OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "RichText files|*.rtf"; ofd.DefaultExt = "rtf";

if (ofd.ShowDialog() ==

System.Windows.Forms.DialogResult.OK)

{

Form frm = new Form(); frm.Width = 400;

frm.Text = "Текстовый документ: " + System.IO.Path.GetFileName(ofd.FileName);

frm.MdiParent = this;

RichTextBox tbx = new RichTextBox(); tbx.Name = "RichTB";

tbx.Dock = DockStyle.Fill; frm.Controls.Add(tbx); tbx.LoadFile(ofd.FileName); frm.Show();

}

}

}

}

Индикатор выполнения.

Элемент управления ProgressBar визуально показывает выполнение длительной операции о дним из трех способов:

Сегментированные блоки, число которых постепенно увеличивается слева направо.

Непрерывная полоса, заполняемая слева направо.

Помеченный блок, перемещаемый в полосе ProgressBar.

Стиль отображаемого индикатора ProgressBar определяет свойство Style . Без такого визуального индикатора пользователи, работающие с приложением, могут решить, ч то оно перестало о твечать на запросы. Испо льзование в приложении индикатора ProgressBar позво ляет предупредить по льзователя, ч то приложение выпо лняет длительную задачу и при этом работает нормально.

Свойства Maximu m и M inimu m определяют диапазон значений, описывающий хо д выполнения задачи. Свойство Min imu m обычно устанавливается равным нулю, а свойство Maximu m обычно устанавливается равным значению, соответствующему завершению задачи. Например, чтобы правильно показать хо д выполнения при копировании группы файлов, свойство Maximu m можно установить равным суммарному числу копируемых файлов. Также зачастую удобно обозначить за Maximu m = 100 – выполнение всех действий.

Свойство Va lue предоставляет ход выполнения приложением данной операции. Значение, отображаемое индикатором выполнения ProgressBar, только приблизительно соответствует текущему значению свойства Value.

Существует неско лько способов изменения значения, отображаемого индикатором выполнения ProgressBar, помимо непосредственного изменения свойства Value. Можно использовать свойство Step для задания конкретного

значения приращения свойства Value, вызывая после э того мето д PerformStep для увеличения этого значения на величину, определяемую свойством Step. Для изменения значения приращения можно использовать метод Increment и задать значение, ко торое будет использовать для приращения свойства Value.

Разберем в примере каждый из способов: bool MaxRaise = false;

private void btnIncrement_Click(object sender, EventArgs e)

{

if (progressBar1.Value - 15 >= progressBar1.Minimum && MaxRaise)

{

progressBar1.Increment(-15);

}

if (progressBar1.Value + 15 <= progressBar1.Maximum && !MaxRaise)

{

progressBar1.Increment(15);

}

if (progressBar1.Value == progressBar1.Minimum || progressBar1.Value == progressBar1.Maximum)

{

MaxRaise = !MaxRaise;

}

Text = progressBar1.Value.ToString();

}

private void btnStep_Click(object sender, EventArgs e)

{

if (progressBar1.Value + progressBar1.Step <= progressBar1.Maximum)

{

progressBar1.PerformStep();

}

Text = progressBar1.Value.ToString();

}

private void btnValue_Click(object sender, EventArgs e)

{

if (progressBar1.Value < progressBar1.Maximum && !MaxRaise)

{

progressBar1.Value++;

}

else if (progressBar1.Value > progressBar1.Minimum && MaxRaise)

{

progressBar1.Value--;

}

if (progressBar1.Value == progressBar1.Minimum || progressBar1.Value == progressBar1.Maximum)

{

MaxRaise = !MaxRaise;

}

Text = progressBar1.Value.ToString();

}

Выполнение операций в фоновом потоке.

Существует множество часто выполняемых операций, на выполнение которых может потребоваться много времени. Пример:

загрузка изображений;

вызовы веб-служб;

загрузка и передача файлов (в том числе, одноранговыми приложениями);

сложные вычисления, выполняемые локально;

транзакции базы данных;

доступ к ло кальному диску при условии его низкой скорости по

сравнению со временем доступа к оперативной памяти.

Если какая -либо операция будет выполняться в течение до лгого времени, и при этом требуется не допустить, ч тобы пользовательский интерфейс перестал отвечать на запросы пользователя или "завис", м ожно использовать класс BackgroundWorker для выполнения операции в другом потоке .

При выполнении лабораторной работы вам как раз предлагается промоделировать ситуацию использование локальных вычислений, которые занимают продо лжительное время.

Компонент Bac kgroundWorker позволяет форме или элементу управления выполнить операцию в асинхронном режиме, в потоке , о тличном от основного потока пользовательско го интерфейса приложения. Для испо льзования компонента BackgroundWorker необхо димо просто указать, какой им енно требующий времени рабочий метод должен выполняться в фоновом режиме, и затем вызвать метод RunWorkerAsync. Вызывающий по ток продолжит выполняться в нормальном режиме, в то время как рабочий метод перейдет в асинхронный режим. После завершении выполнения метода компонент

Backg roundWorker генерирует событие RunWorkerCo mpleted, ко торое при необхо димости может содержать результат выпо лнения операции.

Компонент BackgroundWorker может быть добавлен через вкладку Компоненты Панели инструментов, а может быть создан в процессе выполнения вашей программы.

Чтобы начать асинхронную операцию, надо воспользоваться методом RunWorkerAsync, который открывает доступ к событию DoWork. С э тим событием связывается обработчик DoWork. Мето д имеет необязательный параметр object, который, в свою очередь, может использоваться для передачи аргументов рабочему методу.

Обработчик же событий DoWork принимает параметр DoWorkEventArgs, имеющий свойство Argument. Это свойство получает параметр от метода RunWorkerAsync и может быть передано рабочему методу, который будет вызываться в обработчике событий DoWork.

Кроме того, с BackgroundWorker связанно еще два обработчика событий: это события окончания выпо лнения действий, производимых в этом фоновом потоке (обработчик RunWorkerCo mpleted); и событие информирующее об изменении процента выполнения задачи (обработчик ProgressChanged). Если нужно, чтобы фоновая операция сообщала о своем выполнении, то надо инициировать событие ProgressChanged. Это можно сделать, вызвав метод ReportProgress. В качестве параметра ко торый принимает значение от 0 до 100, несущий информацию о хо де выполнения. Реализация разумного способа измерения процента выполнения фоновой операции от всей задачи является обязанностью разработчика.

В классе BackgroundWorker определены следующие полезные свойства :

Имя

Описание

 

 

 

CancellationPending

Возвращает значение,

показывающее, запросило ли

приложение отмену фоновой операции.

 

 

 

 

CanRaiseEvents

Возвращает значение,

по казывающее, может ли

компонент вызывать событие.

 

 

 

IsBusy

Возвращает значение, показывающее, выпо лняет ли

объект BackgroundWorker асинхронную операцию.

 

Получает или задает значение , показывающее,

WorkerReportsProgress может ли объект BackgroundWorker сообщать о хо де выполнения.

Получает или задает значение , показывающее,

WorkerSupportsCancellation поддерживает ли объект BackgroundWorker отмену асинхронной операции.

Для пояснения применения фонового потока рассмотрим пример программы нахождения чисел Фибоначчи. Вычисление крупных чисел Фибоначчи (с порядковым номером более 35) может занять немало времени, однако основной поток пользовательского интерфейса не будет прерван, и форма будет о твечать на запросы пользователя во время вычисления.

Чтобы создать BackgroundWorker с помощью конструктора, с вкладки Компоненты панели э лементов перетащите BackgroundWorker на вашу форму.

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

Реализация асинхронных обработчиков событий

1.В окне Свойства (при выбранном компоненте BackgroundWorker) нажмите кнопку События. Дважды щелкните события DoWork и RunWorkerCo mpleted, ч тобы создать обработчики событий.

2. Создайте в форме в новый метод с именем Co mputeFibonacci.Э тот метод будет выполнять вычисления, он будет запущен в фоновом режиме. Код демонстрирует рекурсивную реализацию алгоритма Фибоначчи — она весьма неэффективна, вычисление больших чисел Фибоначчи с ее помощью занимает немало времени, но благодаря ей, вы сможете разобраться с работой компонента Backg roundWorker.

// Именно в этот методе происходят высисления

long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)

{

//Параметр n должен быть >= 0, но <= 91.

//Так как при n > 91, происходит переполнение long.

if ((n < 0) || (n > 91))

{

throw new ArgumentException(

"value must be >= 0 and <= 91", "n");

}

long result = 0;

//Прерывает выполнение задания в случае отмены пользователем

//Заметьте, что вызов CancelAsync может установить

//CancellationPending в true сразу поле

//последнего вызова этого метода, поэтому

//у этого кода не будет возможности установить флаг

//DoWorkEventArgs.Cancel в состояние true. Это означает,

//что в вашем обработчике события RunWorkerCompleted

//RunWorkerCompletedEventArgs.Cancelled не будет установлен

//в состояние true.

if (worker.CancellationPending)

{

e.Cancel = true;

}

else

{

if (n < 2)

{

result = 1;

}

else

{

result = ComputeFibonacci(n - 1, worker, e) + ComputeFibonacci(n - 2, worker, e);

}

// Формирует информацию о ходе выполнения задачи. int percentComplete =

(int)((float)n / (float)numberToCompute * 100); if (percentComplete > highestPercentageReached)

{

highestPercentageReached = percentComplete; worker.ReportProgress(percentComplete);

}

}

return result;

}

3.В обработчике событий DoWork добавьте вызов метода

Co mputeFibonacci. Возьмите первый параметр Co mputeFibonacci из свойства Argu mentDoWorkEventArgs. Параметры BackgroundWorker и DoWorkEventArgs будут использованы позднее для поддержки отчета о хо де выполнения и о тмены. Присвойте значение, возвращенное из

Co mputeFibonacci, свойству Res ultDoWorkEventArgs. Результат будет доступен для обработчика событий RunWorkerCo mpleted.

//Это обработчик события, в котором фактически выполняются

//действия, которые могут занять много времени.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

//Получаем BackgroundWorker, переданный с событием.

BackgroundWorker worker = sender as BackgroundWorker;

//Назначаем результат вычисления

//свойству Result объекта DoWorkEventArgs.

//Тем самым сделав его доступным для

//обработчика события RunWorkerCompleted.

e.Result = ComputeFibonacci((int)e.Argument, worker, e);

}

4.У кнопки в обработчике события Click добавьте ко д, запускающий

асинхронную операцию в фоновом потоке.

private void startAsyncButton_Click(System.Object sender, System.EventArgs e)

{

//Обнуляем текст в label, выводящем результат. resultLabel.Text = String.Empty;

//Делаем неактивным элемент управления

//UpDown до тех пор, пока асинхронная

//операция не выполнится.

this.numericUpDown1.Enabled = false;

//Делаем неактивной кнопку Start

//до тех пор, пока асинхронная

//операция не выполнится.

this.startAsyncButton.Enabled = false;

//Делаем активной кнопку Cancel

//пока асинхронная операция выполняется.

this.cancelAsyncButton.Enabled = true;

//Получаем значение элемента управления UpDown. numberToCompute = (int)numericUpDown1.Value;

//Обнуляем переменную для отслеживания хода выполнения. highestPercentageReached = 0;

//Запуск фоновой операции. backgroundWorker1.RunWorkerAsync(numberToCompute);

}

5.Назначьте результат вычислений средству управления resultLabel в

обработчике событий RunWorkerCo mpleted.

//Обработчик события, связанного с результатом

//выполнения фоновой операции.

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

// Во-первых, обработаем исклюяения, которые могли возникнуть. if (e.Error != null)

{

MessageBox.Show(e.Error.Message);

}

else if (e.Cancelled)

{

//Далее обрабатываем ситуацию отмены задания

//Обратите внимание, что из-за возможного

//состояния гонки в обработчике DoWork

//флаг Cancelled может быть не установлен,

//хотя событие CancelAsync было вызвано.

resultLabel.Text = "Canceled";

}

else

{

// И, наконец, обрабатываем выполненное задание resultLabel.Text = e.Result.ToString();

}

//Активируем элемент управления UpDown this.numericUpDown1.Enabled = true;

//Активируем кнопку Start. startAsyncButton.Enabled = true;

//Делаем кнопку Cancel неактивной. cancelAsyncButton.Enabled = false;

}

Добавление отчета о ходе выполнения и поддерж ки отмены

Для длительных асинхронных операций часто желательно сообщать пользователю о хо де выполнения и дать ему возможность отменить операцию. Класс BackgroundWorker предоставляет событие, позволяю щее публиковать ход выполнения фоновой операции. Также предоставляется флаг, по зво ляющий рабочему коду обнаружить вызов CancelAsync и прервать свою работу.

1.Для внедрения о тчета о ходе выполнения в окне Свойства выберите backgroundWorker1. Установите свойствам WorkerReportsProgress и WorkerSupportsCancellation значение t rue.

2.Объявите две переменные в вашей форме. Они будут использоваться

для отслеживания хода выполнения. private int numberToCompute = 0;

private int highestPercentageReached = 0;

3.Добавьте обработчик события для события ProgressChanged. В обработчике события ProgressChanged обновите ProgressBar со свойством ProgressPercentage параметра ProgressChangedEventArgs .

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]