|
Программирование >> Вывод графики
память и облегчить задачу сборщику мусора, мы вызываем Dispose() после завершения работы с каждым созданным экземпляром шрифта. Если этого не делать, то после 10 или 20 вызовов операций перерисовки окажется израсходованным огромное количество памяти для сохранения ненужных более шрифтов. Редактирование текстового документа: пример CapsEditor Теперь мы подошли к наиболее расширенному примеру настоящей главы. Пример CapsEditor предназначен для демонстрации того, как принципы рисования, которые мы изучили до сих пор, можно применить в более реалистичном контексте. Пример CapsEditor не потребует изучения никакого нового материала, помимо реагирования на пользовательский ввод мышью, но покажет, как управлять отображением текста таким образом, чтобы обеспечить достаточную производительность приложения, в то же время гарантируя постоянное обновление содержимого клиентской области главного окна. Программа CapsEditor дает возможность пользователю читать текстовый файл, который затем отображается строка за строкой в текстовой области. Если пользователь выполнит двойной щелчок на строке, строка преобразуется к верхнему регистру. Это, по сути, все. Но даже несмотря на столь ограниченный набор возможностей, вы увидите, что работа, которую необходимо выполнить для того, чтобы обеспечить правильное отображение текста в правильном месте при гарантии нормальной производительности - достаточно сложная задача. В частности, здесь появится новый элемент: содержимое документа может изменяться - либо при выборе команды меню для чтения нового файла, либо при двойном щелчке на строке. В первом случае потребуется обновить размер документа, чтобы линейки прокрутки работали правильно, и затем перерисовать все заново. Во втором случае необходимо тщательно проверить, изменился ли размер документа, и какой текст должен быть повторно отображен. Этот раздел мы начнем с внешнего вида CapsEditor. Когда приложение впервые запускается, оно не имеет загруженного документа и выглядит, как показано на рис. 33.17. Меню File (Файл) включает две команды: Open (Открыть), которая вызывает диалоговое окно OpenFileDialog и читает выбранный в нем файл, а также Exit (Выход), закрывающая приложение. На рис. 33.18 показана программа CapsEditor, отображающая свой собственный исходный файл, Form1.cs (с несколькими строками, переведенными в верхний регистр в результате двойных щелчков на них). Рис. 33.17. Внешний вид приложения CapsEditor после первого запуска Рис. 33.18. Приложение CapsEditor отображает содержимое файла Form1.cs Размеры вертикальной и горизонтальной линеек прокрутки корректны. Клиентская область прокручивается ровно настолько, чтобы можно было увидеть весь документ. CapsEditor не пытается переносить строки текста - пример и без того достаточно сложен. Он просто отображает каждую строку текста в точности, как она была прочитана. Никаких ограничений на размер файла не накладывается, но предполагается, что это текстовый файл, который не содержит в себе никаких непечатаемых символов. Начнем с добавления оператора using: using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.IO; using System.Windows.Forms; Это потому, что мы будем использовать класс StreamReader, находящийся в пространстве имен System.IO. Добавим некоторые новые поля в класс Form1: #region Constant fields private const string standardTitle = CapsEditor ; текст заголовка по умолчанию private const uint margin = 10; горизонтальное и вертикальное поля отступов клиентской области #endregion #region Member fields документ private readonly List<TextLineInformation> documentLines = new List<TextLineInformation> (); private uint lineHeight; высота одной строки в пикселях private Size documentSize; необходимый размер клиентской области для отображения всего документа private uint nLines; количество строк в документе private Font mainFont; шрифт, используемтй для отображения строк документа private Font emptyDocumentFont; шрифт, используемхй для отображения пустого сообщения private readonly Brush mainBrush = Brushes.Blue; кисть, используемая для отображения текста документа private readonly Brush emptyDocumentBrush = Brushes.Red; кисть, используемая для отображения пустого сообщения private Point mouseDoubleClickPosition; положение указателя мыши при двойном щелчке private readonly OpenFileDialog fileOpenDialog = new OpenFileDialog(); стандартный диалог открытия файла private bool documentHasData = false; устанавливается true, если документ содержит данные #endregion Большинство этих полей не требуют пояснений. Поле documentLines - List< TextLineInformation>, содержащий текст прочитанного файла. В действительности это поле, которое хранит содержимое документа. Каждый элемент documentLines содержит одну строку текста. Поскольку это поле относится к типу List<TextLineInformation>, а не к простому массиву, в него можно динамически добавлять новые элементы в процессе чтения файла. Как уже упоминалось ранее, каждый элемент documentLines содержит информацию об одной строке текста. Сама эта информация представлена экземпляром другого класса- TextLineInformation: class TextLineInformation public string Text; public uint Width; TextLineInformation выглядит классическим случаем, более подходящим для использования структуры вместо класса, потому что это всего лишь пара полей. Однако обращение к его экземплярам всегда выполняется через элементы List< TextLineInformation>, что предполагает их сохранение в виде ссылочных типов. Каждый экземпляр TextLineInformation сохраняет строку текста, а потому представляет минимальный элемент, отображаемый как единое целое. В общем случае для каждого подобного элемента в GDI+ вы, вероятно, захотите хранить вместе с текстом его мировые координаты, указывающие место, где он должен быть отображен, а также его размер (страничные координата: часто изменяются, когда пользователь прокручивает документ, в то время как мировые остаются неизменными до тех пор, пока другие части документа не будут каким-то образом модифицированы). Но в данном случае помимо текста мы сохраняем только ширину (Width) элемента. Причина в том, что высота в данном случае определяется выбранным шрифтом. Она остается неизменной для всех строк текста, а потому хранить ее отдельно для каждой строки незачем. Мы сохраняем ее один раз - в поле Form1.lineHeight. Что же касается позиции, то в данном случае координата x равна величине поля отступа, а координата y легко вычисляется: margin + lineHeight*(количество строк, предшествующих данной) Если бы мы попытались отображать и манипулировать, скажем, отдельными словами вместо целых строк, то позицию x каждого слова пришлось бы вычислять, используя сумму ширин всех предыдущих слов данной строки. Однако в целях простоты здесь мы ограничимся трактовкой каждой строки текста как единого целого. Теперь обратимся к главному меню. Эта часть приложения в большей степени относится к миру Windows Forms (см. главу 31), чем к GDI+. Мы добавим команды меню, используя представление конструктора в Visual Studio 2008, но переименуем их в menuFile, menuFileOpen и menuFileExit. Далее добавим обработчики команд меню FileOpen (ФайлОткрыть) и FileExit (ФайлВыход), используя окно свойств Visual Studio 2008. Эти обработчики событий имеют имена, сгенерированные Visual Studio 2008 - menuFileOpen Click() и menuFileExit Click(). Добавим дополнительный код инициализации к конструктору Form1: public Form1() InitializeComponent(); CreateFonts(); fileOpenDialog.FileOk += delegate { LoadFile(fileOpenDialog.FileName); }; fileOpenDialog.Filter = Text files (*.txt)*.txtC# source files (*.cs)*.cs ; Здесь добавлен обработчик события щелчка для экземпляров, которое возникает, когда пользователь щелкает на кнопке OK диалогового окна File Open (Открытие файла). Мы также устанавливаем фильтр, чтобы можно было загружать только текстовые файлы, а именно - файлы с расширениями .txt и файлы исходного кода на C#, поэтому данное приложение можно использовать для просмотра исходного кода самого примера. CreateFonts() - вспомогательный метод, который разбирается с используемыми шрифтами: private void CreateFonts() mainFont = new Font( Arial , 10); lineHeight = (uint)mainFont.Height; emptyDocumentFont = new Font( Verdana , 13, FontStyle.Bold);
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |