|
|
|||||||||||||||||||||||||||||
|
Изучаем Visual Studio .NET. Часть 3. Компоненты Windows-приложенийИсточник: КомпьютерПресс, № 6'2002 Алексей Федоров, Наталия Елманова
ОглавлениеГенерация кода Windows-приложенийДля того чтобы изучить принципы создания Windows-приложений, давайте создадим новый проект. Пусть это будет Windows-приложение на языке Visual Basic .NET. После указания каталога, в котором расположено создаваемое решение, и имени проекта мы получим заготовку проекта, состоящего из пустой формы, не содержащей других интерфейсных элементов. Выделим в окне Solution Explorer элемент Form1.vb и из контекстного меню этого элемента выберем пункт View code. Мы увидим, что в редакторе кода содержатся сведения о том, что класс Form1 является наследником класса System.Windows.Forms.Form. Заглянув в блок "Windows Form Designer generated code", мы обнаружим там следующий инициализационный код:
Что означает код, приведенный выше? Описание класса формы Form1 начинается с конструктора New(), в котором вызываются конструктор базового класса и процедура InitializeComponent - в ней инициализируются компоненты, располагаемые на форме (на данный момент отсутствующие). Изначально процедура InitializeComponent выполняет одно-единственное действие - устанавливает значение свойства формы Text (надпись на ее заголовке). Далее переписывается метод Dispose класса формы, отвечающий за освобождение ресурсов, потребляемых формой и содержащихся на ней компонентов. Отметим, что в этом методе неуправляемые ресурсы должны быть обязательно освобождены, а управляемые ресурсы должны стать доступны сборщику мусора (подробнее об управляемых и неуправляемых ресурсах вы можете прочитать в цикле статей, посвященных Microsoft .NET Framework). Теперь добавим к форме кнопку (компонент Button) и выясним, что изменилось при этом в сгенерированном Visual Studio .NET коде. Мы обнаружим, что среди полей класса Form1 теперь имеется поле Button1 - наследник класса System. Windows.Forms.Button, а в процедуре InitializeComponent теперь появился код создания экземпляра этого класса и присвоения значений его свойствам Location, Size, Name, Text, TabIndex, а также появился код, отвечающий за размещение и отображение кнопки на форме:
Иными словами, когда мы имеем дело с контейнерами визуальных компонентов (каковым является, например, форма), последние становятся полями классов первых, и это находит отражение в сгенерированном коде. Если мы будем изменять свойства компонентов с помощью окна Properties, соответствующим образом изменится и сгенерированный код. Теперь попробуем обработать событие Click кнопки, щелкнув по ней дважды. При этом будет сгенерирована заготовка функции - обработчика события, в которую следует вписывать код его обработки:
Таким образом, сгенерированный средой разработки код приложения полностью описывает его визуальный интерфейс, взаимное расположение элементов и их свойства. Описав принципы, на которых основана генерация кода приложений при визуальном дизайне, мы можем приступить к рассмотрению компонентов, реализующих интерфейсные элементы, которые можно использовать в приложениях. Интерфейсные элементы Windows-приложенийОбщие свойства, события и методыБазовым классом всех интерфейсных элементов Windows-приложений в .NET Framework является класс System. Windows.Forms.Control, содержащийся в пространстве имен System. Windows.Forms. Именно в этом классе определены общие для всех интерфейсных элементов свойства, события и методы. Перечислим наиболее важные из них:
Из наиболее важных методов этого класса следует отметить методы BringToFront, SendToBack, Show, Hide, Contains, Refresh, Update, а из событий - события, связанные с перемещением мыши и нажатием на клавиши MouseDown, MouseMove, MouseUp, MouseLeave, MouseHover, MouseEnter, MouseWheel, KeyDown, KeyPress, KeyUp, Click, с применением операции drag-and-drop: DragDrop, DragEnter, DragLeave, DragOver, а также связанные с изменением размера элемента - Resize - и его перерисовкой - Paint. Все эти свойства, методы и события присутствуют у всех элементов управления, поскольку они являются наследниками класса Control, остальные же свойства, события и методы элементов управления предназначены для использования в приложениях их специфической функциональности. К сожалению, объем данной статьи не позволит нам подробно описать иерархию классов, применяемых в Windows-приложениях, и рассказать обо всех особенностях соответствующих интерфейсных элементов. Поэтому здесь мы ограничимся примером создания Windows-приложения, использующего некоторые из редко применяемых, но тем не менее достаточно интересных интерфейсных элементов. Применение компонентов DataSet, DataGrid и DateTimePickerВ предыдущей части данной статьи мы создали в качестве примера MDI-приложение, отражающее графические файлы и использующее интерфейсные элементы PictureBox, Button, MainMenu. Сегодня мы создадим простейший персональный информационный менеджер, который позволяет ввести сведения о будущих событиях и напоминать о событии в указанное время. Для этого создадим новый проект и разместим на форме нашего приложения две кнопки и по одному элементу управления TextBox, DateTimePicker и DataGrid. Изменим надписи на кнопках (свойство Text) на «Добавить событие» и «Скрыть форму». Установим свойство FormBorderStyle формы равным FixedSingle - это предотвратит изменение пользователем ее размеров. С помощью компонента DateTimePicker пользователь может ввести дату и время события, о котором ему следует напомнить. В элементе управления Text1 он может ввести текст напоминания. Однако список событий, содержащий эти данные, нужно где-то сохранить, и разумным представляется сохранить его не просто в памяти, а в файле на жестком диске - в этом случае сведения о будущих событиях не будут потеряны после перезапуска приложения. Пусть это будет XML-документ. Поскольку список событий - это, по существу, набор данных, воспользуемся компонентом DataSet с вкладки Data в палитре компонентов. Так как этот компонент невизуальный, при перетаскивании его на форму он окажется в нижней части окна дизайнера форм. Компонент DataSet представляет интерфейс к кэшу в памяти, в котором могут содержаться одна или несколько таблиц (in-memory tables), которые могут быть результатом запроса к базе данных, чтения XML-документа, а могут быть созданы в самом приложении «из ничего». В данный момент этот набор данных не содержит ни одной таблицы, и сейчас мы займемся ее созданием. Для создания таблицы со списком событий выберем только что созданный компонент DataSet1 и щелкнем мышью возле свойства Tables в окне Properties. В результате получим окно Tables Collection Editor, в котором мы можем нажать клавишу Add и добавить в коллекцию Tables класса DataSet1 новую таблицу, которой присвоим имя Schedule. Далее следует определить, что представляют собой поля этой таблицы. Для этого в редакторе Tables Collection Editor щелкнем мышью возле свойства Columns, после чего на экране появится окно Columns Collection Editor редактора коллекции полей созданной таблицы. С помощью кнопки Add добавим в нее три поля - EventID, DateTime и Message и установим их типы данных равными System.Int32, System.DateTime и System.String. Установим значение свойства AutoIncrement поля DateTime равным True - это позволит не заботиться о присвоении значений этому полю в коде приложения. Нажимаем кнопку Close в редакторе Columns Collection Editor. В редакторе Tables Collection Editor, который по-прежнему открыт, установим значение PrimaryKey таблицы Schedule равным имени первой колонки и закроем этот редактор. Структура таблицы готова. Установим свойство DataSource элемента управления DataGrid1 равным DataTable1. Теперь изменим внешний вид элемента управления DataGrid - как минимум, нам нужно, чтобы ширина колонок соответствовала отображаемым данным. Для этой цели нам следует создать коллекцию стилей отображения таблиц - DataGridTableStyles. Щелкнем по кнопке возле свойства TableStyles элемента управления DataGrid1 и в появившейся диалоговой панели создадим один из элементов такой коллекции. Установим его свойство MappingName равным имени отображаемой таблицы - Schedule; теперь эта таблица будет иметь такой вид, который описан в данном стиле. В этой же диалоговой панели можно выбрать и другие параметры отображения данных (например, цвет текста, фона, обрамления, линий и т.д.). В общем случае элемент управления DataGrid может быть связан с компонентом DataSet, содержащим несколько таблиц, - тогда можно отображать разные таблицы различными стилями. Помимо определения внешнего вида таблицы как таковой, следует позаботиться и о ее колонках. Для этой цели нам необходимо создать коллекцию стилей колонок таблицы GridColumnStyles, щелкнув по кнопке рядом с названием соответствующего свойства. Создадим три элемента этой коллекции - по одному на каждое поле таблицы - и установим их свойства Width (ширина) и HeaderText (заголовок) в соответствии с данными, содержащимися в данной таблице. Установим также значение свойства Format второй колонки равным «f» - в этом случае данные типа DateTime отображаются полностью и в соответствии с региональными настройками данного компьютера. В результате элемент управления DataGrid1 приобретет вид, изображенный на следующем рисунке: Теперь нам следует позаботиться о сохранении данных в файле (пусть он называется c:\Schedule.xml) и считывании их из файла. Представляется вполне разумным считывать файл при загрузке формы, поэтому мы создадим обработчик события Load формы:
В этом фрагменте кода мы считываем файл c:\Schedule.xml, если он существует, и создаем новый (пустой), если он не найден. Имеет смысл также сохранять файл с данными о событиях при добавлении в него новых записей. Поэтому реализуем соответствующий код в обработчике события Click кнопки с надписью «Добавить событие»:
В этом фрагменте кода мы создаем новый объект DataRow и присваиваем его второму и третьему полю значения, считанные из элементов управления DateTimePicker1 и TextBox1 (значения же первого поля будут присваиваться автоматически, поскольку мы указали, что оно автоинкрементное). Затем добавляем этот объект DataRow в коллекцию строк созданной ранее таблицы, сохраняем ее в XML-файле и обновляем содержимое элемента управления DataGrid. Далее изменим внешний вид и поведение элемента управления DateTimePicker1, позволив пользователю выбирать отдельно год, месяц, день, час и минуты. Для этого установим свойство ShowUpDown этого элемента управления равным True и в уже созданный обработчик события Load формы добавим фрагмент кода (он выделен жирным шрифтом), в котором значение свойства Format этого элемента управления устанавливается равным Custom, а значение свойства CustomFormat - равным строке "MMMM dd, yyyy - dddd, hh:mm", что позволит вводить в этом элементе год, месяц, день, час и минуты и отображать день недели. Заодно запретим вводить значения времени, относящиеся к прошлому:
Вот как примерно выглядит XML-файл со списком событий после их ввода с помощью созданного приложения:
Итак, мы научились вводить описания событий, отображать их в DataGrid и сохранять в XML-файле. Наша следующая задача - добавить к приложению возможность оповещать о них пользователя. Этим мы займемся в следующем разделе. Применение компонентов Timer и NotifyIconСамый простой (хотя и не самый эффективный) способ оповещения пользователя о событии - это периодический просмотр списка событий с целью выяснения, не наступило ли время оповестить о каком-либо из них. Для выполнения некоторых периодических действий можно использовать компонент Timer - это невизуальный компонент, обладающий регулярно повторяющимся событием Tick и свойством Interval, равным числу миллисекунд, через которые наступает это событие. Добавим компонент Timer в наше приложение и установим его свойство Interval равным 60 000, что соответствует одной минуте, а свойство Enabled равным True. Создадим обработчик события Tick:
В этом обработчике события мы проверяем, не должны ли в течение ближайшей минуты наступить какие-либо события из созданного пользователем списка, а если должны, то вывести на экран сообщение о событии. Итак, теперь наше приложение позволяет не только вводить список событий, но и, будучи запущенным, оповещает об их наступлении. Однако, на наш взгляд, оно обладает существенным недостатком - его форма постоянно присутствует на экране, что является не самой лучшей реализацией приложений подобного назначения. Поэтому нашим следующим шагом будет отображение приложения не в виде формы, а в виде значка в специальной области системной панели, отображающей состояние запущенных сервисов и приложений. Для этой цели мы можем использовать компонент NotifyIcon, позволяющий создать такую пиктограмму. Это невизуальный компонент, основными свойствами которого являются Icon (графический файл с пиктограммой - в нашем примере это изображение будильника), Text (надпись на ярлычке с подсказкой) и ContextMenu. Прежде чем установить значение свойства ContextMenu этого элемента, следует перенести в приложение компонент ContextMenu (он также невизуальный). Выбрав из контекстного меню созданного нами компонента ContextMenu1 пункт Edit Menu, мы можем отредактировать его, добавив к нему необходимые пункты, например «Список событий» и «Закрыть». Создадим обработчики этих событий:
Приведенный выше фрагмент кода реализует следующие возможности: при выборе пункта «Список событий» главная форма приложения становится видимой, а при выборе пункта «Закрыть» приложение закрывается. Осталось только заставить форму закрываться при нажатии кнопки «Скрыть форму»:
Далее свойству ContextMenu компонента NotifyIcon можно присвоить значение ContextMenu1. Теперь при запуске приложения и скрытии формы можно управлять приложением с помощью контекстного меню, появляющегося при щелчке правой клавишей мыши над соответствующей пиктограммой. Таким образом, мы создали приложение, напоминающее пользователю о событиях, если он предварительно ввел сведения о них. ЗаключениеВ этой статье мы рассказали о базовых принципах организации Windows-приложений и привели ряд примеров использования компонентов, реализующих интерфейсные элементы. На этом мы завершаем наше знакомство с Visual Studio .NET. Дальнейшее рассмотрение этого продукта выходит за рамки тематики нашего журнала.
|
|