лекция. Зиборов. Справочник для опытных и как пособие для начинающих программистов. Компактдиск содержит исходные коды примеров из книги
Скачать 7.39 Mb.
|
Рис. 4.3. Диалоговое окно при выходе из программы В программе следует обработать каждый из возможных ответов пользователя по алгоритму, представленному на рис. 4.4. Рис. 4.4. Алгоритм обработки ответа пользователя программы Обращаю внимание читателей на ветвь алгоритма "Отмена" (Cancel). Это случай, когда пользователь передумал выходить из программы и желает вернуться к редактированию файла. Для реализации этого случая (см. листинг 4.3) обработка события FormClosing предусматривает булево свойство е.Cancel, которому можноприсвоить значение true, означающее отказ от закрытия программы (пользователь передумал), т. е. в этом случае процедура Form1_FormClosing не закончится выходом из программы. Аналогично, если пользователь согласился сохранить данные, то он попадает в стандартный диалог сохранения файла, и если при этом он передумал (диалог сохранения закрыт кнопкой Отмена или комбинацией клавиш <Alt>+<F4>), то cледует предложить пользователю продолжить редактирование файла: е.Сancel = true. Как видно, в процедуре Form1_FormСlosing, к сожалению, не удается избежать вложенных операторов условия. Убедиться в работоспособности программы можно, открыв соответствующее решение в папке ТекстовыйРедактор. Пример 26. Программа тестирования знаний студента по какому-либо предмету В связи с внедрением в образование так называемого Болонского процесса-процесса сближения и гармонизации систем образования стран Европы с целью создания единого европейского пространства высшего образования — процедур проверки знаний студентов осуществляется в том числе посредством тестирования по различным предметам преподавания. Причем тестированию уделяется наибольшее внимание. В данном примере создадим инструмент для тестирования студентов, напишем программу, которая читает заранее подготовленный преподавателем текстовый файл с вопросами по какому-либо предмету, выводит в экранную форму каждый вопрос с вариантами ответов. Студент выбирает правильный вариант ответа, а в конце тестирования программа подводит итоги проверки знаний, выставляет общую оценку и, в качестве обоснования поставленной оценки, показывает вопросы, на которые студент ответил неправильно. Фрагмент такого текстового файла для проверки знаний по информатике представлен в листинге 4.4. Листинг 4.4. Содержимое текстового файла для тестирования студента по информатике Информатика и программирование 1/6. Основные компоненты вычислительной системы: процессор, звуковая карта, видеокарта, монитор, клавиатура; монитор, клавиатура, материнская плата, процессор процессор, ОП, внешняя память, монитор, клавиатура 3 2/6. Во время исполнения прикладная программа хранится: в ПЗУ в процессоре в оперативной памяти 3 3/6. Иерархию усложнения данных можно представить в виде: Бит - Байт - Поле - Запись - Файл - База Данных Запись - Файл - Бит - Байт - База Данных - Поле База Данных - Байт - Поле - Запись - Бит - Файл 1 4/6. Укажите строку, содержащую неверное утверждение 1 Кбайт = 1024 байт; 1 Гбайт = 1024 Мбайт 1 Мбайт это примерно миллион байт; 1 байт = 8 бит 1 Гбайт это примерно миллион байт; 1 Мбайт = 1024 Кбайт 3 5/6. Экспоненциальное представления числа -1,84Е-04 соответствует числу: -0,000184 -0,00184 -18400 1 6/6. Текстовые данные кодируют с использованием: таблиц размещения файлов FAT, NTFS и др. таблиц символов Windows 1251, Unicode, ASCII и др. структурированного языка запросов SQL 2 J Структура этого файла такова. Первой строкой приведенного текстового файла является название предмета или темы, по которой проводится тестирование. Это название программа будет выводить в строке заголовка экранной формы. Для краткости изложения в данном тесте приводятся только шесть вопросов. На компакт-диске, прилагаемом к данной книге, представлен более полный файл test_полный.txt, с помошью которого автор тестирует знания своих студентов. Как видно из листинга 4.4, каждый вопрос имеет три варианта ответа. Строка с числом 1- 2 или 3 означает номер правильного ответа. Программа будет считывать это число и сравнивать с номером варианта, который выбрал тестируемый. Дробь, например 4/6 приведена для того, чтобы тестируемый понимал, в какой точке траектории в данный момент находится, т. е. он отвечает на четвертый вопрос, а всего их шесть. Таким образом, задача понятна, приступаем к ее программированию. Запустим Visual Studio 2010, закажем новый проект шаблона Windows Forms Application Затем из панели элементов перенесем в форму две командных кнопки, текстовую метку и три переключателя RadioButton. Далее через щелчок правой кнопкой мыши перейдем к вкладке программного кода (листинг 4.5). Листинг 4.5. Программа тестирования знаний студента // Программа тестирует студента по какому-либо предмету обучения using System; using System.Windows.Forms ; // Другие директивы using удалены, поскольку они не используются в данной программе namespace Тестирование { public partial class Form1 : Form { int СчетВопросов =0; // Счет вопросов int ПравилОтветов =0; // Количество правильных ответов int НеПравилОтветов = 0; // Количество неправильных ответов string[] НеПравилОтветы; // Массив вопросов, на которые даны неправильные ответы int НомерПравОтвета; // Номер правильного ответа int ВыбранОтвет; // Номер ответа, выбранный студентом // Чтобы русские буквы читались корректно, объявляем объект Кодировка System.Text.Encoding Кодировка = System.Text.Encoding.GetEncoding(1251); System.IO.StreamReader Читатель; public Form1() { InitializeComponent(); button1.Text = "Следующий вопрос"; button2.Text = "Выход"; // Подписка на событие изменение состояния // переключателей RadioButton: radioButton1.CheckedChanged += new System.EventHandler(ИзмСостПерекл); radioButton2.CheckedChanged += new System.EventHandler(ИзмСостПерекл); radioButton3.CheckedChanged += new System.EventHandler(ИзмСостПерекл); НачалоТеста(); } void НачалоТеста() { Try { // Создание экземпляра StreamReader для чтения из файла Читатель = new System.IO. StreamReader(System.IO.Directory.GetCurrentDirectory() + @"\test.txt", Кодировка); this.Text = Читатель.ReadLine(); // Название предмета // Обнуление всех счетчиков: СчетВопросов = 0; ПравилОтветов = 0; НеПравилОтветов = 0; НеПравилОтветы = new string[100]; } catch (Exception Ситуация) { // Отчет о всех ошибках MessageBox.Show(Ситуация.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } ЧитатьСледВопрос(); } void ЧитатьСледВопрос() { Label1.Text = Читатель.ReadLine(); // Считывание вариантов ответа: radioButton1.Text = Читатель.ReadLine(); radioButton2.Text = Читатель.ReadLine(); radioButton3.Text = Читатель.ReadLine(); // Выясняем, какой ответ - правильный: НомерПравОтвета = int.Parse(Читатель.ReadLine()); // Переводим все переключатели в состояние "выключено": radioButton1.Checked = false; radioButton2.Checked = false; radioButton3.Checked = false; // Первая кнопка не активна, пока студент не выберет вариант ответа Button1.Enabled = false; СчетВопросов = СчетВопросов +1; // Проверка, конец ли файла: if (Читатель.EndOfStream == true) button1.Text = "Завершить"; } private void ИзмСостПерекл(object sender, EventArgs e) { // Кнопка "Следующий вопрос" становится активной, и ей передаем фокус Button1.Enabled = true; button1.Focus (); RadioButton Переключатель = (RadioButton)sender; string tmp = Переключатель.Name; // Выясняем номер ответа, выбранный студентом: ВыбранОтвет = int.Parse(tmp.Substring(11)); } private void button1_Click(object sender, EventArgs e) { // Щелчок на кнопке // "Следующий вопрос/Завершить/Начать тестирование снач". // Счет правильных ответов: if (ВыбранОтвет == НомерПравОтвета) ПравилОтветов = ПравилОтветов + 1; if (ВыбранОтвет != НомерПравОтвета) { // Счет неправильных ответов: НеПравилОтветов = НеПравилОтветов + 1; // Запоминаем вопросы с неправильными ответами: НеПравилОтветы[НеПравилОтветов] = label1.Text; } if (button1.Text == "Начать тестирование сначала") { buttonl.Text = "Следующий вопрос"; // Переключатели становятся видимыми, доступными для выбора: radioButtonl.Visible = true; radioButton2.Visible = true; radioButton3 .Visible = true; // Переход к началу файла НачалоТеста(); return; } if (button1.Text == "Завершить") { Читатель.Close(); // Закрываем текстовый файл // Переключатели делаем невидимыми: radioButton1.Visible = false; radioButton2.Visible = false; radioButton3.Visible = false; // Формируем оценку за тест: Label1.Text = string.Format("Тестирование завершено.\n" + "Правильных ответов: {0} из {1}.\n" + "Оценка в пятибалльной системе: {2:F2}.", ПравилОтветов, СчетВопросов, (ПравилОтветов * 5F) / СчетВопросов); // 5F - это максимальная оценка Button1.Text = "Начать тестирование сначала"; // Вывод вопросов, на которые Вы дали неправильный ответ string Str = "СПИСОК ВОПРОСОВ, НА КОТОРЫЕ ВЫ ДАЛИ " + "НЕПРАВИЛЬНЫЙ ОТВЕТ:\n\n"; for (int i = 1; i <= НеПравилОтветов; i++) Str = Str + НеПравилОтветы!i] + "\n"; // Если есть неправильные ответы, то вывести через MessageBox // список соответствующих вопросов: if (НеПравилОтветов != 0) MessageBox.Show(Str, "Тестирование завершено"); } if (button1.Text == "Следующий вопрос") ЧитатьСледВопрос(); } private void button2_Click(object sender, EventArgs e) { // Щелчок на кнопке "Выход" this.Close (); } } } } В начале программы объявляем переменные, которые должны быть "видны" из всех процедур класса Form1. Сразу после выполнения процедуры InitializeComponent организуем подписку на событие "изменение состояния переключателей" RadioButton одной процедурой ИзмСостПерекл. В данной программе изменение состояния любого из трех переключателей будем обрабатывать одной процедурой ИзмСостПерекл. Далее в процедуре НачалоТеста открываем файл test.txt, в котором содержится непосредственно тест, и читаем первую строку с названием предмета или темы, подлежащей тестированию. При этом обнуляем счетчик всех вопросов и счетчики вопросов, на которые студент дал правильные и неправильные ответы. Затем вызываем процедуру ЧитатьСледВопрос, которая читает очередной вопрос, варианты ответов на него и номер варианта правильного ответа. Тут же проверяем, не достигнут ли конец читаемого программой файла. Если достигнут, то меняем надпись на первой кнопке на "Завершить". В данной программе надпись на первой кнопке является как бы флагом, который указывает, по какой ветви в программе следует передавать управление. При выборе студентом того или иного варианта испытуемый может сколь угодно раз щелкать на разных переключателях, пока не выберет окончательно вариант ответа. Программа будет фиксировать выбранный вариант только на этапе Щелчка на кнопке Следующий вопрос. В процедуре обработки события "изменение состояния переключателей" выясняем, какой из вариантов ответа выбрал студент, но делаем вывод, правильно ли ответил студент или нет, только при обработке события "щелчок" на первой кнопке. В процедуре обработки события щелчок на первой кнопке ведем счет правильных и неправильных ответов, а также запоминаем в строковый массив вопросы, на которые студент дал неверный ответ. Если достигнут конец файла и надпись на кнопке стала "Завершить", то закрываем текстовый файл, все переключатели делаем невидимыми (уже выбирать нечего) и формируем оценку за прохождение теста, а также через MessageBox выводим список вопросов, на которые испытуемый дал ошибочный ответ. Фрагмент работы тестирующей программы представлен на рис. 4.5. На рис. 4.6 показан финальный фрагмент работы тестирующей программы, где выведено обоснование оценки тестирования со списком вопросов, на которые студент ответил неправильно. Рис. 4.5. Интерфейс тестирующей программы Рис. 4.6. Финальный фрагмент работы программы Вы можете совершенствовать данную программу. Например, можно добавить элемент управления Timer, чтобы ограничить время сдачи теста. Убедиться в работоспособности программы можно, открыв решение Тестирование.sin в папке Тестирование. Пример 27. Простой RTF-редактор Читателю, вероятно, известно, что Visual С# 2010 имеет элемент управления RichTextBox (форматированное текстовое поле), так же как и предыдущие версия Visual С#. Этот элемент управления позволяет осуществлять форматирование текста в стандарте RTF(один из форматов MS Word). В формате RTF в текст вводят ся специальные коды форматирования, несущие информацию о гарнитуре, разме pax шрифтов, стилях символов и абзацев, выравнивании и других возможностях форматирования. Напишем очень простой RTF-редактор, который читает как RTF-файлы, так и обычные текстовые файлы в кодировке Windows 1251, но сохраняет файлы на диск в любом случае в формате RTF. Для этой цели перенесем из панели элементов управления Toolbox элементы управления RichTextBox, меню MenuStrip, SaveFileDialog и OpenFileDialog. В выпадающем меню Файл предусмотрим такие пункты меню, как показано на рис. 4.7: Открыть в формате RTF, Открыть в формате win1251, Сохранить в формате RTF и Выход. Текст программы приведен в листинге 4.6. Листинг 4.6. Простой RTF-редактор //программа простейшего RTF-редактора using System; using System.Windows.Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace RTF_edit { public partial class Forml : Form { public Form1() { InitializeComponent(); base.Text = "Простой RTF-редактор"; richTextBoxl.Clear(); openFileDialog1.FileName = @"c:\Text2.txt"; saveFileDialog1.Filter = "Файлы RTF (*.RTF)|*.RTF"; // Подписка на обработку двух событий одной процедурой: this.oткpытьBФopмaтeRTFToolStripMenuItem.Click += new System. EventHandler(this.ОТКРЫТЬ); this. oткpытьBФopмaтeWinl251ToolStripMenuItem.Click += new System. EventHandler(this.ОТКРЫТЬ); } private void ОТКРЫТЬ(object sender, EventArgs e) { // Процедура обработки событий открытия файла в двух разных форматах. // Выясняем, в каком формате открыть файл: ToolStripMenuItem t = (ToolStripMenuItem)sender; string format = t.Text; // - читаем надпись на пункте меню try { // Открыть в каком-либо формате: if (format == "Открыть в формате RTF") { openFileDialog1.Filter = "Файлы RTF (*.RTF)|*.RTF"; openFileDialog1.ShowDialog(); if (openFileDialog1.FileName == null) return; richTextBox1.LoadFile(openFileDialog1.FileName); } if (format == "Открыть в формате Winl251") { openFileDialog1.Filter = "Текстовые файлы (*.txt)|*.txt" ; openFileDialog1.ShowDialog(); if (openFileDialog1.FileName == null) return; richTextBox1.LoadFile(openFileDialog1.FileName, RichTextBoxStreamType. PlainText) ; } richTextBox1.Modified = false; } catch (System.IO.FileNotFoundException Exc) { MessageBox.Show(Exc.Message + "\nНет такого файла", "Ошибка”, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } catch (Exception Exc) { // Отчет о других ошибках MessageBox.Show(Exc.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private void coxpaнитьBФopмaтeRTFToolStripMenuItem_Click( object sender, EventArgs e) { saveFileDialog1.FileName = openFileDialog1.FileName; if (saveFileDialog1.ShowDialog() == DialogResult.OK) Запись(); } void Запись() { try { richTextBox1.SaveFile(saveFileDialog1.FileName); richTextBox1.Modified = false; } catch (Exception Exc) { // Отчет обо всех возможных ошибках: MessageBox.Show(Exc.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private void ВыходToolStripMenuItem_Click (object sender, EventArgs e) { this.Close() ; } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (richTextBox1.Modified == false) return; // Если текст модифицирован, то выясняем, записывать ли файл? DialogResult МВох = MessageBox.Show( "Текст был изменен. \nСохранить изменения?", "Простой редактор", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation); // YES - диалог; NO - выход; CANCEL - редактирование if (МВох == DialogResult.No) return; if (MBox == DialogResult.Cancel) e.Cancel = true; if (MBox == DialogResult.Yes) { if (saveFileDialog1.ShowDialog() == DialogResult.OK) { Запись(); return; } else e.Cancel = true; // Передумал выходить из ПГМ } // - DialogResult.Yes } } } Структура программы аналогична программе простого текстового редактора, описанного в предыдущем разделе. Сразу после выполнения процедуры InitializeComponent задаем начальные значения некоторым переменным (см. текст программы). Здесь же выполняем подписку на обработку одной процедурой открыть двух событий — выбор пунктов меню Открыть в формате RTF и Открыть в формате Win1251. Подробно особенности обработки событий, создаваемых разными объектами, обсуждены нами в примерах 17, 18 и др. (см. главу 3). Из этих примеров читатели знают, что сведения об объекте, создавшем событие, находятся в объектной переменной sender. Какой пункт меню указал пользователь, можно узнать, конвертировав переменную sender в объект t класса ToolStripMenuItem. В таком случае мы можем прочитать в свойстве Text название пункта меню, которое выбрал пользователь. Таким образом, в строковую переменную format попадает или строка "Открыть в формате RTF", или строка "Открыть в Формате Winl251". Метод LoadFile объекта richTextBox1 загружает либо файл в Формате RTF, либо файл в обычном текстовом формате. Перехватчик ошибок catch сообщает пользователю либо о том, что такого файла нет, либо если пользователь использует пункт меню Открыть в формате RTF для открытия текстового файла, он получает сообщение "Недопустимый формат файла". Сохранение файла (рис. 4.7) выполняется также с использованием стандартного диалога SaveFileDialog. Непосредственное сохранение файла удобнее всего поднять в отдельной процедуре Запись(), поскольку эту процедуру необходимо вызывать также при выходе из программы, когда в документе имеются несохраненные изменения richTextBox1 .Modified = True. Рис. 4.7. Простой RTF-редактор В основе процедуры Запись () также лежит блок try.. .catch: выполнить noпытку (try) сохранения файла (saveFile) и при этом перехватить (catch) возможные недоразумения и сообщить о них пользователю в диалоговом окне MessageBox. Выход из программы организован абсолютно так же, как и в программе из предыдущего примера. Вдобавок обработаны два события — пункт меню Выход и всевозможные закрытия программы традиционными способами Windows. Предусмотрен диалог с пользователем в случае имеющих место несохраненных данных. Замечу, для закрытия приложения следует осторожно пользоваться методом Exit объекта Application (можно сказать, с оглядкой). Этот метод подготавливает приложение к закрытию. Да, метод Application.Exit() не вызывает события формы Сlosing. Но попробуйте проследить за поведением программы после команды Application.Exit с помощью отладчика (клавиша <F11>). Вы убедитесь, что после команды Application.Exit управление перейдет следующему оператору, затем — следующему, и так до конца процедуры. Если на пути встретится функция MessageBox, то программа выдаст это диалоговое окно, и это несмотря на то, что уже давно была команда Application.Exit. Аналогично ведет себя метод Сlose элемента Form (если вы работаете с проектом Windows Application), который вызывается таким образом: this .Сlose(). Да, this.Сlose вызывает событие формы Сlosing. Этот метод закрывает форму и освобождает все ресурсы. Но для освобождения ресурсов после команды this.Сlose управление также пройдет все операторы процедуры. Таким образом, для немедленного выхода из процедуры следует комбинировать названные методы с return. Убедиться в работоспособности программы можно, открыв решение RTF_edit.sln в папке RTF_edit. Пример 28. Печать текстового документа Любой текстовый редактор (и не только текстовый) должен иметь возможность печати на принтере. Мы сознательно не добавляли такую возможность в текстовые редакторы, приведенные в предыдущих разделах, чтобы не запутать читателя. Понятно, что чем больше функциональности имеет программа, тем сложнее ее программный код. тем труднее текст программы для понимания. А наша задача — выразительно и ярко демонстрировать технологии в максимально простой форме. Программа, представленная в данном разделе, имеет такие скромные возможности: открыть в стандартном диалоге Windows текстовый файл, просмотреть его в окне программы (в текстовом поле) без возможности изменения текста (ReadOnly) и при желании пользователя вывести этот текст на принтер. Таким образом, чтобы создать данную программу, следует в форму перенести следующие элементы управления: текстовое поле TextBox, меню MenuStrip с пунктами меню: Открыть, Печатать и Выход, а также элементы управления OpenFileDialog и PrintDocument. Текст программы представлен в листинге 4.7. Листинг 4.7. Печать текстового документа // Программа позволяет открыть в стандартном диалоге текстовый файл, // просмотреть его в текстовом поле без возможности изменения текста // (Readonly) и при желании пользователя вывести этот текст на принтер. using System; using System.Drawing; using System.Windows.Forms; // Другие директивы using удалены, поскольку они не используются в данной программе namespace TXT_print { public partial class Form1 : Form { System.IO.StreamReader Читатель; public Form1() { InitializeComponent(); base.Text = "Открытие текстового файла и его печать"; textBox1.Multiline = true; textBox1.Clear(); textBox1.Size = new System.Drawing.Size(268, 112); textBox1.ScrollBars = ScrollBars.Vertical; textBox1.Readonly = true; //До тех пор, пока файл не прочитан в текстовое поле, //не должен быть виден пункт меню "Печать..." печатьToolStripMenuItem.Visible = false; openFileDialog1.FileName = null; } private void открытьToolStripMenuItem_Click(object sender, EventArgs e) { // Щелчок на пункте меню "Открыть": openFileDialog1.Filter = "Текстовые файлы (*.txt)|*.txt|All files (*.*)|*.*"; openFileDialog1.ShowDialog(); if (openFileDialog1.FileName == null) return; try { // Создание потока StreamReader для чтения из файла Читатель = new System.IO.StreamReader(openFileDialogl.FileName, System.Text.Encoding.GetEncodihg(1251)); // - здесь заказ кодовой страницы Win1251 для русских букв textBoxl.Text = Читатель.ReadToEnd(); Читатель.Close(); печатьToolStripMenuItem.Visible = true; } catch (System.IO.FileNotFoundException Exc) ' { MessageBox.Show(Exc.Message + "/nНет такого файла", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } catch (Exception Exc) { // Отчет о других ошибках: MessageBox.Show(Exc.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } private void печатьToolStripMenuItem_Click(object sender, EventArgs e) { // Пункт меню "Печать" try { Читатель = new System.IO.StreamReader(openFileDialog1.FileName, System.Text.Encoding.GetEncoding(1251)); // - здесь заказ кодовой страницы Winl251 для русских букв try { printDocumentl.Print(); } finally { Читатель.Close(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } private void printDocument1_PrintPage(object sender, System.Drawing. Printing.PnntPageEventArgs e) { Single linesPerPage = 0; Single yPos = 0; int count = 0; Single leftMargin = e.MarginBounds.Left; Single topMargin = e.MarginBounds.Top; String line = null; Font printFont = new Font("Times New Roman", 12.0F); // Вычисляем количество строк на одной странице linesPerPage = е.MarginBounds.Height / printFont.GetHeight(e.Graphics); // Печатаем каждую строку файла while (count < linesPerPage) { line = Читатель .ReadLine (); if (line == null) break; // выход из цикла yPos = topMargin + count * printFont.GetHeight(e.Graphics); // Печать строки e.Graphics.DrawString(line, printFont, Brushes.Black, leftMargin, yPos, new StringFormat()); count += 1; } // Печать следующей страницы, если есть еще строки файла if (line != null) е.HasMorePages = true; else e.HasMorePages = false; } private void выходToolStripMenuItem_Click(object sender, EventArgs e) { // Выход из программы base.Close(); } } } Здесь при обработке события загрузки формы Form1_Load запрещаем пользователю редактировать текстовое поле: Readonly = true. Также назначаем свойству печатьToolstripMenuitem.visible = false (пункт меню Печать), т.е. в начале работы программы пункт меню Печать пользователю не виден (поскольку пока распечатывать нечего, необходимо вначале открыть текстовый файл). Остальные присваивания при обработке события Form1_Load очевидны. При обработке события "щелчок на пункте меню" Открыть вызываем стандартный диалог openFiieDialog и организуем чтение файла через создание потока StreamReader Эти процедуры мы уже рассматривали подробно в разделах о текстовых редакторах, поэтому оставим их без комментария. Замечу только, что nocле чтения файла в текстовое поле назначаем видимость пункту меню Печать печатьToolStripMenuItem.Visible = true, поскольку уже есть, что печатать на принтере (файл открыт). Представляет интерес обработка события "щелчок на пункте меню" Печать. Этот пункт написан автором по технологии, приведенной в справочной системе С# (MSDN). Здесь во вложенных блоках try. . .finally. . .catch программа еще раз создает поток StreamReader, а затем запускает процесс печати докумнта printDocument1.Print. Если ничего более не программировать, только метод printDocument1.Print, то принтер распечатает лишь пустую страницу. Чтобы принтер распечатал текст, необходимо обработать событие PrintPage (см. текст программы), которое создает объект PrintDocument. То есть роль метода Print – это создать событие PrintPage. Обратите внимание на обработку события PrintDocument1. PrintPage. Пример обработки этого события приведен в MSDN. Вначале перечислены объявления ременных, значения некоторых из них получаем из аргументов события е, например, leftMargin — значение отступа от левого края, и т. д. Назначаем шрифт печати — Times New Roman, 12 пунктов. Далее в цикле while программа читает каждую строку line из файла— Читатель .ReadLine (), а затем распечатывает ее командой (методом) Drawstring. Здесь используется графический объект Graphics, который получаем из аргумента события е. В переменной count происходит счет строк. Если количество строк оказывавается большим, чем число строк на странице linesPerPage, то происходит выход из цикла, поскольку страница распечатана. Если есть еще страницы, а программа выясняет это, анализируя содержимое переменной line, если ее содержимое отличается от значения null (line ! = null), то аргументной переменной e.HasMorePage назначаем true, что инициирует опять событие PrintPage и подпрограмм PrintDocument1. Print начинает свою работу вновь. И так, пока не закончатся все страницы е.HasMorePages = False для печати на принтере. Рис. 4.8. Фрагмент работы программы печати текстового файла На рис. 4.8 показан интерфейс приложения. убедиться в работоспособности программы можно, открыв решение ТХT_print.sln в папке TXT_print. Пример 29. Чтение/запись бинарных файлов с использованием потока данных Обычно программа либо что-то читает с диска в оперативную память, либо что-то пишет на диск. Писать, читать можно либо в бинарный (двоичный) файл, либо в текстовый (литерный, строковый) файл. Разница между ними состоит в том, что текстовый файл можно прочитать текстовым редактором, например Блокнотом, а бинарный — нет. Название "бинарный" (двоичный) — условное, поскольку, по сути и текстовый, и бинарный файлы являются двоичными файлами. Операции с двоичным файлом гораздо более скоростные, и такой файл занимает существенно меньшее пространство на диске. Зато текстовый файл можно читать и редактировать любым текстовым редактором. Обычно программист выбирает компромисс между этими двумя преимуществами. Приведем пример самого простейшего случая записи на диск бинарного файла с данными об успеваемости одного студента. Понятно, что эта программа может быть маленькой частью большой программы по обработке успеваемости студентов в вузе. Данная программа принимает от пользователя сведения только об одном студенте в текстовые поля формы. При нажатии кнопки Сохранить программа записывает введенные сведения в двоичный файл, а при нажатии кнопки Читать читает эти сведения из двоичного файла в текстовые поля формы. Итак, в форме имеем три текстовых поля, куда пользователь может записать соответственно номер студента по порядку, фамилию студента и его средний балл успеваемости. Поэтому в форму из панели Toolbox перенесем три текстовых поля TextBox, три метки Label и две командных кнопки: Читать и Сохранить. Таким образом, получим пользовательский интерфейс, показанный на рис. 4.9. Текст программы приведен в листинге 4.8. Листинг 4.8. Чтение/запись бинарных файлов //Программа для чтения/записи бинарных файлов с использованием потока данных using System; using System. Windows . Forms ; using System.IO; // - добавляем пространство имен для сокращения программного кода // Другие директивы using удалены, поскольку они не используются в данной программе namespace Read_Write_bin { public partial class Form1 : Form { public Form1() { InitializeComponent(); base.Text = "Успеваемость студента"; label1.Text = "Номер n/n"; label2.Text = "Фамилия И.О."; label3.Text = "Средний балл"; textBox1.Clear(); textBox2.Clear(); textBox3.Clear(); button1.Text = "Читать"; button2.Text = "Сохранить"; } private void button1_Click(object sender, EventArgs e) { // ЧТЕНИЕ БИНАРНОГО ФАЙЛА. // Если такого файла нет if (File.Exists(@"C:\student.usp") == false) return; // Создание потока Читатель var Читатель = new BinaryReader(File.OpenRead(@"C:\student.usp")); try { int Номер_пп = Читатель.Readlnt32(); string ФИО = Читатель .ReadString (); Single СредБалл = Читатель.ReadSingle(); textBox1.Text = Convert.ToString(Номер_пп); textBox2.Text = Convert.ToString(ФИО); textBox3.Text = Convert.ToString(СредБалл); } finally { Читатель.Close(); } } private void button2_Click(object sender, EventArgs e) { // ЗАПИСЬ БИНАРНОГО ФАЙЛА. // Создаем поток Писатель для записи байтов в файл BinaryWriter Писатель = new BinaryWriter( File.Open(@"C:\student.usp", FileMode.Create)); try { int Номер_пп = Convert.ToInt32(textBoxl.Text); string ФИО = Convert.ToString(textBox2.Text); Single СредБалл = Convert.ToSingle(textBox3.Text); Писатель.Write(Номер_пп); Писатель.Write(ФИО); Писатель.Write(СредБалл); } finally { Писатель.Close(); } } } } Как видно, сразу после выполнения процедуры InitializeComponent организована инициализация (присвоение начальных значений) элементам формы: текстовых полей, меток и кнопок. Запись файла на диск происходит при обработке события button2 .Сlick, т. е. щелчок мышью на кнопке Сохранить (рис. 4.9). Для этого создаем поток байтов Писатель для открытия файла student.usp. Если такой файл не существует, то он создается(Create), а если файл уже есть, то он перезаписывается. Как видно, для упрощения программы мы не использовали элемент управления ОpenFileDialog ддя открытия файла в диалоге. Рис. 4.9. Фрагмент работы программы чтения/записи бинарных файлов Далее преобразуем записанное в текстовых полях в более естественные типы данных. Номер по порядку Номер_пп — это тип int, преобразование в целый тип может быть реализовано операцией Сonvert.ToInt32 (можно использовать другие функции преобразования), для переменной СредБалл (средний балл) больше всего подходит тип с плавающей точкой Single, при этом преобразование осуществляется операцией Convert.ToSingle. Преобразование для строковой переменной фио является необязательным и приведено для симметрии записей. Операторы Писатель.write записывают эти данные в файл. После блока Finally происходит обязательное закрытие (Сlose) файла. Чтение файла выполняется при обработке события "щелчок мышью на кнопке" Читать. Как уже упоминалось, для максимального упрощения в данной программе не предусмотрено открытие файла через стандартный диалог, поэтому вначале процедуры выясняем, существует ли такой файл. Если файла C:\student.usp нет, то программируем выход (return) из обработчика данного события. Заметьте, чтобы программисту было максимально легко отслеживать ветви оператора if, мы написали: "Если файла нет, то return". При этом длинная ветвь логики "если файл есть" не включена непосредственно в оператор if. Поэтому этот фрагмент программного кода читается (воспринимается) программистом легко. Далее создается поток байтов Читатель из файла student.usp, открытого для чтения. Чтение из потока в каждую переменную реализовано с помощью функции ReadInt32 — читать из потока Читатель в переменную типа int, аналогично функциям ReadString и ReadSingle. Далее осуществлено конвертирование этих переменных в строковый тип Сonvert.ToString. Как видно, можно было изначально все текстовые поля записывать в файл без конвертирования, но при дальнейшем развитии этой программы значения полей все равно пришлось бы преобразовывать в соответствующий тип. После блока Finally происходит закрытие (Сlose) файла. Блок Finally выполнится всегда, даже если перед ним была команда return. Дальнейшее развитие данной программы может быть по пути добавления в файл сведений о других студентах. В таком случае при чтении файла будет неопределенность количества студентов. Тогда следует обработать ситуацию достижения конца файла: catch (EndOfStreamException е) а затем закрыть файл. Убедиться в работоспособности программы можно, открыв peшение Read_Write_bin.sln в папке Read_Write_bin. |