Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содержание] [Дальше]

Визуальное проектирование приложений C#


А.В. Фролов, Г.В. Фролов

Глава 4. Создание редактора текста.. 2

Приложение SimpleNotepad.. 2

Добавление меню... 3

Переименование меню и строк меню.. 5

Подключение меню к форме. 6

Вставка поля редактирования.. 7

Обработка событий.. 7

Работа с файлами документов.. 8

Создание нового документа. 8

Открытие существующего файла. 8

Сохранение файла. 10

Печать документа.. 12

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

Редактирование меню File. 14

Подключение пространств имен. 14

Настройка параметров страницы документа. 14

Предварительный просмотр документа перед печатью.. 15

Отображение окна печати документа. 16

Обработка события PrintPage. 17

Закрытие главного окна редактора текста. 19

Реализация функций меню Edit.. 21

Реализация функций меню Format.. 22

Шрифт символов. 23

Цвет символов. 23

Стиль символов. 24

Выравнивание параграфов. 28

Реализация функций меню Help. 29

Добавление новой формы.. 29

Отображение формы.. 31

Редактирование класса HelpAboutForm.. 31

Создание инструментальной панели.. 32

Добавление инструментальной панели в окно приложения. 32

Подключение списка изображений. 32

Наполнение списка изображений. 33

Редактирование кнопок инструментальной панели. 34

Строка состояния.. 37

Добавление строки состояния. 37

Настройка свойств строки состояния. 37

Привязка строки состояния к меню.. 38

 

Глава 4. Создание редактора текста

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

Наш редактор будет способен редактировать не только текстовые документы, но и документы в формате RTF (Rich Text Format). При этом мы воспользуемся стандартным для Microsoft Visual Studio Studio .NET компонентом RichTextBox.

Вот только основные возможности этого компонента:

·         символы текста могут иметь любое шрифтовое оформление;

·         доступны как растровые шрифты, так и шрифты TrueType;

·         имеется возможность задавать оформление параграфов текста, такое как выравнивание влево или вправо, центровка и задание отступов;

·         не вызывает особого затруднения печать документов, загруженных для редактирования;

·         в компонент RichTextBox может работать как с текстом без шрифтового или какого либо другого оформления, так с документами в формате RTF.

Формат RTF предназначен для хранения текста вместе со шрифтовым оформлением и оформлением параграфов. Детальное изучение формата RTF выходит за рамки нашей книги. При необходимости вы сможете найти полное описание этого формата в MSDN (http://msdn.microsoft.com). Однако в большинстве случаев Вам не придется создавать самостоятельно текстовые файлы в формате RTF, так как для этого можно использовать многие текстовые процессоры (например, Microsoft Word for Windows или WordPad).

Чтобы Вы получили некоторое представление о том, что представляет собой текстовый файл в формате RTF, приведем небольшой пример.

Для преобразования в формат RTF мы взяли следующую текстовую строку:

This is RTF test file. This is RTF test file. This is RTF test file.

Мы загрузили эту строку в приложение SimpleNotepad (исходные тексты которого будут приведены ниже). Затем мы задали для текста шрифтовое оформление и сохранили как текст в формате RTF. Вот что получилось в результате:

{\rtf1\ansi\deff0\deftab720{\fonttbl{\f0\fnil MS Sans Serif;}{\f1\fnil\fcharset2 Symbol;}{\f2\fswiss\fprq2 System;}{\f3\fswiss\fprq2 Arial;}{\f4\froman\fprq2 Calisto MT;}}
{\colortbl\red0\green0\blue0;}
\deflang1033\pard\plain\f4\fs38 This is RTF test file. This is RTF test file. This is RTF test file.
\par
\par
\par
\par }

Компонент RichTextBox можно использовать для создания достаточно мощного текстового редактора, напоминающего приложение WordPad. Однако для него можно найти и другое применение. Например, приложение может отображать с его помощью красиво оформленные сообщения.

Приложение SimpleNotepad

Создайте новое приложение с названием SimpleNotepad, используя для этого технику, описанную в предыдущих главах нашей книги.

Отредактируйте имя формы, которая по умолчанию называется Form1. Для этого, после того как мастер проектов создаст приложение, измените свойство формы Name, заменив имя Form1 на SimpleNotepadForm. Далее, в окне Solution Explorer переименуйте файл form1.cs в SimpleNotepadForm.sc.

Теперь откройте исходный текст формы, и отредактируйте метод Main таким образом, чтобы методу Application.Run передавалась ссылка на новый объект класса SimpleNotepadForm:

 

static void Main()
{
  Application.Run(new SimpleNotepadForm());
}

Теперь наше пространство имен будет называться SimpleNotepad, а класс формы, играющей роль главного окна приложения — SimpleNotepadForm:

namespace SimpleNotepad
{
  public class SimpleNotepadForm : System.Windows.Forms.Form
  {
     …
  }
}   

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

Далее замените стандартный значок, создаваемый системой Microsoft Visual Studio .NET каким-нибудь другим. Используйте для этого технику, описанную в предыдущей главе. Например, для приложения SimpleNotepad Вы можете использовать файл note01.ico или другой из каталога Program Files\Microsoft Visual Studio .NET\Common7\Graphics\icons\Writing.

Вы также можете изменить размеры окна, принятые по умолчанию, т.к. в окне размером 300 на 300 пикселов редактировать текст неудобно.

Добавление меню

Для того чтобы добавить меню в главное окно нашего приложения, перетащите из панели инструментов Toolbox значок меню с названием MainMenu.

Как только Вы это сделаете, окно дизайнера форм примет вид, показанный на рис. 4-1.

Рис. 4-1. Добавление меню

В нижней части этого окна появится значок программного компонента — меню mainMenu1. Кроме того, непосредственно под заголовком окна появится пустое пока меню, представленное полем с надписью Type Here (что можно перевести как «печатать здесь»).

Напечатайте в этом поле строку «&File». В результате этого в окне нашего приложения появится меню File (рис. 4-2).

Рис. 4-2. Создание меню File

Обратите внимание, что первая буква в названии меню подчеркнута. Это получилось потому, что мы поставили перед ней префикс &. Этим префиксом отмечается буква, предназначенная для ускоренного выбора меню (или строки меню) при помощи клавиатуры.

По мере того как Вы будете вводить названия строк меню File, поле ввода Type Here будет опускаться вниз. Таким способом Вы сможете ввести все необходимые строки данного меню.

Чтобы отредактировать строку меню, щелкните ее правой клавишей мыши. На экране появится контекстное меню, показанное на рис. 4-3.

Рис. 4-3. Контекстное меню

С помощью строки Insert New Вы можете вставить новую строку меню между уже существующих строк. Строка Insert Separator предназначена для вставки разделительной линии между строками меню. И, наконец, при помощи строки Edit Names можно отредактировать идентификаторы строк и меню.

Если же Вам нужно изменить введенные названия строк и меню, это можно сделать по месту, выбрав нужную строку мышью.

Создайте меню File, чтобы оно было таким, как показано на рис. 4-4.

Рис. 4-4. Меню File

По своему назначению строки этого меню полностью соответствуют строкам стандартного редактора текста Microsoft Notepad.

Далее создайте меню Edit (рис. 4-5).

Рис. 4-5. Меню Edit

В меню Edit мы реализуем только самые важные функции, опустив пока поиск, замену и некоторые другие стандартные функции приложения Microsoft Notepad.

Меню Format (рис. 4-6) состоит только из одной строки Font, с помощью которой пользователь сможет изменить шрифт текста.

Рис. 4-6. Меню Format

И, наконец, последнее меню нашего приложения, это меню Help (рис. 4-7).

Рис. 4-7. Меню Help

В этом меню мы предусмотрели только одну строку — About.

Переименование меню и строк меню

Прежде чем продолжить работу с меню, давайте переименуем идентификаторы меню и строк меню таким образом, чтобы с ними было удобнее работать в программе. Для этого в окне дизайнера формы щелкните правой клавишей мыши главное меню приложения и затем выберите из контекстного меню строку Edit Names (рис. 4-8).

Рис. 4-8. Редактирование имен меню и строк меню

Как только Вы это сделаете, рядом с каждой строкой меню появится имя, созданное дизайнером форм по умолчанию (рис. 4-9). Это имена вида menuItem1, menuItem2 и т.д.

Рис. 4-9. Просмотр имен меню и строк меню

Отредактируйте имена меню и строк меню, как это показано на рис. 4-10.

Рис. 4-10. Новые имена меню File

Эту процедуру нужно будет повторить для каждого создаваемого Вами меню и для каждой строки меню. При этом меню верхнего уровня File, Edit, Format и Help должны называться menuFile, menuEdit, menuFormat и menuHelp. Имена строк меню формируются путем добавления к имени меню текста, отображаемого в строке меню. Например, строка New меню File называется menuFileNew, а строка About меню Help menuHelpAbout.

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

Подключение меню к форме

Теперь, когда меню готово, его нужно подключить к форме. Для этого отредактируйте свойство формы с именем Menu. Раскройте список возможных значений этого свойства и выберите в этом списке строку mainMenu1.

После этого Вы можете оттранслировать приложение и запустить его, нажав кнопку F5. Убедитесь, что меню отображается, и Вы можете выбирать его строки. Если все нормально, можно переходить к следующему этапу проектирования нашего редактора текста.

Вставка поля редактирования

Редактирование текста в нашем приложении будет выполнять компонент RichTextBox. Этот компонент очень похож на текстовый редактор TextBox, с которым мы уже имели дело в предыдущей главе.

Перетащите из панели Toolbox в форму приложения simpleNotepad компонент RichTextBox.

Настройте свойства компонента, чтобы он занимал все окно приложения. Далее отредактируйте свойство Dock. Это свойство задает расположение выбранного компонента внутри содержащей его формы (рис. 4-11).

Рис. 4-11. Редактирование свойства Dock

Выберите расположение по центру, щелкнув кнопку, показанную на рис. 4-8 в нажатом состоянии.

И, наконец, сотрите текстовую строку, задающую значение свойства Text. Напомним, что это значение определяет текст, который будет отображаться в поле редактирования сразу после отображения формы. В нашем случае никакой предварительной инициализации редактора текстом не требуется.

Запустите полученное приложение на выполнение. В результате на экране должно появиться окно, показанное на рис. 4-12 (в нем мы уже набрали две строки текста).

Рис. 4-12. Работа над дизайном редактора текста завершена

Обработка событий

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

Создайте обработчики событий для всех строк меню File, Edit, Format и Help, дважды щелкнув каждую строку этих меню. Все обработчики событий создаются с пустым телом:

private void menuFileNew_Click(object sender, System.EventArgs e)
{
}

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

Работа с файлами документов

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

Создание нового документа

Если пользователь выберет строку New из меню File, наш редактор текста SimpleNotepad должен создать новый текстовый документ. В простейшем случае для этого достаточно просто очистить содержимое окна редактирования, вызвав метод Clear:

private void menuFileNew_Click(object sender, System.EventArgs e)
{
  textBox1.Clear();
}

Метод Clear вызывается для объекта textBox1, т.е. для нашего окна редактирования, занимающего все пространство формы.

Открытие существующего файла

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

Значок компонента OpenFileDialog будет показан в нижней части формы (рис. 4-13), причем этот компонент получит идентификатор openFileDialog1. Этот идентификатор будет использован нами в программном коде, открывающем исходный файл.

Рис. 4-13. Добавление компонента OpenFileDialog

Добавьте в класс SimpleNotepadForm новый метод MenuFileOpen, предназначенный для открытия существующего файла. Вот его исходный текст:

/// <summary>
/// Открытие существующего файла
/// </summary>
private void MenuFileOpen()
{
  if(openFileDialog1.ShowDialog() ==
     System.Windows.Forms.DialogResult.OK &&
     openFileDialog1.FileName.Length > 0)
  {
     try
     {
       richTextBox1.LoadFile(openFileDialog1.FileName,
          RichTextBoxStreamType.RichText);
     }
     catch (System.ArgumentException ex)
     {
       richTextBox1.LoadFile(openFileDialog1.FileName,
          RichTextBoxStreamType.PlainText);
     }

     this.Text = "Файл [" + openFileDialog1.FileName + "]";
  }
}

Далее добавьте вызов этого метода в обработчик события menuFileOpen_Click, получающий управление при выборе строки Open из меню File:

private void menuFileOpen_Click(object sender, System.EventArgs e)
{
  MenuFileOpen();   
}

Если все сделано правильно, то после трансляции исходного приложения редактор сможет открывать существующие файлы для редактирования.

Как работает метод MenuFileOpen?

Получив управление, он вызывает метод openFileDialog1.ShowDialog, отображающий на экране стандартное диалоговое окно выбора файла (рис. 4-14).

Рис. 4-14. Стандартное диалоговое окно выбора файла

Настраивая свойства компонента openFileDialog1, можно изменять внешний вид этого окна.

Задайте фильтр имен открываемых файлов. Для этого выделите компонент openFileDialog1 в окне дизайнера форм левой клавишей мыши, а затем измените свойство Filter. Присвойте этому свойству следующую текстовую строку:

RTF files|*.rtf|Text files|*.txt|All files|*.*

Строка фильтра состоит из блоков, разделенных символом |. Первый блок задает название типа файла RTF files, отображаемое в поле Files of type диалогового окна выбора файла (рис. 4-14), а второе — маску для имен файлов. Таким образом, для открытия документов RTF используется маска *.rtf.

Далее следует название формата Text files, соответствующее обычным текстовым файлам. Для этого формата применяется маска *.txt.

И, наконец, чтобы приложение могло открывать файлы любых типов (All files), используется маска *.*.

Продолжим работу. Оттранслируйте и запустите наше приложение. Выбрав с помощью только что описанного окна текстовый файл или файл RTF, щелкните кнопку Open. Выбранный файл будет загружен в окно нашего редактора текста (рис. 4-15).

Рис. 4-15. Загружен файл для редактирования

Полный путь к редактируемому файлу будет отображен в заголовке главного окна нашего приложения.

Подробнее о диалоговых окнах мы расскажем в главе 5 с названием «Диалоговые окна». Сейчас же мы только отметим, что если в окне выбора файла пользователь щелкнул кнопку Open, метод ShowDialog возвращает значение System.Windows.Forms.DialogResult.OK. В этом случае мы также дополнительно проверяем, что файл был выбран. Для этого мы сравниваем длину строки полного пути к выбранному файлу openFileDialog1.FileName.Length с нулем:

if(openFileDialog1.ShowDialog() ==
  System.Windows.Forms.DialogResult.OK &&
  openFileDialog1.FileName.Length > 0)
{
  …
}

В том случае, когда пользователь выбрал файл, метод MenuFileOpen предпринимает попытку загрузить этот файл в редактор richTextBox1, вызывая для этого метод LoadFile:

try
{
  richTextBox1.LoadFile(openFileDialog1.FileName,
     RichTextBoxStreamType.RichText);
}
catch (System.ArgumentException ex)
{
  richTextBox1.LoadFile(openFileDialog1.FileName,
     RichTextBoxStreamType.PlainText);
}

В качестве первого параметра этому методу передается путь к файлу, а в качестве второго — тип файла RichTextBoxStreamType.RichText. Этот тип соответствует формату RTF.

В том случае, если выбранный файл имеет формат, отличный от RTF, в методе LoadFile возникает исключение System.ArgumentException. Наш обработчик этого исключения выполняет повторную попытку загрузить файл, но на этот раз уже как обычный текст. Для этого мы передаем в качестве второго параметра методу LoadFile значение RichTextBoxStreamType.PlainText.

Сохранение файла

Чтобы добавить в наше приложение возможность сохранения документов, перетащите из панели инструментов Toolbox в окно дизайнера форм компонент SaveFileDialog. Этот компонент получит идентификатор saveFileDialog1 (рис. 4-16).

Рис. 4-16. Добавление компонента SaveFileDialog

После добавления этого компонента отредактируйте его свойства Filter и FileName.

Свойство Filter должно иметь значение, обеспечивающее работу с документами RTF:

RTF files|*.rtf

Для свойства FileName задайте значение doc1.rtf. При этом по умолчанию документы будут сохраняться в файле с этим именем (рис. 4-17).

Рис. 4-17. Стандартное диалоговое окно сохранения файла

Для сохранения документов в файле добавьте в класс SimpleNotepadForm следующий метод с именем MenuFileSaveAs:

/// <summary>
/// Сохранение документа в новом файле
/// </summary>
private void MenuFileSaveAs()
{
  if(saveFileDialog1.ShowDialog() ==
     System.Windows.Forms.DialogResult.OK &&
  saveFileDialog1.FileName.Length > 0)
  {
     richTextBox1.SaveFile(saveFileDialog1.FileName);
     this.Text = "Файл [" + saveFileDialog1.FileName + "]";

  }
}

Здесь метод saveFileDialog1.ShowDialog отображает стандартный диалог сохранения файлов, показанный на рис. 4-17. Если пользователь указал имя файла и щелкнул в этом диалоге кнопку Save, метод MenuFileSaveAs сохраняет документ в файле, вызывая для этого метод richTextBox1.SaveFile.

Далее этот метод отображает имя сохраненного файла в заголовке окна нашего приложения:

this.Text = "Файл [" + saveFileDialog1.FileName + "]";

Заметим, что существует несколько перегруженных вариантов метода SaveFile.

Если методу SaveFile задан один параметр (путь к сохраняемому файлу), то документ будет сохранен в формате RTF.

Второй параметр позволяет выбрать тип сохраняемого файла.

Если передать через этот параметр значение RichTextBoxStreamType.PlainText, документ будет сохранен в виде текстового файла с потерей форматирования. В том случае, когда нужно сохранить документ в формате RTF, используйте значение RichTextBoxStreamType.RichText. Можно также сохранить документ и как текст Unicode, для чего нужно передать через второй параметр методу SaveFile значение RichTextBoxStreamType.UnicodePlainText.

Определив в исходном тексте нашего приложения метод MenuFileSaveAs, добавьте его вызов в обработчики событий от строк Save и Save As меню File:

private void menuFileSave_Click(object sender, System.EventArgs e)
{
  MenuFileSaveAs();
}

private void menuFileSaveAs_Click(object sender, System.EventArgs e)
{
  MenuFileSaveAs();
}

Для простоты мы сделаем функции этих строк меню одинаковыми.

Печать документа

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

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

Добавьте в окно дизайнера форм компоненты PrintDocument, PrintDialog, PrintPreviewDialog и PageSetupDialog, как это показано на рис. 4-18 (если все программные компоненты не помещаются в видимую часто окна, щелкните окно с компонентами правой клавишей мыши и выберите строку Line Up Icons, для того чтобы упорядочить значки компонентов).

Рис. 4-18. Добавлены компоненты для печати документов

Далее Вам нужно будет настроить свойства добавленных компонентов.

Компонент PrintDocument предназначен для вывода данных документа на принтер. Сразу после добавления этот компонент получает идентификатор printDocument1.

Свойства компонента PrintDocument описывают, как именно нужно распечатывать документ. Запишите в свойство DocumentName компонента PrintDocument строку SimpleNotepad Document. Эта строка будет идентифицировать наш документ при отображении состояния очереди печати. Значения остальных свойств оставьте без изменения.

С помощью компонента PrintDialog наше приложение выведет на экран стандартное диалоговое окно печати документа (рис. 4-19).

Рис. 4-19. Стандартное диалоговое окно печати документа

Перед использованием компонента PrintDialog задайте значение его свойства Document, равным printDocument1. Этим Вы обеспечите связь компонента PrintDialog с компонентом PrintDocument, указав таким способом документ, который следует распечатать.

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

Окно предварительного просмотра документа перед печатью показано на рис. 4-20.

Рис. 4-20. Окно предварительного просмотра документа перед печатью

Здесь пользователь сможет увидеть документ в таком виде, в котором он появится после печати на принтере. С помощью кнопки с изображением принтера пользователь сможет отправить документ на печать прямо из окна предварительного просмотра.

Что же касается окна настройки параметров страницы документа (рис. 4-21), то с его помощью пользователь сможет выбрать размер бумаги, расположение документа на бумаге, поля и т.д.

Рис. 4-21. Окно настройки параметров страницы документа

Итак, Вы добавили в приложение компоненты, необходимые для печати и настроили их свойства. Теперь, чтобы все это заработало, нужно будет создать все необходимые обработчики событий.

Редактирование меню File

Прежде чем продолжить создание приложения SimpleNotepad, отредактируйте меню File.  В этом меню должны быть строки PageSetup, Print Preview и Print.

Создайте также обработчики сообщений для перечисленных выше строк меню.

Подключение пространств имен

Для работы с классами, предназначенными для выполнения операций с потоками и печати, добавьте в начало листинга нашей программы следующие строки:

using System.IO;
using System.Drawing.Printing;

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

Настройка параметров страницы документа

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

Добавьте в класс SimpleNotepadForm метод MenuFilePageSetup, предназначенный для вывода на экран диалогового окна настройки параметров страницы документа, показанного на рис. 4-21:

/// <summary>
/// Настройка параметров страницы
/// </summary>
private void MenuFilePageSetup()
{
  pageSetupDialog1.ShowDialog();
}

Далее вставьте вызов этого метода в тело обработчика событий для строки PageSetup меню File:

private void menuFilePageSetup_Click(object sender,
  System.EventArgs e)
{
  MenuFilePageSetup();   
}

После трансляции и запуска приложения строка Page Setup меню File начнет выполнять свои функции.

Предварительный просмотр документа перед печатью

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

Прежде всего, добавьте в класс SimpleNotepadForm поля m_myReader и m_PrintPageNumber, как это показано ниже:

/// <summary>
/// StringReader для печати содержимого редактора текста
/// </summary>
private StringReader m_myReader;

/// <summary>
/// Номер текущей распечатываемой страницы документа
/// </summary>
private uint m_PrintPageNumber;

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

Добавьте в класс SimpleNotepadForm метод MenuFilePrintPreview, предназначенный для вывода на экран окна предварительного просмотра документа, показанного на рис. 4-20:

/// <summary>
/// Предварительный просмотр перед печатью документа
/// </summary>
private void MenuFilePrintPreview()
{
  m_PrintPageNumber = 1;

  string strText = this.richTextBox1.Text;
  m_myReader = new StringReader(strText);
  Margins margins = new Margins(100,50,50,50);

  printDocument1.DefaultPageSettings.Margins = margins;
  printPreviewDialog1.ShowDialog();

  m_myReader.Close() ;
}

Кроме того, добавьте вызов метода MenuFilePrintPreview в тело обработчика события строки Print Preview меню File:

private void menuFilePrintPreview_Click(object sender,
  System.EventArgs e)
{
  MenuFilePrintPreview();
}

Как работает метод MenuFilePrintPreview?

Прежде всего, этот метод записывает в поле m_PrintPageNumber значение 1, т.к. просмотр начнется с первой страницы документа:

m_PrintPageNumber = 1;

Далее метод читает текущее содержимое окна редактирования в поток m_myReader класса StringReader:

string strText = this.richTextBox1.Text;
m_myReader = new StringReader(strText);

Затем метод MenuFilePrintPreview отображает окно предварительного просмотра, задавая для него размеры полей отступов на странице:

Margins margins = new Margins(100,50,50,50);
printDocument1.DefaultPageSettings.Margins = margins;
printPreviewDialog1.ShowDialog();

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

Отобразив панель предварительного просмотра, метод MenuFilePrintPreview закрывает поток m_myReader:

m_myReader.Close();

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

Отображение окна печати документа

Для отображения стандартного диалогового окна печати документов (рис. 4-19) добавьте в класс SimpleNotepadForm метод MenuFilePrint:

/// <summary>
/// Печать документа
/// </summary>
private void MenuFilePrint()
{
  m_PrintPageNumber = 1;

  string strText = this.richTextBox1.Text;
  m_myReader = new StringReader(strText);

  Margins margins = new Margins(100,50,50,50);
  printDocument1.DefaultPageSettings.Margins = margins;

  if (printDialog1.ShowDialog() == DialogResult.OK)
  {
     this.printDocument1.Print();
  }
  m_myReader.Close() ;
}

Вызов этого метода должен осуществляться в обработчике событий строки Print меню File:

private void menuFilePrint_Click(object sender, System.EventArgs e)
{
  MenuFilePrint();
}

Расскажем о том, как работает метод MenuFilePrint.

Печать нашего документа будет начинаться с первой страницы, поэтому в поле m_PrintPageNumber мы записываем значение 1:

m_PrintPageNumber = 1;

Далее мы читаем текущее содержимое окна редактирования текста в поток m_myReader класса StringReader:

string strText = this.richTextBox1.Text;
m_myReader = new StringReader(strText);

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

Далее мы задаем границы отступов на распечатываемой странице и отображаем диалоговое окно печати документа. Если пользователь щелкает в этом окне кнопку OK, документ printDocument1 отправляется на печать методом Print:

Margins margins = new Margins(100,50,50,50);
printDocument1.DefaultPageSettings.Margins = margins;

if (printDialog1.ShowDialog() == DialogResult.OK)
{
  this.printDocument1.Print();
}

Далее ненужный более поток m_myReader закрывается методом Close:

m_myReader.Close() ;

Заметим, что на данном этапе приложение еще не в состоянии распечатать документ (точно так же, как оно пока не в состоянии заполнить окно предварительного просмотра перед печатью). Причина этого в том, что приложение пока еще не знает, каким именно образом нужно печатать наш документ.

Обработка события PrintPage

Чтобы в нашем приложении заработали функции предварительного просмотра документа перед печатью и функция печати, необходимо создать обработчик события PrintPageEventHandler. Для этого нужно дважды щелкнуть левой клавишей мыши значок компонента printDocument1 (рис. 4-18). После этого дизайнер форм создаст пустое тело этого обработчика событий.

Далее Вам придется набрать вручную довольно объемистый исходный текст обработчика, представленный ниже:

/// <summary>
/// Обработка события PrintPage
/// </summary>
private void PrintPageEventHandler(object sender,
  System.Drawing.Printing.PrintPageEventArgs e)
{
  int       lineCount = 0;       // счетчик строк
  float     linesPerPage = 0;  // количество строк на одной странице
  float     yLinePosition = 0; // текущая позиция при печати по
                               // вертикальной оси
  string currentLine = null;  // текст текущей строки
      
  // Шрифт для печати текста
  Font printFont = this.richTextBox1.Font;  

  // Кисть для печати текста
  SolidBrush printBrush = new SolidBrush(Color.Black);

  // Размер отступа слева
  float leftMargin = e.MarginBounds.Left;

  // Размер отступа сверху
  float topMargin = e.MarginBounds.Top +
     3*printFont.GetHeight(e.Graphics);
      
  // Вычисляем количество строк на одной странице с учетом отступа
  linesPerPage = (e.MarginBounds.Height -
     6*printFont.GetHeight(e.Graphics)) /
     printFont.GetHeight(e.Graphics);

  // Цикл печати всех строк страницы
  while(lineCount < linesPerPage &&
     ((currentLine=m_myReader.ReadLine()) != null))
  {
     // Вычисляем позицию очередной распечатываемой строки
     yLinePosition = topMargin + (lineCount *
       printFont.GetHeight(e.Graphics));

     // Печатаем очередную строку
     e.Graphics.DrawString(currentLine, printFont, printBrush,
       leftMargin, yLinePosition, new StringFormat());

     // Переходим к следующей строке
     lineCount++;
  }

  // Печать колонтитулов страницы

  // Номер текущей страницы
  string sPageNumber = "Page " + m_PrintPageNumber.ToString();

  // Вычисляем размеры прямоугольной области, занимаемой верхним
  // колонтитулом страницы
  SizeF stringSize = new SizeF();
  stringSize = e.Graphics.MeasureString(sPageNumber, printFont,
     e.MarginBounds.Right - e.MarginBounds.Left);
      
  // Печатаем номер страницы
  e.Graphics.DrawString(sPageNumber, printFont, printBrush,
     e.MarginBounds.Right - stringSize.Width, e.MarginBounds.Top,
     new StringFormat());

  // Печатаем имя файла документа
  e.Graphics.DrawString(this.Text, printFont, printBrush,
     e.MarginBounds.Left, e.MarginBounds.Top, new StringFormat());

  // Кисть для рисования горизонтальной линии,
  // отделяющей верхний колонтитул
  Pen colontitulPen = new Pen(Color.Black);
  colontitulPen.Width = 2;

  // Рисуем верхнюю линию
  e.Graphics.DrawLine(colontitulPen,
     leftMargin,
     e.MarginBounds.Top + printFont.GetHeight(e.Graphics) + 3,
     e.MarginBounds.Right, e.MarginBounds.Top +
     printFont.GetHeight(e.Graphics) + 3);

  // Рисуем линию, отделяющую нижний колонтитул документа
  e.Graphics.DrawLine(colontitulPen,
     leftMargin, e.MarginBounds.Bottom - 3,
     e.MarginBounds.Right, e.MarginBounds.Bottom - 3);

  // Печатаем текст нижнего колонтитула
  e.Graphics.DrawString(
  "SimpleNotepad, (c) Александр Фролов, http://www.frolov.pp.ru",
     printFont, printBrush,
     e.MarginBounds.Left, e.MarginBounds.Bottom, new StringFormat());

  // Если напечатаны не все строки документа,
  // переходим к следующей странице
  if(currentLine != null)
  {
     e.HasMorePages = true;
     m_PrintPageNumber++;
  }

  // Иначе завершаем печать страницы
  else
     e.HasMorePages = false;

  // Освобождаем ненужные более ресурсы
  printBrush.Dispose();
  colontitulPen.Dispose();
}

Если при наборе Вы не допустили никаких ошибок, этот обработчик событий обеспечит правильное функционирование приложения в режимах предварительного просмотра документа и его печати. На рис. 4-22 мы показали внешний вид распечатанной страницы документа.

Рис. 4-22. Внешний вид распечатанной страницы документа

Комментарии в тексте обработчика событий PrintPageEventHandler поясняют назначение отдельных программных строк. Заметим, что для полного понимания действий, выполняемых нашим обработчиком событий, требуется предварительное знакомство с графической подсистемой Graphics Device Interface Plus (GDI+), реализованной компанией Microsoft в рамках библиотеки классов .NET Framework. Мы посвятим этой теме 10 главу нашей книги.

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

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

Хотя наше приложение допускает форматирование редактируемых документов и даже вставку в них через универсальный буфер обмена Clipboard графических изображений, на печать будет выводиться только текст. Для печати документа с учетом форматирования и наличия графических изображений обработчик сообщений PrintPageEventHandler нужно сильно усложнить.

Закрытие главного окна редактора текста

Окно нашего редактора текста должно быть закрыто, когда пользователь выбирает из меню File строку Exit. Это легко достигается добавлением метода Close в тело обработчика сообщения menuFileExit_Click:

private void menuFileExit_Click(object sender, System.EventArgs e)
{
  this.Close(); // изменения в документе будут потеряны!
}

Однако здесь возникает проблема: окно редактора текста будет закрыто и в том случае, если пользователь не сохранил сделанные им изменения.

Чтобы решить эту проблему, нам нужно каким-то образом отслеживать наличие изменений в окне редактирования текста. К счастью, это сделать достаточно просто.

Определим в классе SimpleNotepadForm поле m_DocumentChanged, в котором будем хранить флаг, отмечающий изменения, сделанные пользователем в документе:

private bool m_DocumentChanged = false;

В новом или только что загруженном документе изменений нет, поэтому начальное значение этого флага равно false.

Далее откройте окно визуального проектирования формы и щелкните дважды левой клавишей мыши редактор текста richTextBox1. В результате дизайнер форм создаст для нас обработчик события richTextBox1_TextChanged. Этот обработчик получит управление, как только пользователь внесет любые изменения в содержимое редактируемого документа.

Вам остается только обеспечить изменение состояния флага m_DocumentChanged внутри этого обработчика:

private void richTextBox1_TextChanged(object sender,
  System.EventArgs e)
{
  m_DocumentChanged = true;  
}

Если пользователь редактировал документ, а потом решил создать новый, выбрав из меню File строку New, изменения, внесенные в старый документ, могут быть потеряны. Чтобы избежать этого, проверьте флаг m_DocumentChanged перед тем как очищать содержимое редактора текста. Если в редакторе есть не сохраненные изменения, необходимо вызвать метод MenuFileSaveAs, предназначенный для сохранения документа:

private void menuFileNew_Click(object sender, System.EventArgs e)
{
  if(m_DocumentChanged)
     MenuFileSaveAs();
  richTextBox1.Clear();
}

После того как пользователь сохранил изменения в документе, флаг m_DocumentChanged необходимо сбросить. Для этого отредактируйте исходный текст метода MenuFileSaveAs, добавив в него строку сброса указанного флага:

private void MenuFileSaveAs()
{
  if(saveFileDialog1.ShowDialog() ==
     System.Windows.Forms.DialogResult.OK &&
     saveFileDialog1.FileName.Length > 0)
  {
     richTextBox1.SaveFile(saveFileDialog1.FileName);
     m_DocumentChanged = false;
  }
}

Теперь подготовьте обработчик события menuFileExit_Click следующим образом:

private void menuFileExit_Click(object sender, System.EventArgs e)
{
  if(m_DocumentChanged)
     MenuFileSaveAs();
  this.Close();
}

И, наконец, надо выполнить еще одну проверку флага m_DocumentChanged — в методе Dispose, который вызывается при закрытии окна приложения:

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
  if(m_DocumentChanged)
     MenuFileSaveAs();

  if( disposing )
  {
     if (components != null)
     {
       components.Dispose();
     }
  }
  base.Dispose( disposing );
}

Теперь, когда пользователь попытается закрыть программу редактора с помощью строки Exit меню File или с помощью соответствующей кнопки строки заголовка окна, не сохранив сделанные изменения, на экране появится стандартное диалоговое окно, предлагающее ему сохранить документ.

Реализация функций меню Edit

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

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

Добавьте в меню Edit нашего редактора текста строку Redo. Затем подготовьте обработчики событий от всех строк меню Edit следующим образом:

private void menuEditUndo_Click(object sender, System.EventArgs e)
{
  richTextBox1.Undo();
}

private void menuItem1_Click(object sender, System.EventArgs e)
{
  richTextBox1.Redo();
}

private void menuEditCut_Click(object sender, System.EventArgs e)
{
  richTextBox1.Cut();
}

private void menuEditCopy_Click(object sender, System.EventArgs e)
{
  richTextBox1.Copy();
}

private void menuEditPaste_Click(object sender, System.EventArgs e)
{
  richTextBox1.Paste();
}

private void menuEditDelete_Click(object sender, System.EventArgs e)
{
  richTextBox1.Cut();
}

private void menuEditSelectAll_Click(object sender,
  System.EventArgs e)
{
  richTextBox1.SelectAll();
}

Теперь наше приложение Simple Notepad сможет обмениваться текстовыми и графическими данными с другими приложениями Microsoft Windows через универсальный буфер обмена Clipboard.

Рис. 4-23. Мы вставили в документ текст и графическое изображение

Реализация функций меню Format

Прежде чем продолжить работу над нашим приложением, модифицируйте меню Format, добавив в него строку Color, а также два меню второго уровня — Character Style (рис. 4-24) и Paragraph Alignment (рис. 4-25).

Рис. 4-24. Меню второго уровня Characters Style

Рис. 4-25. Меню второго уровня Paragraph alignment

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

Не забудьте также изменить имена строк меню таким образом, чтобы с ними было легче работать в программе. Мы, например, назвали строку меню Left меню второго уровня Paragraph Alignment как menuFormatParagraphAlignmentLeft. Остальные строки меню второго уровня получили аналогичные названия.

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

Шрифт символов

Чтобы пользователь мог выбирать шрифт фрагмента текста, выделенного в окне редактирования программы SimpleNotepad, перетащите мышью из панели Toolbox в окно дизайнера формы компонент FontDialog. Этот компонент отображает на экране стандартное диалоговое окно выбора шрифта Font, показанное на рис. 4-26.

Рис. 4-26. Стандартное диалоговое окно выбора шрифта Font

Чтобы отобразить это диалоговое окно, добавьте вызов метода fontDialog1.ShowDialog в тело обработчика события menuFormatFont_Click, получающего управление при выборе строки Font из меню Format:

private void menuFormatFont_Click(object sender, System.EventArgs e)
{
  if (fontDialog1.ShowDialog() == DialogResult.OK)
  {
     richTextBox1.SelectionFont = fontDialog1.Font;
  }
}

После того как пользователь выбрал нужный ему шрифт, обработчик события переписывает этот шрифт из свойства fontDialog1.Font в свойство richTextBox1.SelectionFont. Свойство SelectionFont позволяет изменить шрифт фрагмента текста, выделенного пользователем (или программой) в окне редактирования.

Цвет символов

Чтобы пользователь мог изменять цвет выделенного фрагмента текста, добавьте в проект компонент ColorDialog, перетащив его значок мышью из панели Toolbox в окно дизайнера форм.

Этот компонент отображает на экране стандартное диалоговое окно выбора цвета (рис. 4‑27).

Рис. 4-27. Стандартное диалоговое окно выбора цвета Color

Далее модифицируйте обработчик событий для строки Color меню Format следующим образом:

private void menuFormatColor_Click(object sender, System.EventArgs e)
{
  if (colorDialog1.ShowDialog() == DialogResult.OK)
  {
     richTextBox1.SelectionColor = colorDialog1.Color;
  }
}

Здесь программа вначале отображает диалоговое окно выбора цвета, вызывая для этого метод colorDialog1.ShowDialog. Затем она переписывает выбранный цвет из свойства colorDialog1.Color диалогового окна в свойство richTextBox1.SelectionColor окна редактирования текста. В результате цвет фрагмента текста, выделенного пользователем перед выполнением данной операции, будет изменен.

Стиль символов

Редактируя документы, пользователи часто применяют выделение слов и фрагментов текста жирным шрифтом, наклоном, подчеркиванием или перечеркиванием. Для подобного форматирования в нашей программе предусмотрены строки меню второго уровня Character Style с названиями Bold, Italic, Underline и Strikeout, соответственно.

Рис. 4-28. Изменение стиля символов

Реализация соответствующей функциональности возложена на методы SetBold, SetItalic, SetUnderline и SetStrikeout, которые мы добавили в обработчики сообщений строк меню Character Style:

private void menuFormatFontCharacterStyleBold_Click(object sender,
  System.EventArgs e)
{
  SetBold();
}

private void menuFormatFontCharacterStyleItalic_Click(object sender,
  System.EventArgs e)
{
  SetItalic();
}

private void menuFormatFontCharacterStyleUnderline_Click(
  object sender, System.EventArgs e)
{
  SetUnderline();
}

private void menuFormatFontCharacterStyleStrikeout_Click(
  object sender, System.EventArgs e)
{
  SetStrikeout();
}

Все эти методы имеют одинаковую внутреннюю структуру, поэтому подробно мы расскажем только об одном из них — о методе SetBold.

Вот исходный текст данного метода:

/// <summary>
/// Установка стиля символов Bold
/// </summary>
private void SetBold()
{
  if (richTextBox1.SelectionFont != null)
  {
     System.Drawing.Font currentFont = richTextBox1.SelectionFont;
     System.Drawing.FontStyle newFontStyle;

     if (richTextBox1.SelectionFont.Bold == true)
     {
       newFontStyle = FontStyle.Regular;
     }
     else
     {
       newFontStyle = FontStyle.Bold;
     }

     richTextBox1.SelectionFont = new Font(
       currentFont.FontFamily, currentFont.Size, newFontStyle);

     CheckMenuFontCharacterStyle();
  }
}

Получив управление, метод SetBold прежде всего, определяет шрифт фрагмента текста, выделенного пользователем, анализируя свойство richTextBox1.SelectionFont. Если шрифт определить не удалось, и это свойство содержит значение null, наша программа не делает никаких изменений.

В противном случае программа сохраняет текущий шрифт в переменной currentFont класса System.Drawing.Font.

Далее метод SetBold проверяет, был ли выделен фрагмент текста жирным шрифтом, анализируя свойство richTextBox1.SelectionFont.Bold. Если это свойство содержит значение true, то метод SetBold снимает выделение, если нет, то устанавливает его.

Для снятия выделения программа записывает в переменную newFontStyle значение FontStyle.Regular, а для установки — значение FontStyle.Bold.

В дальнейшем содержимое переменной newFontStyle будет использована методом SetBold для изменения оформления выделенного фрагмента текста. Это изменение выполняется следующим образом:

richTextBox1.SelectionFont = new Font(
  currentFont.FontFamily, currentFont.Size, newFontStyle);

Здесь программа вначале создает новый шрифт как объект класса Font, передавая конструктору через параметры семейство currentFont.FontFamily текущего шрифта (установленного до выполнения операции), размер текущего шрифта currentFont.Size, а также новый стиль шрифта из переменной newFontStyle.

Далее этот шрифт записывается в свойство richTextBox1.SelectionFont, что и приводит к изменению стиля символов выделенного фрагмента текста.

Обратите также внимание, что при выделении фрагмента текста жирным шрифтом мы одновременно отмечаем «галочкой» строку меню Bold. С этой целью мы вызываем созданный нами метод CheckMenuFontCharacterStyle:

/// <summary>
/// Установка отметки строк меню Font->CharacterStyle
/// </summary>
private void CheckMenuFontCharacterStyle()
{
  if(richTextBox1.SelectionFont.Bold == true)
  {
     menuFormatFontCharacterStyleBold.Checked = true;
  }
  else
  {
     menuFormatFontCharacterStyleBold.Checked = false;
  }

  if(richTextBox1.SelectionFont.Italic == true)
  {
     menuFormatFontCharacterStyleItalic.Checked = true;
  }
  else
  {
     menuFormatFontCharacterStyleItalic.Checked = false;
  }

  if(richTextBox1.SelectionFont.Underline == true)
  {
     menuFormatFontCharacterStyleUnderline.Checked = true;
  }
  else
  {
     menuFormatFontCharacterStyleUnderline.Checked = false;
  }

  if(richTextBox1.SelectionFont.Strikeout == true)
  {
       menuFormatFontCharacterStyleStrikeout.Checked = true;
  }
  else
  {
     menuFormatFontCharacterStyleStrikeout.Checked = false;
  }
}

Метод CheckMenuFontCharacterStyle по очереди проверяет стилевое оформление выделенных фрагментов, отмечая «галочками» соответствующие строки меню CharacterStyle или снимая со строк этого меню отметки. Вот как, например, происходит обработка отметки строки Bold меню CharacterStyle:

if(richTextBox1.SelectionFont.Bold == true)
{
  menuFormatFontCharacterStyleBold.Checked = true;
}
else
{
  menuFormatFontCharacterStyleBold.Checked = false;
}

Если фрагмент текста, выделенный пользователем, оформлен жирным шрифтом, свойство  richTextBox1.SelectionFont.Bold содержит значение true. В этом случае наша программа отмечает «галочкой» строку Bold меню CharacterStyle, записывая значение true в свойство Checked данной строки:

menuFormatFontCharacterStyleBold.Checked = true;

Когда оформление жирным шрифтом снимается, программа убирает галочку, записывая в свойство Checked значение false:

menuFormatFontCharacterStyleBold.Checked = false;

Исходные тексты методов SetItalic, SetUnderline и SetStrikeout приведены ниже:

/// <summary>
/// Установка стиля символов Italic
/// </summary>
private void SetItalic()
{
  if (richTextBox1.SelectionFont != null)
  {
     System.Drawing.Font currentFont = richTextBox1.SelectionFont;
     System.Drawing.FontStyle newFontStyle;
     CheckMenuFontCharacterStyle();

     if (richTextBox1.SelectionFont.Italic == true)
     {
       newFontStyle = FontStyle.Regular;
     }
     else
     {
       newFontStyle = FontStyle.Italic;
     }

     richTextBox1.SelectionFont = new Font(
       currentFont.FontFamily, currentFont.Size, newFontStyle);

     CheckMenuFontCharacterStyle();
  }
}

/// <summary>
/// Установка стиля символов Underline
/// </summary>
private void SetUnderline()
{
  if (richTextBox1.SelectionFont != null)
  {
     System.Drawing.Font currentFont = richTextBox1.SelectionFont;
     System.Drawing.FontStyle newFontStyle;
     CheckMenuFontCharacterStyle();

     if (richTextBox1.SelectionFont.Underline == true)
     {
       newFontStyle = FontStyle.Regular;
     }
     else
     {
       newFontStyle = FontStyle.Underline;
     }

     richTextBox1.SelectionFont = new Font(
       currentFont.FontFamily, currentFont.Size, newFontStyle);

     CheckMenuFontCharacterStyle();
  }
}

/// <summary>
/// Установка стиля символов Strikeout
/// </summary>
private void SetStrikeout()
{
  if (richTextBox1.SelectionFont != null)
  {
     System.Drawing.Font currentFont = richTextBox1.SelectionFont;
     System.Drawing.FontStyle newFontStyle;

     if (richTextBox1.SelectionFont.Strikeout == true)
     {
       newFontStyle = FontStyle.Regular;
     }
     else
     {
       newFontStyle = FontStyle.Strikeout;
     }

     richTextBox1.SelectionFont = new Font(
       currentFont.FontFamily, currentFont.Size, newFontStyle);

     CheckMenuFontCharacterStyle();
  }
}

Эти методы мы оставляем Вам для самостоятельного изучения.

Выравнивание параграфов

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

После небольшой доработки наш редактор текста тоже сможет выравнивать параграфы текста, а также графические изображения, вставленные в документ через универсальный буфер обмена Clipboard (рис. 4-29).

Рис. 4-29. Выравнивание параграфов текста

Для изменения выравнивания текста мы создали в меню Fomat меню второго уровня Paragraph Alignment (рис. 4-25). Его строки Left, Right и Center обеспечивают выравнивание, соответственно, по левой и правой границе документа, а также центровку.

Подготовьте обработчики событий для строк меню Paragraph Alignment следующим образом:

private void menuFormatFontParagraphAlignmentLeft_Click(
  object sender, System.EventArgs e)
{
  richTextBox1.SelectionAlignment = HorizontalAlignment.Left;
}

private void menuFormatFontParagraphAlignmentRight_Click(
  object sender, System.EventArgs e)
{
  richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
}

private void menuFormatFontParagraphAlignmentCenter_Click(
  object sender, System.EventArgs e)
{
  richTextBox1.SelectionAlignment = HorizontalAlignment.Center;
}

Все эти обработчики событий строк меню Paragraph Alignment изменяют значение свойства richTextBox1.SelectionAlignment, задающего выравнивание параграфа текста, выделенного пользователем (для выделения параграфа с целью изменения выравнивания достаточно установить в него текстовый курсор).

Чтобы текущий (выделенный) параграф выровнять по левой границе документа, в это свойство нужно записать значение HorizontalAlignment.Left. Для выравнивания по правой границе воспользуйтесь значением HorizontalAlignment.Right. И, наконец, для центровки параграфа потребуется значение HorizontalAlignment.Center.

Реализация функций меню Help

В этом разделе мы займемся наделением функциональностью строки About меню Help. Обычно при выборе этой строки в приложениях Microsoft Windows на экране появляется диалоговое окно, содержащее сведения о приложении, такие как название, версия, имена и электронные адреса авторов приложения и пр.

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

Добавление новой формы

Для того чтобы добавить в проект новую форму, щелкните правой клавишей мыши строку названия проекта SimpleNotepad в окне Solution Explorer. Затем выберите из меню Add  строку Add Windows Form (рис. 4-30).

Рис. 4-30. Добавление в проект новой формы

На экране появится окно Add New Item, показанное на рис. 4-31. В правой части этого окна, содержащей значки шаблонов, будет выделен шаблон Windows Form.

Рис. 4-31. Ввод имени новой формы

Введите в поле Name строку HelpAboutForm.cs, а затем щелкните кнопку Open. В результате к проекту будет добавлена новая форма, а также новый класс HelpAboutForm.

Далее отредактируйте эту форму, добавив в нее описание программы, а также кнопку OK, предназначенную для закрытия окна формы. На рис. 4-32 мы показали вид формы About SimpleNotepad, подготовленной для нашего приложения.

Рис. 4-32. Диалоговая форма About SimpleNotepad в окне дизайнера форм

Чтобы форма вела себя как стандартное диалоговое окно ОС Microsoft Windows, необходимо настроить некоторые ее свойства. Проведите настройку в соответствии с табл. 4-1.

Таблица 4-1. Настройка свойств формы About SimpleNotepad

Свойство

Значение

FormBorderStyle

FixedDialog

StartPosition

CenterParent

ControlBox

false

MinimizeBox

false

MaximizeBox

false

ShowInTaskbar

false

Text

About SimpleNotepad

Сделаем некоторые замечания к этой таблице.

Свойство FormBorderStyle определяет вид рамки, расположенной вокруг окна формы. Если задать здесь значение FixedDialog, окно формы будет снабжено такой же рамкой, как и все стандартные диалоговые окна Microsoft Windows.

Свойство StartPosition позволяет указать расположение формы в момент ее отображения. Мы указали здесь значение CenterParent, в результате чего окно About SimpleNotepad появится в центре главного окна нашего приложения. Вы также можете выполнить центровку окна относительно экрана (значение Center Screen), позволить ОС Microsoft Windows самой расположить окно на экране (значение Windows Default Locations). Существуют и другие способы определения начального расположения окна формы. Вы можете выбрать соответствующие значения из меню при редактировании свойства StartPosition.

Для того чтобы убрать кнопки и управляющее меню из заголовка окна диалоговой формы, мы установили значения свойств MinimizeBox, MaximizeBox и ControlBox равными false.

Кроме того, мы «выключили» свойство ShowInTaskbar, записав в него значение false, для того чтобы окно диалоговой формы не было представлено своим значком в панели задач ОС Microsoft Windows.

И, наконец, свойство Text задает текст заголовка нашей диалоговой формы.

Отображение формы

Чтобы окно формы About SimpleNotepad появилось на экране при выборе пользователем строки About меню Help, мы добавили две строки в исходный текст обработчика событий menuHelpAbout_Click:

private void menuHelpAbout_Click(object sender, System.EventArgs e)
{
  Form dlgAbout = new HelpAboutForm();
  dlgAbout.ShowDialog();
}

Первая строка создает новую форму dlgAbout как объект класса HelpAboutForm, а вторая отображает форму на экране, вызывая для этого метод dlgAbout.ShowDialog. Мы показали форму на рис. 4-33 (на этом рисунке главное окно приложения снабжено инструментальной панелью, которую мы скоро создадим).

Рис. 4-33. Отображение диалоговой формы About SimpleNotepad

Редактирование класса HelpAboutForm

Итак, теперь наша форма About SimpleNotepad появляется на экране, однако закрыть ее можно только при помощи комбинации клавиш Alt-F4, используемой в ОС Microsoft Windows для закрытия окон.

Чтобы заставить работать элементы управления, расположенные в форме About SimpleNotepad, необходимо создать для них обработчики событий. Эта операция выполняется таким же образом, что и для главной формы нашего приложения.

Создайте обработчики событий, щелкнув дважды все элементы управления формы в окне дизайнера форм.

Для кнопки OK, закрывающей форму, добавьте в текст обработчика события button1_Click вызов метода this.Close:

private void button1_Click(object sender, System.EventArgs e)
{
  this.Close();
}

Напомним, что этот метод закрывает форму и убирает ее с экрана.

Что же касается элементов управления LinkLabel, то здесь мы использовали тот же самый программный код, что и в нашем самом первом приложении Hello, описанном во 2 главе книги:

private void linkLabel1_LinkClicked(object sender,
  System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
{
  linkLabel1.Links[linkLabel1.Links.IndexOf(e.Link)].Visited = true;
  System.Diagnostics.Process.Start(linkLabel1.Text);
}

private void linkLabel2_LinkClicked(object sender,
  System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
{
  linkLabel2.Links[linkLabel2.Links.IndexOf(e.Link)].Visited = true;
  System.Diagnostics.Process.Start(" mailto:alexandre@frolov.pp.ru");
}

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

Во многих приложениях, созданных для ОС Microsoft Windows, наиболее часто использующиеся строки меню дублируются кнопками, расположенными на инструментальных панелях. Такие панели есть, например, в главном окне самой системы разработки приложений Microsoft Visual Studio .NET.

В этом разделе мы расскажем, как снабдить окно приложения SimpleNotepad такой инструментальной панелью, и как создать для этой панели обработчик событий.

Добавление инструментальной панели в окно приложения

Чтобы добавить инструментальную панель в окно приложения, перетащите мышью ее значок ToolBar из инструментальной панели Microsoft Visual Studio .NET в окно проектирования формы нашего приложения.

По умолчанию окно инструментальной панели появится в верхней части формы. В только что добавленной панели нет ни одной кнопки. На рис. 4-34 мы показали инструментальную панель такой, какой она будет уже после добавления кнопок.

Рис. 4-34. Инструментальная панель в окне приложения SimpleNotepad

Сразу после добавления окно инструментальной панели будет расположено над окном редактора текста. Чтобы исправить это положение, щелкните правой кнопкой мыши окно редактора текста, а затем выберите из контекстного меню строку Bring to Front. В результате окна примут правильное взаимное расположение.

Заметим, что строка Send to Back только что упомянутого меню позволяет выполнить обратную операцию, а именно, переместить указанный элемент на задний план.

Подключение списка изображений

В панели инструментов Toolbox системы Microsoft Visual Studio .NET имеется компонент ImageList, специально предназначенный для хранения списков изображений. Этот компонент можно использовать совместно с инструментальной панелью ToolBar.

Добавьте компонент ImageList в наш проект, перетащив его значок в нижнюю часть окна проектирования формы. Он получит идентификатор imageList1 (рис. 4-35).

Рис. 4-35. Программные компоненты приложения SimpleNotepad

Теперь нужно подключить пустой пока список изображений imageList1 к инструментальной панели. Для этого в окне редактирования свойств Properties присвойте свойству ImageList инструментальной панели значение imageList1.

Наполнение списка изображений

Прежде чем приступать к наполнению списка изображениями, Вам нужно подготовить эти изображения при помощи любого графического редактора. Что касается инструментальной панели приложения SimpleNotepad, то здесь подойдет набор изображений, входящий в комплект Microsoft Visual Studio .NET. Вы найдете их в каталоге Program Files\Microsoft Visual Studio .NET\Common7\Graphics\bitmaps\OffCtlBr.

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

После этого Вам необходимо отредактировать свойство Images компонента imageList1. При этом запустится редактор набора изображений, окно которого показано на рис. 4-36.

Рис. 4-36. Окно редактора списка изображений

Для добавления изображения в список щелкните кнопку Add, а затем выберите файл нужного изображения при помощи стандартного диалогового окна Open (рис. 4-37).

Рис. 4-37. Выбор файла изображения

Добавив все необходимые изображения, расположите их в правильном порядке, пользуясь кнопками с изображением стрелок. При необходимости можно удалять отдельные изображения из списка при помощи кнопки Remove.

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

Следующий этап — создание и редактирование кнопок инструментальной панели. Для выполнения этой работы необходимо запустить специальный редактор кнопок (рис. 4-38). Чтобы сделать это, измените свойство панели инструментов Buttons.

Рис. 4-38. Редактор кнопок инструментальной панели

Вначале с помощью кнопки Add добавьте в панель 10 кнопок. Восемь из них будут играть роль настоящих кнопок, а две будут использованы для разделения трех групп кнопок.

После добавление кнопки выделите кнопку с идентификатором toolBarButton1 (как это показано на рис. 4-38), и в окне toolBarButton1 Properties отредактируйте свойство ImageIndex. Редактирование будет заключаться в выборе одного из изображений, сохраненных Вами ранее в списке imageList1  (рис. 4-39).

Рис. 4-39. Редактирование свойства ImageIndex

Выполните эту операцию для кнопок с номерами 0-2, 4-6, 8 и 9.

Что же касается кнопок 3 и 7, то эти кнопки будут играть роль разделителей между группами кнопок, ответственных за выполнение различных групп операций. Для этих кнопок установите значение свойства Style, равным Separator.

После сохранения результатов и запуска приложения  внешний вид инструментальной панели будет соответствовать рис. 4-34.

Редактируя свойство кнопок с именем Text, Вы можете разместить на кнопках инструментальной панели подписи с кратким описанием их назначения. Свойство ToolTipText позволит Вам снабдить каждую кнопку окном с поясняющим сообщением, которое появляется при наведении на кнопку курсора мыши (рис. 4-40).

Рис. 4-40. Усовершенствованная панель инструментов

По умолчанию подписи к кнопкам располагаются под графическими изображениями. Но есть и другой вариант — подписи могут находиться справа, как это показано на рис. 4-41.

Рис. 4-41. Изменение расположения подписей

Чтобы задать такое расположение, установите свойство TextAlign панели инструментов, равным значению Right. По умолчанию это свойство имеет значение UnderNeath, задающее расположение подписей в соответствии с рис. 4-40.

Установив свойство Divider равным значению false, можно избавиться от горизонтальной линии, отделяющей главное меню приложения от инструментальной панели.

Еще одно интересное свойство инструментальной панели — свойство Wrappable. По умолчанию оно имеет значение true, из-за чего при уменьшении размера окна по горизонтали может произойти свертка инструментальной панели (рис. 4-42).

Рис. 4-42. Свертка инструментальной панели

Чтобы этого не происходило, установите значение свойства Wrappable равным false.

И, наконец, установив значение свойства Appearance, равным Flat, можно создать панель инструментов с плоскими кнопками (рис. 4-43).

Рис. 4-43. Панель с плоскими кнопками

Строка состояния

Последнее добавление, которое мы сделаем к программе SimpleNotepad, — это добавление строки состояния. Строка состояния представляет собой узкое окно, располагающееся внизу окна приложения и предназначенная для отображения текущего состояния программы.

Добавление строки состояния

Чтобы добавить строку состояния, перетащите мышью из панели Toolbox в форму приложения значок элемента управления StatusBar.

Окно строки состояния окажется над окном редактора текста. Чтобы расположить окна правильно, щелкните правой кнопкой мыши окно редактора текста, а затем выберите из контекстного меню строку Bring to Front.

В результате этих действий главное окно нашего приложения примет вид, показанный на рис. 4-44.

Рис. 4-44. В окно приложения добавлена строка состояния

Настройка свойств строки состояния

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

Чтобы отобразить текстовую строку в простом режиме, достаточно присвоить эту строку свойству stastusBar1.Text, например:

stastusBar1.Text = "Приложение SimpleNotepad";

Мы же настроим строку состояния для работы в более интересном режиме с использованием панелей.

Прежде всего, мы приравняем свойство Text нашей панели пустой строке.

Для отображения панелей приравняйте свойству ShowPanels значение true.

Далее мы отредактируем свойство Panels, чтобы поделить строку состояния на несколько панелей. Редактирование этого свойства выполняется при помощи редактора набора панелей, показанного на рис. 4-45.

Рис. 4-45. Редактор набора панелей

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

Первая панель (с идентификатором statusBarPanel1) должна изменять свой размер в зависимости от ширины окна. Поэтому для нее установите значение свойства AutoSize равным Spring.

Кроме того, присвойте свойству Text панелей statusBarPanel1 и statusBarPanel2 пустую строку. Это нужно сделать потому, что содержимое панелей будет определяться программой.

Привязка строки состояния к меню

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

Прежде всего, создадим обработчик событий. Для этого выделите мышью меню File в окне дизайнера формы, а затем щелкните кнопку Events (с изображением высоковольтного разряда) в окне Properties (рис. 4-46).

Рис. 4-46. Создаем обработчик события MunuSelect

Здесь находится список событий и обработчиков для них. Назначьте для события Select обработчик MenuSelect, щелкнув соответствующую строку списка дважды левой клавишей мыши или введя его имя с клавиатуры, как это показано на рис. 4-45. После того как Вы нажмете клавишу Enter, будет создано пустое тело обработчика события MenuSelect:

private void MenuSelect(object sender, System.EventArgs e)
{

}

На данном этапе наш обработчик будет вызываться только тогда, когда пользователь раскроет меню File. Теперь нам нужно подключить этот обработчик ко всем строкам главного меню приложения с целью обработки события Select.

Для этого Вам нужно по очереди выделить все строки меню нашего приложения, а затем для каждой строки выбрать в только что упомянутом окне Properties из списка события Select  обработчик события MenuSelect (рис. 4-47).

Рис. 4-47. Выбираем из меню обработчик события MunuSelect

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

Далее измените исходный текст метода MenuSelect следующим образом:

private void MenuSelect(object sender, System.EventArgs e)
{
  MenuItem mi = (MenuItem)sender;
  string ms;
  switch(mi.Text)
  {
     case "&New": ms = "Новый документ"; break;
     case "&Open...": ms = "Открыть документ"; break;
     case "&Save": ms = "Сохранить документ"; break;
     case "&Save As...": ms = "Сохранить документ"; break;
     case "Page Set&up...": ms = "Параметры страницы"; break;
     case "Print Pre&view...": ms = "Предварительный просмотр";
       break;
     case "&Print...": ms = "Печатать документ"; break;
     case "Exit": ms = "Завершение работы"; break;
     case "&Undo": ms = "Отменить ввод"; break;
     case "&Redo": ms = "Повторить ввод"; break;
     case "Cu&t": ms = "Вырезать"; break;
     case "&Copy": ms = "Скопировать"; break;
     case "&Paste": ms = "Вставить"; break;
     case "&Delete": ms = "Удалить"; break;
     case "&Select All": ms = "Выделить все"; break;
     case "&Font...": ms = "Выбрать шрифт"; break;
     case "C&olor...": ms = "Выбрать цвет"; break;
     case "&Bold": ms = "Жирный шрифт"; break;
     case "&Italic": ms = "Наклонный шрифт"; break;
     case "&Underline": ms = "Подчеркивание"; break;
     case "&Strikeout": ms = "Перечеркивание"; break;
     case "&Left": ms = "Выравнивание по левой границе"; break;
     case "&Right": ms = "Выравнивание по правой границе"; break;
     case "&Center": ms = "Центровка"; break;
    case "&About...": ms = "О программе"; break;
     default: ms = ""; break;
  }
  statusBarPanel1.Text = ms;
}

Здесь мы вначале используем первый параметр метода с именем sender, через который передается ссылка на объект, вызвавший появление события. В нашем случае это будет строка меню, выбранная пользователем.

Программа преобразует эту ссылку к типу MenuItem, предназначенному для хранения такого рода ссылок, и записывает ее в переменную mi:

MenuItem mi = (MenuItem)sender;

Далее программа анализирует текст строки меню, хранящийся в свойстве mi.Text, с помощью оператора switch. В зависимости от того, какая строка меню вызвала событие, в переменную ms записывается тот или иной текст, поясняющий назначение данной строки меню.

Затем этот текст записывается в левую панель строки состояния statusBarPanel1:

statusBarPanel1.Text = ms;

Что же касается правой панели строки состояния statusBarPanel2, то в запись него выполняется обработчиком события richTextBox1_TextChanged. Измените его следующим образом:

private void richTextBox1_TextChanged(object sender,
  System.EventArgs e)
{
  m_DocumentChanged = true;  
  statusBarPanel2.Text = "Изменено";
}

Напомним, что этот обработчик получает управление, когда пользователь изменяет содержимое окна редактирования. При этом в правой панели строке состояния будет отображаться слово «Изменено» (рис. 4-48).

Рис. 4-48. Теперь строка состояния нашего приложения работает

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

private void menuFileNew_Click(object sender, System.EventArgs e)
{
  if(m_DocumentChanged)
     MenuFileSaveAs();

  richTextBox1.Clear();
  statusBarPanel2.Text = "";
  m_DocumentChanged = false;
}


private void MenuFileOpen()
{
  if(openFileDialog1.ShowDialog() ==
     System.Windows.Forms.DialogResult.OK &&
     openFileDialog1.FileName.Length > 0)
  {
     try
     {
       richTextBox1.LoadFile(openFileDialog1.FileName,
          RichTextBoxStreamType.RichText);
     }
     catch (System.ArgumentException ex)
     {
       richTextBox1.LoadFile(openFileDialog1.FileName,
          RichTextBoxStreamType.PlainText);
     }
     this.Text = "
Файл [" + openFileDialog1.FileName + "]";
     statusBarPanel2.Text = "";
     m_DocumentChanged = false;
  }
}

private void MenuFileSaveAs()
{
  if(saveFileDialog1.ShowDialog() ==
     System.Windows.Forms.DialogResult.OK &&
     saveFileDialog1.FileName.Length > 0)
  {
     richTextBox1.SaveFile(saveFileDialog1.FileName);
     this.Text = "
Файл [" + saveFileDialog1.FileName + "]";
     m_DocumentChanged = false;
     statusBarPanel2.Text = "";
  }
}

Во все эти методы необходимо добавить следующую строку, очищающую правую панель строки состояния:

statusBarPanel2.Text = "";

[Назад] [Содержание] [Дальше]