close

Вход

Забыли?

вход по аккаунту

?

А. Климов ПРОГРАММИРОВАНИЕ КПК и СМАРТФОНОВ

код для вставкиСкачать
Этот файл был взят с сайта http://all-ebooks.com Данный файл представлен исключительно в ознакомительных целях. После ознакомления с содержанием данного файла Вам следует его незамедлительно удалить. Сохраняя данный файл вы несете ответственность в соответствии с законодательством. Любое коммерческое и иное использование кроме предварительног о ознакомления запрещено. Публикация данного документа не преследует за собой никакой коммерческой выгоды. Эта книга способствует профессиональному росту читателей и является рекламой бумажных изданий. Все авторские права принадлежат их уважаемым владельцам. Если Вы являетесь автором данной книги и её распространение ущемляет Ваши авторские права или если Вы хотите внести изменения в данный документ или опубликовать новую книгу свяжитесь с нами по email. А. Климов ПРОГРАММИРОВАНИЕ КПК и СМАРТФОНОВ на .NET Compact Framewor k Переход на .NE T Compac t Framewor k 2.0 Поэтапный процесс создания приложения Особенности программирования для смартфонов Создание игр для мобильных устройств ^ i v Климов Александр Программирование КПК и смартфонов на .NET Compac t Framework Заведующий редакцией А. Кривцов Ведущий редактор А. Юрчеико Технический редактор С. Романов Литературный редактор И. Шапошников Корректоры И. Рощина. А. Моносов Верстка С. Романов ББК 32.973.23-01 8 УДК 004.382.7 Климов А. П. К49 Программирование КПК и смартфонов на .NET Compact Framework. — СПб.: Питер, 2007. — 320 с: ил. ISBN 978-5-91180-270- 7 5-91180-270- 8 Мобильные устройства все активнее вторгаются в нашу жизнь. Карманные компьютеры и смартфоны давно утратили статус дорогих игрушек и перекочевали в разряд необходимых устройств. Значит, для них необходимо создавать программное обеспечение. Так почему вы должны оставаться в стороне? Прочитав эту книгу, вы получите знания, которые позволят вам уверенно создавать программы для карманных компьютеров и смартфонов. А огромное коли­
чество разнообразных примеров поможет быстро совершенствоваться и развивать­
ся, обогащая свой опыт работы. Книга предназначена для начинающих программистов. © ООО «Питер Пресс», 2007 Все лрава защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав. Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги. ISBN 978-5-91180-270- 7 ООО «Питер Пресс*, 198206, Санкт-Петербург, Петергофское шоссе, д. 73, лит. А29. Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 - литература учебная. Подписано в печать 13.10.06. Формат 60*90/16. Усл. п. л. 20. Тираж 2500 экз. Заказ № 2873. Отпечатано с готовых диапозитивов в ООО «Типография Правда 1906». 195299, Санкт-Петербург, Киришская ул., 2. Тел.: (812) 531-20-00, (812) 531-25-5S. Краткое содержание Введение 11 Глава 1. Знакомство с .NET Compact Framework 14 Глава 2. Первое приложение для .NET Compact Framework ..19 Глава 3. Элементы управления 27 Глава 4. Улучшаем элементы управления 72 Глава 5. Мышь и клавиатура 83 Глава 6. Графика 8 9 Глава 7. Разработка приложений 115 Глава 8. Эмулятор и другие утилиты 154 Глава 9. Программирование для смартфонов 166 Глава 10. Windows Mobile 5.0 177 Глава 11. Создание игр 210 Глава 12. Связь 26 1 Глава 13. Использование неуправляемого кода 272 Глава 14. Кирпичики .NET Compact Framework 314 Послесловие 31 8 Содержание Введение 11 На кого рассчитана эта книга 11 Требования 11 Примеры к книге 12 Благодарности 12 Отзывы и предложения 13 От издательства 13 Глава 1. Знакомство с .NET Compact Framework 14 Мобильные устройства 14 Общие сведения 15 Развитие .NET Compact Framework 15 Что нового в .NET Compact Framework 2.0 16 Что нового в Visual Studio .NET 2005 18 Глава 2. Первое приложение для .NET Compact Framework.... 19 Первые шаги 19 Кнопки минимизации и закрытия формы 23 Размеры и позиция формы 23 Меню 24 Панель ввода SIP 25 Стилус вместо мыши 26 Глава 3. Элементы управления 27 Сходство и различия 27 Урезанная функциональность элементов управления '. 30 Элемент Form 30 Свойство FormBorderStyle 30 Свойство Control Box 31 Свойства MinimizeBox и MaximizeBox 31 Свойство WindowsState 31 Размеры и расположение формы 31 Элементы управления 32 Элемент Button 32 Элемент TextBox 33 Элемент Label 34 Элемент RadioButton 34 Содержание 5 Элемент Panel 3 6 Элемент CheckBox 3 6 Элемент ComboBox 3 7 Элемент ListBox 3 8 Элемент NumericUpDown 3 9 Элемент DomainUpDown 4 1 Элемент ProgressBar 4 2 Элемент StatusBar 4 3 Элемент ТгаскВаг 4 4 Элемент Tool Ваг 4 5 Элемент Main Menu 4 7 Элемент ContextMenu 4 8 Элемент Timer 4 8 Элементы OpenFileDialog и SaveFileDialog 49 Элементы HScrollBar и VScrollBar 50 Список рисунков (ImageList) 5 1 Элемент PictureBox 5 2 Элемент List View 5 3 Элемент TabControl 5 5 Элемент TreeView „ 57 Элемент InputPanel 5 8 Элемент управления DataGri d 61 Элемент Splitter 6 3 Элемент MonthCalendar 64 Элемент DateTimePicker 6 4 Элемент DocumentList 6 5 Элемент Notification , 68 Элемент HardwareButton 69 Глава 4. Улучшаем элементы управления 72 Текстовые поля 7 2 Управление полосой прокрутки 7 3 Многострочный текст в кнопке 74 Увеличение ширины выпадающего списка ComboBox 77 ListBox 7 9 ListView 8 0 Создание кнопки, содержащей изображение 81 Список с расширенными возможностями 81 Текстовое поле для ввода чисел 8 1 Сортировка элементов ListView ,. 82 Использование элемента DateTimePicker 82 Глава 5. Мышь и клавиатура 8 3 Мышь и стилус 8 3 Курсоры 8 3 .л-.,^~-. 6 Содержани е Песочные часы 8 3 Обработка события Tap-and-Hold •..., 84 Клавиатура 8 5 Клавиши навигации 8 6 Выключение устройства 8 7 Дополнительные материалы 8 8 Глава 6. Графика 8 9 Классы для программирования графики 89 Класс Реп 8 9 Класс Brush 9 0 Класс SolidBrush 9 0 Класс TextureBrush 9 0 Класс Color 9 1 Класс Font 9 2 Класс Icon 9 2 Класс Bitmap 9 2 Структура Point 9 3 Структура Rectangle ; 93 Графические методы 9 3 Создание собственных методов DrawPie и FillPie 95 Создание фонового рисунка для формы 99 Копирование рисунка 100 Поддержка прозрачности 102 Округленные прямоугольники 105 Создание экранных снимков 107 Метод Lockbits 110 Графический редактор 111 Дополнительные материалы 114 Глава 7. Разработка приложений 115 Активация и деактивация формы , 115 Закрыть или свернуть окно .-. 116 Пиктограмма приложения 117 Создание собственных диалоговых окон 118 Создание заставки Splash Screen 120 Поворот экрана 12 2 Рекомендации по дизайну форм 123 Готовые приложения •. 123 Файловый менеджер для смартфона 124 Зачем нужен файловый менеджер 124 Графический интерфейс программы 125 Код программы 126 Диспетчер задач 13 5 Графический интерфейс программы » 135 Содержание 7 Код программы 13 6 Активация и закрытие приложения 138 Перечисление процессов 139 Закрытие процесса 141 Маленький блокнот 141 Первые шаги 14 2 Стандартные операции с текстом 143 Распространение приложений 148 Создание cab-файла 149 Создание проекта 150 Дополнительные материалы 153 Глава 8. Эмулятор и другие утилиты 154 Программы для отладки приложений 154 Эмулятор 15 4 Запуск эмулятора 15 5 Настройка эмулятора 156 Эмуляция карточки памяти 158 Изменение ориентации экрана 160 Выход в Интернет через эмулятор 161 Изменение внешнего вида эмуляторов 163 Эмулятор как отдельный продукт 163 Новая версия эмулятора 163 Набор утилит Visual Studio Remote Tools 164 Remote Zoom-in 16 4 Remote File Viewer 16 5 Remote Registry Editor 165 Глава 9. Программирование для смартфонов 166 Особенности программирования для смартфонов 166 Создание приложения для смартфона - 167 Создание меню 16 8 Элементы управления 169 Режимы ввода 17 2 Переопределение клавиш Soft Key 174 Прокручивание формы 175 Глава 10. Windows Mobile 5.0 177 Первый взгляд 17 7 Улучшенная продуктивность 177 Поддержка мультимедиа 178 Поддержка управляемого кода 178 Windows Mobile 5.0 API 178 Взаимодействие с ActiveSync 179 Новые возможности системы 180 8 Содержани е Подготовка к работе 18 0 Microsoft. WindowsMobile.PocketOutlook 182 Встречи (Appointment) 18 2 Работа с адресной книгой 18 5 Электронная почта 18 8 SMS-сообщения 18 9 Прием и обработка SMS-сообщений 191 Телефония 19 4 State and Notifications Broker 19 5 Мультимедиа 20 0 Выбор изображения 20 0 Работа с фотокамерой 20 2 Повторение пройденного 20 4 Встречи 20 4 Отсылка письма 20 6 Мелочь, а приятно 20 9 Метод Directory.Exists 20 9 Метод Bitmap.Save() 20 9 Глава 11. Создание игр 21 0 Игры на мобильных устройствах 21 0 Продуктовая аркада 211 Начало работы 21 2 Добавление изображения в программу 212 Использование встроенных ресурсов «. 213 Вывод картинки на экран 21 3 Создание анимации 21 4 Отражения 21 6 Управление скоростью движения объекта 218 Добавляем новый объект 22 0 Устранение мерцания 22 1 Хлеб — всему голова 22 3 Обнаружение столкновений 22 5 Столкновения батона и мяча 22 8 Новые объекты 23 0 Размещение помидоров 23 1 Уничтожение томатов 23 4 Счет игры 23 5 Ведение счета 23 6 Звуковые эффекты 23 7 Дальнейшие улучшения 241 Тестирование 24 5 И опять добавляем новые объекты 24 6 Управление таблицей результатов 25 0 Переключение между формами 25 1 Содержание 9 Отображение дочернего окна 252 Получение имени игрока ,. 253 Хранение лучших результатов 254 Улучшение графики ...256 Спрайты 259 Другие игры . 260 Глава 12. Связь 261 Инфракрасное соединение 261 История и теория 261 Класс IrDAClient 262 Создание программы для работы с ИК-связью 262 Обнаружение устройств 264 Передача данных 265 Технология Bluetooth 269 Несколько слов о связи 271 Глава 13. Использование неуправляемог о кода 272 Вызов функций Windows API 272 Определение платформы 273 Пароли 275 Перезагрузка КПК 277 Еще раз о перезагрузке 278 Поворот экрана 278 Прячем кнопку Start 281 Панель задач 283 Запуск других приложений 284 Названия специальных файлов 286 Использование звуковых файлов 288 Системные звуки 293 Системное время 293 Создание ярлыка 294 Количество строк в текстовом поле 295 Реестр 296 Наличие внешней клавиатуры 307 Информация о пользователе 309 Наличие дополнительной клавиатуры 310 Виброзвонок 311 Глава 14. Кирпичики .NET Compact Framework 314 Узнать версию .NET Compact Framework 314 Узнать версию операционной системы 314 Получить путь к запущенному приложению 315 Специальные папки 315 Узнать имя устройства 315 10 Содержани е Узнать ориентацию экрана 31 6 Открытие файлов программой по умолчанию 316 Создание и отправка письма 31 6 Кнопки навигации . . 317 Послесловие 31 8 Что дальше? 31 8 Полезные ресурсы 31 9 .NET Compact Framework 2.0 Redistributable 319 Microsoft ActiveSync 4.1 31 9 Русская версия эмулятора для Windows Mobile 5.0 Smartphone 319 Русская версия эмулятора для КПК под управлением Windows Mobile 5.0 Pocket PC 319 Сайт Роба Майлза 32 0 Сайт Кристиана Форсберга 32 0 OpenNETCF.org 32 0 Google 32 0 Введение Сейчас очень трудно найти книги, в которых описываются пробле­
мы программирования для карманных компьютеров и смартфонов при помощи технологии .NET Compact Framework. Одним из глав­
ных источников получения информации является сама документа­
ция, входящая в состав Visual Studio 2005. Но одной документации явно недостаточно. Именно поэтому и появилась на свет книга, ко­
торую вы держите в руках. Ни в коем случае не стоит рассматривать данное произведение как полный и исчерпывающий учебник, в котором можно найти отве­
ты на все вопросы. В книге объясняются базовые принципы про­
граммирования для компактных устройств. И автор надеется, что книга послужит стимулом для дальнейшей работы в этой области. На кого рассчитана эта книга Книга рассчитана в первую очередь на программистов, уже имею­
щих опыт программирования на .NET Framework, которые хотят расширить свой кругозор за счет освоения .NET Compact Framework. После освоения новой технологии у разработчика по­
явится возможность перенести некоторые программы с обычных компьютеров на другую платформу, тем самым увеличивая число своих потенциальных клиентов. Требования Чтобы работать с примерами из этой книги, необходимо иметь на компьютере пакет программ Visual Studio 2005. Обратите внимание на то, что некоторые облегченные версии Visual Studio 2005 (в част­
ности, Express-версии) не поддерживают разработки программ для мобильных компьютеров. Часть примеров можно использовать и в ста-
12 Введени е рой версии Visual Studio 2003, однако в этом случае разработчику придется вручную переписывать код из-за несовместимости фор­
матов разных версий Visual Studio. Впрочем, настоящего програм­
миста этим не испугаешь! Примеры к книге Практически любая книга по программированию снабжается сопут­
ствующими примерами. Не стала исключением и книга, которую вы держите в руках. Каждая глава сопровождается массой приме­
ров, а найти их код можно на сайте издательства «Питер». В качестве основного языка программирования был выбран язык С#, но в третьей главе были также добавлены примеры программи­
рования на языке Visual Basic.NET. Если среди читателей книги найдется много приверженцев этого языка, то на сайте автора бу­
дут выложены все необходимые примеры, написанные на Visual Basic.NET. Благодарности Любую работу в наше время тяжело делать в одиночку. Хорошо, когда находятся люди, готовые поддержать инициативу. Поэтому хотелось бы назвать тех, без которых эта книга могла не появиться на свет. В первую очередь надо поблагодарить издательство «Питер» (www.piter.com), которое согласилось выпустить данную книгу. Хочу также выразить благодарность тем людям, которые любезно разрешили использовать исходные коды своих чудесных программ в качестве учебных примеров. Это Кристиан Форсберг (Christian Forsberg), Алекс Яхнин (Alex Yakhnin) и Роб Майлз (Rob Miles). Без примеров этих авторов книга получилась бы скучной и неинте­
ресной. Так получилось, что во время написания книги мне пришлось по­
менять работу. В этом процессе участвовали несколько человек, которые способствовали переходу. Поэтому хочется поблагодарить и этих людей, благодаря которым у меня появились дополнитель­
ные возможности для работы над книгой. В первую очередь хоте­
лось бы отметить генерального директора гостиничного комплекса «Вега» Воробьева Алексея Петровича, главного инженера Миклу-
I От издательства 1 3 шова Владимира Павловича и начальницу АХО Егоркину Галину Владимировну. Хочу поблагодарить также всех сотрудников отде­
ла информационных технологий, возглавляемого Рогулиным Вик­
тором Васильевичем, которые делились своими знаниями на но­
вой работе: Храмцову В., Пузикову Н. (особенно), Фетисову Е., Шумова Е., Князева Л., Алдохина А., Дробота В., Терехова А., Не-
чеухина Н. Отзывы и предложения Все свои отзывы и критические замечания вы можете посылать на электронный адрес автора rusproject(S>maiLru. Также стоит почаще за­
глядывать на сайт http://rusproject.narod.ru, на котором я постараюсь размещать новые дополнительные материалы по тематике книги. От издательства Ваши замечания, предложения и вопросы отправляйте по адресу электронной почты comp@piter.com (издательство «Питер», ком­
пьютерная редакция). Мы будем рады узнать ваше мнение! Все исходные тексты, приведенные в книге, вы можете найти по адресу http://www.piter.com/downtoad. Подробную информацию о наших книгах вы найдете на веб-сайте издательства: http://www.piter.com. J4\ Глава 1 Знакомство с .NET Compact Framework Мобильные устройства Мобильные устройства все активнее вторгаются в нашу жизнь. Все чаще можно встретить в метро молодых людей, увлеченно работа­
ющих с карманным компьютером. Я сам несколько раз был свиде­
телем того, что обладателями КПК были девушки. Это говорит о том, что данные устройства уже утратили статус дорогой игруш­
ки технократов и рассматриваются как необходимое устройство, которое вскоре будет таким же доступным, как обычный сотовый телефон. В последнее время на рынок активно выходят смартфоны под уп­
равлением операционной системы Windows Mobile 5.0. Пока в этом сегменте рынка прочные позиции удерживают смартфоны под уп­
равлением Symbian, производимые фирмами Nokia и Sony Ericsson. Но умение Microsoft завоевывать себе место под солнцем давно ста­
ло общеизвестным. Достаточно вспомнить противоборство браузе­
ров Netscape и Internet Explorer, а также КПК Palm и PocketPC. Эта тенденция позволяет считать, что и «умные» телефоны под уп­
равлением Windows Mobile скоро потеснят своих конкурентов. И в этой ситуации очень ярко проявляется преимущество изуче­
ния .NET Compact Framework. Если вы знакомы с программирова­
нием для .NET Framework, то вам не составит труда перейти к ос­
воению особенностей программирования для КПК и мобильных телефонов под управлением Windows Mobile. Ведь писать програм­
мы придется в уже знакомой вам среде Visual Studio .NET. Более того, вам даже не обязательно иметь сам карманный компьютер или смартфон для проверки написанного кода, так как в Visual Studio .NET уже имеются эмуляторы для этих мобильных устройств. С помощью этой книги читатель сможет научиться самостоятель­
но писать программы для мобильных устройств. Это позволит рас­
ширить круг своих знаний, а также улучшить сбыт программ, если вы занимаетесь программированием коммерческих приложений. Развитие .NET Compact Framework 15 В данной книге все примеры написаны на новых языках семейства .NET, таких как С# и Visual Basic .NET. Общие сведения Главная страница, посвященная .NET Compact Framework, нахо­
дится по адресу http://msdn.microsoft.com/netframework/programming/ netcf/default.aspx. Там можно найти все последние новости о рас­
сматриваемой технологии, обновления программ, ссылки на дру­
гие полезные сайты, примеры. Технология .NET Compact Framework поддерживается операцион­
ными системами Pocket PC 2000, Pocket PC 2002, Windows Mobile 2003, Windows Mobile 2005 и Windows CE .NET 4.1. Конечно, технология .NET Compact Framework несколько отлича­
ется от .NET Framework. Подробную информацию о различиях между этими технологиями можно найти на странице по адресу msdn.microsoft.com/library/default.asp?url-/library/en-us/dv_evtuv/ html/etconComparisonsWithNETFramework.asp. Считается, что .NET Compact Framework является частью полной библиотеки .NET Framework. Действительно, между двумя этими платформами очень много общего. Но все же говорить о .NET Compact Framework как о подмножестве полной .NET Framework не совсем корректно. Дело в том, что .NET Compact Framework поддерживает серию классов, которых нет в полной библиотеке классов. Эти классы созданы спе­
циально для мобильных устройств и позволяют поддерживать, на­
пример, программную клавиатуру, возможности инфракрасной свя­
зи и отправки SMS. Библиотека .NET Compact Framework действительно компактна. Вместо 20 Мбайт полного пакета .NET Framework, устанавливаемо­
го на настольные компьютеры, она занимает около 2 Мбайт. Полная версия .NET Framework содержит 18 700 классов и 80 000 методов, a .NET Compact Framework — всего лишь 4 700 классов и 13 000 ме­
тодов. Но следует помнить, что это лишь приблизительная оценка. Развитие .NET Compact Framework Поначалу .NET Compact Framework устанавливалась в карманные компьютеры Pocket PC отдельно. Это порождало определенные проблемы для разработчиков. Не каждый пользователь хотел уста­
навливать пакет .NET Compact Framework, необходимый для рабо-
16 Глава 1. Знакомство с .NET Compact Framework ты программы, когда оперативной памяти и так не хватает. Впер­
вые библиотека .NET Compact Framework стала встраиваться в пор­
тативные устройства под управлением Windows Mobile 2003 (Pocket PC 2003). На борту компьютеров под управлением Windows Mobile Second Edition уже находился пакет .NET Compact Framework 1.0 SP2). По уверениям Microsoft, работа с ресурсами стал быстрее на 600%, работа с XML с помощью класса XMLTextReader стала быстрее на 40%, а работа с ADO.NET — на 20%. Естественно, с выходом библиотеки .NET Compact Framework 2.0 создатели сно­
ва стали говорить о повышении быстродействия и надежности. Но следует учитывать, что устройства с предустановленной библиоте­
кой .NET Compact Framework 2.0 еще не выпускаются, и пользова­
тель должен сам установить необходимый пакет. Возможно, когда книга выйдет из печати, в мире уже появятся устройства встроен­
ной версией .NET Compact Framework 2.0. Что нового в .NET Compact Framework 2.0 Список основных изменений в .NET Compact Framework 2.0 при­
веден на странице msdn.microsoft.com/netframework/programming/ netcf/defauLt.aspx?puU»/library/en-us/dnnetcomp/html/whats_ new_netcf2.asp. К основным улучшениям библиотеки .NET Compact Framework 2.0 относятся усовершенствованные возможности созда­
ния пользовательского интерфейса, новая мобильная база данных, существенные усовершенствования эмулятора, усиленная поддерж­
ка СОМ Interop и Managed Interfaces для D3D. Библиотека .NET Compact Framework 2.0 расширила существующую функциональ­
ность в .NET CF 1.0 новыми возможностями. Также разработчики добавили поддержку новых классов, которые ранее были доступны только в полной .NET Framework. В этой книге обязательно будут рассмотрены наиболее значительные новинки. А сейчас можно лишь упомянуть основные моменты. Пакет .NET Compact Framework 2.0 в Visual Studio 2005 стал поддер­
живать новые элементы управления, позволяющие создавать очень сложные приложения без написания громоздкого кода. В частности, появилась поддержка элементов управления MonthCalendar и DateTime Picker, позволяющих создавать интерфейс календаря. Также появил­
ся новый элемент для уведомлений Notification. Кроме него разра­
ботчики получили доступ к элементам DocumentList и HardwareButton. Более подробно они будут рассматриваться в главе, посвященной эле­
ментам управления. Новое свойство ScreenOrientation позволяет без Что нового в .NET Compact Framework 2.0 17 использования неуправляемого кода вращать экран устройства Кро­
ме того, стало значительно проще создавать собственные элементы управления, как это делается в полной версии .NET Framework. Элементы пользовательского интерфейса в .NET Compact Frame­
work 2.0 теперь поддерживают присоединение (docking). При пе­
ремещении элемента управления к определенной стороне контей­
нера он всегда заполняет эту сторону контейнера. Некоторые элементы управления стали поддерживать свойство AutoScaleMode. Свойство AutoScaleMode показывает, как нужно пере­
рисовывать элемент при изменении разрешения экрана. Также фор­
мы стали поддерживать свойство AutoScrol 1. Помимо этого класс Control теперь поддерживает методы SuspendLayout и ResumeLayout. Также в .NET Compact Framework появилась полно­
ценная поддержка буфера обмена. Класс Graphics тоже получил новые возможности, и теперь при по­
мощи свойств DpiX и Dpi Y разработчик может узнать размеры экра­
на. Помимо этого сейчас можно отображать текст под различными углами при помощи класса Log Font. Также разработчик может созда­
вать перья заданных цвета и размера. Значительно улучшена работа с растровыми изображениями. Про­
граммисты получили новые возможности для создания изображе­
ний и сохранения их в файле или потоке. В приложениях стало проще манипулировать изображениями при помощи комбинации методов LockBits и UnlockBits в сочетании с новым классом BitmapData. Приложения, использующие при работе с изображе­
ниями неуправляемый код, теперь могут получать дескриптор объекта Bitmap через метод GetHbitmap. Новые возможности позволяют разрабатывать приложения с исполь­
зованием управляемого кода для Windows Mobile 5.0 при помощи классов из пространства имен Microsoft.Windows.DirectX. Поддержка DirectX позволяет писать игры с использованием управляемого кода, обеспечивая более быструю разработку приложений, чем при исполь­
зовании неуправляемого DirectX. Так как на рынке все чаще стали появляться устройства со встро­
енной клавиатурой, то в библиотеку .NET Compact Framework 2.0 была введена поддержка клавиатуры. Также следует отметить, что элементы управления теперь распоз­
нают события KeyUp, KeyDown и Keypress. Объект Form теперь имеет свойство KeyPreview. 18 Глава 1. Знакомство с .NET Compact Framework Тестировать программы тоже стало намного проще, так как эмуля­
тор в .NET Compact Framework 2.0 подвергся значительной пере­
работке. Перечень функциональных возможностей эмуляторов Pocket PC и смартфонов был расширен, что облегчает создание, проверку и развертывание приложений. Особое внимание было уделено поддержке сетевого взаимодействия. Кроме того, было улучшено быстродействие эмулятора. Появилась поддержка про­
граммы ActiveSync, можно работать с общими папками и исполь­
зовать четыре СОМ-порта. Также эмулятор стал поддерживать ра­
боту с портретным и альбомным режимами отображения. Кроме того, эмулятор теперь эмулирует работу ARM-процессора. Технология Smart Device CAB Project упростила развертывание мобильных приложений. Эта технология позволяет использовать в визуальном редакторе перемещение файлов, создавать папки и записи в реестре. Теперь создавать САВ-проект для установочно­
го пакета так же просто, как при создании проекта Windows Installer для настольных компьютеров. Новый компилятор теперь создает еще лучший и более быстрый код. Если в предыдущей версии использовались два J IT-компиля­
тора, то теперь .NET CF 2.0 применяет единый компилятор для всех поддерживаемых процессоров. В области безопасности добавлена поддержка идентификации NTLM и Kerberos. Также улучшена работа с технологией XML, и в распоря­
жение программистов поступил новый класс ХпЛSerial ization. Намного удобнее стало разрабатывать дизайн форм в среде разра­
ботки Visual Studio .NET 2005. Процесс создания программы стал еще более наглядным. Программист может создавать собственные элементы управления так же, как и для обычных приложений. Что нового в Visual Studio .NET 2005 Если у вас уже был опыт программирования под Visual Studio .NET 2003, то вы заметите, что на панели инструментов появились новые элементы управления. Они будут рассматриваться в главе, посвящен­
ной элементам управления. При разработке дизайна приложения бу­
дет заметно, как Windows Forms Designer помогает выравнивать эле­
менты и предлагает выбрать минимальное расстояние между элементами. Автоматически проявляющиеся линии выравнивания по­
могают создавать аккуратные интерфейсы за очень короткий срок. Так­
же появилась возможность разработки приложений, которые могут переключаться между портретным и альбомным режимами экрана. Глава 2 Первое приложение для •NET Compact Framework Первые шаги Практика — это самый лучший способ научиться программировать для мобильных устройств под .NET Compact Framework. Чтобы по­
верить в свои силы, нужно создать простейшее приложение. На его примере можно будет изучить различия между .NET Compact Framework и обычной .NET Framework. Прежде всего нужно запустить среду разработки Microsoft Visual Studio 2005 и создать новый проект. Первые различия в процессе разработки можно увидеть уже на этой стадии. Если для создания обычных приложений надо было выбрать раздел Windows, то на этот раз необходимо выбрать раздел Smart Device. В этом разделе содер­
жатся подразделы, которые отвечают за создание приложений для КПК, смартфонов и устройств под управлением операционной си­
стемы Windows СЕ. Итак, нужно указать язык программирования Visual CU, перейти в раздел Smart Device и выбрать подраздел Pocket PC 2003 (рис. 2 Л). ПРИМЕЧАНИЕ Список подразделов на рисунке может отличаться от списка под­
разделов на вашем компьютере. Например, пункты Windows Mobile 5.0 Pocket PC и Windows Mobile 5.0 Smartphone появились после уста­
новки соответствующих пакетов SDK. В выбранном подразделе присутствуют несколько шаблонов для ре­
ализации различных задач. Как правило, используется шаблон Device Application. Нужно отметить, что существует еще один похожий шаб­
лон с названием Device Application (1.0). Эти два шаблона различают­
ся применяемой версией.№Т Compact Framework. По умолчанию в Visual Studio 2005 используется .NET Compact Framework версии 2.0. Если выделить первый шаблон, то в строке состояния можно уви­
деть сообщение A project for creating a .NET Compact Framework 2.0 forms 20 Глава 2. Первое приложение для .NET Compact Framework application for Pocket PC 2003 and later. В примерах будет использовать­
ся, как правило, версия 2.0, так как она имеет ряд преимуществ. New Proj ec t : Databas e \ j ; Starte r Kits i * Otht r Language * \ ; я vtsuaiC# Jwiptatae;;. Visual Studi o instate d template s \ \ i Windows & Smar t Device : : с : :': r : : 6 : •.: : A • ; Smartphwt 2003 l-wmftwfCE M ;• Windows MobSe 5.0 P| Windows МоЫв 5.0 s i Database Starte r Kits * Visual J * : * Visua l c* * 1 * Othe r Projec t Types Bern*; ;::::ii: :•:&;:. tiftuv fcevtar: ; AppSteatk*» Device AppWcatU. Class Librar y Contro l Librar y Consol e Applicatio n Empt y Projec t (10 ) Ctesslftrar y Consol e (1.0) AppltaU... 2.0 fcri w аийклйоп for Pocfcr t PC 2003 end late' I D*vtc*App«cattori l :.•.:•;•• Рис. 2.1. Выбор типа платформы После того как будет выбран шаблон для приложения, требуется изменить имя проекта. По умолчанию используется название DeviceApplicationl, но наше первое приложение получит имя FirstPocketPCApp_CS. После нажатия кнопки 0К откроется окно сре­
ды разработки с необычным видом формы. Если при программиро­
вании программ для настольных компьютеров отображается толь­
ко форма, то в данном случае на экране будут показаны не только форма, но и внешний вид целевого устройства. При желании раз­
работчик может даже изменить внешний вид карманного компью­
тера, создав специальные файлы. Если вы предпочитаете работать с классическим видом формы, то можно отключить отображение устройства, оставив на экране только форму. Для этого нужно щелк­
нуть правой кнопкой мыши на форме и в появившемся контекст­
ном меню выбрать пункт Show Skin. Повторный выбор этого пункта вернет на экран стандартный вид формы. Обычно в качестве первого примера создается стандартная про­
грамма, которая выводит приветствие на экран. На форме надо рас­
положить кнопку Button и элемент Label для отображения надписи. Также потребуется написать код для обработчика события Click созданной кнопки. Этот код приведен в листинге 2.1. Первые шаги 21 Листинг 2.1 privat e void butSayHello_Click(objec t sender, EventArgs e) { lblHello.Tex t • "Здравствуй, мир!"; } Теперь можно запустить проект при помощи команды Start Debugging или клавиши быстрого вызова F5. При этом на экране появится диалоговое окно Deploy (рис. 2.2). Ctepfoy Ri ^^e^et PCAppj K (Pocket PC 2003 SE Square Emulator i Pocket PC 20O3 SE Square VW Emulator ; Fock?t PC 20OJ SE VGA Emulator PlSriow m* th$s Aatog **ch t»n« 3 depfc? the ejjpfccBfton Рис. 2.2. Диалоговое окно Deploy В основном списке окна перечислены устройства, на которых может выполняться написанная программа. Проверять работу приложения можно как на реальном устройстве, так и при помощи эмулятора. Как правило, при отладке программы используют эмуляторы и толь­
ко в финальной части тестируют программу на реальном устройстве. Легко заметить, что создание программ для КПК совсем не требует наличия карманного компьютера. Автор не раз был свидетелем того, как разработчик на своем сайте признавался, что написал приложе­
ние, не имея КПК. А пользователи благодарили автора за хорошую программу и подтверждали ее работоспособность на своих реальных моделях. Практически все примеры из этой книги сначала запускались на эмуляторе. Поэтому можно выбрать любой эмулятор из предложен­
ного списка. Чаще всего применяется эмулятор Pocket PC 2003 SE. После выбора соответствующего значения в списке нужно нажать кнопку Deploy. Сначала на экране монитора будет отображен сам эмулятор (рис. 2.3), а спустя некоторое время в эмуляторе будет запущена созданная программа. 22 Глава 2. Первое приложение для .NET Compact Framework Рис. 2.3. Первый запуск эмулятора Мышью можно щелкнуть на кнопке с надписью Поздороваться. В ре­
зультате на форме появится строка Здравствуй, мир! (рис. 2.4). r«u иъ* о Эдывствуй, нк:1 Рис. 2.4. Отображение сообщения Теперь, когда вы поверили в свои силы, вам захочется начать пере­
писывать свои старые программы, написанные на С# или Visual Basic .NET, для карманных компьютеров. Но торопиться все же не стоит. Между полной версией .NET Framework и .NET Compact Размеры и позиция формы 23 Framework существует довольно много различий, которые придет­
ся последовательно устранять. Кнопки минимизации и закрытия формы При создании пустой формы можно заметить, что отображается всего одна кнопка минимизации вместо привычных трех кнопок свертыва­
ния, восстановления и закрытия формы. Причем кнопка минимиза­
ции в виде крестика очень похожа на кнопку закрытия формы в обыч­
ных настольных приложениях. У начинающих программистов это поначалу вызывает недоумение. Стандартная модель поведения про­
грамм на карманном компьютере устроена таким образом, что когда пользователь нажимает кнопку с крестиком, то он сворачивает про­
грамму, а не закрывает ее. Об этом говорит значение True свойства формы Mi nimizeBox. Если для этого свойства задать значение False, то вместо кнопки с крестиком будет отображаться кнопка ОК (рис. 2.5). При нажатии на эту кнопку программа завершит свою работу. Здравствуй, M^D I -•'&лШ9ЯшЩЯШШ Рис. 2.5. Приложение с кнопкой ОК Размеры и позиция формы По умолчанию любая форма занимает весь экран. Ее верхний левый угол находится в точке с координатами (0, 26). Если попробовать вручную изменить значения свойства Location, то среда разработки проигнорирует эти попытки и вернет значения. Что же касается раз­
меров формы, то при желании все же можно изменить высоту и ши­
рину формы. Но на практике подобная необходимость встречается редко, поэтому мы не будем заострять на этом внимание. 24 Глава 2. Первое приложение для .NET Compact Framework Меню По умолчанию в создаваемой форме уже содержится элемент управ­
ления MainMenu. Однако в первом примере он не применялся. Чтобы использовать этот элемент управления, нужно создать простое меню с одним пунктом. В области Component tray нужно выделить мышью элемент mainMenul. На форме появится надпись Type here. В этой об­
ласти нужно ввести слово «Поздороваться». Для обработчика собы­
тия menu Itemize lick будет применяться тот же код, который вызы­
вался при нажатии на кнопку. Код обработчика события приведен в листинге 2.2. Листинг 2.2 privat e void menuIteml_Click(objec t sender, EventArgs e) { 1ЫHello.Text = "Здравствуй, мир!": } После запуска программы можно заметить, что созданный пункт меню располагается в нижней части экрана, в отличие от настоль­
ных приложений, в которых меню располагается в верхней части окна (рис. 2.6). К»**! • Ъ\ •ii Гк*еетстви* Рис. 2.6. Меню в нижней части окна Следует отметить, что меню в приложениях для Pocket PC распо­
лагается не на форме, а на панели задач. Также на панели задач на­
ходится значок виртуальной клавиатуры SIP для ввода информа­
ции. Когда пользователь запускает приложение, то его меню появляется на панели задач. Но если удалить меню из формы, то при запуске программы панель задач вообще не будет отображать­
ся (рис. 2.7). Панель ввода SIP 25 В^"* Здравствуй, мир! Рис. 2.7. Экран без панели задач Панель ввода SIP В этой главе уже упоминалась виртуальная клавиатура. Большин­
ство карманных компьютеров не имеют встроенных клавиатур для ввода информации. Вместо клавиатуры в этом случае использует­
ся специальная панель ввода SIP (Software Input Panel), которая позволяет вводить текст (рис. 2.8). •о™* вча-* •• Пр*ветствие ЦЭ| 1 | 2 | Э| 4 | 5 | 6 | 7 | 8 | 9 | 0 | Л7 TablqIw|ё |f |t |у |u| I | о]р Ш cAP|a|«|d|f lolhl iJfc 111 ;l; I Shiftlz |x| с |v |b|n|m|, |. \l | суМПЯ MB .. n • .'. .. Рис. 2.8. Активированная панель ввода SIP Для работы с виртуальной клавиатурой в .NET Compact Framework используется класс InputPanel. Так как панель ввода находится на па­
нели задач, то необходимо, чтобы панель задач была видимой. А ранее уже говорилось что, если форма не имеет меню, то панель задач будет невидима. В результате при попытке создания экземпляра класса InputPanel на форме, не имеющей меню, будет отображено сообщение об ошибке. 26 Глава 2. Первое приложение для .NET Compact Framework Стилус вместо мыши Подавляющее число пользователей настольной версии Windows пользуются мышью. В карманных компьютерах роль мыши выпол­
няет стержень из пластика, называемый стилусом. Конечно, у сти­
луса нет правой кнопки для вызова контекстного меню. У него во­
обще кнопок нет. Вместо кнопок в карманных компьютерах применяется технология Tap-and-Hold. Для выделения элемента управления пользователь должен точно попасть в него кончиком стилуса. По аналогии с мы­
шью, можно легко щелкнуть по экрану (Click), а можно нажать на экран и удерживать стилус на месте (Press). Глава 3 Элементы управления Сходство и различия Несмотря на свою схожесть, .NET Compact Framework уступает в функциональности базовой библиотеке .NET Framework. Это относится и к элементам управления. К счастью, кнопки, списки и текстовые поля все же присутствуют в мобильной версии. Кро­
ме того, в .NET Compact Framework 2.0 была добавлена под­
держка еще нескольких элементов управления, которые отсут­
ствовали в .NET Compact Framework 1.0. ^-.r;.-r;.-.\*v-/.v--.-//-//r-y/-/.:.-.-.-.-..-w.';.'.M.™.; »МК>анК.< * MnraJf.vnu*e«c.«dO * Mtowft.yftJlSMCNy&rv^j * Ncra»:*.Vlfu*Biue vsa Щ * Мзак&МййС «МсгокАЛча * M.-roMfe.vn.vbvCOJc?>OM: >s Ntn*jfc.V№3? •* «»ow«fMcosOMng«d6»a ! Яэтсмсэв Enuoieztb I RcgWVMtmber? ! ;•. fteoe-v fea t ие$жгу+«у« Enuntnao :• S«5tonevl*d?ve**fli J S«efcnEnd«!e<«nftWxl. 4 StfKinEfldhgfvmtArps fjstry НИИЬйГД ?.:ShowjUi4_. _ | Щ Я Щ | | | user. This field reads the Windows registry base key HKEY.CURRENTCONRG, « 38 CmnwtUser * 3 BxaGse n » 3 8 focyHechirtg * 3 8 Us«S Contains information about the current user preferences. This field reads the Windows registry base key HKEY_CURRENT_USER Contains dynamic registry data. This Meld reads the Windows registry base key HKEY_DYN_DATA. Contains the configuration data for the local machine. This field reads the Windows registry base key HKEY_LOCAL_MACHINE. Contains performance Information for software components. This fle'd reads the Windows registry base key HKEY_PERFORMANCE_DATA> Contains information about the defauit user |дой:оп Tht« я«чн rmti* rhp Wfcndows... Ш • Рис. 3.1. Свойства, поддерживаемые в .NET Compact Framewor k Нужно помнить, что даже поддерживаемые элементы управления имеют порой ограниченные возможности. Чтобы узнать, какие свойства, методы или события не поддерживаются элементом уп-
28 Глава 3. Элементы управления равления, нужно запустить справочную систему, найти нужный класс и просмотреть все члены класса. Если нужное свойство под­
держивается в .NET Compact Framework, то у его описания будет присутствовать значок мобильного устройства (рис. 3.1). В каче­
стве примера можно открыть страницу с описанием класса Regi stry. Легко заметить, что поля CurrentUser, LocalMachi пе и Users поддерживаются в .NET Compact Framework, а поля DynData и PerfomanceData — нет. Но даже если необходимый объект поддерживается в .NET Compact Framework, то все равно следует внимательно ознакомиться с его опи­
санием. Например, поле Local Machine поддерживается только в .NET Compact Framework 2.0, поэтому при разработке нужно решить, стоит ли использовать это поле (рис. 3.2). {uflMc«I J Иэ"ВМ1Шм—с юрдуц МочкА' McKme.Viu«Bxv» Mo<ot9fc.VM C ME"O»)fc-Vtt.VbCafc0O N Мо*сжЛЛ**132 й irewwbjMC edtnaMR ; -POMttHtXhtlntnmWO : *яч«егуВ(»ж -4 йсдигоШуии Ktgatrv«cyi4ni«Kfio МДОуШЫКМ tnwN Мраумиорвии E« • S*Bfcr?Kfe»/«ntAai . ScMunekteKvertKand. * *»a*i dngff v»fK*o« Windows 98, Windows 2000 SP4, Windows CE, Windows Millennium Edition, Windows Mobile for Pocket PC, Windows Mobile for Smartphone, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter I Edition The .NET Framework does not support all versions of every platform. For a list of 1 the supported versions, see System. Rejul.re.f.reflts. Ver si o n I nf or ma t i o n .NET Framewor k Supported In: 2.0,1.1. 1.0 .NET Compact Framewor k Supported i n: 2.0 Рис. 3.2. Просмотр поддержки версий .NET Compact Framework В следующем списке перечислены элементы управления, которые не входят в состав классов библиотеки .NET Compact Framework 1.0: Q CheckedListBox; Q ColorDialog; Q ErrorProvider; Сходство и различия 2 9 • FontDialog; Q GroupBox; Q HelpProvider; • LinkLabel (поддерживается в .NET Compact Framework 2.0); • Noti fi cationBubble; P Notify Icon; • элементы управления, связанные с печатью; • RichTextBox; • Spl i tter (поддерживается в .NET Compact Framework 2.0). В Compact .NET Framework 2.0 были добавлены новые элементы управления, которые перечислены в следующем списке. • MonthCal endar — месячный календарь, позволяющий в наглядном виде выбрать необходимую дату. Q DateTi mePi cker — элемент для выбора даты и времени. Он достаточ­
но компактен, что позволяет широко использовать его в приложе­
ниях. Q WebBrowser — элемент, который реализует функциональность браузера. • Noti f i cati on — элемент, с помощью которого приложение может посылать пользователю различные уведомления без остановки текущей запущенной программы. Уведомления могут отобра­
жаться как обычным текстом, так и в формате HTML. Q DocunentLi st — элемент управления, обеспечивающий стандартный механизм для управления файлами. Пример работы данного эле­
мента можно увидеть при открытии файлов в приложениях Excel Mobile и Word Mobile. Элемент DocumentLi st позволяет перемещать­
ся по файловой системе и выполнять стандартные файловые опе­
рации. Р DataGrid — элемент для отображения данных в табличном виде. Теперь может использоваться и в приложениях для смартфонов. • LinkLabel — элемент управления для создания гипертекстовых ссылок. Q Spl 1 tter — элемент управления, позволяющий изменять разме­
ры других элементов. О HardwareButton — элемент управления, позволяющий управлять кнопками карманного компьютера. ~Л 30 Глава 3, Элементы управления Урезанная функциональност ь элементов управления Кроме отсутствия некоторых элементов управления, в .NET Compact Framework также была урезана функциональность имею­
щихся элементов. Наиболее часто употребляемые элементы управ­
ления с урезанной функциональностью приведены в следующем списке: Q AcceptButton; a Cancel Button; D AutoScrol 1 (поддерживается в .NET Compact Framework 2.0); a Anchor (поддерживается в .NET Compact Framework 2.0); Q элементы Multiple Document Interface (MDI); • KeyPreview (поддерживается в .NET Compact Framework 2.0); a Tablndex (поддерживается в .NET Compact Framework 2.0); a TabStop (поддерживается в .NET Compact Framework 2.0). Также наложены ограничения на технологию drag and drop и на поддержку графики. Во многих классах поддерживаются не все свойства, события и методы. Однако в .NET Compact Framework 2.0 ограничений стало меньше. Например, элементы управления теперь обладают свойствами Tablndex и TabStop. Элемент Form Элемент Form является контейнером для элементов управления и является рабочей площадкой для создания пользовательского ин­
терфейса программы. Класс Form имеет несколько свойств, которые мо­
гут различаться в зависимости от выбранной целевой платформы. Свойство FormBorderStyle Свойство FormBorderSytle определяет стиль формы. По умолчанию используется стиль FormBorderStyle.FixedSingle. При этом форма заполняет все рабочее место экрана, и пользователь не может изме­
нять размеры формы или перемещать ее по экрану. При установке значения FormBorderStyle. None создается форма без рамки и заголов­
ка. В этом случае можно изменять размеры и расположение формы Элемент For m 3 1 программно, но пользователь по-прежнему не может манипулиро­
вать формой. Свойство ControlBox Свойство ControlBox отвечает за отображение контейнера для эле­
мента управления. Если свойство ControlBox имеет значение True, то контейнер будет отображаться. В противном случае он на экран не выводится. Для устройств Pocket PC подобный контейнер мо­
жет содержать только одну кнопку. Свойства MinimizeBox и MaximizeBox В приложениях для Pocket PC форма может содержать только одну кнопку. Она отвечает либо за минимизацию формы, либо за ее за­
крытие. Разработчик может управлять внешним видом кнопки при помощи свойства Mi ni mi zeBox. Если оно имеет значение True, то кноп­
ка при нажатии будет сворачивать форму. Значение Fal se позволя­
ет создавать кнопку закрытия формы. Значение свойства Maximi zeBox игнорируется системой. Свойство WindowsState Свойство WindowsState определяет состояние окна при первона­
чальной загрузке. Разработчик может использовать значения FormWindowState.Normal и FormWindowState. Maximi zed. Если свойство имеет значение FormWi ndowState. Normal то форма заполняет весь эк­
ран, за исключением нижней полоски меню и верхней полоски системного меню Start (Пуск). При использовании значения FormWi ndowState. Maximi zed форма заполняет экран полностью, скры­
вая системное меню Start (Пуск), но при этом нижняя полоса меню остается видимой. Размеры и расположение формы Свойство Size позволяет задавать размеры формы. Это свойство игнорируется, если свойство FormBorderStyle имеет значение Fi xedSi ngleProperty. Свойство Location задает координаты верхнего левого угла формы. Но так как форма обычно заполняет весь экран, то в большинстве случаев это свойство не используется. 32 Глава 3. Элементы управления Элементы управления В этом разделе будут рассмотрены основные элементы управления, которые используются для формирования пользовательского ин­
терфейса. Особое внимание будет уделено различиям и особенно­
стям поведения этих элементов. Элемент Button Для создания обычной кнопки используется класс System.Win­
dows . Forms. Button. Эта кнопка обладает всеми основными функци­
ями, которые есть у такого же класса в полной версии .NET Framework. Кнопка предназначена для обработки нажатия стилуса на соответствующую область экрана. В этом случае возникает со­
бытие Click. Код, приведенный в листинге 3.1, является обработчи­
ком этого события. Он выводит текущее время в текстовое поле после нажатия на кнопку с надписью Узнать время. Листинг 3.1 privat e voi d butGetTime_C1ick(objec t sender, EventArg s e) { txtCurTime.Text = DateTime.Now.ToLongTimeStringO: } Рисунок 3.3 показывает приложение в момент нажатия на кнопку. mrftnn. I«wt! В Т&*+. Текут4ае время 11:56:30 """••:-.•":•' •"':•':•:"'.:::-.'"'-'.-r::.i:t"''"r-S'.'J ! -..,•!-
Рис. 3.3. Результат нажатия н а кнопку Текст на кнопке может быть только однострочным. Если он не по­
мещается на кнопке, то будет обрезан. Поэтому нужно быть очень осторожным при выборе текста для кнопки. В следующей главе, посвященной улучшениям элементов управления, приведен при-
Элементы управления 3 3 мер создания кнопки с многострочным текстом, которая создается при помощи неуправляемого кода с использованием функций Windows API. Функциональность элемента управления Button очень сильно уре­
зана п о сравнению с полной версией .NET Framework. В частности, у данного элемента нет свойств Image и ImageList, которые применя­
ются для отображения на кнопке графики. Элемент TextBox В предыдущем примере дата отображалась в текстовом поле. Это поле создается при помощи класса TextBox, который позволяет вво­
дить текст. Данный элемент поддерживает такие стандартные свой­
ства, как BackColor и ForeColor. Событие CI ick элементом TextBox не поддерживается, но разработчик может воспользоваться события­
ми Keypress, KeyUp и KeyDown. Следует отметить особенность этого элемента. Несмотря на то что класс TextBox поддерживает свойство PasswordChar, при вводе пароля на экране всегда будет использовать­
ся символ звездочки. Задать другой символ не получится. Также текстовое поле не поддерживает свойство CharacterCasing, позволяющее в автоматическом режиме преобразовывать символы текста в нужный регистр. Впрочем, данный недостаток легко ис­
править, что иллюстрирует фрагмент кода, приведенный в листин­
ге 3.2. Листинг 3.2 privat e voi d txtCurTime_KeyPress(objec t sender. KeyPressEventArg s e) { if(Char.IsLetterCe.KeyChar) ) { // сохраняем текущую позицию каретки in t po s - txtCurTime.SelectionStart; // переводин в верхний регистр txtCurTime.Tex t - txtCurTime.Text. Insert(txtCurTime.Select i onSt a rt, Char.ToUpper(e.KeyChar).ToStr i ng()); // перенещаен каретку в новую позицию txtCurTime.SelectionStar t = po s + 1: е.Handled - true; > > 2-2873 ИР» 34 Глава 3. Элементы управления ПРИМЕЧАНИЕ У смартфонов внешний вид текстовых полей несколько отличается от стандартного вида. В частности, текстовое поле не имеет окантовки. Более подробно о текстовых полях в приложениях для смартфонов рассказывается в соответствующей главе. Элемент Label В рассмотренном примере также использовался элемент Label для отображения текстовой строки. Как правило, надпись использует­
ся для отображения некоторого текста, который пользователь не может изменить. Сама отображаемая строка задается при помощи свойства Text. Текст на экране можно выравнивать с помощью свой­
ства TextAl ign. Разработчик может использовать значения Topleft, TopCenter и TopRight. При изменении текста в метке инициируется событие TextChanged. При создании элемента нужно следить за дли­
ной отображаемой строки. Если текст слишком большой и не поме­
щается в пределах элемента, то он попросту обрезается. В отличие от полной версии .NET Framework, элемент Label в .NET Compact Framework не поддерживает такие свойства, как AutoSize, BorderStyl е, Image, ImageList и многие другие. Также не под­
держивается событие CI ick. Впрочем, на практике редко возникает нужда в обработке этого события. Элемент RadioButton Элемент управления RadioButton позволяет создавать переключа­
тели, объединенные в группы. Вся группа переключателей должна располагаться в контейнере. Примером такого контейнера может служить сама форма, но чаще используется элемент Panel. Когда пользователь выбирает один переключатель, то остальные переключатели в контейнере автоматически переводятся в выклю­
ченное состояние. Приложение может иметь несколько групп эле­
ментов RadioButton. В любом случае группы переключателей не за­
висят друг от друга. При изменении состояния переключателя в классе Radi oButton ини­
циируются события CI ick и CheckedChanged. Событие CI ick возника­
ет, когда пользователь щелкает стилусом на самом переключателе. Событие CheckedChanged возникает, когда состояние элемента RadioButton меняется программно или в результате действий пользо-
Элементы управления 35 вателя. Событие CI ick не инициируется, когда свойство CheckedChan-
ged меняется программно. Для демонстрации примера работы с элементом RadioButton можно создать аналог популярной телеигры «Кто хочет стать миллионером?». На экране будет отображаться вопрос, а пользователь должен выб­
рать из представленных вариантов единственньш правильный ответ. Код, реализующий основную функциональность приложения, приве­
ден в листинге 3.3. Листинг 3.3 privat e voi d radC1ubl_CheckedChanged(objec t sender. EventArg s e ) { i f (this.radClubl.Checked ) MessageBox.Show ("Увы, вы проиграли". "Ошибка!"); } privat e void radClub2_CheckedChanged(objec t sender. EventArgs e) { i f (this.radC1ub2.Checked ) MessageBox.Show ("Поздравляю! Вы выиграли миллион!", "Миллион!"): } privat e void radClub3_CheckedChanged(objec t sender. EventArgs e) { if (this.radClub3.Checked ) MessageBox.Sho w ("Увы, вы проиграли", "Ошибка!"); privat e voi d radClub4_CheckedChanged(objec t sender. EventArg s e ) { if (this.radClub4.Checked ) MessageBox.Sho w ("Увы. вы проиграли"/'Ошибка!"); } На рис. 3.4 показан внешний вид этого приложения. В полной версии .NET Framework в качестве контейнера для пере­
ключателей часто используется элемент GroupBox, который на дан­
ный момент не поддерживается в библиотеке .NET Compact 2* 36 Глава 3. Элементы управления Framework. Также не поддерживаются некоторые свойства, к кото­
рым относятся Appearance, Image и ImageList. Ra«o8utt.oft И ^"*; Кто стал чемпионом России по футболу а 2005 году? On* Поздравляю! Вы выдрали МИЛЛИ НI <§)ЦСКА О ^партах ОЭонит '-ЙШЬ^ЙЙЙ «Si- .,. „*шш Рис. 3.4. Демонстрация работы независимых переключателей Элемент Panel Элемент управления Panel используется в качестве контейнера для размещения других элементов управления. Так как .NET Compact Framework не поддерживает элемент управления GroupBox, то для группировки таких элементов, как переключатели Radi oButton, при­
ходится использовать именно Panel. В версии .NET Compact Framework элемент не поддерживает свой­
ства BorderStyle, BackGroundlmage и AutoScroll. Элемент CheckBox Элемент управления CheckBox позволяет создавать независимый пере­
ключатель в виде флажка. Элемент CheckBox имеет свойство CheckState, позволяющее определить состояние переключателя. Программист может использоваться значения Unchecked, Checked и Indetermi nate. Зна­
чение Unchecked свидетельствует о том, что флажок в переключателе не взведен. Если переключатель все же включен, то используется зна­
чение Checked. Но значение Indeterminate требует некоторых поясне­
ний. Состояние Indeterminate используется, когда для свойства ThreeState элемента CheckBox установлено значение True. Если свойство CheckState имеет значение Indeterminate, то элемент окрашен серым цветом, но, тем не менее, считается помеченным. При этом пользова­
тель не может изменить состояние переключателя. Элементы управления 3 7 Также элемент не распознает событие CI i ck, если свойство AutoCheck имеет значение False. Для этого свойства нужно задать значение True, чтобы пользователь мог пользоваться стилусом для работы с переключателем. Также элемент также не поддерживает некоторые свойства, в част­
ности, ImageIndex. Элемент ComboBox Элемент управления ComboBox позволяет создавать поле со списком выбора. Благодаря своей компактности этот элемент управления хорошо подходит для тех задач, когда требуется экономить место на экране. Поле со списком выглядит как обычное текстовое поле TextBox со стрелкой, которая расположена в правой части поля. Ког­
да пользователь щелкает по стрелке, то открывается список с пред­
варительно заданными элементами. Когда пользователь выбирает определенный пункт списка или снова щелкает по стрелке, то спи­
сок снова сворачивается. Добавлять текстовые элементы в ComboBox можно как в режиме про­
ектирования, так и программно во время работы программы. В листинге 3.4 приведен пример добавления пунктов программным путем. Для этого нужно вызвать метод Add в свойстве коллекции Items элемента ComboBox. Отдельные пункты можно удалять с помо­
щью метода Remove, а чтобы удалить все пункты сразу, применяется метод Clear. Приведенный пример показывает, как можно добавить три строки в элемент ComboBox с именем comboBoxl. Листинг 3.4 comboBoxl.Iterns.Add("Мурзик"); comboBoxl.Items.Add("Барсик"); comboBoxl.Iterns.Add("Рыжи к"); Чтобы узнать, какой элемент выбрал пользователь, применяет­
ся свойство Selectedlndex или Selectedltem. Свойство Selectedlndex возвращает порядковый номер выбранного пункта. Этот номер можно использовать для доступа к выбранному пункту при по­
мощи свойства Items. Следует помнить, что нумерация элемен­
тов начинается с нуля. Пример работы со свойством Selectlndex приведен в листинге 3.5. Также в этом листинге показано, как можно получить доступ к выбранному пункту при помощи свой­
ства Selectedltem. «,'лЬа 38 Глава 3. Элементы управления Листинг 3.5 // Получим выделенный пункт с помощью SelectedInde x strin g selltem = (stri ng ) cmbCats.Items[cmbCats.SelecteclIndex]; MessageBox.Show(selltem); // Второй способ - получим пункт с помощью Selectedlte m stri n g selltem - cmbCats.Selectedltem.ToStringO: MessageBox.Show(selItem); В полной версии .NET Framework у элемента ComboBox для свойства DropDownStyle можно задавать значения Simple, DropDownList или DropDown. В .NET Compact Framework значение Simple не использует­
ся. До выхода .NET Compact Framework 2.0 также не поддержива­
лось и значение DropDown. Кроме того, по умолчанию в .NET Compact Framework применяется значение DropDownList, тогда как в полной версии .NET Framework по умолчанию используется стиль DropDown. Также не поддерживаются многие методы из основной версии биб­
лиотеки. В .NET Compact Framework 2.0 у поля со списком появи­
лась поддержка методов BeginUpdate и EndUpdate, которые позволяют избежать мерцания при загрузке большого числа элементов. ВНИМАНИЕ Внешний вид и поведение элемента ComboBox в смартфонах немно­
го отличается от аналогичных элементов в КПК. Более подробно об отличиях будет рассказано в соответствующей главе. Элемент ListBox Элемент ComboBox хорош для приложений с ограниченными простран­
ствами формы, а список Li stBox можно использовать, если на экране достаточно места для отображения всех пунктов списка. Список Listbox сразу показывает все имеющиеся элементы списка, при не­
обходимости добавляя вертикальную полоску прокрутки, если все элементы списка не могут быть отображены одновременно. Элементы ComboBox и Li stBox имеют почти одинаковый набор свойств и методов. В листинге 3.6 показано, как можно программно доба­
вить несколько строк в список ListBox. Листинг 3.6 1stFruit.Items.Add("Яблоко"): lstFruit.Items.Add(Tpyuia H); I st Fr ui t.I t ems.Add("Слива"): 1stFruit.Items.Add("Персик"): Элементы управления „ _____ 39 Свойство Selected Index содержит порядковый номер выбранного элемента списка. Если указать этот индекс в коде приложения, то выбранный элемент будет немедленно выделен в списке соответ­
ствующим цветом. Если никакой элемент не выбран, то свойство Selectedlndex имеет значение -1. Также класс поддерживает свой­
ство Selectedltem, которое соответствует одноименному свойству класса ComboBox. Из часто используемых свойств элемента Li stBox в полной версии NET Framework можно выделить свойство MultiColumn, которое не поддерживается в .NET Compact Framework. В нем отсутствует го­
ризонтальная полоска прокрутки, даже если строки текста не уме­
щаются в списке полностью. Также не поддерживается многостроч­
ное выделение, поэтому пользователь может выбрать только один элемент списка. Элемент NumericUpDown Элемент Numeri cUpDown позволяет создавать счетчик с числовым полем ввода. Такой элемент интерфейса помогает пользователю быстро вы­
брать число из заданного диапазона. Элемент может работать только с целыми числа типа Integer. Десятичные значения округляются. Разработчик управляет поведением элемента NumericUpDown при по­
мощи свойств Minimum, Maximum, Value и Increment. Свойства Mini mum и Maxi mum определяют максимальное и минимальное значения элемен­
та. Свойство Value содержит текущее значение в поле ввода. Свой­
ство Increment определяет величину увеличения или уменьшения значения в поле, когда пользователь нажимает кнопки со стрелка­
ми. Текущее значение всегда увеличивается и уменьшается на зна­
чение свойства Increment, даже если результат выходит за диапа­
зон, определенный свойствами Minimum и Maximum. Пользователь также может изменить свойство Value, просто указав соответствующее значение в поле. Если это значение находится в ин­
тервале между Mi nimum и Maxi mum, тогда свойства Val ue и Text изменятся в соответствии с введенным значением. Если новое значение выходит за рамки заданных значений, то свойство Text отображает введенное число, а свойство Va 1 ue принимает значение, которое приписано свой­
ству Maximum Чтобы запретить пользователю указывать числа в поле ввода, нужно для свойства Readonly задать значение True. При изменении значения элемента NumericUpDown инициируется со­
бытие Va 1 ueChanged. Оно возникает только в том случае, если значе-
40 Глава 3. Элементы управления ние меняется программно или когда пользователь нажал кнопки со стрелками. При вводе числа событие не инициируется. В листинге 3.7 продемонстрирован пример использования элемента NumericUpDown и обработки события ValueChanged. Листинг 3.7 privat e voi d numericUpDownl_ValueChanged(objec t sender, EventArg s e) { i n t year • (int)this.numericUpDownl.Value: this.1ЫNote.Text = "Вы выбрали " + year.ToStringO + " год"; } На рис. 3.5 показано, как функционирует элемент NumericUpDown. (*№«гЮв*' Ш ?Н * Выберите гад Вы еыбрат i960 гад Рис. 3.5. Выбор года при помощи элемента NumericUpDow n При работе с элементом NumericUpDown следует учитывать одну осо­
бенность его функционирования. Предположим, пользователь на­
жимает кнопку со стрелкой вверх, постоянно увеличивая значение счетчика на величину свойства Increment. При достижении макси­
мального значения, определенного в свойстве Maximum, счетчик со­
хранит значение, которое не будет отображено на экране. Теперь, когда пользователь начнет уменьшать значения с помощью кнопки со стрелкой вниз, то отчет пойдет не от максимального значения, которое отображено в поле ввода, а от последнего значения перед достижением максимума. Стоит проиллюстрировать эту ситуацию. Итак, у нас установлено текущее значение, равное 1992. Значение свойства Increment равно 6, а максимум ограничен значением 2006. Последовательные нажа­
тия стрелки вверх доведут значение с 1992 до 2006. Итак, макси-
Элементы управления 4 1 мальное значение достигнуто. Теперь надо нажать кнопку с о стрел­
кой, направленной вниз. Казалось бы, на экране должно быть пока­
зано число 2000 (2006 - 6), но следует учитывать, что перед превы­
шением максимального значения счетчик запомнил число 2004. Именно от него будет отсчитываться разница, и на экране будет отображено число 1998. Элемент DomainUpDown Элемент DomainUpDown позволяет создавать счетчик с текстовым по­
лем ввода. Этот элемент похож на элемент NumericUpDown, а его функ­
циональность схожа с теми возможностями, которые предоставля­
ют ComboBox или ListBox. Но в элементе DomainUpDown вместо чисел используются строки. Этот элемент очень широко применяется для построения интерфейса, так как он весьма компактен и не занима­
ет много места на маленьком экране карманного компьютера. Сле­
дует учитывать, что пользователь не может увидеть весь список. Если свойство Readonly имеет значение True, то пользователь может выбирать только заранее заданные строки из списка. Если это свой­
ство имеет значение False, то пользователь сможет добавить свой текст в поле ввода. Впрочем, напечатанный текст все равно не вой­
дет в список. Так же как и элемент Numer i cUpDown, данный элемент управления со­
держит текстовое поле и две кнопки со стрелками с правой сторо­
ны. Пользователь может использовать эти стрелки для прокрутки списка строк или ввести в поле свой текст, если свойство Readonly имеет значение Fal se. При создании объекта свойство Selectedlndex имеет значение - 1, показывающее, что ни один элемент списка пока еще не выбран. Если нужно выделить тот или иной пункт списка при загрузке эле­
мента, то в свойстве Selectedlndex нужно указать соответствующий порядковый номер. В листинге 3.8 приведен пример, иллюстрирую­
щий программное добавление строк в список и методику обработ­
ки события SelectedltemChanged. Листинг 3.8 privat e voi d Forml_Load(objec t sender. System.EventArg s e) { domainUpDownl.Items.Add("Item 1"): domai nUpDownl.Items.Add("Item 2"); domai nUpDownl.Iterns.Add("Item 3"); domainUpDownl.Items.Add("Item 4"); продолжение & 42 Глава 3. Элементы управления Листинг 3.8 {продолжение) domainUpDownl.Readonl y - true; } privat e voi d domainUpDownl_SelectedItemChanged ( objec t sender. System.EventArg s e) { labell.Text - domainUpDownl.Selectedlndex.ToStringO; label2.Text * domainUpDownl.Items[domainL)pDownl.Selectedlndex].ToStringC): } Элемент ProgressBar Элемент управления ProgressBar предназначен для индикации процес­
са выполнения какой-либо операции. Как правило, данный элемент активно используется при выполнении долгих операций, чтобы пользователь получил иллюзию контроля над работой приложения. Чаще всего разработчик оперирует свойствами Mi ni mum, Maxi mum и Va 1 ue. Свойства Mi ni mum и Maxi mum задают минимальное и максимальное зна­
чения свойства Val ие. А свойство Val ие определяет текущее значение индикатора. Как правило, данный элемент отображается в момент начала дол­
гой операции, а после ее завершения делается невидимым с помо­
щью метода Hide или свойства Visible. Для демонстрации работы индикатора прогресса было создано при­
ложение, которое позволит отследить время варки яиц вкрутую. Предположим, что для варки достаточно трех минут. Нужно поло­
жить яйца в воду и запустить таймер. П о истечении трех минут при­
ложение должно отобразить соответствующее сообщение. Основ­
ной код приложения приведен в листинге 3.9. Листинг 3.9 privat e voi d tmrCook_Tick(objec t sender. EventArg s e) { i f (this.progressBarl.Valu e < this.progressBarl.Maximum ) { this.progressBarl.Valu e + - 1: 1Ы Counter. Tex t = th i s. progressBarl. Va l ue.ToStringO; } if (this.progressBarl.Valu e > = this.progressBarl.Maximum ) { tmrCook.Enable d - false; Элементы управления 4 3 } MessageBo x ShowC"Яйца сварились!"); this.progressBarl.Valu e - 0: 1ЫCounter.Text - "О": } privat e voi d butStart_Click(objec t sender. EventArg s e) { tmrCook.Enable d = true; } На рис. 3.6 показан внешний вид приложения в момент отсчета вре­
мени. туи Влркаяиц Рис. 3.6. Индикатор прогресса, позволяющий сварить яйца вкрутую Элемент StatusBar Строка состояния выглядит как небольшая полоска в нижней час­
ти приложения, в которой отображается текстовая информация для пользователя. Этот элемент интерфейса реализуется при помощи элемента StatusBar. Чтобы изменить текст в элементе StatusBar, до­
статочно присвоить новое значение свойству Text. На рис. 3.7 пока­
зан внешний вид приложения в тот момент, когда пользователь нажимает на кнопку, а в листинге 3.10 приведен пример кода, кото­
рый меняет текст в строке состояния. Листинг 3.1 0 privat e voi d butClickMe_Click(objec t sender, EventArg s e) { th1s.statusBarl.Tex t - "Вы нажали на кнопку"; } 44 Глава 3. Элементы управления stat....- aw Рис. 3.7. Пример работы со строкой состояния Строка состояния поддерживает только одну информационную панель, а также не распознает события Click. Элемент TrackBar Элемент управления TrackBar предназначен для установки число­
вого значения при помощи перемещения ползунка по числовой шкале. Основную работу с элементом разработчик выполняет при помощи свойств Minimum, Maximum и Value. Ползунок может распола­
гаться как вертикально, так и горизонтально. Ориентация ползун­
ка задается при помощи свойства Ori entati on. Свойство Ti ck Frequency регулирует дистанцию между метками шкалы. По умолчанию зна­
чение свойства TickFrequency равно единице. Свойства SmallChange и LargeChange определяют шаг изменения зна­
чения Value. Свойство SmallChange задает изменения основного зна­
чения, когда пользователь нажимает на одну из кнопок навигации на самом карманном компьютере или на смартфоне. Свойство LargeChange показывает, на сколько будет изменено основ­
ное значение, когда пользователь щелкнет стилусом на самом пол­
зунке. При изменении значения свойства Value инициируется со­
бытие ValueChanged. Для иллюстрации работы ползунка нужно создать новый проект и разместить на форме два элемента TrackBar. Один из них будет расположен горизонтально, а второй — вертикально. При этом по­
ложение ползунков на шкалах будет синхронизировано (рис. 3.8). В листинге 3.11 приведен код, отвечающий за функциональность ползунков. Элементы управления 45 Рис. 3.8. Пример работы с ползунками Листинг 3.1 1 privat e voi d trackVertVa l ueChanged < objec t sender. EventArg s e) { this.trackHoriz.Valu e = this.trackVert.Value; } privat e voi d trackHorizValue C hange d (objec t sender, EventArg s e ) { this.trackVert.Valu e - this.trackHoriz.Value: } Когда пользователь передвинет один ползунок, то второй ползунок автоматически будет переведен в то же положение, что и первый. Элемент ToolBar Элемент управления ToolBar позволяет создавать собственную па­
нель инструментов. Во многих случаях использование панели ин­
струментов может принести разработчику больше выгод, чем при­
менение меню. Следует учитывать, что панель инструментов позволяет использовать изображения, что делает работу с этим эле­
ментом удобным и наглядным. В .NET Compact Framework элемент Tool Ва г не может содержать текст. Все инструменты маркируются только при помощи графических изображений. Изображения свя­
зываются с элементом при помощи класса ImageLi st. В приложени­
ях для КПК панель инструментов всегда располагается в нижней части экрана справа от пунктов меню (рис. 3.9). Чтобы добавить в приложение элемент Tool Bar, нужно сначала пе­
реместить на форму элемент управления ImageLi st. Значок ImageLi st 46 Глава 3. Элементы управления появится в нижней части окна Form Designer рядом с элементом mainMenu. В окне редактора свойств Properties нужно выбрать свой­
ство Images и нажать кнопку редактирования. В результате будет открыто диалоговое окно Image Collection Editor. „•човювг.м™ Ш 'd"i-
Г^ичвр работы с контекспым меню © Праги*"** ;«»*к«*:-
Рис. 3.9. Панель инструментов в приложении В этом окне следует добавить изображения, предназначенные для панели инструментов. Рекомендуется использовать картинки размером 16 х 16 пикселов, чтобы при их отображении не воз­
никло искажений. Выбранные изображения включаются в состав приложения, и их не придется отдельно поставлять вместе с про­
граммой. Теперь на форму надо перенести элемент Tool Ваг. В его свойстве ImageList надо указать имя добавленного ранее элемента ImageList. В рассматриваемом примере использовалось имя по умолчанию ImageLi stl. Затем надо перейти к свойству Buttons и активировать окно редактора ToolBarButton Collection Editor. Так как было добавлено три изображения для кнопок, то нужно три раза нажать кнопку Add. Для каждой добавленной кнопки следует задать свойство Imagelndex. При необходимости разработчик может изменить стиль отображения кно­
пок. По умолчанию используется стиль PushButton, который создает обычные кнопки. Если для кнопки задать стиль DropDown8utton, то при ее нажатии будет отображаться меню или другое окно. Справа от кнопки на панели инструментов отображается стрелка. Стиль Separator позволяет создавать разделители между кнопками на панели управления. Стиль ToggleButton позволяет создавать пе­
реключаемую кнопку. При первом нажатии она переходит в акти­
вированное состояние, в котором и остается до тех пор, пока кноп­
ку не нажмут повторно. Элементы управления 4 7 Итак, панель инструментов уже готова, хотя еще не было написано ни одной строчки кода. При щелчке на кнопках элемента Tool Ваг возникает событие ButtonCl ick. В листинге 3.12 приведен код, обра­
батывающий нажатие первых двух кнопок. Листинг 3.1 2 privat e voi d toolBar l ButtonClick(objec t sender. ToolBarButtonCl i ckEventArg s e) { if (e.Butto n -= this.toolBarButtonl ) { MessageBox.ShowCBbi выбрали первую кнопку"); } else i f (e.Butto n — this.toolBarButton2 ) { MessageBox.ShowCBb i выбрали вторую кнопку"); } } Элемент MainMenu Меню является одним из самых важных элементов графического ин­
терфейса Windows-приложений. Не являются исключением и про­
граммы для мобильных устройств. П о умолчанию н а форме уже при­
сутствует элемент Mai nMenu. Но при добавлении в проект новой формы на ней меню н е появляется, и его нужно добавить вручную. Следует помнить, что в приложениях для Pocket PC меню распо­
лагается в нижней части окна программы, тогда как в приложениях для обычных компьютеров меню располагается в верхней части окна. Если в программе одновременно присутствуют меню и панель инструментов, то они будут отображаться вместе. Но меню будет прижато к левой границе окна, а панель инструментов — к правой. Пример работы с меню приведен в листинге 3.13. Листинг 3.1 3 privat e void mnuAbout_Click(objec t sender, EventArgs e) { MessageBox.Show("3TO ноя програниа!"); } privat e voi d mnuExit_Click<objec t sender, EventArg s e) { this.CloseO; } .л.*шШш 48 Глава 3. Элементы управления Элемент ContextMenu Элемент ContextMenu позволяет создавать контекстные меню для других элементов интерфейса. Этот элемент очень похож на эле­
мент управления Mai nMenu. Но если Mai nMenu всегда связан с формой приложения, то ContextMenu можно связать с любым элементом фор­
мы. Так как в КПК не используется мышь, то вызов контекстного меню вызывается операцией tap-and-hold вместо привычного щел­
чка правой клавишей мыши. ВНИМАНИЕ Если вы пользуетесь эмулятором, то для имитации tap-and-hold нуж­
но щелкнуть левой кнопки мыши и не отпускать ее некоторое время. Чтобы добавить элемент ContextMenu в приложение, нужно сначала переместить его значок на форму. Он появится в нижней части ре­
дактора Form Designer, там же, где и элемент MainMenu. Но на самом деле во время выполнения программы контекстное меню будет ото­
бражаться рядом с выбранным элементом интерфейса. Также кон­
текстное меню можно создавать программно во время запуска при­
ложения. При вызове контекстного меню инициируется событие Popup. Когда пользователь выбирает какой-то пункт меню, то возникает событие CI ick. Чтобы привязать созданное контекстное меню к конкретному элементу интерфейса, нужно выбрать его на форме и в свойстве ContextMenu указать созданное контекстное меню. ПРИМЕЧАНИЕ До выхода .NET Compact Framework 2.0 элемент управления ContextMenu не поддерживал свойство ContextMenu.SourceControl. Элемент Timer Элемент Timer позволяет выполнять некоторые действия по исте­
чении заданных интервалов времени. Чаще всего для работы с тай­
мером разработчик применяет событие Tick. Данное событие ини­
циируется только в том случае, если свойство Enabled имеет значение True. Если нужно остановить таймер, то достаточно при­
своить данному свойству значение False. Интервал отсчета времени задается свойством Interval, а его зна­
чение указывает используемый промежуток времени в миллисекун-
Элементы управления 4 9 дах. Если рабочий интервал таймера должен составлять 3 с, то надо установить значение 3000. Этот элемент управления уже применялся при работе с объектом ProgressBar. Элементы OpenFileDialog и SaveFileDialog Практически в каждом приложении пользователь должен иметь воз­
можность сохранить файл или открыть его. Разработчикам регуляр­
но приходится реализовывать подобную функциональность в своих программах. При желании можно самому придумать и разработать интерфейс для подобной задачи. Но можно воспользоваться и стан­
дартными диалоговыми окнами открытия и сохранения файла. Именно для этого применяются элементы управления OpenFi leDialog и SaveFileDialog. К сожалению, в версии .NET Compact Framework возможности данных элементов управления серьезно урезаны. Раз­
работчик может манипулировать файлами только в пределах папки My Documents и вложенных папок следующего уровня. Поэтому пап­
ка My Documents\Programming\Sample будет уже недоступна. Рассматриваемые элементы управления размещаются в нижней части дизайнера формы рядом с элементом Mai nMenu. При работе с данными элементами прежде всего надо позаботиться о свойстве Filter, которое ограничивает список доступных файлов, фильт­
руя их по расширению. Свойство InitalDirectory содержит имя папки, в которой по умолчанию располагаются файлы. Если это свойство оставить пустым, то обзор файлов начнется с самой пап­
ки My Documents. Основным методом для этих элементов является ShowDi al од. После его вызова на экране отображается модальное окно, в котором пользова­
тель должен нажать кнопку 0К или Cancel. При этом метод ShowDi al од возвращает значения Dialog.Result.OK и DialogResult.Cancel соответ­
ственно. Если получено значение DialogResul t. OK, то пользователь на­
жал кнопку 0К и в свойстве Filename содержится полный путь к вы­
бранному файлу. Пример работы с элементами OpenFileDialog и SaveFileDialog при­
веден в листинге 3.14. Листинг 3.1 4 private void butOpen_Click(objec t sender, EventArgs e) { ofd.Fi l ter - "DLL|*.dll|Картинки|*.jpg": продолжение & 50 Глава 3. Элементы управления Листинг 3.14 (продолжение) ofd. I ni t i a l Director y - "WMy Documents\\Templates n; i f (DialogResult.OK «» ofd.ShowDialogO) { statusBarl.Tex t • ofd.FileName: else { statusBarl.Tex t e "Вы нажали на кнопку Отмена!": } } Элементы HScrollBar и VScrollBar Элементы управления HScrol 1 Ваг и VScrol 1 Ваг позволяют создавать полосы прокрутки для элементов, которые изначально не облада­
ют этой функциональностью. Пользоваться этими полосами про­
крутки совсем не сложно. Свойство Minimum задает значение эле­
мента, когда ползунок находится в крайней левой или в крайней верхней позиции, для HScrollBar или VScrollBar соответственно. Свойство Maximum, задает максимальное значение для полос про­
крутки. Значение свойства Val ue зависит от положения ползунка. Оно всегда находится в диапазоне между значениями свойств Minimum и Maximum. Когда пользователь щелкает на полосе прокрутки, то свойство Va 1 ue изменяется в соответствии со значением, заданным в свойстве LargeChange. Когда пользователь нажимает на кнопку навигации со стрелкой, то свойство Val ue изменяется в соответствии со значени­
ем, заданным в свойстве SmallChange. Следует обратить внимание на то, что если ползунок находится в положении, определяемом свойством Maximum, то свойство Value не равно значению Maximum. В этом случае значение свойства Value вычисляется по формуле Maximum - LargeChange+ 1. При изменении свойства Val ue инициируется событие ValueChanged. В листинге 3.15 приведен пример работы с полосами прокрутки. Листинг 3.15 privat e voi d vScrollBarlValueChange d (objec t sender. EventArg s e ) { thi s.l bl Scrol l.Tex t - this.vScrollBarl.Value.ToStringC); } Элементы управления 51 На рис. 3.10 показан внешний вид приложения. Если переместить ползунок в нижнюю часть полосы прокрутки, то значение в соот­
ветствии с формулой будет равно 91. ОУДОиэа О Рис. 3.10. Пример работы с полосами прокрутки Список рисунков (ImageList) Элемент управления ImageLi st уже рассматривался при знакомстве с элементом Tool Bar. Элемент ImageList используется для хранения коллекций растровых изображений. Как и многие другие элемен­
ты, список рисунков не отображается во время выполнения про­
граммы, а используется как контейнер, из которого по мере необхо­
димости извлекаются хранимые изображения. Как правило, данный элемент используется совместно с такими элементами управления, как ListView, TreeView и Tool Ваг. Изображения можно добавлять в элемент управления во время ра­
боты приложения. Для этого используется метод Add, который вхо­
дит в состав члена класса Images. Сами картинки могут располагать­
ся как в отдельных файлах, так и в ресурсах приложения. В листинге 3.16 показано, как можно добавить картинку из ресурсов в ImageLi st, а затем отобразить ее в элементе интерфейса PictureBox. Листинг 3.1 6 Bitmap image - new Bitmap(Assembly.GetExecutingAssembly(). GetMani festResourceStream(@"ImageL i st_CS.home.gi f") ): imgList.Images.Add(image); picTest.Imag e - imgList.Images[0]: Изображение добавляется в начало списка, и его порядковый но­
мер будет равен нулю. Если в ImageList уже было одно изображе-
52 Глава 3» Элементы управления ние, то новая картинка будет иметь порядковый номер, равный еди­
нице. Это иллюстрируется листингом 3.17. Листинг 3.17 privat e voi d butFrcjnImageL1st_C"lick ( objec t sender, EventArg s e) { picTest.Image - imgList.Images[l]; } Все картинки, находящиеся в ImageList, имеют одинаковый размер. По умолчанию используется размер 16x16 пикселов. Разработчик может изменить размеры изображений, используя свойство ImageSi ze. Если менять отображаемые картинки при помощи таймера, то мож­
но даже создать небольшую мультипликацию. Для этого достаточно список рисунков заполнить набором изображений, а затем поочеред­
но отображать их в графическом поле. Элемент PictureBox Элемент управления PictureBox используется для отображения гра­
фики. Данный элемент имеет ограниченную функциональность и не позволяет растягивать картинку в соответствии с размерами графи­
ческого поля. В листинге 3.18 приведен фрагмент кода, который позволяет загру­
зить изображение из графического файла. Листинг 3.18 privat e voi d butFromF i "I e_Click < objec t sender. EventArg s e) { picTest.Image - new Bitmap^XWi ndows\banner.gif"); } Если использовать этот способ для добавления картинки, то нужно добавить изображение в проект и для свойства Bui Id Action в окне свойств Properties задать значение Content. В процессе подготовки приложения к инсталляции изображение будет рассматриваться как часть программы. В рассмотренном примере использовалась готовая картинка, которая находится в папке Windows. Также можно загрузить изображение из ресурсов приложения. В этом случае надо добавить картинку в проект и для свойства Build Action задать значение Embedded Resource. Тогда не придется специально вклю­
чать изображения в состав инсталлятора. В листинге 3.19 приведен пример, иллюстрирующий добавление изображения из ресурсов. Элементы управления 53 Листинг 3.1 9 pri vat e voi d butRes_Click(objec t sender. EventArgs e) { // Загружаем из ресурсов picTest.Imag e = ne w Bitmap(Assembly.GetExecutingAssembly(). GetMan i festResourceStream ( "PictureBoxCS. krist i na. jpg")); } Элемент ImageList имеет свойство ImageSize, которое задает разме­
ры хранимых изображений. Перед загрузкой картинки в графичес­
кое поле можно установить требуемые размеры изображения с по­
мощью данного свойства, как показано в листинге 3.20. Листинг 3.2 0 privat e voi d butImgList_Click(objec t sender, EventArg s e) { // изменяем размеры картинки imageListl.ImageSiz e = ne w System.Drawing.Size(160, 120); // загружаем картинку с измененными размерами picTest.Imag e = imageListl.Images[0]: } На рис. 3.11 показан внешний вид приложения, в котором для ра­
боты с изображениями применяются все три описанных варианта. И Tfr^fco s w из ШйЙШ»^1пми<<й^ Рис. 3.11. Пример работы с элементом PictureBox Элемент ListView Элемент управления ListView похож на элемент ListBox, но вместо обычного текста данный элемент может показывать изображения. Фактически, правая часть рабочего окна Проводника в Windows ХР является типичным примером использования этого органа уп-
54 Глава 3. Элементы управления равления. Элементы в ListView могут отображаться в виде табли­
цы, списка, а также как группа крупных и мелких значков. За спо­
соб отображения содержимого отвечает свойство View. Значение Details позволяет отображать содержимое в виде таблицы, значе­
ние List создает список, значение Largelcon позволяет отображать элементы списка в виде больших пиктограмм, а значение Smal 1 Icon отображает их как маленькие пиктограммы. В режиме Detai 1 s элемент управления Li stVi ew позволяет создавать дополнительные столбцы. Их можно добавлять как во время про­
ектирования, так и во время исполнения программы. Пример до­
бавления столбцов во время работы приложения приведен в лис­
тинге 3.21. Листинг 3.2 1 privat e voi d ForralLoad C objec t sender. EventArg s e) { // Устанавливаем нужный вид listViewl.Vie w - View.Details; // Выделяем всю строку при выделении любого элемента listViewl.FullRowSelec t = true; ColumnHeade r columnHeader l - ne w ColumnHeader O ColumnHeade r columnHeader 2 - ne w ColumnHeader O ColumnHeade r columnHeader 3 - ne w ColumnHeader O columnHeaderl.Tex t - "Фамилия"; columnHeader2.Tex t =• "Имя"; columnHeader3.Tex t = "E-mail"; 1i stViewl.Columns.AddCcolumnHeaderl); 1istViewl.Columns.AddCcolumnHeader2): 1i stViewl.Columns.AddCcolumnHeader3): ListViewlter n Contact l = ne w ListViewltem("Иванов"); Contactl.Subltems.AddCMBaH"); Contactl.Subltems.AddC"ivan@ivanov.ru"); ListViewIte m Contact 2 = ne w ListViewltem("Петров"); Contact2.Sublterns.AddC"Петр"); Contact2.Sublterns.AddC"peter@petrov.ru"); ListViewIte m Contact 3 - ne w ListViewItemC"Сидоров"): Contact3.Sublterns.AddC"Арнольд"); Элементы управления 55 Contacts.Subltems.Add("goatPsidorov.ru"); 1istViewl.Items.Add(Contactl); 1 istViewl.Items.Add(Contact2); 1istViewl.Items.Add(Contact3); } На рис. 3.12 показан внешний вид приложения со списком в виде таблицы. и***» Q ?|Ц*5-19 О Иванов Иван KanOWvanav.r u Петров Петр peter<bpetrov.n j Сидоров Арнопм ooatCKidorov.r u Рис. 3.12. Пример работы с элементом ListView В полной версии .NET Framework элемент управления Li stVi ew под­
держивает свойство MultiSelect, позволяющее одновременно выб­
рать несколько элементов из списка. Версия .NET Compact Frame­
work не поддерживает данное свойство, поэтому пользователь мо­
жет выбрать только один элемент. Элемент TabControl Элемент управления TabControl очень удобен при создании интер­
фейсов для устройств с малыми размерами экрана, так как он позво­
ляет создавать многостраничные диалоговые окна. Вкладки, реали­
зуемые этим элементом, имеют ярлычки, на которых отображаются заголовки страниц. И пользователь может легко переключаться меж­
ду страничками, просто щелкая по этим ярлычкам. В устройствах Pocket PC вкладки располагаются в нижней части окна. Следует обратить внимание на то, что элемент TabControl все­
гда располагается в верхнем левом углу контейнера. Например, если поместить TabControl на форму, то он появится в ее верхнем левом углу. Если же нужно изменить расположение этого элемента, то надо 56 Глава 3. Элементы управления поместить его на панель, которая является контейнером. При пере­
мещении панели будет перемещаться и TabControl. Элемент TabControl следует расположить на форме. У него по умолча­
нию будут созданы вкладки tabPagel и tabPage2. Если нужно добавить новую вкладку, то следует щелкнуть на маленькой стрелке в верхней части элемента TabControl и выбрать пункт меню Add Tab (рис. 3.13). TabContro l JCS - Mi crosof t Visual Siudr a data forma t Tooe Windo •: :; . .."." ' :•:•: • •'•:• ••••-••"• ' ::• ' *П«Й1МвНЙ Рис. 3.13. Добавление новой закладки в элементе TabControf В результате у элемента TabControl появится новая закладка, которую можно настроить в соответствии с потребностями разработчика. Так­
же программист может воспользоваться услугами редактора TabPage Collection Editor для добавления новых закладок. В этом случае надо выбрать элемент TabControl в дизайнере формы, найти свойство Та bPages и нажать кнопку редактирования этого свойства. В результате будет открыт редактор закладок. Для управления закладками можно также выделить TabControl, щелкнуть на нем правой кнопкой мыши и вы­
брать пункты контекстного меню Add Tab или Remove Tab. Элементы управления 57 Для определения текущей вкладки используется свойство Select-
Index. При изменении данного свойства инициируется событие Sel ectedlndexChanged, что иллюстрирует код, приведенный в листин­
ге 3.22. Листинг 3.2 2 privat e voi d tabControll_SelectedlndexChanged(objec t sender, EventArg s e) { switc h (th i s.tabControl1.Selectedlndex ) { cas e 0: MessageBox.ShowC'B w выбрали первую вкладку"); break: cas e 1: MessageBox.ShowCBb t выбрали вторую вкладку"); break; cas e 2: MessageBox.ShowCBb i выбрали третью вкладку"); break; } } Элемент TreeView Элемент управления TreeVi ew позволяет представить данные в иерар­
хическом виде. Именно в этом виде отображается структура дисковой системы в левой части рабочего окна программы Проводник Windows. Основой элемента TreeView являются объекты TreeNode и Nodes. При работе с TreeView также широко используется элемент управления ImageLi st, используемый как хранилище изображений для узлов. Заполнять древовидную структуру можно как на этапе конструи­
рования формы, так и в о время выполнения программы. Для созда­
ния дерева в дизайнере формы нужно переместить на нее элемент TreeView. Затем следует выбрать свойство Nodes и запустить редак­
тор TreeNode Editor. Кнопка Add Root отвечает за создание узлов де­
рева. Кнопка Add Child позволяет добавить дочерний узел к выбран­
ному узлу. Кнопка Delete удаляет выбранный узел. Чтобы задать текст, отображаемый в узлах, можно использовать свой­
ство Text Также в узлах можно использовать изображения, для чего применяется комбинация элемента управления ImageLi st и свойства Sel ected ImageLi st. Для определения текущего узла используется свой­
ство SelectedNode. 58 Глава 3. Элементы управления На рис. 3.14 показан внешний вид приложения, использующего эле­
мент интерфейса TreeVi ew. В ЧЛ 4i 12:3t © ЕЗЯЭИЭЯЗЯ ф Узел с дочерними узпами :- Пэостоуэвп Рис. 3.14. Использование элемента TreeView Элемент InputPanel Элемент управления InputPanel позволяет вводить текстовую ин­
формацию при помощи виртуальной клавиатуры или панели рас­
познавания знаков SIP (Soft Input Panel). Так как в полной версии .NET Framework данного элемента нет, то стоит рассмотреть его несколько подробнее. Как правило, в карманных компьютерах нет клавиатуры, поэтому для ввода данных используется виртуальная клавиатура. В обыч­
ном состоянии она неактивна и находится в свернутом состоянии. Чтобы ее активировать, нужно щелкнуть стилусом по значку кла­
виатуры в нижнем правом углу экрана, где располагается меню или панель инструментов Tool Ваг. Тогда виртуальная клавиатура по­
явится на экране, и пользователь сможет вводить текст. Разработчик может программно управлять состоянием клавиатуры. Например, с помощью элемента InputPanel можно узнать текущее состояние SIP, возможность ее отображения и деактивации. Свойств и методов у элемента InputPanel не так уж много, но наиболее часто используемые члены класса приведены в следующем списке. • Bounds — прямоугольник, определяющий размеры и позицию SIP. О VisibleDesktop — прямоугольная часть экрана, на которой не отображается SIP. • Enabl ed — возможность работы с SIP. • Enabl edChanged — событие, возникающее при изменении состоя­
ния SIP. Элементы управления 5 9 Свойства Bounds и Vi sibleDesktop доступны только для чтения и оп­
ределяют прямоугольники, п о которым можно судить о положении SI P и размерах клиентской области, не занятой SIP. Свойство Vi si Ы eDesktop определяет прямоугольник, которым ограничена об­
ласть экрана, не закрытая SIP. Когда виртуальная клавиатура ото­
бражается, то условный прямоугольник поднимается от полоски навигации над окном SIP. Когда SIP скрыт, то прямоугольник за­
нимает все пространство экрана. Свойство Bounds описывает размеры и позицию виртуальной клавиа­
туры, когда она отображается на экране. Причем размеры этого пря­
моугольника не меняются, даже если виртуальная клавиатура скрыта. Свойство Enabled имеет значение True, если виртуальная клавиату­
ра отображается на экране. Значение свойства можно задавать про­
граммно, скрывая или отображая клавиатуру. Если в приложении необходимо использовать элемент InputPanel, нуж­
но следить, чтобы он при активации н е загородил элементы управле­
ния н а форме, иначе пользователь просто не сможет ввести данные. Можно размещать текстовые поля на форме как можно выше, что­
бы элемент InputPanel не мешал вводить данные. Но помимо этого можно отслеживать состояние виртуальной клавиатуры и при ее отображении передвигать вверх поля для ввода информации. В этом случае чаще всего приходится применять полосы прокрутки. Мож­
но проиллюстрировать такое поведение примером, в котором эле­
мент InputPanel будет активироваться, если текстовое поле полу­
чит фокус. А когда TextBox потеряет фокус, то клавиатура должна исчезнуть. Эта функциональность реализуется кодом, приведенным в листинге 3.23. Листинг 3.2 3 privat e voi d txtTest_GotFocusСobject sender, EventArg s e) { // Когда пользователь выбирает текстовое поле. // то автоматически активируем SI P inputPanell.Enable d - true; } privat e voi d txtTest_LostFocus(objec t sender, EventArg s e) { // При потере фокуса сворачиваем SIP inputPanell.Enable d - false; 60 Глава 3. Элементы управления В этом примере пришлось добавить на форму кнопку, иначе тек­
стовое поле будет автоматически получать фокус при загрузке фор­
мы и приложение не будет работать так, как это задумывалось из­
начально. В данном случае именно кнопка будет получать фокус ввода при загрузке формы. Но для этого надо присвоить свойству кнопки Tablndex нулевое значение. Теперь если стилусом активировать текстовое поле, то автоматиче­
ски будет отображена виртуальная клавиатура. Если на появившей­
ся клавиатуре нажать клавишу Ta b или щелкнуть на кнопке с надпи­
сью Просто кнопка, то панель ввода автоматически свернется. [Чзы-скок ЦЗ| 1| 2| 3| 4| 5| 6| 7| 9| 9| 0[ - = » TatolqM»! МЧУ l ull | о |F [ I CAPlalsldlf l Ol MUl cl l TT SMrt|i|K|c|v|b|nlm | , j. |/ Рис. 3.15. Пример работы с элементом InputPanel Элемент InputPanel поддерживает событие EnabledChanged, которое возникает при активации и деактивации панели ввода. С его помо­
щью можно расширить функциональность примера. Следует доба­
вить еще одно текстовое поле в нижнюю часть формы. Сейчас нужно отследить событие Enabl edChanged и отреагировать на него должным образом. При активации панели ввода текстовое поле должно сдви­
нуться вверх, чтобы пользователь мог ввести свои данные. Пример обработки этого события приведен в листинге 3.24. Листинг 3.2 4 privat e voi d inputPanell_EnabledChanged(objec t sender, EventArg s e ) { // Отслеживаем состояние панели ввода // Свойство Bound s возвращает разнеры и позицию SI P i f (inputPanell.Enable d — true ) Элементы управления 61 this.txtJump.To p = 200 - inputPanell.Bounds.Height; else this.txtJump.To p = 200: } На рис. 3.15 показан внешний вид окна тестового приложения. В этом примере позиция текстового поля была подобрана опытным путем, но в реальных проектах разработчик может программно вы­
числить высоту формы, панели ввода, текстового поля и других элементов формы, чтобы более точно определить позицию сдвига. Элемент управления DataGrid Элемент управления DataGri d позволяет отображать данные в виде таблицы, как это сделано в электронной таблице M S Excel. Ка к и мно­
ги е другие элементы управления, он имеет обрезанные возможности по сравнению с полной версией .NET Framework. Например, отклю­
чена поддержка свойства DataMember. Элемент управления DataGrid может быть связан с источниками данных при помощи свойства DataSource. Рассмотрим простейший пример работы с данным элементом. Прежде всего, потребуется создать XML-файл, содержащий некоторые данные. Для примера был использован файл artists.xmL, в котором содержится информа­
ция о некоторых известных артистов шоу-бизнеса. Файл содержит записи о фамилии, дате рождения и адресе проживания. Создан­
ный файл нужно добавить в проект, расположить на форме элемент DataGrid и присвоить ему имя grdArtists. В листинге 3.25 приведен код обработчика события Forml_Load. Листинг 3.25 privat e voi d FormlLoad C objec t sender. EventArg s e) { Cursor.Curren t - Cursors.WaitCursor; tr y { // Загружаем данные DataSe t DataSe t d s * ne w DataSetО: ds.ReadXml(@"\Progra m Files\DataGrid_CS\artists.xml"); grdArtists.DataSourc e - ds.Tables[0]; } catc h (Exception ) { продолжение & 62 Глава 3. Элементы управления Листинг 3.2 5 (продолжение) MessageBox.ShowC'He ногу загрузить данные в DataGrid!", thi s.Text): • } // Устанавливаем стили DataGridTableStyl e ts = new DataGridTableStyleO; ts.MappingName * "Order"; DataGridColumnStyl e artistDat e - new DataGridTextBoxColumn(); artistDate.MappingNara e » "BirthDate"; artistDate.HeaderTex t s "Дата рождения"; ts.Gr i dColumnStyles.Add(art i stDate): DataGridColumnStyl e artistName - new DataGri dTextBoxCol uranO: artistName.MappingNam e = "ArtistName"; artistName.HeaderTex t - "Артист"; artistName.Widt h - this.Widt h - artistDate.Widt h - 35; ts.GridColumnStyles.Add(artistName); grdArtists.TableStyles.Add(ts); Cursor. Cur rent - Cursors. Default; } В данном примере из XML-файла извлекаются данные, относящи­
еся к фамилии артиста и дате его рождения. Остальная информа­
ция игнорируется. При загрузке приложения в элемент DataGri d за­
писываются требуемые данные. На рис. 3.16 показан внешний вид приложения. • VJ4KID15 О И*Пуг""—* 31-0J- W ПАаЛеулв 07-08-4 7 СРотврг 01-04-1 0 МДмксои 27-11-5 3 Е.Грвбвтггов гэ-омз IJ4MM N 24-054 5 AEyfeoe Рис. 3.16. Пример работы с элементом DataGrid Элементы управления 63 Также стоит прочитать статью «Using the DataGrid Control in Pocket PC Applications», которую можно найти в справочной сис­
теме MSDN. В этой статье иллюстрируются различные приемы про­
граммирования, которые помогут расширить возможности данно­
го элемента. Элемент Splitter Элемент управления Splitter появился только в .NET Compact Framework 2.0. В предыдущей версии его не было. Этот элемент реализует разделитель, который используется для изменения размеров закрепленных элементов управления во время выпол­
нения программы. Элемент Splitter обычно используется вмес­
те с элементами управления, содержащими данные переменной длины. Стоит рассмотреть работу данного элемента на конкретном при­
мере. На форме следует расположить графическое поле Pi ctureBox и присвоить его свойству Dock значение Тор. Затем на форме надо расположить объект Splitter и его свойству Dock тоже присвоить значение Тор. Также следует расположить на форме текстовое поле TextBox. Его свойству Multiline надо присвоить значение True, а свойству Dock — значение Fi 11. Внешний вид получившегося при­
ложения показан на рис. 3.17. :ен мот Рыжж. готорьй ж we t вместе с автором »*гм под одной фышай Рис. 3.17. Применение элемента Splitter в приложении Даже без единой строчки написанного кода запущенное прило­
жение будет вполне функционально. Если нужно увеличить об­
ласть текстового поля для ввода новых данных, то достаточно на­
жать стилусом на разделителе и передвинуть его чуть выше. 64 Глава 3. Элементы управления Элемент MonthCalendar Элемент управления MonthCalendar появился только в последней версии .NET Compact Framework 2.0. Данный элемент позволяет легко выбрать необходимую дату. Для создания тестового приложения на форме надо разместить эле­
менты MonthCal endar и Label. Метка должна получить им я 1 bl Sel ectDate, а для свойства Text нужно задать значение Выбранная дата. Затем следу­
ет дважды щелкнуть на элементе monthCalendarl, чтобы задать код об­
работчика события DateChanged. Этот код приведен в листинге 3.26. Листинг 3.2 6 privat e voi d monthCalendarl_DateChanged<objec t sender, DateRangeEventArg s e) { lblSelectDate*Tex t - "Выбранная дата: " + monthCalendar1.SelectionStart.ToShortDateStringC); } Теперь можно запустить приложение и выбрать любую дату из ме­
сячного календаря. Выбранная дата будет автоматически отобра­
жаться в надписи lbl Sel ectDate, как показано на рис. 3.18. , -ЧопНы. itend. Q ^ Щ 1&2 * О I 2S гб 27 2G ЭТ 30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 2 б Щ2 в 29 30 31 1 Z 3 4 S Today: 5/16/06 еь?р»+чя дат* 7/27/0 6 Рис. 3.18. Выбираем дату и з месячного календаря Элемент DateTimePicker Элемент управления DateTimePicker также является новым элемен­
том, который появился в .NET Compact Framework 2.0. Он позво­
ляет выбирать не только дату, но и время. На форме надо разместить элемент DateTimePicker и две текстовые метки Label, в которых будут отображаться дата и время. Затем нуж-
Элементы управления 65 но дважды щелкнуть на элементе DateTimePicker, чтобы задать код обработчика события Va 1 ueChanged. Этот код приведен в листинге 3.27. Листинг 3.2 7 privat e voi d dateTiraePickerlValueChanged(objec t sender. EventArg s e) { IblDate.Text = "Дата: " + dateTi mePi cker1.Value.ToShortDateStr i ng(): IMTime.Text • "Время: " + dateTimePickerl.Value.ToShortTimeStrtngC): } Внешний вид получившегося приложения показан на рис. 3.19. •TiroePlcke» Q Ъ Н - 10(31 © КГЕЗЕ . xiy 27,гоое* Дата: 7/27/06 Время: 10:30 AM Рис. 3.19. Выбор даты при помощи элемента DateTimePicke r В документации MSDN есть небольшая статья «How to: Use the DateTimePicker Class in the .NET Compact Framework», в которой приводится небольшой пример использования этого элемента. Под­
держка данного элемента появилась и в смартфонах под управле­
нием Windows Mobile 5.0. Но в этом случае внешний вид элемента будет несколько иным. Элемент DocumentList Новый элемент управления DocumentList, который появился в .NET Compact Framework 2.0, может заменить такие элементы, как SaveFi 1 eDi al од и OpenFi 1 eOi al og, так как имеет все необходимые сред­
ства для работы с файлами. Помимо этого элемент DocumentLi st име­
ет дополнительные возможности, которые наверняка понравятся разработчикам программ. Он позволяет очень просто реализовать 3-2873 л , ..-•- %*> 66 Глава 3. Элементы управления основные задачи манипулирования файлами, к которым относятся копирование, удаление, переименование и перемещение файлов. С помощью этого элемента также можно сортировать файлы п о име­
ни, дате создания, размеру. Кроме того, существует даже возмож­
ность посылать файлы по электронной почте или передавать на другое устройство при помощи инфракрасной связи. Элемент DocumentLi st работает с файлами в пределах папки My Documents, включая подпапки. Следует обратить внимание на то, что DocumentLI st является классом из пространства Mi crosof t. Wi ndowsCE. Forms и н е является частью полной версии .NET Framework. Поэтому есть смысл поближе познакомиться с данным элементом. Для разработки тестового приложения сначала потребуется создать новый проект, а затем переместить на форму элемент DocumentLi st. Для свойства Name надо задать значение DoclistFile, свойство Dock должно получить значение Тор, свойству Heigth присваивается зна­
чение 160, а для свойства Selected Directory задается значение My Documents. Также на форме надо разместить элементы ComboBox и StatusBar. Элементу ComboBox надо присвоить имя cboFileType. Затем следует выбрать свойство Items и открыть окно редактора String Collection Editor. Для списка надо задать значения BM P и WAV. Затем нужно дважды щелкнуть на элементе ComboBox, чтобы задать код обработчика события SelectedlndexChanged. Код обработчика приведен в листинге 3.28. Листинг 3.2 8 privat e voi d cboFileType_SelectedIndexChanged(objec t sender, EventArg s e ) { i f (cboFilelype.Tex t = ИВМР") { docListFile.Filte r = "Рисунки (*.bmp)|*.bmp": docListFile.SelectedDirector y = "My Pictures"; } else { docListFile.Filte r - "Звуки (*.wav)|*.wav"; docListFile.SelectedDirector y - "My Music"; } } Элементы управления 67 Данный код динамически меняет значение свойства Filter элемен­
та DocumentList для отображения файлов определенного типа. Так­
же меняется папка просмотра файлов. Если пользователь выберет расширение .BMP, то следует выбрать папку My Pictures, специально предназначенную для хранения картинок. При выборе типа фай­
лов .WAV выбирается папка My Music. Теперь следует дважды щелкнуть на элементе DocumentList, чтобы создать обработчик события Document Activated. Соответствующий код приведен в листинге 3.29. Листинг 3.2 9 privat e voi d docListFile_DocumentActivated(objec t sender, Mi crosoft.WindowsCE.Forms.DocumentL i stEventArg s e) { statusBarl.Tex t - e.Path; // работа с выбранным файлом } Перед началом тестирования стоит скопировать несколько соответ­
ствующих файлов в папки My Pictures и My Music. После запуска про­
граммы нужно перейти в поле со списком и выбрать тип файлов. После этого будет активирован элемент DocumentList с выбранной папкой. Из списка документов можно будет выбрать конкретный файл. Следует обратить внимание на то, что выбранный файл имеет кон­
текстное меню при помощи которого можно выполнять базовые операции с файлом (рис. 3.20). Путь к выбранному файлу отобра­
жается в строке состояния. Рис. 3.20. Выбор файла при помощи элемента DocumentLis t .-'h 68 Глава 3. Элементы управления Элемент Notification Еще один новый элемент управления, который появился в послед­
ней версии .NET Compact Framework 2.0, носит имя Notification. Данный элемент управления позволяет отображать интерактив­
ные сообщения. В документации по данному элементу приводит­
ся довольно интересный пример с использованием HTML-текста. Но в книге можно ограничиться более наглядным примером. Для создания тестового примера нужно переместить на форму эле­
менты Notification и Button. При нажатии на кнопку необходимо отобразить соответствующее сообщение. Это реализуется при по­
мощи кода, приведенного в листинге 3.30. Листинг 3.3 0 privat e voi d button l Cl1ck(objec t sender. EventArg s e) { noti fi cati onl.Tex t - "Позвони родителям!*: notification!..Captio n = "Демонстрация примера"; noti fi cati onl.Cri ti ca l = true; // Уведомление висит на экране 10 секунд noti fi cati onl.I ni ti al Durati o n - 10; noti fi cati onl.Vi si bl e - true; } На рис. 3.21 показано сообщение, которое будет отображаться на экране КПК в течение 10 секунд. Рис. 3.21. Вывод сообщения с помощью элемента Notificatio n ПРИМЕЧАНИЕ Элемент Notification применяется только в приложениях для кар­
манных компьютеров. Смартфоны его не поддерживают. Элементы управления 6 9 Элемент HardwareButton На карманных компьютерах кроме клавиш навигации присутствуют также дополнительные кнопки, при помощи которых активируются часто запускаемые приложения. Как правило, в состав программного обеспечения КПК входит утилита, с помощью которой можно назна­
чить каждой из этих кнопок определенные команды. Но можно пред­
ставить ситуацию, когда для создаваемой игры нужно, чтобы управ­
ление осуществлялось с помощью этих кнопок. Тогда необходимо переопределить на время поведение кнопок в вашем приложении. И сделать эт о можно с помощью элемента HardwareButton, который по ­
явился в .NET Compact Framework 2.0. Следует рассмотреть пример использования этого нового элемента. Прежде всего нужно создать новый проект и поместить на панели Component tray два элемента HardwareButton с именами hrdLeftRotate и hrdRightRotate. Для каждой переопределяемой кнопки необходимо создать свой экземпляр элемента HardwareButton. В рассматриваемом примере будут переопределяться вторая и третья кнопки. Также на форме надо разместить графическое поле Pi ctureBox. В него надо загрузить любое изображение и растянуть картинку таким обра­
зом, чтобы она заняла верхнюю половину экрана. Изображение надо пристыковать к верхней части формы. Для этого свойству Dock при­
сваивается значение Тор. Также на форме надо разместить надпись Label, при помощи которой будут отображаться подсказки. Надпись следует пристыковать к нижней части формы. Для этого свойству Dock присваивается значение Bottom. У обоих добавленных элементов HardwareButton нужно отыскать свойство Associ atedControl и задать зна­
чение Forml. Также надо изменить значения свойств HardwareKey. Для первого элемента применяется значение Appl icationKey2, что соответ­
ствует второй кнопке. Для второго элемента задается значение Appl i cati опКеуЗ, что соответствует третьей кнопке под экраном. Теперь, когда вс е необходимые свойства установлены, нужно написать код для события FormlKeyUp. Код приведен в листинге 3.31. Листинг 3.3 1 privat e voi d Form l Load(objec t sender. EventArg s e) { label1.Tex t = "Нажмите вторую кнопку для поворота экрана на 90 градусов": } privat e voi d FormlJCeyUpСobject sender. KeyEventArg s e) t продолжение & 70 Глава 3. Элементы управления Листинг 3.31 {продолжение) switch ((HardwareKeys)e.KeyCode ) { cas e HardwareKeys.ApplicationKey2: if (SystemSettings.ScreenOrientatio n = ScreenOr i entat i on.AngleO ) { SystemSett i ngs.ScreenOr i entat i o n -
ScreenOr i entat i on.Angle90; label1.Tex t - "Нажмите третью кнопку для поворота экрана в первоначальную позицию"; } break; cas e HardwareKeys.ApplicatiопКеуЗ: if (SystemSettings.ScreenOrientatio n — ScreenOrientation.Angle90 ) { SystemSettings.ScreenOrientatio n • ScreenOr i entation.AngleO; labe l 1.Tex t * "Нажните вторую кнопку для поворота экрана на 90 градусов"; } break; } default: break; Рис. 3.22. Поворот экрана при помощи аппаратных кнопок Элементы управления 71 Запустите программу и попытайтесь нажимать по очереди на вто­
рую и третью кнопки под экраном карманного компьютера. Резуль­
тат показан на рис. 3.22. ПРИМЕЧАНИЕ В документации говорится, что различные модели КПК имеют раз­
личное число кнопок, причем не все из них поддерживаются на систем-
помуровне. Например, Windows Mobile 2003 для Pocket PC поддержива­
ет четыре кнопки, a Windows Mobile 5.0 для Pocket PC поддерживает пять кнопок. При этом класс HardwareButton не поддерживается смартфонами и другими устройствами на базе Windows СЕ, которые не является устройствами Pocket PC. Глава 4 Улучшаем элементы управления В каждой новой версии Visual Studio .NET разработчики из Microsoft добавляют новые элементы управления, а также улучша­
ют функциональность уже существующих элементов. Особенно это заметно на примере .NET Compact Framework. Уже простое срав­
нение имеющихся элементов управления в версиях 1.0 и 2.0 пока­
зывает, как много было добавлено новых элементов управления. Но, тем не менее, Microsoft не может создать элементы на все случаи жизни. Поэтому программистам иногда приходится создавать соб­
ственные элементы. Также для улучшения существующих элемен­
тов программисты прибегают к различным трюкам и хитростям. В этой главе будут рассмотрены некоторые приемы, которые, воз­
можно, пригодятся в вашей практике. Текстовые поля Текстовые поля довольно часто используются в приложениях. В принципе, они достойно справляются с поставленными задача­
ми, имея необходимую функциональность. Предположим, что на форме расположены несколько текстовых полей для ввода инфор­
мации. Для улучшения удобства использования применяется сле­
дующий трюк: после того как пользователь ввел необходимые дан­
ные в текстовом поле и нажал клавишу Enter, фокус переходит к следующему текстовому полю. Код, реализующий подобный меха­
низм работы, приведен в листинге 4.1. Листинг 4.1 privat e voi d textBoxl_KeyUp(objec t sender, KeyEventArg s e) { if (e.KeyCod e =* Keys.Enter ) textBox2.Focus(); } privat e voi d textBox2_KeyUp(objec t sender, KeyEventArg s e) { Управление полосой прокрутки 73 i f (e.KeyCode — Keys.Enter) textBox3.Focus(); } privat e voi d textBox3J(eyUp(objec t sender » KeyEventArg s e) { i f (e.KeyCode — Keys.Enter) textBoxl.FocusO; } Управление полосой прокрутки При отображении большого текста пользователь может применять полосу прокрутки для перемещения п о тексту. Разработчик может использовать сообщение WMVScrol 1 для программного управления полосой прокрутки. Например, можно использовать этот механизм для создания эффекта автоматической прокрутки текста. Для иллюстрации примера нужно расположить на форме текстовое поле и отобразить в нем какой-нибудь длинный текст. В примере ис­
пользуется отрывок из произведения А. Пушкина «Дубровский». Так­
же н а форме надо расположить четыре кнопки, при помощи которых пользователь сможет управлять отображением текста, прокручивая его на одну строчку или страницу вниз и вверх. В листинге 4.2 приведен код, который реализует описанный способ отображения текста. Листинг 4.2 [Dl1Import("cored!1.dll") ] exter n stati c in t SendMessagedntPt r hWnd, uin t Msg, int wParam. in t lParam); /// <summary > /// Сообщение Window s для работы с полосой прокрутки /// </summary > cons t in t WM_VSCROL L = 0x115; // константы для сообщения WMVSCROL L cons t in t SB_LINEU P = 0: cons t in t SB_LINEDOW N - 1; cons t in t SB_PAGEU P - 2; cons t in t SB_PAGEDOW N - 3; privat e voi d FormlJ_oad<objec t sender, EventArg s e) продолжение & 74 Глава 4. Улучшаем элементы управления Листинг 4.2 (продолжение) I I Отрывок из повести А.С.Пушкина "Дубровский" txtBook.Tex t = @иНесколько лет тону назад в одной из своих поместий жил старинный русский барин* Кирила Петрович Троекуров. Его богатство, знатный род и связи давали ену большой вес в губерниях, где находилось его инение. Соседи рады были угождать налейшим его прихотям; губернские чиновники трепетали при его имени; Кирила Петрович принимал знаки подобострастия как надлежащую дань; дом его всегда был полон гостями, готовыми тешить его барскую праздность, разделяя шумные, а иногда и буйные его увеселения".; } privat e voi d butUp_Click(objec t sender, EventArg s e) { // на одну строчку вверх SendMessage(txtBook.Handle. WM_VSCROLL. SBJ.INEUP. 0); } privat e voi d butDown_Click(objec t sender. EventArg s e) { // на одну строчку вниз SendMessage(txtBook.Handle, WMJ/SCROLL. SB_LINED0WN, 0); } privat e voi d butPageUp C lic k (objec t sender. EventArg s e) { // на одну страницу вверх SendMessage(txtBook.Handle. WM_VSCR0LL. SB_PAGEUP, 0); } privat e voi d butPageDown_Click(objec t sender. EventArg s e) { // на одну страницу вниз SendMessage(txtBook.Handle. WMJ/SCROLL SB_PAGEDOWN. 0): } Внешний вид приложения показан на рис. 4.1. Многострочный текст в кнопке По умолчанию текст для кнопок может содержать только одну стро­
ку. Но при желании можно изменить этот стиль с помощью функ­
ций GetWi ndowLong и SetWi ndowLong, как показано в листинге 4.3. Многострочный текст в кнопке 75 ^ « В Ti** 4 и <* О ват ему большой вес в губесн * ил, где находилось его имени е. Соседи рады были угождать •илвйшим его гцмютлм; губерЩ - о* * iMHCMHVte i трепетали пр и его ичеми; Кирила Петрович принимал |нам годобсстрасти я как надлежащую дань; дом е го всегда был полон гостями, г Рис. 4.1. Программная прокрутка текста Листинг 4.3 [DllImportС"coredll .<П1")] privat e stati c exter n IntPt r GetCaptureO; [Dl l ImportCcoredl l .dll") ] privat e stati c exter n in t GetWindowLongCIntPt r hWnd, int nlndex); [Dlllmport("coredl1.dl1") ] privat e stati c exter n int SetWindowLong(IntPt r hWnd, int nlndex. in t dwNewLong); publi c cons t in t GWL_STYL E = -16: // стиль многострочного текста publi c cons t in t BS_MULTILIN E 0x2000; privat e voi d Forml_Load(objec t sender. EventArg s e) { IntPt r hWnd: in t style: this.butMultiline.Captur e = true: hWn d = GetCaptureO; this.butMultiline.Captur e - false; styl e » GetWindowLongChWnd. GWL_STYLE); SetWindowLongChWnd. GWL_STY1_E. styl e | BS MULTILINE): } В этом примере для сравнения использовались две кнопки. На каж­
дой из них размещен достаточно длинный текст (рис. 4.2). 76 Глава 4. Улучшаем элементы управления Щ Mui«fii*eSuttonj?S Microsof t Visual Studio !&?&Щ'ШШ:?&Ф&;М& йй>«а оаа Ьс* т*Щ Рис. 4.2. Вид кнопок в процессе программирования При загрузке формы выполняется изменение стиля для первой кнопки butMulti 1 ine, а вторая кнопка остается без изменений. Пос­
ле запуска приложения можно заметить, что длинный текст в пер­
вой кнопке разбивается на две строки и полностью умещается в границах кнопки. Во второй кнопке слова обрезаются, и текст про­
сто нельзя прочитать (рис. 4.3). ril*n< ififciem» Q 4Jj) Ц?1?;3в ф Рис. 4.3. Создание многострочного текста на кнопке Увеличение ширины выпадающего списка ComboBox 77 ВНИМАНИЕ Данный пример был написан еще для .NET Compact Framework 1.0. В .NET Compact Framework 2.0 нет надобности вызывать функцию GetCaptureQ для получения дескриптора hWnd, так как теперь поддерживается свойство ControLHandle. Увеличение ширины выпадающего списка ComboBox Выпадающий список у комбинированного окна равен ширине само­
го комбинированного окна ComboBox. Но можно обойти это ограниче­
ние с помощью неуправляемого кода, как показано в листинге 4.4. Листинг 4.4 /// <summary > /// Сообщение, получающее размеры выпадающего списка /// комбинированного окна /// </summary > cons t in t CB_GETDROPPEDWIDT H » 0x015f; /// <summary > /// Сообщение, устанавливающее размеры выпадающего списка /// комбинированного окна /// </sunimary > cons t in t CBJETDROPPEDWIDT H * 0x0160: [DllImport("coredll.dll") ] stati c exter n in t SendMessagedntPt r hwnd. in t msg, in t wParam, in t lParam); privat e voi d Form l Load(objec t sender, EventArg s e ) { comboBoxl.Iterns.Add("Раз") comboBoxl.Items.Add("Два") comboBoxl.Items.Add("Три") comboBox2.Items.Add("Длинный текст"): comboBox2.Items.Add("Очень длинный текст"); comboBox2.Items.Add("H y очень длинный текст"); // Устанавливаем желаемую ширину SendMessage(comboBox2.Handle. CB_SETDROPPEDWIDTH, 200, 0); // Получим ширину выпадающего окна продолжение # 78 Глава 4. Улучшаем элементы управления Листинг 4.4 (продолжение) i nt retval - SendMessage(comboBox2.Handle, CBJ5ETDR0PPEDWIDTH. 0. 0); this.Text - retval .ToStringO; На форме надо разместить два элемента ComboBox. Один из них бу­
дет стандартным. А второй элемент обработает сообщение CB_SETDROPPEDWIDTH со значением второго параметра 200. В резуль­
тате выпадающий список будет в ширину занимать 200 пикселов. После запуска программы сначала надо обратить внимание на ра­
боту первого комбинированного окна (рис. 4.4). Оно ведет себя стандартным образом. '«•wboe©* ft КЗ Td *- %&. О Рис. 4.4. Стандартный размер выпадающего списка Теперь нужно перейти ко второму комбинированному окну. У него размер выпадающего списка увеличился, что позволяет увидеть весь текст (рис. 4.5). ип*о8дан !* Q "^ЦгЧ:*» О Цл»«ныи текст Эчеч*» д/ы+ьй твмгг •Ьо*»*> дли »»tii текст Рис. 4.5. Увеличенный размер выпадающего списка у ComboBox ListBox 7 9 ListBox Элемент ListBox имеет множество возможностей, которые пока не реализованы в рамках платформы .NET Compact Framework. В ча­
стности, данный элемент не позволяет осуществлять поиск элемен­
тов по первым символам. Но для решения этой задачи можно ис­
пользовать сообщение LB_FINDSTRING. Чтобы создать тестовое приложение, нужно добавить на форму спи­
сок ListBox и текстовое поле TextBox. Также потребуется ввести код, приведенный в листинге 4.5. Листинг 4.5 cons t in t LBJINDSTRIN G = Ox018F: cons t in t LBJINDSTRINGEXAC T = 0x01A2; [Dlllmportrcoredl l .dll") ] stati c exter n in t SendMessageCIntPt r hwnd, in t msg. in t wParam. strin g lParam); privat e voi d textBoxlTextChanged C objec t sender. EventArg s e) { //поиск строки n o вводимым символам 1i stBoxl.Selectedlnde x = SendMessage(1 i stBoxl.Handle. LBFINDSTRING, -1. textBoxl.Text); privat e voi d Forml_Load(objec t sender, EventArg s e) { 1istBoxl.Items.Add("bank"): listBoxl.Items.Add("banana"); 11stBoxl.Items.Add("bal1"); 1i stBoxl.Items.Add("bounty"); listBoxl.Items.AddCbar"); } После запуска проекта можно попробовать ввести в текстовом поле любое слово. Если в списке есть слова, начинающиеся с введенных символов, то они начнут выделяться в списке. Например, можно сначала ввести символ Ь, затем а и, наконец, L Сначала будет выде­
лено слово bank, а после третьего введенного символа выделение перейдет на слово ball. Существует также сообщение LB_FINDSTRINGEXACT, которое осуществ­
ляет поиск по целому слову без учета регистра. Имеет смысл при-
80 Глава 4. Улучшаем элементы управления менять его, когда список содержит сотни записей и отыскивание нуж­
ного слова становится утомительным занятием. Чтобы показать при­
менение этого сообщения, нужно добавить в предыдущий пример до­
полнительную кнопку и ввести код, приведенный в листинге 4.6. Листинг 4.6 privat e void button l Click(objec t sender, EventArgs e) { listBoxl.SelectedInde x - SendMessageClistBoxl.Handle. LB_FINDSTRINGEXACT, •1. "ball"); } ListView Возможно, вы замечали, что в некоторых программах используется элемент ListView с градиентной заливкой. Например, такое оформ­
ление интерфейса можно увидеть в списке контактов. Оказывается, совсем не сложно применить такую раскраску в своем приложении. Но для этого надо использовать стиль LVSGRADIENT, как показано в листинге 4.7. Листинг 4.7 usin g System.Runtime.InteropServices; [DlllmportCcoredl l .dll") ] stati c exter n in t SendMessagedntPt r hwnd. uin t msg, int wParam, in t IParam): cons t in t LVS_EX_GRADIEN T = 0x20000000; cons t in t LVMSETEXTENDEDLISTVIEWSTY1 E =• 0x100 0 + 54; // Создаем градиентный фон для ListVie w privat e voi d CreateGradientListView(ListVie w listView ) { // Получим дескриптор ListVie w IntPt r hL V « listView.Handle; // Устанавливаем расширенный стиль SendMessageChLV. (uint)LVM_SETEXTENDEDLISTVIEWSTYLE. 0. LVS_EX_GRADIENT); // Обновляем вид Текстовое поле для ввода чисел 8 1 listView.RefreshO; } privat e voi d FormlLoadCobjec t sender, EventArg s e) { CreateGradientListViewd istViewl); } Создание кнопки, содержащей изображение В статье «How to: Create a Custom Image Button Control», кото­
рую можно отыскать по адресу msdn2.microsoft.com/en-us/library/ msl72532(VS.80).aspx, описывается процесс создания кнопки, ко­
торая может содержать в качестве фона любое изображение. В пер­
вых версиях .NET Compact Framework кнопку вообще нельзя было сделать цветной, так как не существовало свойства BackColor. Потом данный недостаток бы л исправлен, но стандартными средствами пока не получится отобразить на кнопке рисунок. С помощью примера, который приводится в статье, можно обойти это ограничение. Список с расширенными возможностями В другой статье — «How to: Create an Owner-Drawn List Box», расположенной по адресу msdn2.microsoft.com/en-us/library/ ms229679(VS.80).aspx, — описывается создание списка ListBox с расширенными возможностями. В этом примере показано, как можно использовать графику и различные шрифты для отобра­
жения элементов списка. Текстовое поле для ввода чисел Очень часто программистам необходимо создавать текстовые поля, в которых можно указывать только числа. В статье «How to: Create a Numeric Text Box», которую можно найти по адресу msdn2.microsoft.com/en-us/library/ms229644(VS.80).aspx, рассказыва­
ется об одном из таких способов. Прочитав статью, вы поймете, как создавать текстовые поля, допускающие ввод только чисел, минуса, пробела и разделителя десятичных чисел. 82 Глава 4. Улучшаем элементы управления Сортировка элементов ListView Элемент управления ListView в .NET Compact Framework не поддер­
живает метод Sort, который позволил бы отсортировать элементы списка. В документации MSDN есть статья «How to: Sort ListView Items», в которой рассказывается о том, как решить эту проблему при помощи метода Sort класса ArrayList и интерфейса I Comparer. Использование элемента DateTimePicker Элемент управления DateTimePicker появился только в последней вер­
сии .NET Compact Framework 2.0. В документации MSDN есть ряд замечательных статей о том, как создать собственный элемент DateTimePi cker для программ, работающих на платформе .NET Compact Framework 1.0. Стоит ознакомиться со статьями «Adding Designer Support to the .NET Compact Framework DateTimePicker Control» и «Microsoft .NET Compact Framework-based DateTimePicker Control». Глава 5 Мышь и клавиатура Мышь и стилус Взаимодействие с программой пользователь осуществляет с помо­
щью стилуса или аппаратных кнопок на самом устройстве. А где же мышь и клавиатура? Ну, предположим, что клавиатуру можно за­
менить ее виртуальным аналогом на экране КПК. Она имеет прак­
тически ту же функциональность, что и настоящая клавиатура. Кро­
ме того, следует помнить, что существуют КПК с настоящей, хоть и маленькой, клавиатурой. Что же касается мыши, то ее роль с ус­
пехом выполняет стилус. Но стоит заметить, что у стилуса нет возможности эмулировать на­
жатие правой кнопки мыши. Когда пользователь применяет сти­
лус, то генерируются события MouseDown, MouseMove, Mouseup и Click. Первые три события могут сообщить о позиции курсора, как и со­
бытия из настольной версии Windows. Курсоры Так как пользователь при работе использует стилус, то Windows Mobile не отображает на экране устройства стандартную стрелку курсора. Предполагается, что пользователь может самостоятельно попасть острым концом стилуса в маленькую кнопку или другой элемент. Но у мобильных систем курсоры все же есть. Первый из них является аналогом песочных часов в настольной версии Windows и выглядит как анимированный круг с разноцветными секторами. Второй курсор можно увидеть при вызове контекстно­
го меню. Он выглядит как множество маленьких красных кружков, которые постепенно появляются вдоль воображаемой окружности. Песочные часы При выполнении длительных ресурсоемких операций нужно по­
казать пользователю, что устройство работает, а не зависло. Луч-
84 Глава 5. Мышь и клавиатура ше всего вывести на экран устройства курсор ожидания. В кар­
манных компьютерах в качестве такого курсора используются не песочные часы, как в настольных компьютерах, а анимированный разноцветный круг. Установить данный тип курсора в приложе­
нии очень просто, что иллюстрирует фрагмент кода, приведенный в листинге 5.1. Листинг 5.1 // Устанавливаем курсор ожидания Cursor.Curren t - Cursors.WaitCursor; // возвращаем курсор по умолчанию Cursor.Curren t e Cursors.Default; На рис. 5.1 показано приложение с соответствующим курсором. 0YJ4ri;«b ф Рис. 5.1. Отображение курсора ожидания Обработка события Tap-and-Hold Так как в карманных компьютерах не используется правая кнопка мыши, то для вызова контекстного меню используется операция Tap-and-Hold. Пользователь касается стилусом экрана и некоторое время удерживает нажатие. Если элемент, на поверхности которо­
го находится стилус, связан с элементом ContexMenu, то на экране появится контекстное меню. А что делать, если мы хотим создать собственный обработчик события Tap-and-Hold? В этом случае надо добавить в проект таймер и написать код для обработки собы­
тий Mouse_Down, MouseJJp и timerl_Tick. Для таймера следует задать интервал, необходимый для инициирования события. Сам код при­
веден в листинге 5.2. Клавиатура 8 5 Листинг 5.2 privat e voi d Forml_MouseDown(objec t sender, MouseEventArg s e) { // включаем таймер timer!.Enable d = true; } privat e voi d Forml_MouseUp(objec t sender. MouseEventArg s e) { timer!.Enable d - false; labell.Text - ""; } privat e void timerl_Tick(objec t sender, EventArgs e) { labell.Text • "Вы нажали на экран"; } Клавиатура На большинстве карманных ком­
пьютеров нет стандартной клавиа­
туры, поэтому ввод текста осуществ­
ляется с помощью виртуальной кла­
виатуры SIP. В Visual Studio 2005 клавиатура SIP представлена эле­
ментом InputPanel. Но в послед­
нее время стали появляться устрой­
ства с настоящей встроенной клави­
атурой. Как правило, эти устройства имеют квадратный экран. Среда раз­
работки поддерживает эмуляторы подобных моделей (рис. 5.2). Эти эмуляторы в своем названии содер­
жат слово «Square». Кроме того, на устройствах имеют­
ся клавиши навигации, клавиша Enter и кнопки запуска определен­
ных приложений. Все эти клавиши могут обрабатывать стандартные со­
бытия. ) Л N L ^ _ - з й Рис. 5.2. Эмулятор устройства с клавиатурой 86 Глава 5. Мышь и клавиатура Клавиши навигации Если вы в процессе создания приложения в режиме работы с фор­
мой щелкнуть мышью на любой из кнопок навигации, то среда раз­
работки сгенерирует код для этих кнопок в событии FormKeyDown. В листинге 5.3 приведен пример обработчика этого события. Листинг 5.3 privat e voi d Forml_KeyDown(objec t sender, KeyEventArg s e) { if (Ce.KeyCod e -= System.Windows.Forms.Keys.Up) ) { labe l 1.Tex t = "Клавиша Вверх": } if ((e.KeyCod e = System.Windows.Forms.Keys.Down) ) { labe l 1.Tex t = "Клавиша Вниз"; } if ((e.KeyCod e =• System.Windows.Forms.Keys.Left) ) { labe l 1.Tex t - "Клавиша Влево": } if ((e.KeyCod e = System.Windows.Forms.Keys.Right) ) { label1.Tex t - "Клавиша Вправо": } if ((e.KeyCod e — System.Windows.Forms.Keys.Enter) ) { labell.Text = "Клавиша Enter": } Hfcwih « »«• « ^ Q ^ ^ w ф "ttjWUv* [К KV*f Кламы Влево 1Ц 11 2 [314151 6 171819101. 1-1» ТдЫq|w|e |f I t 1 у Щ11 |oI p [ 1 cwi yi i i di f i ui hi i i i t i i i.-R Bhffl|*|x|c|v|b|n|m|, |. I/I *- 1 * I t M -
эшии Рис. 5.3. Обработка нажатий клавиш навигации Выключение устройства 8 7 Как видите, приложение определяет нажатую клавишу при помо­
щи перечисления System.Windows. Forms. Keys. Если открыть виртуаль­
ную клавиатуру и нажать на клавиши со стрелками, то можно убе­
диться, что они тоже инициируют событие FormJCeyDown (рис. 5.3). Если протестировать пример на устройстве с настоящей клавиату­
рой, то можно заметить, что приложение правильно обрабатывает нажатие на встроенные клавиши со стрелками. Выключение устройства На карманных компьютерах также есть кнопка выключения уст­
ройства. На самом деле при нажатии на эту кнопку устройство не выключается, а переходит в особый спящий режим. В мобильных устройствах программы и данные хранятся в памяти, и если уст­
ройство действительно выключить, то все приложения и данные просто пропадут. Разработчик может программно перевести уст­
ройство в спящий режим, имитируя нажатие этой кнопки выклю­
чения с помощью функции API keybd_event, как показано в листинге 5.4. Листинг 5.4 /// <summary> /// Функция имитирует нажатия клавиш на клавиатуре /// </suiranary> /// <рагатп name="bVk">BwpTyanbHbEu код клавиши для имитации /// нажатия и отпускания клавиши</рагат> /// <рагат пате="Ь$сап">Зарезервировано - установлено в /// 0</param> /// <param пате=ис1иР1ад$п>Флаг</рагат> /// <рагат пате-"^Ех1га1пто">Дополнительная инфорнация</рагаш> [DlllmportC"cored11.c n 1", CharSet - CharSet.Unicode) ] publi c stati c exter n void keybd_event(byt e bVk. byt e bScan, i nt dwFlags, i nt dwExtralnfo): // константа для кнопки выключения устройства publi c const i nt VK_0EM_8 - OxDF; privat e void butOff_Click(objec t sender, EventArgs e) { // Имитируем нажатие кнопку выключения устройства keyb d eventCVK_0EM_8. 0. 0. 0); } 88 Глава 5. Мышь и клавиатура Дополнительные материалы Если вы хотите узнать о клавиатуре еще больше, то стоит обратить внимание на блог Алекса Яхнина, который можно найти по адресу blog.opennetcf.org/ayakhrrin. Там можно найти статью «Keyboard hook in the CF v2». В данной статье рассказывается о перехвате всех сооб­
щений, которые посылаются при нажатии любых кнопок устройства. Также может быть полезна статья «Custom SIP Control for CF». Автор статьи предлагает собственную реализацию элемента InputControl, который содержит свою виртуальную клавиатуру. Этот пример мо­
жет пригодиться при создании приложения, в котором не использует­
ся стандартная панель ввода SIP, но при этом пользователь должен иметь возможность ввода информации. Глава 6 Графика Классы для программирования графики Программирование графики в .NET Compact Framework опирает­
ся на те же базовые приемы, что и работа с графикой для полной версии .NET Framework. Все основные классы для работы с графи­
кой сосредоточены в пространстве имен System. Drawn ng. С помощью этих классов можно рисовать всевозможные фигуры, отображать линии, работать с изображениями и даже манипулировать текстом. В качестве своеобразного холста для графических опытов можно использовать поверхность формы или элементов управления. Са­
мым главным классом является класс Graphics, который предостав­
ляет поверхность и методы для вывода графики. Также широко ис­
пользуются в графике такие классы, как Pen, Brush, Color, Rectangle, Line, Image. Класс Pen Класс Pen используется для создания пера, при помощи которого проводятся прямые и кривые линии. В отличие от полной версии .NET Framework, поддерживающей четыре перегруженных версии кон­
структора Pen, .NET Compact Framework позволяет создавать перо только с помощью двух конструкторов. При вызове метода Pe n (Col or) создается перо указанного цвета. Конструктор Pe n (Col or, Si ngl e) по­
зволяет создавать перо указанных цвета и ширины. Но второй вари­
ант поддерживается только в .NET Compact Framework 2.0. В листинге 6.1 приведен пример создания перьев синего и красного цветов. Затем при помощи перьев создаются две линии. Листинг 6.1 privat e voi d Form l Paint(objec t sender, PaintEventArg s e) { Graphic s g = e.Graphics; // Синее перо толщиной 1 пиксел Pe n bluePe n - ne w Pen(Color.Blue): // Красное перо толщиной 5 пикселов продолжение * 90 Глава 6. Графика Листинг 6.1 (продолжение) Pen redFatPen • new Pen(Color.Red. 5); g.DrawLinetbluePen. 10. 10. 230. 10); g.Drawl_ine(redFatPen. 10. 20. 230. 20); } Класс Brush Класс Brush является абстрактным классом для создания кистей, с по­
мощью которых можно рисовать фигуры и текст на графической по­
верхности. Библиотека .NET Compact Framework поддерживает клас­
сы SolidBrush и TextureBrush. К сожалению, класс LinearGradientBrush, позволяющий рисовать красивые фигуры, в настоящее время не под­
держивается. Класс SolidBrush При создании объекта Sol idBrush нужно просто указать цвет, кото­
рый будет использоваться для отображения фигур. Чтобы сменить цвет кисти, достаточно указать новый цвет в свойстве Color. В лис­
тинге 6.2 приведен код, который позволяет нарисовать зеленый круг и желтый прямоугольник. Листинг 6.2 privat e void FormlPaint (objec t sender. Pai ntEventArgs e) { Graphics g « e.Graphics; // Создаем кисть зеленого цвета SolidBrush myBrush = new SolidBrush(Color.Green); // Рисуем закрашенный круг g.FillEllipse(myBrush. 10. 30. 30, 30): // Меняем цвет кисти на желтый myBrush.Color » Color.Yellow; // Рисуем закрашенный прямоугольник g.FillRectangleCmyBrush. 50. 30. 50. 25); } Класс TextureBrush Класс TextureBrush позволяет создавать текстурную кисть. Подоб­
ная текстурная кисть позволяет не рисовать однородным цветом, Класс Color 91 а применять текстурное заполнение отображаемых графических примитивов. Использование подобной кисти позволяет добивать­
ся красивых эффектов. В листинге 6.3 приведен пример использо­
вания текстурной кисти с использованием изображения, входяще­
го в состав Windows Mobile 2003. Листинг 6.3 privat e voi d Forml_Paint(objec t sender, PaintEventArg s e) { Graphic s g - e.Graphics; // выбираем рисунок Imag e mylmag e - ne w Bitmap(@"\Windows\alerts.bmp"): // создаем текстурную кисть TextureBrus h textur e - ne w TextureBrush(mylmage); // Рисуем эллипс, заполненный рисунком g.FillEllipseCtexture. 10, 60. 120, 120); g.DisposeO; } ВНИМАНИЕ Такие свойства класса TextureBrush, как Transform и WrapMode, не поддерживаются в .NET Compact Framework 2.0. Класс Color При создании перьев или кистей применялся класс Color. Он позво­
ляет задавать цвет либо с помощью предопределенного названия, либо указывая составные части цвета в модели RGB. Например, для создания красного цвета можно использовать код, приведенный в листинге 6.4. Листинг 6.4 // красный цвет по названию Color redColor - Col or.Red; // красный цвет из компонентов RGB Color redColor 2 - Color.FromArgb(255, 0. 0); // Выводим на экран две красные линии g.DrawLine(new Pen(redColor). 10. 190, 100, 190); g.DrawLine(new Pen(redColor2), 10, 195. 100, 195); 92 Глав а 6. Графика Класс Font Класс Font используется для вывода текста. Как ни странно, вывод текстовой информации тоже является графической операцией, что немного смущает новичков. Из четырнадцати возможных переза­
груженных версий конструктора класса в .NET Compact Framework доступно только три. Для создания объекта Font нужно определить семейство шрифтов, размер символов и стиль начертания. Пример использования шрифта приведен в листинге 6.5. Листинг 6.5 Fon t myFon t = ne w Font("Таnoma", 9. FontStyle.Italic): g.DrawString C "Карманный компьютер". myFont, myBrush. 14. 200); Класс Icon Объект Icon используется методом Drawlcon для отображения пик­
тограмм. Предположим, что необходимо использовать пиктограм­
му, хранящуюся в ресурсах программы. В таком случае понадобит­
ся код, приведенный в листинге 6.6. Листинг 6.6 Ico n mylco n = ne w IconCAssembly.GetExecutingAssemblyO. GetMan i festResourceStream("MyApp.Icon.i со")): Класс Bitmap Класс Bi tmap предназначен для работы с растровыми изображения­
ми. Программист может загрузить картинку в объект Bitmap из по­
тока Stream, скопировать из существующего объекта Bitmap или за­
грузить из файла. Также можно создать новый пустой объект Bitmap, указав только размеры картинки. Ранее класс Bitmap уже использо­
вался при создании текстурной кисти. Но при этом применялся родственный объект Image. В листинге 6.7 приведен новый вариант создания кисти. Листинг 6.7 // выбираем рисунок Bitma p mylmag e - ne w Bitmap(@"\Windows\a1erts.bmp"); // создаем текстурную кисть TextureBrus h textur e - ne w TextureBrush(mylmage); Графические методы 9 3 Структура Point Структура Point содержит координаты X и У для указания распо­
ложения некоей точки. В библиотеке .NET Compact Framework поддерживается только один конструктор для создания объекта Point, в котором указываются эти координаты. Структура Po i nt час­
то используется в методах DrawPolygon и Fi 11 Polygon, которые будут рассматриваться позже. Структура Rectangle Структура Rectangl е определяет размер и расположение прямоуголь­
ника. В мобильной версии используется только один конструктор, определяющий прямоугольник п о координатам левого верхнего угла, ширине и высоте, что иллюстрирует код, приведенный в листинге 6.8. Листинг 6.8 Rectangl e myRectangl e « ne w Rectangle(10. 10. 70. 210): Графические методы В предыдущих примерах уже были использованы несколько мето­
дов для работы с графикой. Но сейчас следует поговорить о них бо­
лее подробно. Прежде всего нужно помнить, что для работы с графи­
ческими методами необходимо сначала создать объект Graphics. Существует несколько способов получения объекта Graphics, и они будут рассматриваться достаточно подробно. Метод CreateGraphics формы или элемента управления позволяет получить объект Graphics, предоставляющий возможность рисовать на форме или элементе управления. Этот метод демонстрируется в листинге 6.9. Листинг 6.9 Graphics g = this.CreateGraphicsC): Метод Fromlmage создает новый объект Graphics из заданного объек­
та Image. При помощи этого метода можно изменять существую­
щее изображение или создавать новое изображение. Причем об­
работанное изображение можно потом сохранить в графическом файле. Использование метода иллюстрирует код, приведенный в листинге 6.10. 94 Глав а 6. Графика Листинг 6.10 Bitmap bmp - new Bitmap(150. 90): Graphics g я Graphics.Fromlmage(bmp); Метод OnPaint класса Form получает в качестве параметра объект PaintEventArgs. Одним из членов данного объекта является объект Graphi cs, связанный с формой. Переопределяя метод ОпРа i n t класса Form, можно получить доступ к объекту Graphics из параметра PaintEventArgs, после чего можно работать с графикой в клиентской области формы. Вызов этого метода показан в листинге 6.11. Листинг 6.11 Protecte d overrid e voi d OnPaint(PaintEventArg s e) { Graphics g « e.Graphics; } Получив любым из перечисленных способов доступ к объекту Graphics, программист может рисовать фигуры, линии, кривые, изображения и текст при помощи различных методов. Самые распространенные графические методы будут рассмотрены в этом разделе главы. Метод Drawlmage рисует заданный объект Image в указанной пози­
ции экрана Всего существует четыре перегруженные версии мето­
да. Но в самой простой его версии достаточно указать координаты выводимой картинки, как показано в листинге 6.12. Листинг 6.12 g.DrawImageCmylmage, 10. 10); С помощью метода Drawlmage можно выводить на экран не все изоб­
ражение, а только его часть. В этом случае надо указать размеры прямоугольника, который определяет размеры выводимой облас­
ти картинки, как показано в листинге 6.13. В примере используется перечисление Graphi csUni t. P i xel, которое позволяет указывать еди­
ницы измерения. Листинг 6.13 Bitmap шуВМР - new Bitmap(@"\windows\banner.gif"); Rectangl e portio n - new Rectangled. 1, 150, 25); g.DrawImageCmyBMP. 20, 220. portion. GraphicsUnit.Pixel); Метод Fill Rectangle уже применялся при рассмотрении кистей. Метод DrawRectangle использует перо вместо кисти, поэтому на эк­
ран выводится незакрашенный прямоугольник. Создание собственных методов DrawPie и FillPie 95 Чтобы нарисовать достаточно сложную фигуру, можно задать массив точек и соединить их прямыми отрезками, после чего можно закра­
сить получившуюся фигуру. Для этого разработчик может использо­
вать методы DrawPolygon и Fill Polygon. В листинге 6.14 приведен код, который позволяет нарисовать простой ромб по указанным точкам. Листинг 6.14 // Нарисуен ромб // Зададим массив точек Point[ ] arrPoint -
i new Point(150.50). new Point(200,100). new Point(150.150). new Point(100.100). new Point(150.50). }: g.DrawPolygon(bluePen, arrPoint); Если все рассмотренные ранее методы объединить в одно прило­
жение и затем запустить его, то на экране устройства будет отобра­
жено несколько графических образов, как показано на рис. 6.1. 0?н<*к*пи4* Q ?|М- 1&2S © Ф ''ЙЯИ*-
Рис. 6.1. Основные приемы работы с графикой Создание собственных методов DrawPie и FillPie В данный момент .NET Compact Framework не поддерживает графи­
ческие методы DrawPi е и Fi 11 Pie, которые позволяли бы рисовать кру­
говые секторы. Но можно создать свою версию этих методов, исполь­
зуя математические вычисления, как показано в листинге 6.15. 96 Глава 6. Графика Листинг 6.15 ///<summary > ///Рисуем закрашенный сектор ///Параметры функции ///д ///solidBrus h ///х.у ///widt h ///heigh t ///startAngl e ///endAngl e Объект Graphic s Кисть для закраски сегмента Координаты центра Ширина сегмента Высота сегмента Значение начального угла Значение конечного угла ///</summary > privat e voi d FillPieCGraphic s g. SolidBrus h solidBrush, in t x, in t y, in t width, in t height, doubl e startAngle. doubl e endAngle ) { doublet ] xAngl e - ne w double[12]: doublet ] yAngl e - ne w double[12]; doubl e angleIncremen t - (endAngl e - startAngle ) / 10; doubl e angl e =» startAngle; for (i nt i • 0; i <= 10: i++) { xAngle[i ] - x + (Math.Cos(angl e * (Math.PI / 180)) * (widt h /2 ) ); yAngle[i ] - у + (Math.Sin(angl e * (Math.PI / 180)) * (heigh t /2 ) ): angle += anglelncrement: } xAngle[ll ] = x + (Math.Cos(endAngl e * (Math.PI / 180)) * (widt h /2 ) ); yAngle[ll ] = у + (Math.SinCendAngle * (Math.PI / 180)) * (heigh t /2 ) ); Point[ ] anglePoints { new Point(x. y). new PointС С new PointС С new PointСС new PointС( new PointCC new PointCC new PointCC new PointCC new PointCC nt)xAngle[0]. nt)xAngle[l], nt)xAngle[2], nt)xAngle[3]. nt)xAngle[4]. nt)xAngle[5], nt)xAngle[6]. nt)xAngle[7]. nt)xAngle[8]. Cint)yAngle[0] ) Cint)yAngle[l] ) Cint)yAngle[2] ) Cint)yAngle[3] ) Cint)yAngle[4] ) Cint)yAngle[5] ) Cint)yAngle[6] ) Cint)yAngle[7] ) Cint)yAngle[8] ) Создание собственных методов DrawPie и FillPie 97 ^ new Point((int)xAngle[9]. (int)yAngle[9]) f new Point((int)xAngle[10], (int)yAngle[10]). new Poi nt((i nt)xAngl e[l l ]. (i nt)yAngl e[l l ] ) } g.FillPolygon(solidBrush, anglePoints); ///<summary > /// Рисуен границы сектора ///g - Объект Graphic s ///pe n - Перо для рисования сегнента ///х.у • Центр сегмента ///widt h - Ширина сегмента ///heigh t • Высота ///startAngl e - Значение начального угла ///endAngl e - Значение конечного угла ///</suiranary > privat e voi d DrawPie(Graphic s g. Pe n pen, in t x. in t y. in t width, in t height, doubl e startAngle. doubl e endAngle ) { double[ ] xAngl e - ne w double[12]; double d yAngl e = ne w double[12]; doubl e angielncremen t - (endAngl e - startAngle ) / 10; doubl e angl e - startAngle: fo r (i nt i - 0; i < e 10; i++) { xAngle[i ] = x + (Math.Cos(angl e * (Math.PI / 180)) * (width / 2)); yAngle[i ] - у + (Math.Sin(angl e * (Math.PI / 180)) * (height /2 ) ): angle += angielncrement: } xAngleCll ] = x + (Math.Cos(endAngl e * (Math.P I / 180) ) * (widt h /2)); yAngle[ll ] = у + (Math.Sin(endAngl e * (Math.P I / 180) ) * (heigh t /2)): Point[ ] anglePoint s • { ne w Point(x.y). ne w Point((int)xAngle[0].(int)yAngle[0]), new Poi nt((i nt)xAngl e[l ].(i nt)yAngl e[l ]). продолжение^ 4-2873 98 Глава 6. Графика Листинг 6.1 5 (продолжение) new Point(( i new Point(( i new Point(( i new Point(( i new Point(( i new Poi nt(( i new Poi nt(( i new Pointed" new Pointed new Pointed }: nt)xAng1e[2]. nt)xAngle[3]. nt)xAngle[4]. nt)xAngle[5]. nt)xAngle[6]. nt)xAngle[7], nt)xAngle[8]. nt)xAngle[9]. nt)xAngle[10 ] nt)xAngle[ll ] eint)yAngle[2] ) eint)yAngle[3] ) eint)yAngle[4] ) eint)yAngle[5] ) eint)yAngle[6] ) eint)yAngle[7] ) eint)yAngle[8] ) eint)yAngle[9] ) ,Cint)yAngle[10]). ,eint)yAngle[ll] ) g.DrawPolygonepen. anglePoints); } privat e voi d FormlPaintCobjec t sender, PaintEventArg s e) { // Выводим несколько секторов на экран DrawPieCe.Graphics, new Pen(Color.Red). 130, 165, 100. 100. 0. 45); FillPieCe.Graphics. new SolidBrushCColor.Green). 120. 160. 100. 100. 46. 90): FillPieCe.Graphics. new SolidBrushCColor.Yellow). 120. 160. 100, 100, 91, 120): FillPieCe.Graphics. new SolidBrushCColor.Blue). 120. 160. 100. 100. 121. 260); FillPieCe.Graphics. new SolidBrushCColor.Red). 120, 160. 100. 100. 261. 360); } Рис. 6.2. Создание секторов Результа т работы этой программы показан на рис. 6.2. Создание фонового рисунка для формы 9 9 Создание фонового рисунка для формы К сожалению, .NET Compact Framework не поддерживает свойство Backgroundlmage, которое создает фоновый рисунок для формы. Но каждый программист может восполнить данный пробел, переопре­
деляя метод OnPaint. Нужно создать новый проект и разместить на форме какой-нибудь элемент управления, например кнопку. Кнопка н е будет выполнять никаких функций. Она потребуется лишь для демонстрации тех­
нологии. Также надо добавить в проект изображение, которое бу­
дет использоваться в качестве фона для формы. В нашем примере картинка будет внедрена в программу как ресурс, хотя можно за­
грузить ее из обычного графического файла. Чтобы все работало так, как запланировано, необходимо переопределить метод OnPai nt(). Новый код метода приведен в листинге 6.16. Листинг 6.16 protecte d overrid e voi d OnPaint(PaintEventArg s e) { // получим картинку из ресурсов Bitma p backgroundlmag e - ne w Bi tmap ( Assemb l у. GetExecut i ngAssemb l у (). GetMan i festResourceStream("BackgroundImage_CS.soch i cat.jog") ); e.Graphics.Drawlmage(backgroundlmage. this.CIientRectangle, ne w RectangleCO, 0, backgroundlmage.Width, backgroundlmage.Height). Graph i с sUn i t.P i xel); Рис. 6.3. Заполнение фона формы своим рисунком www !?' 100 Глав а 6. Графика После запуска программы можно будет увидеть, что форма имеет фоновый рисунок, а кнопка расположена поверх фона (рис. 6.3). Копирование рисунка Библиотека .NET Compact Framework 1.0 не поддерживает ме­
тод System. Drawi ng. Image. CI one, позволяющий создать точную ко­
пию картинки. Это ограничение легко обходится с помощью соз­
дания собственных методов. Кроме того, можно расширить воз­
можности метода и добавить функциональность, позволяющую ко­
пировать часть картинки. Соответствующий код приведен в лис­
тинге 6.17. Листинг 6.1 7 // Копируем все картинку protecte d Bitma p CopyBitmap(Bitma p source ) { retur n ne w Bitmap(source); } // Копируем часть картинки protecte d Bitma p CopyBitmapCBitma p source, Rectangl e part ) { Bitma p bm p - ne w Bitmap(part.Width, part.Height); Graphic s g - Graphics.Fromlmage(bmp); g.DrawImageCsource. 0. 0. part. GraphicsUnit.Pixel); g.DisposeO; retur n bmp; } privat e voi d buttonl_Click(objec t sender. EventArg s e) { Graphic s g • CreateGraphicsO; Bitma p myBM P « ne w Bitmap(@"\windows\banner.gif"); // Половина ширины картинки in t lef t - myBMP.Size.Widt h / 2; // Копируем всю картинку Bitma p clon e =» CopyBitmap(myBMP); // копируем левую часть картинки Копирование рисунка 10 1 Bitmap par t = CopyBitmap(myBMP, new Rectangle(0. 0. l ef t, myBMP.Size.Height)); // Выводим три картинки по вертикали: // источник, копию и копию левой части i n t у = 10: // картинка-источник g.DrawImage(myBMP. 10, у); у += myBMP.Height + 10; // картинка-копия g.Drawlmage(clone, 10, у): у -н= clone.Heigh t + 10: // копия левой части картинки g.DrawImage(part, 10, у); у -н= part.Heigh t + 10; g.DisposeO: } privat e voi d button2_Click(objec t sender, EventArg s e) { Graphic s g = CreateGraphicsO; Bitma p myBM P = ne w Bitmap(@ M\windows\banner.gif"): g.CIear(Color.White): in t lef t = myBMP.Size.Widt h / 2; // Копия картинки Bitma p clon e - (Bitmap)myBMP.CloneO; in t у = 10; g.DrawImageCmyBMP. 10, y): у -н= myBMP.Heigh t + 10; g.DrawImageCclone, 10. y): у + = clone.Heigh t + 10; g.DisposeO; } В этом примере создаются две перегруженные версии метода Copylmage. При помощи этого метода можно копировать картинку или ее часть. Для сравнения в примере было показано, как можно скопи­
ровать картинку с помощью метода CI one, доступного в .NET Compact Framework 2.0. Результат работы соответствующего приложения показан на рис. 6.4. 102 Глава 6. Графика В ^ й И? мстят Pocket Pocket Internet Explore* Momctf Pocket int Рис. 6.4. Копирование картинки разными способами Поддержка прозрачности Библиотека .NET Compact Framework позволяет использовать про­
зрачный цвет, но при этом налагает определенные ограничения на эту возможность. Например, в библиотеке нет такого удобного ме­
тода, как MakeTransparent. Но разработчик может задать прозрачный цвет при помощи метода SetCol огКеу класса ImageAttri butes. При этом разрешается использовать один и тот же цвет для минимального и максимального значений цветового ключа в версии метода ImageAttributes.SetColогКеу (Color. Color). ПРИМЕЧАНИЕ Вторая перегруженная версия метода ImageAttributesSetColor-
Key (Color, Color, ColorAdjustType) в .NET Compact Framework не под­
держивается. Используя прозрачный цвет, можно добиться красивых результа­
тов при наложении картинки на картинку. Этот механизм стоит продемонстрировать на конкретном примере. Для тестового приложения были подготовлены две картинки. Пер­
вое изображение является фотографией моего кота, а во втором хранится небольшая табличка с его именем, сделанная за несколь­
ко секунд в стандартной программе Paint. При создании таблички фон был залит красным цветом, а для текстовой строки был вы­
бран другой тон. Затем оба рисунка были добавлены в проект как ресурсы. Чтобы продемонстрировать рассматриваемый эффект, надо рас­
положить на форме две кнопки. При нажатии первой кнопки таб-
Поддержка прозрачности 103 личка будет накладываться на фотографию без изменений. При нажатии на вторую кнопку красный цвет сначала будет опреде­
лен как прозрачный и только потом произойдет наложение изоб­
ражений. Основной код приложения приведен в листинге 6.18. Листинг 6.1 8 privat e voi d butAddImage_Click(objec t sender. EventArg s e) { Graphic s g = CreateGraphicsO; // получим картинки из ресурсов Bitma p imgCa t = ne w B1tmap(Assembly.GetExecutingAssembly(). GetManifestResourceStreamC M Transparent_CS.mycat.jpg")); Bitma p imgNam e = ne w Bitmap(Assembly.GetExecutingAssembly(). GetMan i festResourceStream(Transparent_CS.catname.bmp")); g.DrawImage(imgCat. 0. 0. ne w Rectangle(0. 0. imgCat.Width, imgCat.Height). Graphicsllnit. Pixel); g.DrawImageCimgName, 50, 120, ne w Rectangle(0, 0, imgName.Width, imgName.Height), GraphicsUnit.Pixel); g.DisposeO; } privat e voi d butImage2_Click(objec t sender. EventArg s e) { Graphic s g = CreateGraphicsO; // получим картинки из ресурсов Bitma p imgCa t - ne w Bitmap(Assembly.GetExecutingAssembly(). GetMan i festResourceStream ( "TransparentC S .mycat. jpg")); Bitma p imgNam e - ne w Bitmap(Assembly.GetExecutingAssembly(). GetMan i festResourceStream ( "TransparentCS. catname. bmp")); // Очистим экран g.Clear(Color.White); // Выводим первую картинку g.DrawImage(imgCat. 0, 0, ne w Rectangle(0, 0. imgCat.Width. imgCat.Height). Graph i csUn i t.P i xel); ImageAttribute s att r - ne w ImageAttributesO; // Устанавливаем красный цвет как прозрачный attr.SetColorKey(Co l or.Red, Color.Red); // Выводим вторую картинку с установленными атрибутами продолжение & 104 Глава 6. Графика Листинг 6.1 8 {продолжение) Rectangl e dstRect * new Rectangle(50, 120. imgName.Width, imgName.Height); g.Draw!mage(imgName, dstRect. 0. 0, imgName.Width, imgName.Height, GraphicsUnit.Pixel,attr); g.DisposeO; } ВНИМАНИЕ He забудьте импортировать пространство имен System.Draw-
ingJmaging при работе с этим примером. Если просто наложить одну картинку на другую, то результат бу­
дет, мягко говоря, не очень красивым (рис. 6.5). Шг^.УГ^Щ]^.г _ ^.„ Рис. 6.5. Неудачный вариант наложения двух картинок Если же воспользоваться методом SetColorKey для установки про­
зрачного цвета, то результат наложения двух изображений будет выглядеть достойно (рис. 6.6). Рис. 6.6. Наложение картинки с использованием прозрачности Округленные прямоугольники __ 105 Округленные прямоугольники _^~ Так как .NET Compact Framework не позволяет создавать округ­
ленные прямоугольники встроенными средствами, то необходимо самостоятельно реализовать эту задачу. В этом разделе будет рас­
сматриваться решение, предложенное Алексом Яхниным (Alex Yakhnin) в его блоге bLog.opennetcf.org/ayakhnin/. Для достижения заданного эффекта надо нарисовать серию линий, которые соеди­
няют эллипсы, и закрасить внутреннюю область сплошным цветом (рис. 6.7). О Q Э С Рис. 6.7. Создание прямоугольника с о скругленным углами Соответствующий код приведен в листинге 6.19. Листинг 6.1 9 publi c stati c voi d DrawRoundedRectangleCGraphic s g. Pe n p. Colo r backColor, Rectangl e re. Size size) { Point[ ] point s • new Point[8]: // подготовим точки для фигуры points[0].Х - гс.Left + size.Width / 2; points[0].Y - re.Top + 1; poi nts[l ].X - re.Right - size.Width / 2; points[1].Y = rc.Top + 1; points[2].X - re.Right; points[2].Y - rc.Top + size.Height / 2; points[3].X = re.Right; points[3].Y = re.Botto m - size.Height / 2; points[4].X = re.Right - size.Width / 2; points[4].Y = re.Bottom; points[5].X = re.Lef t + size.Width / 2; points[5].Y - re.Bottom: points[6].X - re.Lef t + 1: points[6].Y = re.Botto m - size.Height / 2; points[7].X = re.Lef t + 1; points[7].Y - rc.Top + size.Height / 2; // ПРИГОТОВИМ КИСТЬ ДЛЯ фона продолжение & 106 Глав а 6. Графика Листинг 6.1 9 (продолжение) Brush fi l l Brus h - new SolldBrush(backColor); // рисуем отрезки и круги для округленног о прямоугольник а g.DrawL1ne(p. re.Lef t + size.Width / 2, re.Top, re.Right - size.Width / 2, re.Top); g.Fi l l El l i pse(fi l l Brush, re.Right - size.Width, re.Top, size.Width, size.Height): g.DrawEllipseCp. re.Right - size.Width, re.Top, size.Width, size.Height); g.DrawLineCp, re.Right, re.Top + size.Height / 2. re.Right, re.Botto m - size.Height / 2); g.Fi l l El l i pse(fi l l Brush, re.Right - size.Width, re.Botto m -
size.Height, size.Width, size.Height); g,DrawEllipse(p. re.Right - size.Width, re.Botto m -
size.Height, size.Width, size.Height); g.DrawLine(p, re.Right - size.Width / 2. re.Bottom. re.Lef t + size.Width / 2. re.Bottom); g.Fi l l El l i pse(fi l l Brush, re.Left. re.Botto m - size.Height, size.Width, size.Height): g.DrawEllipse(p, re.Left, re.Botto m - size.Height. si ze.Wi dth, si ze.Hei ght); g.DrawLineCp. re.Left, re.Botto m - size.Height / 2. re.Left, re.Top + size.Height / 2); g.Fi l l El l i pse(fi l l Brush, re.Left, re.Top. size.Width, size.Height); g.DrawEllipseCp, re.Left, re.Top, si ze. Wi dt h. si ze.Hei ght): // заполняем прямоугольник, скрывая внутренние эллипсы g.Fi11PolygonСfi11Brush. points); // освобождаем ресурсы f i 11Brush.Di spose(); } privat e voi d butDrawRoundedRectangle_Click(objec t sender. EventArg s e) { Graphics g - CreateGraphicsO; Rectangl e re - new RectangleClO. 10. 200, 50): D rawRoundedRect a ngle(g, new Pen(Color.Black), Color.CadetBlue. re, new Size(8, 8)); } Результа т работы этого кода показан на рис. 6.8. Создание экранных снимков 107 QHUyriWMHtrtHt! 0 Т||Ч? W& О Рис. 6.8. Отображение закрашенного прямоугольника со скругленными углами Создание экранных снимков Если при работе с мобильным устройством необходимо сделать скриншоты, то для реализации замысла необходимо использовать внешние устройства. Конечно, можно просто сфотографировать экран, но настоящий программист будет использовать функции Windows API. В этом разделе главы будет рассматриваться пример копирования определенной области окна, всего рабочего окна про­
граммы или любого другого окна. Для демонстрации примера надо разместить на форме список, три кнопки и один таймер. Сам код приведен в листинге 6.20. Листинг 6.2 0 [Dlllmportrcoredll.dir. EntryPoin t - "GetDesktopWindow") ] publi c stati c exter n IntPt r GetDesktopWindowO: [Dlllmportrcoredll.dir. EntryPoin t - "GetDC") ] publi c stati c exter n IntPt r GetDCdntPt r hWnd); [DllImport("coredll.dir, EntryPoint - "ReleaseOC") ] publi c stati c exter n IntPt r ReleaseDCCIntPt r hWnd. IntPt r hDC); [DllImportCcoredll.dll") ] publi c stati c exter n in t BitBltCIntPt r hdcDest. in t nXDest. int nYDest. in t nWidth. in t nHeight, IntPt r hdcSrc. in t nXSrc. in t nYSrc, uin t dwRop); cons t in t SRCCOP Y = OxOOCC0020; privat e voi d ScreenSnotCstrin g filename. Graphic s gx. Rectangl e rect ) { продолжение i& 108 Глав а 6. График а Листин г 6.20 (продолжение) Bitmap bmp - new Bitmap(rect.Width, rect.Height); Graphics g = Graphics.Fromlmage(bmp); BitBTt(g.GetHdcC). 0, 0, rect.Width, rect.Height. gx.GetHdcO. rect.Left, rect.Top. SRCCOPY); bmp.Save(fi1ename, System.Drawi ng.Imagi ng.ImageFormat.Bmp); bmp.DisposeO; g.DisposeO; } privat e voi d butPartOfWindow_Click(objec t sender, EventArg s e) { // Делаем снимок списка ScreenShot(@"\M y DocumentsXsave.bmp", this.CreateGraphicsC). listBoxl.Bounds); } privat e voi d butScreen_Click(objec t sender, EventArg s e) { // Делаем снимок экрана Rectangl e rec t - ne w Rectangle(0.0,240.240); Bitma p bm p = ne w Bitmap(rect.Width, rect.Height): Graphic s g - Graphics.Fromlmage(bmp); IntPt r hwn d - GetDesktopwindowO: IntPt r hd c - GetDC(hwnd); BitBlt(g.GetHdc(), 0. 0. rect.Width, rect.Height, hdc. rect.Left. rect.Top. SRCCOPY); bmp.Save(@"\M y Documents\screen.bmp". System.Drawing.Imaging.ImageFormat.Bmp); // Освобождаем ресурсы ReleaseDC(hwnd, hdc): bmp.DisposeO; g.DisposeO: } privat e voi d timerl_Tick(objec t sender, EventArg s e) { // Делаем снимок экрана через 5 секунд Rectangl e rec t = ne w Rectangle(0, 0. 240. 240); Bitma p bm p e ne w Bitmap(rect.Width, rect.Height); Graphic s g s Graphics.Fromlmage(bmp); IntPt r hwn d я GetDesktopWindowO; IntPt r hd c - GetDC(hwnd); Создание экранных снимков 10 9 BitBlt(g.GetHdcC). О, 0, rect.Width, rect.Height, hdc, rect.Left. rect.Top. SRCCOPY); bmp.Save(@"\My Documents\5sec.bmp". System.Drawing.Imaging.ImageFormat.Bmp); // Освобождаем ресурсы ReleaseDCChwnd. hdc): bmp.DisposeO; g.DisposeO; timerl.Enable d • false; } privat e voi d but5Sec_Click(objec t sender. EventArg s e) { timerl.Enable d = true; } Функция ScreenShot позволяет быстро получить участок экрана и сохранить его в графическом файле. В рассмотренном примере внешний вид списка сохраняется в файле listbox.bmp. Для этого до­
статочно было указать имя файла, объект Graphics и размеры спис­
ка Li stBox. Для получения снимка экрана пример пришлось несколь­
ко усложнить, добавив вызовы функций GetDesktopWindow и GeDC. Если нужно получить снимок другой программы, то придется вос­
пользоваться таймером. После запуска таймера в распоряжении пользователя будет 5 секунд, чтобы запустить другое приложение. Основная программа будет работать в фоновом режиме и сделает снимок экрана. Чтобы проверить работу приложения, нужно запустить програм­
му, нажать каждую кнопку, а затем с помощью программы File Explorer найти сохраненные файлы. ВНИМАНИЕ Нужно проявлять определенную осторожность при работе с ме­
тодом Bitmap.Save(). Дело в том, что в Windows Mobile 2003 и бо­
лее ранних версиях операционных систем библиотека .NET Compact Framework не поддерживает сохранение графических файлов в форматах GIF, JPEG или PNG. Сохранять файлы можно только в формате BMP. Причем во время написания кода редак­
тор не заметит ошибки и позволит запустить программу с непра­
вильным вызовом метода. Однако при вызове метода возникнет исключение NotSupportedException. К счастью, в Windows Mobile 5.0 поддерживаются все четыре графических формата. 110 Глав а 6. Графика Метод Lockbits В .NET Compact Framework 2.0 появилась ограниченная поддерж­
ка метода LockBits, при помощи которого можно манипулировать массивом пикселов изображения. Перечисление ImageLockMode в данном методе позволяет использовать значения Readerite, Readonly и WriteOnly. А перечисление Pixel Format поддерживает значения, перечисленные в следующем списке: • Formatl6bppRgb555; • Format16bppRgb565; Q Format24bppRgb; а Format32bppRgb. Ha сайте MSDN можно найти статью «How to: Use LockBits» с при­
мером, в котором создается картинка и меняется интенсивность си­
них пикселов с помощью метода LockBits. В листинге 6.21 приведен пример, который для большей наглядности пришлось немного из­
менить. Листинг 6.21 privat e Bitma p CreateBitmapCin t width, in t height ) { Bitmap bmp - new ВШар((а"М™с1см5\т$п.д1Г): widt h - bmp.Size.Width; height - bmp.Size.Height; Graphics g - Graphics.FromImage(bmp): g.OisposeO; retur n bmp; } protecte d override void OnPaint(PaintEventArgs e) { Bitma p bm p - CreateBitmapdOO, 100); // Выводим картинку-оригинал e.Graphics.Drawlmage(bmp. 0. 0); MakeMoreBlue(bmp); // Рисуем модифицированную картинку ниже исходного изображения е.Graphics.DrawImageCbmp. 0. 50); bmp.DisposeO; } privat e voi d MakeMoreBlue(Bitma p bmp ) { Графический редактор 11 1 // Задаем формат данных о цвете для каждой точки изображения Pixe l Forma t px f = Pixe l Format.Format24bppRgb; // Блокируем изображение в памяти Rectangl e rec t - ne w RectangleCO. 0, bmp.Width, bmp.Height): BitmapDat a bmpDat a = bmp.LockBitsCrect. ImageLockMode.ReadWrite, pxf): // Получаем адрес первой строки развертки IntPt r pt r * bmpData.ScanO; // Массив, содержащий байты изображения in t numByte s = bmp.Widt h * bmp.Heigh t * 3; byte[ ] rgbValue s - ne w byteCnumBytes]: // Копируем значения RG B в массив Marshal.Copy(ptr. rgbValues, 0, numBytes): // Модифицируем изображение, устанавливая // синий цвет для каждой точки в картинке fo r (in t counte r = 0: counte r < rgbValues.Length; counte r + = 6 ) rgbValues[counter ] *= 255: // Копируем значения RG B обратно в изображение Marshal.Сору(rgbValues. 0. ptr. numBytes); // Разблокируем биты в памяти bmp.UnlockBits(bmpData); } После запуска приложения на экране будут показаны две копии картинки, причем нижнее изображение будет немного отличаться от верхнего насыщенностью цветов. Графический редактор Теперь, когда мы ознакомились с графическими методами, настало время написать простейший графический редактор с минимальны­
ми возможностями. В этом приложении можно будет рисовать при помощи стилуса линии, а также прямые цветные линии из трех цве­
тов. Процесс рисования узоров на экране КПК при помощи стилу­
са гораздо больше похож на реальное рисование, чем рисование мышью в стандартных графических редакторах. Весь код программы сводится к обработке событий мыши MouseOown, MouseMove и NouseUp. В принципе, приемы создания графических эф­
фектов ничем не отличаются от соответствующих приемов, приме­
няемых на обычных персональных компьютерах. Я взял два при­
мера из своей книги «Занимательное программирование на Visual Basic .NET» и перенес код в проект с учетом синтаксиса языка С#, что иллюстрирует листинг 6.22. 112 Глав а 6. Графика Листинг 6.2 2 privat e in t xmd. yjnd; Pe n myPe n = ne w Pen(Color.LightBlue); privat e boo l bPaint; Graphic s g; privat e Pe n erasePen: privat e Poin t ptsStart; privat e Poin t ptsPrevious; privat e Poin t ptsCurrent; privat e voi d FormlMouseDow n (objec t sender, MouseEventArg s e) { // Начинаем рисование bPain t • true; if (mnuLines.Checked ) { ptsStart.X -e.X; ptsStart.Y = e.Y; ptsPreviou s = ptsStart; } if (mnuPaint.Checked ) { // координаты стилуса при нажатии xjn d - e.X; yjn d = e.Y; } privat e voi d Forml_MouseMove(objec t sender, MouseEventArg s e) { if (bPaint ) { if (mnuLines.Checked ) { ptsCurrent.X - e.X; ptsCurrent.Y -e.Y; g - CreateGraphicsO: g.DrawLine(erasePen. ptsStart.X, ptsStart.Y. ptsPrevious.X, ptsPrevious.Y); g.DrawLine(myPen. ptsStart.X. ptsStart.Y, ptsCurrent.X. ptsCurrent.Y); ptsPreviou s = ptsCurrent; g.DisposeO; Графический редактор 11 3 } i f (mnuPaint.Checked) { g - CreateGraphicsO; i n t xjnm « e.X; i n t ymm = e.Y; g.DrawLine(myPen. xmd. y_md. xjran. y_mm); x_md » xmm: y_m d • yjnm: g.DisposeO: } } } privat e voi d Forml_MouseUp(objec t sender. MouseEventArg s e) { bPaint = false; } privat e voi d mnuClear_Click(objec t sender. EventArg s e) { g - CreateGraphicsO; g.Clear(this.BackColor); g.DisposeO; } privat e voi d Forml_Load(objec t sender. EventArg s e) { erasePe n - ne w Pen(this.BackColor); } privat e voi d mnuPaint_Click(objec t sender. EventArg s e) { mnuPaint.Checke d = !mnuPaint.Checked; mnuLines.Checke d - ImnuLines.Checked; } privat e voi d mnuGreenPen_Click(objec t sender. EventArg s e) { myPen.Color = Color.Green; } private void ranuRedPen_Click(ot>ject sender. EventArgs e) { myPen.Color - Col or.Red: } 114 Глава 6. Графика На рис. 6.9 показано, как выглядит созданный графический редак­
тор в работе. HbiHW кулажи. И Уй Ц^ ?:•*? Л У\ I Рис. 6.9. Простейший графический редактор Дополнительные материалы Если нужно ознакомиться с дополнительными материалами по про­
граммированию графики, то стоит обратиться к статьям документа­
ции MSDN. Например, в статье «How to: Display a Gradient Fill» рас­
сказывается^ том, как раскрасить элемент градиентной заливкой. Также в документации MSDN имеется статья уже известного нам Алекса Яхнина «Creating a Microsoft .NET Compact Framework-based Animation Control», в которой рассказывается о создании элемента управления, способного воспроизводить анимированное изображе­
ние, создавая его на основе серии картинок. Тем, кто планирует пи­
сать приложения для устройств под управлением Windows Mobile 5.0, стоит обратить внимание на технологию Windows Mobile DirectX and Direct3D, которая дает дополнительные возможности для рабо­
ты с графикой. Глава 7 Разработка приложений Активация и деактивация формы Модель выполнения программ на карманном компьютере отличает­
ся от поведения программ, работающих на обычном персональном компьютере. Например, на мобильных компьютерах используется один экземпляр запущенной программы. Аналогом подобного пове­
дения на настольных компьютерах является почтовая программа Outlook Express, которая всегда запускается в одном экземпляре. При попытке запуска программы она просто активируется (если уже была запущена). При этом вторая копия программы не запускается. При создании приложения для КПК разработчику не придется при­
лагать никаких усилий для реализации подобного поведения. Среда выполнения .NET Compact Framework сама позаботится о том, чтобы запускался только один экземпляр программы. Следует помнить, что пользователь не должен сам закрывать программу. При запуске новой программы ее окно просто загораживает предыдущую программу. Учитывая подобное поведение, нужно писать программы, которые н е занимают много ресурсов системы. Однажды запущенное приложе­
ни е может находиться в памяти несколько дней, пока пользователь н е перезагрузит компьютер или не закроет программу самостоятельно. Деактивированная программа закроется автоматически, если система обнаружит уменьшение свободной памяти при разрядке батареи. Но, тем н е менее, иногда надо проследить, чтобы пр и закрытии программа освободила ресурсы, которые она использовала. Бывают ситуации, когда приложение поддерживает соединение с базой данных или осу­
ществляет связь с СОМ-портами. В этом случае система может не освободить занимаемые программой ресурсы. Для отслеживания со­
стояния формы используются события Form. Deacti vate и Form. Acti vated В листинге 7.1 приведен пример работы с этими событиями. Листинг 7.1 privat e voi d Forml_Activated(objec t sender, EventArg s e) { // Здесь ваш код для восстановления связей с портами и т. д. продолжение & 116 Глава 7. Разработка приложений Листинг 7.1 {продолжение) Ibllnfo.Tex t = "Приложение активировано"; } privat e void Forml_Deactivate(objec t sender. EventArgs e) с // Здесь ваш код для освобождения ресурсов Ibllnfo.Tex t • "Приложение деактивировано": } Так как приложение в неактивном состоянии может быть закрыто системой, то важно блокировать возможную потерю данных. Для этого нужно использовать событие Deactivate. Закрыть или свернуть окно Закрыть или свернуть — вот в чем вопрос. Компания Microsoft пред­
ложила для мобильных приложений модель поведения программ, отличающую от принятой в настольных компьютерах. Когда пользователь щелкает на кнопке закрытия, то на самом деле окно программы не закрывается, а сворачивается. Для пользователей подобное поведение приложений кажется странным, поэтому не­
которые разработчики создавали программы, которые позволяли закрывать приложения одним нажатием стилуса. Популярность таких программ говорит о том, что не всем пользователям понрави­
лось поведение приложений, которые отнимают ресурсы у систе­
мы. Но сейчас не нужно обсуждать целесообразность такого подхо­
да к закрытию программ. Разработчик может создать приложение, которое позволит выбрать вариант закрытия приложения. Пользо­
ватель может нажать кнопку закрытия, чтобы просто свернуть окно, либо выполнить команду меню Выход, чтобы действительно закрыть приложение. Но бывают ли такие ситуации, когда действительно требуется при­
нудительно закрывать программу? Такая необходимость возника­
ет при отладке и тестировании программы в эмуляторе. При стан­
дартной модели поведения довольно утомительно каждый раз вручную останавливать программу, запущенную в эмуляторе. Ко­
нечно, можно временно присвоить свойству Mi nimi zeBox при отлад­
ке значение False, что поможет избавиться от этой проблемы. Но перед окончательным релизом программы надо все же поставить значение True. Однако полагаться на свою память не стоит. Гораздо проще воспользоваться условной компиляцией. Пиктограмма приложения 117 При создании приложения надо использовать несколько строчек кода в конструкторе формы сразу после вызова процедуры InitializeComponent(), как показано в листинге 7.2. Листинг 7.2 #i f DEBUG MirrimizeBox = false; #els e MirrimizeBox - true; #endi f Этот код стоит вынести на панель инструментов (рис. 7.1), что по­
зволит быстро добавлять эту конструкцию в создаваемые прило­
жения. Отныне все примеры в данной книге будут снабжаться этим кодом. CrateApp-_CS - Mi crosof t Visual $&&& •i i! W_ .. Ш VK«. Redactor Ptojtct Mei Oebug Dec» T«* t W Text H DEBUG MlrtmfeeBox «fate; Mst Мг*пЬевок = true; газ; p_CS Ш I cl ass FQEH U : Tour, | rml() aliz«Caspor:ent {J; frEBUG inircizeBcx • f al s e; ixaisixeScx - t r oe; f Рис. 7.1. Код условной компиляции на панели инструментов Пиктограмма приложения Любая серьезная программа должна иметь собственную пиктограм­
му. Чтобы указать используемую пиктограмму, надо при помощи команды меню Project • Properties открыть диалоговое окно Property 118 Глава 7. Разработка приложений Pages, выбрать раздел Application и указать путь к файлу с пикто­
граммой в свойстве Icon (рис. 7.2). Crat*AjJj»„C$ - Microsof t Visual Studi o Project MO Difiug p«a Tboh Window Cwr»nunjY Help ?Ж taM^'frmi^gejBPJ -f'C»teApp_cs* r; StartPage, Cfcjedt Browser Appfcatoo * ",K As-wmi * ram*: cra»»pp_cs Oaput: typ». V/iYJawsAfcfcalJcn D*»aut п*т*ед»сет >Crat8App_CS *3s»-"iV Wo nrwnen. : ••"•;".•"; Рис. 7.2. Добавление пиктограммы для приложения Создание собственных диалоговых окон Сложные приложения часто используют несколько форм. Напри­
мер, во многих программах имеется диалоговое окно 0 программе, в котором отображаются информация о программе, номер версии, сведения об авторе и логотип компании. Для создания подобных форм хорошо подойдет собственное диа­
логовое окно. Чтобы отобразить такое окно, используется метод ShowDialog. Этот метод делает недоступным родительскую форму, пока диалоговое окно находится на экране. Диалоговое окно может возвращать результат вызова метода ShowDialog не только себе, но и родительскому окну. Предположим, что нужно создать специальное окно авторизации пользователя для доступа к программе. В состав проекта нужно вклю­
чить новую форму, которая будет реализована как диалоговое окно проверки имени пользователя LogonForm. Это будет маленькое окно без четко очерченной границы. В нем надо разместить текстовое поле и две кнопки. Затем надо задать значения свойств FormBorderStyl е, Size и Location. При загрузке основной формы и обработке события Load создается новый экземпляр объекта LogonForm и вызывается как Создание собственных диалоговых окон 119 диалоговое окно. Данное окно может вернуть значения Dial ogResul t. OK, если пользователь ввел имя, или Diа 1 ogResult.Cancel, если он просто закрыл форму. Если было введено правильное имя, то главная форма продолжает свою работу. В противном случае приложение следует закрыть. Соответствующий код приведен в листинге 7.3. ВНИМАНИЕ Чтобы элементы управления диалогового окна были доступны вы­
зывающей форме, их надо объявить с модификатором public. По умолчанию используется модификатор private. Листинг 7-3 privat e voi d FormlJ_oad(objec t sender, EventArg s e) { LogonFor m LogonFr m = ne w LogonFormO; if (LogonFrm.ShowDialog O — DialogResult.Cancel ) { LogonFrm.D i spose(); this.CloseO; } els e { this.Text +• " - " + LogonFrm.txtCheck.Text: LogonFrm.Di spose(); } } После того как форма авторизации будет отображена на экране, нужно обработать события CI i ck для нажатия кнопки проверки вве­
денного имени пользователя или кнопки отмены. Первая кнопка проверяет правильность ввода имени. Если проверка завершилась успешно, то возвращается значение Di а 1 ogResul t. ОК. Это иллюстри­
рует код, приведенный в листинге 7.4. Листинг 7.4 privat e voi d butOKClick C objec t sender. EventArg s e) { if (txtCheck.Tex t — "Alex") { this.DialogResul t « DialogResult.OK; } els e { продолжение & 120 Глава 7. Разработка приложений Листинг 7.4 (продолжение) MessageBox.ShowCB доступе отказано. Попробуйте еще раз". "Вход в программу"); } } Если пользователь не знает имени для доступа к программе, то ему придется нажать кнопку Отмена. В этом случае обработчик собы­
тия butCancel Click, код которого приведен в листинге 7.5, возвра­
щает значение DialogResult.Cancel в главную форму, которая за­
крывает приложение. Листинг 7.5 privat e voi d butCancel_Click(objec t sender. System.EventArg s e) { this.DialogResul t - DialogResult.Cancel; } Создание заставки Splash Screen Многие программы имеют так называемые заставки (splash screen). При загрузке формы сначала отображается окно с логотипом ком­
пании, названием продукта и дополнительной информацией. Сле­
дует реализовать приложение с подобным экраном, чтобы научить­
ся использовать эту технологию. Прежде всего надо создать новый проект и добавить к уже имею­
щейся форме еще одну форму с именем Spl ash. При запуске прило­
жения заставка появится во весь экран с заданным текстом в цент­
ре экрана. Эта форма будет отображаться в течение трех секунд, а затем она автоматически закроется и на экране останется основ­
ная форма. Создание подобного окна практически не отличается от предыду­
щего примера. Но в этом примере надо использовать таймер, кото­
рый будет отвечать за появление и закрытие начальной заставки. Эта же форма будет использоваться как диалоговое окно для стан­
дартного пункта меню 0 программе. Итак, надо создать дополнительную форму AboutForm и задать зна­
чения всех необходимых свойств окна. На форме надо располо­
жить таймер, интервал срабатывания которого будет равен 3 с. Код, реализующий подобное поведение программы, приведен в ли­
стинге 7.6. Создание заставки Splash Screen 121 Листинг 7.6 protecte d overrid e voi d OnPaint(PaintEventArg s e) { StringForma t s f = ne w StringFormatO; sf.Alignmen t - StringAlignment.Center: sf.LineAlignmen t - StringAlignment.Center; Graphic s g = e.Graphics; g.DrawString(".NE T Compac t Framework", this.Font. ne w SolidBrush(Color.Blue), Screen.PrimaryScreen.Bounds, sf); } privat e voi d timer l Tick(objec t sender, EventArg s e) { this.CloseO: } В событии OnPai nt формы AboutForm нужно установить свойства для вывода текста. При желании можно добавить отображение логоти­
па. Через заданный интервал таймер просто закроет это окно. Код для основной формы MainForm приведен в листинге 7.7. Листинг 7.7 publi c MainFormO { Ini t i al i zeComponent(); #i f DEBUG MinimizeBo x - false; #els e MinimizeBo x - true; #endi f AboutFor m abou t = ne w AboutFormO; about.ShowDialog(); privat e voi d mnuAbout_Click(objec t sender. EventArg s e) { AboutForm about = new AboutFormO; about.ShowDi al og(); Теперь пр и запуске приложения на экране сначала будет отображать­
ся заставка. После истечения трех секунд она исчезнет, и пользова­
тель увидит основную форму. , Я» -
122 Глава 7. Разработка приложений Поворот экрана Устройства с операционной системой Pocket PC 2003 Second Edition и старше обрели долгожданную возможность поворачи­
вать содержимое экрана. Раньше пользователям приходилось ус­
танавливать дополнительные программы для достижения такого эффекта. А разработчики получили возможность управлять по­
воротами экрана управляемыми методами только в .NET Compact Framework 2.0. Но зато теперь это можно сделать буквально од­
ной строкой кода. Тем, кто по ряду причин должен по-прежнему использовать .NET Compact Framework 1.0, придется задейство­
вать сложный код с вызовами функций API, который приведен в листинге 7.8. Сначала надо установить ссылку на пространство имен Mi crosof t. Wi ndowsCE. Forms. После этого следует просто исполь­
зовать нужные свойства класса SystemSettings. Листинг 7.8. usin g Microsoft.WindowsCE.Forms; // запоминаем настройки экрана ScreenOrientatio n initialOrientatio n -
SystemSett i ngs.ScreenOrientat i on: privat e voi d butRot90_Click(objec t sender. EventArg s e) { // поворачиваем экран на 9 0 градусов SystemSettings.ScreenOrientatio n -
ScreenOr i entat i on.Angle90; i privat e voi d butRestore_Click(objec t sender, EventArg s e) { // восстанавливаем старую ориентацию if (SystemSettings.ScreenOrientatio n !• initialOrientation ) { tr y { SystemSettings.ScreenOrientatio n = initia l Orientation; } catc h (Exception ) { // Невозможно вернуться к старым настройкам Готовые приложения 12 3 MessageBox.ShowC'He ногу восстановить и + ^предыдущую ориентацию экрана."); } } } Рекомендации по дизайну форм Компания Microsoft выработала определенные рекомендации по дизайну форм, которых следует придерживаться. Эти рекоменда­
ции можно найти в документации MSND. Так, например, в одной из статей указывается, что в некоторых случаях пользователь пред­
почитает пользоваться пальцами вместо стилуса. Поэтому, если форма содержит кнопки, то они должны быть достаточно больши­
ми, чтобы было удобно нажимать на них. Рекомендуемые размеры кнопок для Pocket PC составляют 21 х х 21 пикселов, если пользователь применяет стилус, и 38 х 38 пик­
селов, если он предпочитает нажимать кнопки пальцами. Также надо предусмотреть свободное пространство между элементами, чтобы избежать ошибочных нажатий. Если маленькие кнопки для копирования и удаления файлов находятся рядом, то пользовате­
ли не раз вспомнят вас недобрым словом при неаккуратном нажа­
тии на кнопки. В то же время следует группировать часто используемые элемен­
ты, чтобы пользователю не пришлось часто перемещать стилус. Это тоже достаточно утомительно. Понятно, что к играм эти рекомен­
дации не относятся. В них действуют свои законы. Готовые приложения До сих пор мы с вами изучали примеры, которые могли бы стать частью настоящих программ. Но сейчас пришло время написать не­
сколько приложений, которые уже можно использовать в реальной жизни. Так получилось, что первые две программы были написаны для смартфонов, о которых речь пойдет в дальнейших главах. Но при помощи этих примеров можно получить представление о программировании для этого класса устройств. К тому же, на их основе можно написать аналогичный пример для карманных ком­
пьютеров, что поможет увидеть сходство в написании приложений для разных типов устройств. 124 Глава 7. Разработка приложений Файловый менеджер для смартфона Смартфоны под управлением Windows Mobile 2005 не имеют в со­
ставе системы приложения, которое позволяет просматривать со­
держимое папок и файлов. В этом разделе будет рассматриваться аналог Проводника для смартфона. Основой данного примера по­
служил великолепный проект с открытым исходным кодом, кото­
рый находится на сайте www.businessanyplace.net/Tp~spfileman. Ав­
тор проекта Кристиан Форсберг (Christian Forsberg) любезно разрешил использовать его программу в качестве учебного посо­
бия для этой книги. Интересна история создания этого приложения. Сам автор ориги­
нальной версии писал программу еще на Visual Studio .NET 2003 для смартфонов под управлением системы Smartphone 2003. Когда я скачал исходный код и попытался запустить его, то среда разра­
ботки Visual Studio .NET 2005 предложила конвертировать проект. Я согласился, в результате получил новую версию проекта. Теперь, после переделки, проект запускался в Visual Studio 2005 и исполь­
зовал эмулятор Smartphone 2003. Но мне захотелось использовать пример для смартфонов Windows Mobile 2005. Для этого достаточ­
но было в свойствах проекта выбрать другую платформу. Снова несколько минут раздумий, и Visual Studio выдает еще раз переде­
ланный проект. Но и этого мне мало. Проект по-прежнему исполь­
зует .NET Compact Framework 1.0. После выбора нужных значений свойств проекта, он стал использовать .NET Compact Framework 2.0. Осталось лишь перевести некоторые команды на русский язык и ввести новые возможности .NET Compact Framework 2.0 вместо старых конструкций, которые применялись для .NET Compact Framework 1.0. Зачем нужен файловый менеджер Файловый менеджер необходим как для разработчиков, так и для пользователей. С его помощью можно просматривать содержимое папок, удалять лишние файлы, создавать новые папки и выполнять другие операции с файлами. Трудно сказать, почему Microsoft ре­
шила не включать программу подобного рода в состав системы Windows Mobile. Кстати, производители смартфонов самостоятель­
но добавляют файловые менеджеры собственной разработки в со­
став стандартных программ. Но мы с вами напишем свою програм­
му, что гораздо интереснее. Файловый менеджер для смартфона 125 Графический интерфейс программы У создаваемого приложения будет своя пиктограмма. При запуске программа будет отображать содержимое папки My Documents. Сам графический интерфейс программы очень прост и понятен. Нави­
гация по папкам осуществляется простым выделением нужной пап­
ки и нажатием кнопки Enter. Для перехода на один уровень вверх нужно выделить папку, обозначенную двумя точками. Пункт меню Выход закрывает файловый менеджер, а пункт Меню позволяет вы­
брать выполняемую операцию (рис. 7.3). Рис. 7.3. Общий вид файлового менеджера Меню содержит команды для всех стандартных файловых опера­
ций. Пользователь может удалять, копировать, добавлять и пере­
именовывать файлы. Также имеется возможность работы с ярлы­
ками. Чтобы использовать эту возможность, нужно сначала выбрать файл, выполнить команду Копировать, затем перейти в нужную пап­
ку и выполнить команду Вставить ярлык. При выборе команды Свой­
ства появляется соответствующее окно (рис. 7.4). В этом окне отображается справочная информация о файле или папке. Пользователь сможет найти размер файла, дату его созда­
ния и атрибуты файла, которые можно модифицировать. 126 Глава 7. Разработка приложений Вж •MB Ceatc * 2S.tL2?X3*l*.it Attributes: -> :-: 4 О Ait»* Ffttatiin Рис. 7.4. Окно свойств Код программы Теперь можно приступить к написанию кода. При запуске програм­
мы выполняется обработчик события FormJ_oad. При загрузке ос­
новной формы Mai nForm работает код, приведенный в листинге 7.9. Листинг 7.9 Li stV i ewHelper.SetGrad i ent(1 i stV i ew); strin g b s - Path.DirectorySeparatorChar.ToStringO; // Устанавливаем начальную папку this.pat h = b s + "M y Documents" + bs; // Заполняем список папок и файлов fillListO: Сначала устанавливается внешний вид элемента l i st View с гради­
ентной закраской фона. Затем устанавливается папка по умолча­
нию My Documents, которую программа открывает при загрузке. Ме­
тод f i 11L i st заполняет список L i stVi ew содержимым открываемой папки. Сам код метода приведен в листинге 7.10. Листинг 7.1 0 /// <suimnary > /// Заполнение ListVie w списком папок и файлов /// </summary > privat e voi d fillListО { Cursor.Curren t - Cursors.WaitCursor; // Заполняем ListVie w списком папок и файлов // в выбранной папке ListViewlter n lvi: 1i stV i ew.Beg i nUpdate(); listView.Items.CIear(); // Если не корневая папка if(path.Lengt h > 1) { Файловый менеджер для смартфона 127 // Добавляем папку "Вверх" l vi = new ListViewItem(UPDIR): lvi.Imagelndex * 0; 1istView.Iterns.Add(1vi); } // Добавляем папки string[ ] dir s » Directory.GetDirectories(path); ArrayLis t lis t - ne w ArrayListCdirs.Length); for(in t i - 0; i < dirs.Length; i++ ) list.Add(dirs[iJ): list.Sort(ne w S i mpleComp a rer()); foreach(strin g di r in list ) { lv i - ne w ListViewItem(Path.GetFileName(dir)); lvi.Imagelnde x - 0; 1istView.Items.AddClvi); } // Добавляем файлы stri ng d f i l es - Directory.GetFiles(path); l i s t s new ArrayListCfiles.Length); for(i n t i = 0; i < files.Length; i++) l i st.Add(f i l es[i ]); list.Sort(ne w SimpleComparerO); foreach(strin g f i l e i n l i st ) { lv i - ne w ListViewItem(Path.GetFileName(file)); lvi.Imagelnde x - 1; listView.Items.Add(lvi): } listView.EndUpdateO; if(listView.Items.Coun t > 0) { // выделяем первый элемент 1istView.Iterns[0].Selecte d = true; listView.Items[0].Focuse d - true; } Cursor.Curren t = Cursors.Default; } Итак, посмотрим, что делает метод fillList. Перед заполнением элемента списком файлов надо очистить его содержимое от преды­
дущих записей при помощи метода Clear. После очистки списка надо проверить, является ли папка корневой. Если папка не корне-
128 Глава 7. Разработка приложений вая, то в список добавляется специальная папка «На один уровень выше». Затем в список добавляются все папки в отсортированном порядке. После этого наступает очередь файлов. Они сортируются и зано­
сятся в список. Наконец, первый элемент списка выделяется дру­
гим цветом. Заодно на первом элементе устанавливается фокус вво­
да. Навигация по папкам и файлам осуществляется с помощью кнопок и дополняется кнопкой Action. Код навигации приведен в листинге 7.11. Листинг 7.11 /// <summary > /// Навигация по папкам и файлам /// </summary > /// <para m name="sender"></param > /// <para m name- ne,,></param > privat e voi d listView_ItemActivate(objec t sender. System.EventArg s e) { Cursor.Curren t • Cursors.WaitCursor; ListViewIte m lv i -
listView.Items[listView.SelectedIndices[0]]: boo l isFolde r - Ivi.Imagelnde x — 0: ifClvi.Tex t — UPDIR ) { path - path.SubstringCO, path.SubstringCO. path.Lengt h -
D.LastlndexOf(Path.DirectorySeparatorChar ) + 1): f l ULi st O; } else i f(i sFol der ) { path +» l vi.Text + Path.DirectorySeparatorChar; f i l l Li st O; } else ShellExecute.Start(pat h + l vi.Text): Cursor.Curren t - Cursors.Default; } После нажатия кнопки действия приложение получает информа­
цию о выделенном пункте. Если выделена специальная папка пе­
рехода на один уровень выше, то текущий путь заменяется путем к родительской папке. Если выделена папка, то путь меняется на Файловый менеджер для смартфона 129 путь к выделенной папке. Если выделен файл, то приложение пы­
тается запустить его с помощью ассоциированной программы. Теперь разберем код для команд меню. Для команды Вырезать код приведен в листинге 7.12. Листинг 7.1 2 privat e voi d cutMenuItem_Click(objec t sender, System.EventArg s e ) { ListViewIte m lv i * listView.Items[listView.SelectedIndices[0]]; clipboardFileNam e = this.pat h + lvi.Text: clipboardActio n - ClipboardAction.Cut: } Путь к текущему выбранному файлу сопоставляется с производи­
мым действием. Код, выполняющийся после выбора команды Ко­
пировать, приведен в листинге 7.13. Листинг 7.1 3 privat e voi d copyMenuItem_Click(objec t sender. System.EventArg s e ) { ListViewItem l vi -
listView.Items[listView.SelectedIndices[0]]: clipboardFileNam e - pat h + l vi.Text: clipboardActio n - ClipboardAction.Copy; } Для команды меню Вставить код немного усложняется. Он приве­
ден в листинге 7.14. Листинг 7.1 4 privat e voi d pasteMenuItem_Click(objec t sender. System.EventArg s e ) { // Если файл существует strin g des t - pat h + Path.GetFileName(clipboardFileName); if(File.Exists(dest) ) { ifCMessageBox.ShowCOaKn уже существует, перезаписать?", this.Text, MessageBoxButtons.YesNo. MessageBoxIcon.Quest i on. 5-2873 продолжение •& 130 Глава 7. Разработка приложений Листинг 7.14 (продолжение) MessageBoxDefaultButton.Button2 ) — DialogResult.Yes) File.Delete(dest); else return; } // Перемещаем или копируем strin g s - path.SubstringCO. path.Lengt h - 1); sw i te n (c l i pbo a rdAct i on ) { cas e ClipboardAction.Cut: Fi1e.Move(cl i pboardFi1eName. dest); break; cas e ClipboardAct i on.Copy: Fi1e.Copy(clipboardFileName, dest. false); break; } clipboardActio n - ClipboardAction.None; clipboardFi1eNam e - string.Empty; fillListO; } Перед тем как вставить файл в другую папку, нужно удостоверить­
ся, что в ней нет файла с таким именем. Если же такой файл суще­
ствует, то надо предупредить пользователя и узнать, что он хочет сделать. Код для команды Вставить ярлык приведен в листинге 7.15. Листинг 7.15 privat e voi d pasteShortcutMenuItemClic k (objec t sender. System.EventArg s e) { in t i • 2: strin g s - string.Empty; strin g dest: while(true ) { dest - pat h + "Shortcut" + s + " t o " + Path.GetFi1eName(Path.GetFileNameWi thoutExtens i o n (clipboardFi1eName ) + ".Ink"); i fUFi l e.Exi sts(dest) ) break; s = " Г + LToStringO + ")"; i++: Файловый менеджер для смартфона 131 } StreamWriter sw - new StreamWriter(dest): s = clipboardFileName; if(s.IndexOf(" ") > 0) s = и\и и + s + "\""; s - s.Length.ToString( ) + "#" + s: sw.WriteLine(s); sw.CloseO; f i l l Li st O; } В этом коде создается уникальное имя ярлыка, которое затем запи­
сывается в виде файла с добавлением. К имени ярлыка добавляет­
ся расширение .LNK. Код для команды Переименовать приведен в листинге 7.16. Листинг 7.1 6 privat e voi d renameMenuItem_Click(objec t sender. System.EventArg s e) { Cursor.Curren t - Cursors.WaitCursor; ListViewItem l vi -
1istView.Items[l i stView.SelectedIndices[0]]; bool isFolder - lvi.Imagelnde x = 0; stri n g s; i f(i sFolder ) s = "папку"; else s = "файл"; NameForm nameForm - new NameForm(this. "Переименовать " + s. l vi.Text. new SetNameDelegate(SetRename)); if(nameForm.ShowDialog O — DialogResult.OK) f i l l Li st O; listView.FocusO: } Сначала обрабатывается текущий выделенный элемент. Если пользо­
ватель выделил папку, то для формы nameForm задается соответству­
ющий заголовок Переименовать папку. Также из этой формы передает­
ся в основную форму новое имя папки или файла с помощью метода SetRename, как это показано в листинге 7.17. Листинг 7.1 7 /// <summary > /// Метод для переименования папки или файла продолжение & 5* ФС 132 Глава 7. Разработка приложений Листинг 7.17 (продолжение) III </summary > /// <param пате=япаше,,>Иия папки или файла</рагат> publi c void SetRename(strin g name) { ListViewIte m 1v 1 -
listView.Iterns[listView.SelectedIndices[0]]: boo l isFolde r я lvi.Imagelnde x — 0: strin g itemNam e - pat h + lvi.Text; strin g destNam e = Path.GetDirectoryName(itemName ) + Path.DirectorySeparatorChar.ToString O + name; if(isFolder ) Di rectory.Move(itemName. destName); els e File.Move(itemName, destName); } После того как будет получена информация о выделенном элемен­
те, он переименовывается. Для реализации команды Удалить исполь­
зуется код, приведенный в листинге 7.18. Листинг 7.18 privat e voi d deleteMenuItem_Click(objec t sender. System.EventArg s e) { ListViewItem I vi = listView.Items[listView.SelectedIndices[0]]; bool isFolder - lvi.Imagelndex =» 0; stri n g s e "Are you sure you want to delete " + l vi.Text; if(isFolder ) s +« " and al l i t s content"; s +« "?"; if(MessageBox.Show(s. this.Text. MessageBoxButtons.YesNo, MessageBoxIcon.Question. MessageBoxDefaultButton.Button2 ) = DialogResult.Yes ) { if(isFolder ) Directory.Delete(pat h + l vi.Text. true): else File.Delete(pat h + l vi.Text); f i l l Li st O; } } Файловый менеджер для смартфона 133 Перед удалением папки или файла запрашивается подтверждение действий пользователя. Для создания новой папки используется следующий код, приведенный в листинге 7.19. Листинг 7.19 privat e voi d newFolderMenuItem_Click(objec t sender. System.EventArg s e) { Cursor.Curren t = Cursors.WaitCursor; ListViewItem l vi -
1istView.Items[l i stView.SelectedIndices[0]]; NameForm nameForm = new NameFornKthis. "Новая папка". "". ne w SetNameDelegate(SetNewName)): if(nameForm.ShowDialog( ) = DialogResult.OK) f i l l Li s t O; 1 i stView. Focus 0; } В результате действия этой функции отображается форма NameForm с заголовком Новая папка. Эта форма также передает информацию в главную форму при помощи метода SetNewName, который приведен в листинге 7.20. Листинг 7.20 /// <summary > /// Устанавливает новое имя для папки /// </summary > /// <para m пате^пате^Иня для папки</рагат> publi c voi d SetNewName(strin g name ) { Directory.СreateDirectory(path + name): } Метод создает папку с заданным именем. Как видно, код его чрез­
вычайно прост. Код для выполнения команды Свойства приведен в листинге 7.21. Листинг 7.21 privat e voi d propertiesMenuItem_Click(objec t sender. System.EventArg s e) { Cursor.Curren t - Cursors.WaitCursor; продолжение & 134 Глава 7. Разработка приложений Листинг 7.2 1 (продолжение) ListVlewItem lv1 = listView.Items[listView.SelectedIndices[0]]: Fi l el nf o f i - new FilelnfoCpat h + l vi.Text); PropertiesFor m propertiesFor m - new PropertiesFormCthis. f i. ne w SetNameDelegate(SetRename), ne w SetAttributesDelegate(SetAttributes)): if(propertiesForm.ShowDialog( ) *= DialogResult.OK) f i l l Li s t O; listView.FocusO; } Этот ко д вызывает форму PropertiesForm, которая отображает атрибу­
ты выбранного файла или папки. Также в этой форме пользователь может изменять атрибуты файла при помощи метода SetAttri butes, код которого приведен в листинге 7.22. Листинг 7.2 2 publi c void SetAttributes(Fi1eAttribute s fi l eAttri butes ) { ListViewIte m Iv i -
listView.Items[listView.SelectedIndices[0]]; boo! isFolde r = Ivi.ImageInde x -= 0; if(isFolder ) { Directorylnf o di = new DirectoryInfo(pat h + l vi.Text); di.Attribute s - fi l eAttri butes; } else { Fi l el nf o f i - new FileInfo(pat h + l vi.Text); fi.Attri bute s - fi l eAttri butes; } } Для создания градиентной заливки соответствующего элемента ин­
терфейса применяется метод, код которого приведен в листинге 7.23. Листинг 7.2 3 publi c stati c voi d SetGradient(System.Windows.Forms.ListVie w UstView ) { // Новый вариант // Для .NE T Compac t Framewor k 2.0 Диспетчер задач 13 5 SendMessageClistView.Handle. LVM_SETEXTENDEDLISTVIEWSTYLE. 0. LVS_EX_GRADIENT); l i stVi ew. Refresh О: } Итак, основные трудности реализации программы рассмотрены. Кроме того, в примере присутствуют вызовы функций Windows API для работы с табличным списком ListView. Эти примеры рассмат­
ривались в главе 4, поэтому не стоит повторять их разбор. На са­
мом деле эту программу можно улучшать до бесконечности, добав­
ляя новые функциональные возможности. Надеюсь, у вас это получится. Буду рад, если вы пришлете свои варианты примеров, которые, на ваш взгляд, украсят программу. Диспетчер задач Но мы с вами не расстаемся с программами, написанными Кристиа­
ном Форсбергом. На его сайте можно найти еще одну полезную программу, необходимую как разработчику, так и пользователю. Это Диспетчер задач (Task Manager). Программа подобного рода тоже отсутствует в стандартной поставке Windows Mobile. А ведь эта программа очень полезна в работе. Владелец смартфона под уп­
равлением системы Windows Mobile может узнать много нового о своей системе после запуска этой утилиты. Диспетчер задач по­
кажет все программы, которые размещаются в памяти смартфона, отбирая системные ресурсы. Диспетчер задач также позволяет уда­
лять из памяти ненужные программы и процессы. Не случайно, многие производители сами снабжают свои устройства программа­
ми подобного типа. Если вам не повезло и у вас нет такой програм­
мы, то вы можете сами написать Диспетчер задач. Как и предыдущий пример, оригинальная версия программы была написана на Visual Studio 2003 для смартфонов под управлением Windows Mobile 2003 на платформе .NET Compact Framework 1.0. Следуя нашей традиции, я с согласия автора конвертировал проект для Visual Studio 2005 для целевой системы Windows Mobile 5.0 и с применением .NET Compact Framework 2.0. Графический интерфейс программы Диспетчер задач при запуске показывает список запущенных про­
грамм (рис. 7.5). 136 Глава 7. Разработка приложений ч*»**.. CV Кап «ты Рис. 7.5. Внешний вид программы С помощью меню, размещенного в левой части окна, можно акти­
вировать выбранное приложение. При этом сам менеджер задач закрывается. Меню, расположенное в правой части окна, предос­
тавляет пользователю несколько больше возможностей. Команды этого меню приведены в следующем списке: • Обновить — обновляет список запущенных программ; • Процессы — показывает список запущенных процессов; Q Остановить — останавливает выбранную программу; Q Остановить все — останавливает все запущенные программы; • Вид — показывает информацию о процессе; Р Убить — закрывает процесс; Q 0 программе — выводит информацию об авторе программы; а Готово — закрывает программу. Внешний вид этого меню показан на рис. 7.6. 1 Змжи ИНГИ» Кмпмты ХЯродесы Юнаками* 4 OrsMonti i r* * %0вр1*рлнт {(№•* Рис. 7.6. Команды меню для правой кнопки Код программы При активации основной формы Mai nForm программа получает спи­
сок запущенных программ при помощи процедуры fi 1 ITaskList, код которой приведен в листинге 7.24. Диспетчер задач 13 7 Листинг 7.24 privat e void fi l l TaskLi st O { Cursor.Curren t = Cursors.WaitCursor: // Получим список запущенных приложений window s • WindowHelper.EnumerateTopWindowsO: // Заполняем ListVie w ListViewIte m lvi: 1 i stV i ew. Beg i nlipdat e (); 1istView.Items.CIear(): foreach(Windo w w i n windows ) { l vi » new ListViewItem(w.ToStringO); 1i stView.Items.Add(1vi): } 1i stV i ew.EndUpdate{); if(listView.Items.Coun t > 0) { 1istView.Items[0].Selecte d - true: 1istView.ltems[0].Focuse d = true; } Cursor.Curren t = Cursors.Default; Данная процедура использует класс WindowHelper, который позво­
ляет получить информацию о запущенных приложениях. В листин­
ге 7.25 приведен код метода EnumerateTopWi ndows, который находит все окна запущенных в системе приложений. Листинг 7.25 publi c stati c Window[ ] EnumerateTopWindows O { ArrayLis t windowLis t «= ne w ArrayListO: IntPt r hWn d = IntPtr.Zero; Windo w windo w • null; // Получим первое окно hWn d = GetActiveWindowO; hWn d = GetWindowChWnd. GW_HWNDFIRST); while(hWn d != IntPtr.Zero ) { ifdsWindow(hWnd) && IsWindowVisible(hWnd)) { IntPt r parentWi n - GetParent(hWnd); if ((parentWi n — IntPtr.Zero) ) { продолжение & 138 Глава 7. Разработка приложений Листинг 7.25 (продолжение) i n t lengt h = GetWindowTextLength(hWnd); i f (lengt h > 0) { strin g s - ne w string('\0'r lengt h + 1): GetWindowText(hWnd, s, lengt h +1); s « s.Substrings, s.IndexOfCXO')); if( s != "Tray" && s 1= "Start" && s != "Tas k Manager") { window = new WindowO: window.Handl e - hWnd; window.Text я s: windowList.AddCwindow); } } } } hWnd - GetWindowChWnd. GWHWNDNEXT); } retur n (Window[])windowList.ToArray(typeof(Window)); } В этом методе вызываются функции Windows API, с помощью ко­
торых можно получить список всех открытых окон. Все обнаружен­
ные окна добавляются в список, если они удовлетворяют некото­
рым условиям. Добавляемые окна не должны иметь родительских окон, они должны быть видимыми и иметь заголовок. При этом сам Диспетчер задач не должен попасть в этот список. Все остальные окна записываются в массив. Активация и закрытие приложения Для активации запущенного приложения вызывается функция Win­
dows API SetForegroundWi ndow, которая использует дескриптор окна. Для закрытия приложения используется функция SendMessage с соответ­
ствующим сообщением закрытия WM_CL0SE. Для закрытия сразу всех окон можно использовать функцию Windows API SHCloseApps, кото­
рая закрывает вс е запущенные программы, кроме самого Диспетчера задач. Код, выполняющий эт и действия, приведен в листинге 7.26. Листинг 7.26 publi c stati c voi d ActivateWindowdntPt r hWnd ) { Диспетчер задач __ 139 // Активируем приложение SetForegroundWindow(hWnd); } publi c stati c voi d CloseWindowCIntPt r hWnd ) { // Закрываем приложение SendMessageChWnd. WM_CL0SE. 0. 0); } publi c stati c voi d CloseAppsO { // Закрываем все приложения SHC1oseApps( i nt.MaxValue): } Перечисление процессов Для отображения списка процессов используется функция, код которой приведен в листинге 7.27. Листинг 7.2 7 privat e void fillProcessList O { Cursor.Curren t я Cursors.WaitCursor; // Получаем список запущенных процессов processe s - Process. GetProcessesO; // Заполняем ListVie w ListViewIte m lvi; 1i stV i ew.Beg i nUpdate(); listView.Iterns.CIearО: foreach(Proces s p in processes ) { l vi = new ListViewItem(p.ProcessName): //lvi.SubItems.Add("ID"); listView.Items.Add(lvi): } listView.EndUpdateO; if(listView.Items.Coun t > 0) { 1istView.Items[0].Selecte d = true; listView.Items[0].Focuse d • true; } Cursor.Curren t - Cursors.Default: } 140 Глава 7. Разработка приложений Список активных процессов извлекается при помощи класса Process. Основой класса является метод GetProcesses, приведенный в листинге 7.28. Листинг 7.2 8 publi c stati c Process[ ] GetProcesses( ) { ArrayLis t procLis t - ne w ArrayListO: IntPt r handl e = CreateToolhelp32Snapshot(TH32CS_SNAPPR0CESS. 0); if((int)handl e > 0) { tr y { PR0CESSEIiTRY3 2 peCurrent; PR0CESSENTRY3 2 pe3 2 = ne w PR0CESSENTRY32O; byte[ ] peByte s = pe32.ToByteArray(); in t retva l - Process32First(handle, peBytes); while(retva l = » 1) { peCurren t - ne w PR0CESSENTRY32CpeBytes): Proces s pro c - ne w Process(ne w IntPtr((1nt)peCurrent.PID), peCurrent.Name. (int)peCurrent.ThreadCount. (int)peCurrent.BaseAddress): procL i st.Add(proc); retva l - Process32Next(handle, peBytes); } } catch(Exceptio n ex ) { thro w ne w Exception("Exception: " + ex.Message); } CIoseToolhelp32Snapshot(handle): retur n (Process[])procList.ToArray(typeof(Process)); } els e { thro w ne w Exception("Unabl e to ge t processes!"); } } С помощью данного метода можно узнать детальную информацию о каждом процессе. Маленький блокнот 141 Закрытие процесса Чтобы закрыть процесс, используется метод Kill, ко д которого при­
веден в листинге 7.29. Листинг 7,29 publi c void Ki l l С) { IntPt r hProcess; hProcess == OpenProcess(PR0CESS_TERMINATE. false, (i nt) processId); if(hProces s != (IntPtr ) INVALID_HANDLE_VALUE ) { boo l bRet; bRe t - TerminateProcessChProcess. 0); CIoseHandleChProcess); } } Данный метод также использует вызовы функций Windows API. Функция OpenProcess получает дескриптор процесса, который затем передается функции TerminateProcess для уничтожения процесса. Код, отвечающий за внешний вид элемента управления ListView, полностью идентичен коду из предыдущего примера, поэтому его можно просто скопировать и не рассматривать отдельно. Теперь с помощью Диспетчера задач пользователь сможет узнать список запущенных программ и процессов и даже управлять ими. Маленький блокнот Однажды мой друг, далекий от программирования, попросил меня написать простенький текстовый редактор для карманного ком­
пьютера. Его не совсем устраивало приложение Word Mobile, ко­
торое используется для работы с текстовыми файлами в операци­
онной системе Windows Mobile. Заказчик хотел получить только основные функции стандартного Блокнота из Windows ХР, то есть копирование, вырезание, вставку и удаление текста. Также он хо­
тел обойтись без установки .NET Compact Framework 2.0, так как устаревшая модель его карманного компьютера обладала малой емкостью памяти. В рамках решения поставленной задачи и была написана програм­
ма Блокнотик, которая и будет рассматриваться в этом разделе главы. 142 Глава 7. Разработка приложений Единственная сложность при написании данного текстового ре­
дактора состояла в том, что библиотека .NET Compact Framework 1.0 не поддерживает работу с буфером обмена на уровне управля­
емого кода. Поэтому пришлось прибегать к вызовам функций Windows API. Данный пример можно использовать в качестве основы для тех, кто хочет написать свой текстовый редактор для .NET Compact Framework 1.0. Надо заметить, что если бы я стал писать свой при­
мер с использованием .NET Compact Framework 2.0, то справиться с задачей было бы гораздо легче, так как вторая версия библиотеки поддерживает буфер обмена, который так необходим при операци­
ях с текстом. Первые шаги После запуска Visual Studio .NET 2005 надо создать новый проект. При выборе типа проекта надо указать, что будет использоваться .NET Compact Framework 1.0. Для начала на форме следует разме­
стить текстовое поле с именем txtEdi tor. Для свойства Mul tiline надо задать значение True, а свойство Scrol 1 Bars получит значение Both. Так как текстовое поле обычно занимает все пространство формы, его нужно вручную растянуть до нужного размера. Учитывая, что я писал программу для конкретной модели мобильного устройства, большой ошибки в моих действиях не было. Но не будем забывать, что существуют другие устройства, размеры экрана у которых будут другими. Поэтому стоит устанавливать размеры элементов про­
граммно в соответствии с текущими размерами формы. Также н а первом этапе разработки надо указать позицию текстового поля и установить в нем фокус. Соответствующий код был добавлен в обработчик события Form_Load, что иллюстрирует листинг 7.30. Листинг 7.3 0 privat e voi d MainFormLoadCobjec t sender. EventArg s e) { // устанавливаем позицию текстового поля txtEditor.Locatio n » ne w PointCO. 0); // Приравниваем размеры текстового поля к размерам формы txtEditor.Widt h « this.Width; txtEd i tor. Heigh t =» this. Height: // Устанавливаем фокус txtEditor.FocusO; } Маленький блокнот 143 Если бы программа создавалась для настольного компьютера, то на­
писанный код не вызывал бы никаких сомнений. Но у КПК нет внешней клавиатуры, и для ввода текста используется панель вво­
да SIP. Поэтому на форму надо добавить элемент inputPanel. Так как при активации панель ввода закроет часть формы, то надо на­
писать код для вычисления высоты текстового поля для этого слу­
чая и соответствующим образом изменить обработчик события Form_Load, как показано в листинге 7.31. Листинг 7.31 privat e voi d MainForm_Load(objec t sender. EventArg s e) { // Высоту текстового поля устанавливаем в зависимости от SI P //txtEditor.Heigh t = this.Height; SetTextBoxHeightO; } // устанавливаем размеры текстового поля в зависимости от // активности SI P privat e voi d SetTextBoxHeight O { if (SIP.Enabled ) txtEditor.Heigh t = SIP.VisibleDesktop.Heigh t + 2; els e txtEditor.Heigh t = this.Height; } private void SIPEnabledChangedCobject sender, EventArgs e) { SetTextBoxHeightO; } Стандартные операции с текстом На данной стадии уже создан первый прототип приложения. После запуска программы пользователь может вводить и удалять текст с по­
мощью SIP. Но этого недостаточно для комфортной работы с текстом. Пользователь должен иметь возможность манипулировать текстом, то есть копировать часть текста, вырезать его, удалять и вставлять в нужную позицию. Для этого надо создать меню при помощи эле­
мента mai nMenu. В подменю Правка надо добавить стандартные коман­
ды редактирования текста — Отменить, Вырезать, Копировать, Вставить. Если бы приложение создавалось для среды исполнения .NET 144 Глава 7. Разработка приложений Compact Framework 2.0, то можно было бы использовать класс CI ipboard. Но так как используется .NET Compact Framework 1.0, то придется обратиться к функциям Windows API и использовать не­
управляемый код, что иллюстрирует листинг 7.32. Листинг 7.3 2 // сообщения буфера обмена publi c cons t in t WMCU T = 0x0300; publi c cons t in t W M COP Y - 0x0301; publi c cons t in t WM~PAST E = 0x0302: publi c cons t in t WMCLEA R - 0x0303: publi c cons t in t WMJJND O - 0x0304; // функции AP I [D1TImport("coredll.dir. CharSe t = CharSet.Unicode) ] publi c stati c exter n IntPt r GetFocusO; [DllImport("coredll.dll") ] publi c stati c exter n in t SendMessageCIntPt r hWnd, uin t Message, uin t wParam. uin t IParam); privat e voi d mnuCut_Click(objec t sender, EventArg s e) { // Вырезаем текст SendMessageChwndEditor. WM_CUT. 0, 0); } privat e voi d mnuUndoClick C objec t sender, EventArg s e) { // Отменяем последнее действие SendMessageChwndEditor. WMJJNDO, 0. 0); } privat e voi d mnuCop y ClickCobjec t sender. EventArg s e) { // Копируем выделенный текст SendMessageChwndEditor, WM_C0PY. 0. 0); } privat e voi d mnuPasteClic k С objec t sender, EventArg s e) { // Вставляем текст из буфера обмена SendMessageChwndEditor. WM_PASTE. 0. 0); } privat e voi d mnuDelete_Click(objec t sender. EventArg s e) { Маленький б окнот 14 5 // Удаляен выделенный текст SendMessage(hwndEditor. WMCLEAR. 0. 0); } Теперь необходимо добавить в создаваемое приложение поддержку контекстного меню. Использование контекстного меню избавит пользователя от необходимости постоянно переводить стилус в нижнюю часть экрана для доступа к командам меню. В программу нужно добавить элемент управления ContextMenu и сделать список команд меню, который будет дублировать подпункт основного меню Правка. Созданное контекстное меню надо связать с текстовым по­
лем при помощи свойства ContextMenu. Осталось только скопировать код из команд основного меню в соответствующие места для команд контекстного меню. Например, для команды контекстного меню Ко­
пи ровать надо использовать код, приведенный в листинге 7.33. Листинг 7.3 3 privat e voi d cmenuCopyClic k (objec t sender, EventArg s e) { // Копируем выделенный текст SendMessageChwndEditor. WM_C0PY. 0, 0); } Мы сделали еще один шаг вперед. Теперь наш маленький блокнот умеет работать с текстом. Но приложение нужно еще немного до­
работать. Например, пользователь может во время работы с блок­
нотом переключиться н а другую программу и скопировать в буфер обмена картинку, а затем вернуться обратно к текстовому редакто­
ру. Конечно, картинку нельзя вставить в текстовое поле. Поэтому надо проверить тип содержимого в буфере обмена, и если там со­
держатся не текстовые данные, то нужно заблокировать пункт меню Вставить. Для этого можно использовать функцию IsClip-
boardFormatAva i 1 abl e, а проверку данных в буфере обмена выполнять в событии Popup, как показано в листинге 7.34. Листинг 7.3 4 [DlllmportCCoredl l .dl l") ] privat e stati c exter n bool IsClipboardFormatAvailable(uin t uFormat); // константа для буфера обмена privat e const uint CFJJNICODETEXT - 13; publi c stati c bool IsTextO t продолжение •& 146 Глав а 7. Разработка приложений Листинг 7.34 {продолжение) tr y { retur n IsCl1pboardForraatAvailable(CF_UNIC0DETEXT): } catc h (Exceptio n ex ) { MessageBox.ShowC'H e йогу понять, что содержится в буфере обмена!"); retur n false: } } privat e voi d mnuEditPopupCobjec t sender, EventArg s e) { if (IsTextO ) mnuPaste.Enable d = true; els e mnuPaste.Enabled = false: } Подобные изменения надо сделать и для пунктов меню. Если пользователь не выделил часть текста, то пункты Вырезать, Копиро­
вать и Удалить также должны быть заблокированы. Код, реализую­
щий эту функциональность, приведен в листинге 7.35. Листинг 7.35 //Если текст выделен if (txtEditor.SelectionLengt h > 0) { mnuCut.Enable d я true: mnuCopy.Enable d - true; mnuDelete.Enable d - true; } els e { mnuCut.Enabled = false; mnuCopy.Enabled = false; mnuDelete.Enabled = false; Следующим шагом в развитии программы будет добавление фай­
ловых операций. Работа с текстовым редактором предполагает не только правку текста, но и сохранение текста в файле, а также чте­
ние данных из файла. Для этого в меню создаются соответствую-
Маленький блокнот 14 7 щие команды Создать, Открыть, Сохранить и Сохранить как. Код, свя­
занный с этими командами, приведен в листинге 7.36. Листинг 7.3 6 privat e voi d mnuOpen_Click(objec t sender. EventArg s e) { dlgOpenFile.Filte r = "Текстовые документы (*.txt)|*.txt|Bc e файлы|*.*H: dlgOpenFi1e.ShowDi alog(); i f CFile.Exists(dlgOpenFile.FileName) ) { fnam e = dlgOpenFile.FileName; StreamReade r sr - ne w StreamReader(fname, System.Text.Encod i ng.GetEncod i ng("W i ndows-1251"), false): txtEditor.Tex t - sr.ReadToEndO: fla g - false; sr.CloseO: } } privat e voi d mnuSaveAs_Click(objec t sender, EventArg s e) { SaveFileDialog dlgSaveFile - new SaveFileDialogO; dlgSaveFile.Filter - "Текстовые документы (*.txt)j *.txt|Bc e файлы I*.*"; dlgSaveFi1e.ShowDi alog(); fname - dlgSaveFile.FileName; savedataO; } privat e void savedataO { i f (fname — ") { SaveFileDialo g dlgSaveFil e - ne w SaveFileDialogO; dlgSaveFile.Filte r - "Текстовые документы (*.txt)|*.txt | Все файлы|*.*"; DialogResul t re s - dlgSaveFile.ShowDialogO; if (re s — DialogResult.Cancel ) { return; } fname - dlgSaveFile.FileName; MessageBox.Show(fname): продолжение & 148 Глава 7. Разработка приложений Листинг 7.3 6 {продолжение) } StreamWrite r sw - new StreamWriter(fname. false. System.Text.Encod i ng.GetEncod i ng(nWi ndows-1251")); sw.WriteLineCtxtEditor.Text): sw.FlushO; sw.CloseO; fla g = false: } privat e voi d mnuSave_Click(objec t sender, EventArg s e) { savedataO; } privat e voi d txtEditor_TextChanged(objec t sender, EventArg s e) { fla g - true: } Работа с файлами в .NET Compact Framework не отличается от мето­
дов работы с файлами в полной версии .NET Framework, поэтому заострять внимание на этом коде не нужно. Осталось только доба­
вить в программу некоторые детали, которые придают программе профессиональный вид. Нужно присоединить собственную пикто­
грамму приложения, а также добавить диалоговое окно 0 программе с упоминанием автора программы и логотипом фирмы. Безусловно, вы можете наделить текстовый редактор новыми возможностями или расширить его функциональность. Например, для сохранения и от­
крытия файлов я использовал стандартные диалоговые окна, кото­
рые работают с файлами в пределах папки Мои документы. Но исполь­
зуя код ранее созданного файлового менеджера, можно научить приложение сохранять и открывать файлы в любом месте файловой системы. Также можно доработать меню Формат, позволяющее рабо­
тать с различными кодировками текста. Распространение приложений Даже если вы написали очень полезную программу, она не сможет обрести всемирную известность, пока вы держите ее на своем ком­
пьютере. Нужно все же распространить программу и, если она не бес­
платная, то и немного заработать на отпуск. Программы для настоль­
ных компьютеров распространять довольно просто. Нужно лишь Распространение приложений 149 создать специальный проект для создания установочного пакета, ко­
торый сгенерирует специальный файл установки Microsoft Installer (MSI). К сожалению, для мобильных устройств процесс создания установочных файлов немного отличается. В процессе распростра­
нения программы участвуют три составляющие: настольный компь­
ютер, программа синхронизации Microsoft ActiveSync и программа wceload.exe для извлечения файлов из cab-файлов. Для пользователя процесс установки программы не сильно отлича­
ется от привычной схемы. Сначала он скачивает программу или на­
ходит ее на компакт-диске. Затем запускает установочный msi-файл. Программа Microsoft Installer с помощью специального мастера ус­
тановки помогает пользователю установить программу с нужными настройками. После этого программа считается установленной, и пользователь может запускать ее. Создание cab-файла Прежде чем установочный пакет попадет в руки пользователя, нужно хорошенько поработать над его созданием. Устройства под управле­
нием Windows Mobile не могут напрямую работать с файлами .msi. Вместо этого используются кабинетные файлы с расширением .cab. Таким образом, задача программиста заключается в том, чтобы соста­
вить список команд для программы синхронизации ActiveSync, кото­
рые позволят скопировать cab-файлы на устройство с учетом необхо­
димых установок. Для создания удобного установочного пакета с интуитивно понятным интерфейсом вам необходимо выполнить не­
хитрую последовательность действий. 1. Создать cab-файл для устройства. 2. Добавить в cab-файл дополнительные файлы, используемые программой, например изображения или файлы с данными. 3. Добавить в cab-файл инструкции для записи в реестр. 4. Зарегистрировать cab-файл с помощью ActiveSync, чтобы пользо­
ватель мог установить приложение с настольного компьютера. 5. Написать код для различных дополнительных возможностей, которые будут использоваться установочным пакетом во время установки или деинсталляции. 6. Упаковать все необходимые файлы в один специальный файл установки с расширением .msi. Вы, вероятно, знаете, что кабинетный файл является специальным файлом упаковки и компрессии, с помощью которого можно ежи-
150 Глава 7. Разработка приложений мать файлы, что приведет к уменьшению их размеров. Также в этом файле могут содержаться инструкции для внесения изменений в ре­
естр системы. За обработку cab-файлов на устройстве отвечает ути­
лита wceload.exe, входящая в состав Windows Mobile. Создание проекта Приступим к разработке проекта для создания установочного пакета. Прежде всего нужно запустить уже существующий проект, который планируется подготовить для распространения. В качестве примера будет использоваться проект SmallNotepad. Затем нужно выполнить ко­
манду меню File • Add • New Project. В открывшемся диалоговом окне надо перейти в раздел Other Project Types, выбрать тип Smart Device Cab Project и задать имя нового проекта DeployNotepadCab (рис. 7.7). Рис. 7.7. Выбор нового проекта для распространения приложения В окне свойств надо задать значения свойств Manufacturer и ProductName. Другие свойства позволяют задать минимальные и максимальные тре­
бования к операционным системам, в которых может быть запущена ваша программа. Затем надо запустить редактор File System Editor, нажав соответству­
ющую кнопку в окне свойств. Нужно выбрать пункт Application Folder и в контекстном меню выбрать пункт Add • Project Output (рис. 7.8). Распространение приложений 151 roanNot*pad_CS - Microsoft Visual Studio ^лГ^^^щ^^ё^^т^«^^я^^^^ Рис. 7.8. Выбор параметров проекта В результате этого будет открыто диалоговое окно Ad d Project Output Group (рис. 7.9). Add Project Output Group .•••.-.•••.• ••••-- *.•*-•---»•*».-v.. iJ-An^f „*"-•* Pro йЛ :: jSmaBNotep84_C S , Locaiue d resource s J Defcu g Symbol s Щ Conten t BSe s | Sourc e FiKS Dccum_fltat*o n Fries XML Seflefcz-tto n Assemblie s ?x COfW'SUreBOP • C*J-*N«5 ; Contain. t N Ш.« EXS tw& Sr» ДО рго}*«Л> C« J Cane d i.a.....-..:...-.....«.i i.» ..-.J T I,и -, i; ;, • * Рис. 7.9. Диалоговое окно Ad d Projec t Output Group С помощью данного окна можно выбрать различные типы файлов, необходимые для программы, такие как файлы документации или, например, локализированные ресурсы. Нужно выбрать пункт Primary Output и нажать кнопке ОК. В правой части окна следует щелк­
нуть правой кнопкой мыши на единственном пункте Primary output from SmallNotepad_CS и в контекстном меню выбрать пункт Create Shortcut t o Primary output from SmallNotepad_CS (рис. 7.10). Это позво­
лит включить пиктограмму в список файлов для распространения. 152 Глава 7. Разработка приложений Созданный ярлык надо переместить мышью в папку Program Files Folder. Теперь можно приступать к созданию установочного файла. Рис. 7.10. Создание пиктограммы приложения В меню надо выполнить пункт Build • Build DeployNotepadCab. После этого среда разработки создаст специальный файл с расширением .CAB. При помощи файлового менеджера его нужно найти и запом­
нить его расположение. Теперь надо установить созданный файл на эмуляторе. Для этого выполняется команда меню Tools • Device Emulator Manager. В диало­
говом окне надо выбрать эмулятор. Например, Pocket PC 2003 SE Emulator. В этом же окне следует выполнить команду меню Actions • Connect. При этом выбранный эмулятор будет активирован. Emulator Properties General [Dtsptov ниули* i .Perfchy-ek.:; и ~~_ , ~ п- '-"г**'::-
Itfffi**^***?* ;.'::.':}'. JRJjtUAl t i C53 Caned n-.«i».>VI,.'..,., Рис. 7.11. Активация и настройка эмулятора Дополнительные материалы 153 В окне эмулятора надо выполнить команду меню File • Configure. После этого откроется окно настроек эмулятора, в котором следует перейти в раздел Shared Folder. В этом разделе надо выбрать папку, в которой находится созданный cab-файл (рис. 7.11). Эмулятор бу­
дет считать, что данная папка является карточкой памяти. Если открыть в эмуляторе программу File Explorer (Start • Programs • File ExpLorer) и найти папку Storage Card, то в ней можно будет увидеть ранее созданные установочные файлы. Нужно выбрать файл DeployNotepadCab и запустить его. В результате начнется процесс установки программы на устройство. При установ­
ке автоматически будет создан файл деинсталляции. Он поможет кор­
ректно удалить приложение. Для этого в окне эмулятора надо выпол­
нить команду меню Start • Settings • System • Remove Program. В списке установленных программ надо найти ранее установленное приложе­
ние, выделить его и нажать кнопку Remove (рис. 7.12). j"Se«n<i* Ш Х#**- **•* -Ф Rerno ve Program% Ргодем Ш «toraga memory: Microsof t .NET CF 2.0 EW-Strti a , Mtrosof t .ME T CF го Total rtor ада memor y avalata Y 5660 k ftdlust ram-;c r alocattor i 1 Рис. 7.12. Деинсталляция приложения В результате этого действия будет запущен мастер удаления про­
граммы, который корректно удалит все файлы, относящиеся к при­
ложению. На этом изучение примера создания установочного фай­
ла можно считать законченным. Дополнительные материалы На сайте MSDN есть очень подробная статья «Deploying .NET Compact Framework 2.0 Applications with .cab and .msi Files», в кото­
рой приведены дополнительные сведения о создании и распростра­
нении установочных файлов. Стоит ознакомиться с данным материа­
лом, чтобы использовать все возможности установочных файлов. Глава 8 Эмулятор и другие утилиты Программы для отладки приложении В этой главе речь пойдет об утилитах, необходимых для успешного программирования приложений для мобильных устройств. Пожа­
луй, самой главной из этих утилит является программный эмуля­
тор Device Emulator. Кроме того, в состав Visual Studio 2005 входит несколько вспомогательных утилит, позволяющих выполнять раз­
личные операции на реальном устройстве или на эмуляторе. Эмулятор При создании приложений для КПК и смартфонов необходимо про­
верять работу написанной программы на устройстве, которое силь­
но отличается от настольного компьютера. Когда вы пишете стан­
дартное Windows-приложение, вы можете сразу увидеть его работу, запустив соответствующий исполняемый файл. Написав програм­
му для мобильных устройств, необходимо протестировать ее на со­
ответствующем устройстве, так как ваш настольный компьютер здесь уже не поможет. Но даже если разработчик еще не приобрел карманный компьютер или смартфон под управлением Windows Mobile, то он все равно может тестировать свои приложения. В этом случае надо проверять их работоспособность на специальных эму­
ляторах. Следует отметить, что в некоторых случаях эмулятор все-таки не сможет выполнить эту задачу. Например, он не поможет проверить работу кода, который использует возможности инфракрасной свя­
зи. И, тем не менее, эмулятор является очень мощным и удобным инструментом для отладки приложений. Надо сказать, что качество и возможности эмулятора постоянно улучшаются и совершенствуются. Разработчики, которые програм­
мировали еще на eMbedded Visual Basic и Visual Studio 2003, без со­
мнения, обратят внимание на возросшую скорость работы эмулято­
ра, его надежность и удобство. Эмулятор, поставляемый с Visual Studio 2005, имеет улучшенную поддержку общих папок, програм-
Запуск эмулятора 155 мы синхронизации ActiveSync и последовательных портов. Также эмулятор поддерживает альбомную и книжную ориентацию. Рань­
ше об этом приходилось только мечтать. Особенно приятно отме­
тить тот факт, что можно дополнительно скачать локализованные версии эмуляторов. Например, все примеры для Windows Mobile 5.0 тестировались исключительно на русской версии эмулятора. Запуск эмулятора Итак, при написании своей программы у разработчика есть возмож­
ность выбирать, где тестировать свой код. Как правило, программу сначала проверяют на эмуляторе. Это позволяет быстро исправить ошибки и устранить недочеты. А уже окончательную версию про­
граммы можно и нужно проверить на реальном устройстве. Рассмотрим вариант запуска эмулятора и его настройки. Сначала требуется создать или открыть проект, предназначенный для мо­
бильного устройства, например, первую программу «Здравствуй, мир», которая создавалась в главе 2. После выполнения команды меню Debug • Start Debugging среда разработки Visual Studio отобра­
жает диалоговое окно Deploy (рис. 8.1). Deploy rVstPockei PCAppjS C a n c e l I Pocket PC 2003 se Square Emutewr ! Pocket PC 2003 SE Square VGA Emulator Pocket PC 200Э SE VGA Emulator ' ' -:- :,:.:::•. • • •• -: j vj i fsew nve tWs dtatog each feme J й*&щ 8« apptaaSon Рис. 8.1. Запуск эмулятора В диалоговом окне отображается список, в котором содержатся одно реальное устройство и четыре эмулятора разных типов устройств. Нужно выбрать любой эмулятор из списка. Стандартным выбором в данном случае является значение Pocket PC 2003 SE Emulator. Нуж­
но выделить строку с выбранным эмулятором и нажать кнопку •"*«. 156 Глава 6. Эмулятор и другие утилиты Deploy. Через несколько секунд на экране компьютера появится эмулятор карманного компьютера, в котором будет запущено вы­
бранное приложение. Программист может работать с тестируемой программой так же, как и на реальном устройстве. Кроме того, мож­
но оставить в покое программу и запустить любое приложение, ко­
торое есть на этом эмуляторе. ПРИМЕЧАНИЕ Списки эмуляторов на каждом компьютере разработчика могут различаться, так как можно скачать и установить дополнитель­
ные эмуляторы. Когда будут рассматриваться примеры для уст­
ройств под управлением Windows Mobile 5.0t диалоговое окно будет содержать уже другие эмуляторы. После того как тестирование программы будет завершено, вам надо остановить выполнение программы при помощи команды меню Stop debugging в среде разработки. При этом не стоит закрывать само окно эмулятора, как часто делают начинающие программисты. Если оставить эмулятор работать, то это позволит потратить меньше вре­
мени на повторную загрузку эмулятора при следующей отладке программы. ПРИМЕЧАНИЕ Если ваша программа имеет код для закрытия приложения this.CloseQ, то режим отладки автоматически остановится и выполнять ко­
манду меню Stop debugging не понадобится. Настройка эмулятора Попробуем теперь поработать с различными настройками эмуля­
тора. Для начала следует выполнить команду меню Tools • Options. В открывшемся диалоговом окне Options надо выбрать строку Device Tools, а в ней активировать пункт Devices. Затем в списке Devices надо выбрать элемент Pocket PC 2003 SE и нажать кнопку Properties (рис. 8.2). На экране появится новое диалоговое окно Pocket PC 2003 SE Properties (рис. 8.3). Обратите внимание на то, что по умолчанию программа устанавливается в папку Program Files. После ознакомления со свойствами эмулятора нужно закрыть все диалоговые окна и вернуться в главное окно среды разработки. Там Настройка эмулятора 157 надо выполнить команду меню Tools • Device Emulator Manager. На экране откроется новое диалоговое окно, в котором будут перечис­
лены все имеющиеся эмуляторы (рис. 8.4). : --р Erwrcnnwi I * Project s and Sobtsen s < t? ТеЛ Editor I № Windows Fomns Designe r •: iv. Device Toot s : General | — frewce» Sevens... ! Pocket РЛЙ03 SE Squar e Emulator > i Pocke* P(20q 3 SE Squar e VGA Emutato r ! Pocket РСД003 SE VGA Ermrtac w L i_ .{Shew-jjlf-'ieeshjpt:-
Рис. 8.2. Окноластроек эмулятора gmalator Properties. ?x Ceneraf ©tsptoy l NetwnricJ Peripheral - - . i Q5 to*$* wne: }:\''::^'';\^.1^\Ш^^:,,:.^ fe 8tsmandevtce?,enwtBtors\lrTO И •OS*<*i ROW :*»«в« «Mfesa. *~i Soedfy BAM * »: Л*™-:~-" 0a*rtieCT*sf"<B*:; 1 SfuredWder; , , ;| C;\DocufR«rts and S*ffinfls^AL?XAKDW.M/- Decjments О m ,,- ::-Й "''''f t Рис. 8.3. Окно свойств эмулятора Надо выбрать из списка элемент Pocket PC 2003 SE Emulator, а затем выполнить команду меню Actions • Connect. Менеджер эмуляторов загрузит выбранный эмулятор. На экране появится специальный значок, который сигнализирует об установленном соединении. За-
158 Глава 8. Эмулятор и другие утилиты тем нужно выполнить команду меню Actions • Cradle. Если опера­
ция пройдет успешно, то значок состояния эмулятора изменится. Это означает, что эмулятор КПК соединен с виртуальной док-стан­
цией. Теперь можно синхронизировать данные с помощью програм­
мы синхронизации ActiveSync. По завершении операции нужно выполнить команду меню Actions • Uncradle. D«»' с« Errulato? Manage r иш* • gerresh 1 Windows СЕ 5.0 В-Peek* PC 200 3 •ф^обал PC 200 3 SE Emulator PoCtM PC 200 3 SE VGA Emulator - Pocket PC 200 3 SE Square Emulator Pocket PC 200 3 SE Square VGA Emulator • Smortohone 2<№ -•• Sjnartphon» 200 3 SE Emulator Srnartpfiofl » 200 3 SE QVGA Emulator ^ Window* МоЫе S.0 Smartphon e SDK • - Window* MOW » 5.0 Smartphone Emuiator ; VAndos*» MoW» 5.0 Sroartphon * QVGA Emulator RUS V/Sndows McWe 5.0 Smartphoo e EmuEator • RUS Windows Mobt o S.0 Smartphon» QVGA Emulator В Windows MoW» 50 Pocket PC SDK Windows MoW» 5.0 Pocket PC Emulator •• Window» MoW» 5.0 Podwt PC Phone Emvtetor Windows MoW» 5.0 Pocxet PC VGA Emulator :- Windows MoWe 5.0 Pocket PC Phone VGA Emulator >• Window» MoW» S.0 Pocket PC Square Emulator : WindowsMaMaSAPodca*PCPhon»Squ*r»Emulato r Window» MoW» 5.0 Pocket PC Souare VGA. Emuiator Windows MoW» э.О Pocket PC PhorwSqcar * VGA Emulator i RUS Windows MoW» 5.0 Pocket PC Emulator RUS Window» MoW* S.0 Pocket PC Phone Emulator RUS Window» MoWf 5.0 PocJcet PC Ч-СА Emulator ; RUS Window» МЫ*» 5,0 Pocket PC PhoneVGAEmulator RUS Windows MOW» 5 0 Pocket PC Square Emiaxor RUSWmoows MoW» 5.0 Pocket PC Phor» Squar* Emulator RUS Windows MoW» 5.0 Pocket PC Square VGA Emulator RUS Window» MoWe 5.0 Pocket PC Phone Square VGA Emuta .j Others Рис. 8.4. Список установленных эмуляторов Эмуляция карточки памяти Все модели карманных компьютеров и смартфонов имеют возмож­
ность увеличения объема памяти при помощи различных карточек памяти. Дополнительный объем дискового пространства исполь­
зуют для хранения фильмов, фотографий и других документов. Особенно это актуально для пользователей устройств под управле-
Эмуляция карточки памяти 159 нием Pocket PC 2003, так как после перезагрузки устройства все данные на устройстве стираются. Эмулятор позволяет использовать любую папку настольного компьютера в качестве карточки памя­
ти. Для выбора подключаемой папки нужно в окне эмулятора вы­
полнить команду меню File • Configure и на вкладке General указать соответствующую папку в пункте Shared Folder (рис. 8.5). fLmolator'T »r . i ?* СелиЫ ! danby[ Network' Pwlpherais: f.;^v. *.v.w^.>A>;*:^y<-A>fri^ ^ ш. :••:-• • н-йййй*^ --
• •- - , - - И . .. . W*V • t уж-^"^'-:"-'^'' тЫ%?(ЩГ1-. •• ~s* №stk*w :• >:-7-:-т"- г:;;--^-->-;:.-•: •"•-.- г-о*":'- -:" -•••"<•:'•- :•-•-'—>:•—:••:' ./,. Рис. 8.5. Эмуляция карточки памяти Рис. 8.6. Папка Storage Card 160 Глава 8. Эмулятор и другие утилиты После того как соответствующая папка будет подключена, можно с помощью стандартной программы File Explorer, входящей в состав Windows Mobile, убедиться, что у устройства теперь имеется карточ­
ка памяти, которая представлена как папка Storage Card (рис. 8.6). Изменение ориентации экрана Эмулятор позволяет легко менять ориентацию экрана. Достаточно перейти на вкладку Display после выполнения команды меню File • Configure и выбрать нужный режим в разделе Orientation (рис. 8.7). Emulator Properties m [млмол^^^лд^ И СЛРгоуат WteStfJcrosof t visual Studio S\snwtd»Hc^3bH^Po<^PC,20Q3\P!octof j (ГГ) w< i ww i № —щ?° Рис. 8.7. Настройка ориентации экрана Рис. 8.8. Вращение устройства Выход в Интернет через эмулятор 161 Если выбрать соответствующее значение для поворота экрана, то эмулятор повернет изображение устройства (но не экрана) на 90° (рис. 8.8). Выход в Интернет через эмулятор Совсем не обязательно при отладке своих программ для карманных компьютеров или смартфонов копировать программы на данные ус­
тройства, запускать их и проверять работоспособность приложе­
ний. Гораздо удобнее использовать эмуляторы соответствующих ус­
тройств. Несомненно, вы так и поступали при изучении предыдущих примеров. Но все описанные примеры не использовали ресурсы Интернета. Однако сейчас количество программ, использующих се­
тевые возможности, стремительно растет. К счастью, эмулятор при­
ходит на выручку и в этой ситуации. Если ваш компьютер, на кото­
ром установлен эмулятор, имеет соединение с Интернетом, то можно подключить к Сети и сам эмулятор. Настройка не очень сложна, и все этапы приведены в следующем списке. 1. Создать новый проект в Visual Studio 2005. 2. Запустить программу ActiveSync. Возможно, она неактивна, и ее пиктограмма располагается в области уведомлений. В этом слу­
чае надо щелкнуть правой кнопкой мыши на этом значке и выпол­
нить команду контекстного меню Открыть Microsoft ActiveSync. 3. Вернуться в среду разработки Visual Studio 2005 и выполнить команду меню Tools • Device Emulator Manager. На экране появит­
ся диалоговое окно Device Emulator Manager. 4. Щелкнуть правой кнопкой мыши на соответствующем эмулято­
ре и выполнить команду контекстного меню Connect. На экране появится соответствующий эмулятор. 5. Вернуться в диалоговое окно Device Emulator Manager и снова щелкнуть правой кнопкой на выбранном ранее эмуляторе, а за­
тем выполнить команду Cradle. 6. В диалоговом окне Device Emulator Manager у выбранного эмуля­
тора появится значок, показывающий, что эмулятор теперь под­
ключен к системе настольного компьютера. 7. Автоматически появится сообщение от Microsoft ActiveSync о том, что установлено соединение (рис. 8.9). 8. В этом окне нужно нажать кнопку ОК. 9. На экране появится окно Мастер синхронизации (рис. 8.10). 6-2873 162 Глава 8. Эмулятор и другие утилиты •Ш^(4Щг^Ш Ш То wvt***y* Е-вхй, tutor**', c&ttam, *мю *xi i**w tM^e*te«t)U«»,il>e^f<*««*--
r^n Рис. 8.9. Сообщение ActiveSync Hide Be»* Й ,.-.. .'...•;..•......>'-•. -.-«...*• .-...-•: '^^Й^^^Й1**^:-: • '•• -*- ''•: Рис. 8.10. Окно ActiveSync 10.Так как сейчас синхронизация не нужна, то следует нажать кнопку Cancel И. Появится основное окно программы Microsoft ActiveSync, сиг­
нализирующее, что установлено соединение с компьютером. 12. Закрыть окно программы Microsoft ActiveSync. Программа про­
должает работать в фоновом режиме. В области уведомлений должна отображаться зеленая пиктограмма 13. В очередной раз вернуться в окно программы Device Emulator Manager и закрыть его. Программа также продолжает работать в фоновом режиме, а ее пиктограмма тоже появится в области уведомлений. 14. Настало время настройки эмулятора для доступа в Интернет. Нужно щелкнуть правой кнопкой на зеленом значке ActiveSync и выполнить команду контекстного меню Открыть Microsoft ActiveSync. Затем надо выполнить команду меню File • Connection Settings и выбрать режим This computer is connected to The Internet, после чего останется только нажать кнопку ОК. 15. В эмуляторе надо нажать кнопку Start и щелкнуть на пиктограм­
ме Internet Explorer. В результате будет запущен стандартный браузер. В адресной строке можно указать URL любого суще­
ствующего сайта. Эмулятор должен загрузить выбранный сайт. Новая версия эмулятора 16 3 Теперь компьютер соединен с Интернетом через эмулятор. Это поз­
волит отлаживать программы, использующие соединение с Интер­
нетом. Изменение внешнего вида эмуляторов Разработчик может также создать собственный внешний вид эму­
лятора. Соответствующую информацию можно найти в справоч­
ной системе. Описание внешнего вида эмулятора хранится в XML-
файлах, которые описывают внешний вид устройства. Если нужно, чтобы эмулятор был точно похож на ваше устройство, надо подго­
товить соответствующие рисунки устройства и указать их в файле. Затем в настройках эмулятора можно указать путь к новому фай­
лу. И тогда эмулятор будет выглядеть именно так, как ваше соб­
ственное устройство. Эмулятор как отдельный продукт Эмулятор поставляется вместе с Visual Studio 2005. Эмуляторы для тестирования программ на новых устройствах, таких как Windows Mobile 5.0, также интегрируются в оболочку Visual Studio. И чтобы установить эмулятор на новой машине, раньше приходилось уста­
навливать весь пакет Visual Studio 2005. Разработчики на форумах часто спрашивали: можно ли устано­
вить эмулятор как отдельную программу на компьютере? До не­
давнего времени ответ был отрицательным. Но наконец-то Microsoft прислушалась к просьбам разработчиков и выпустила эмулятор в виде отдельного продукта. Более подробную инфор­
мацию об этом можно найти на веб-странице www.microsoft.com/ downloads/details. aspx?FamilyId-C62D54A5-183A-4AlE-A7E2-
CC500EDlF19A&displaylang-en. Новая версия эмулятора Работы над улучшением эмулятора не прекращаются. На странице «Microsoft Device Emulator 2.0 Beta — Community Technology Preview», которая располагается по адресу http://www.microsoft.com/ downloads/details. aspx?FamilyID-13f5de85-30cd-4506-9c5b-
a2068falee9e&DisptayLang=en, выложена бета-версия эмулятора, ко­
торая будет работать с будущей версией Windows СЕ 6.0. В новой .i "ф. 164 Глава 8. Эмулятор w другие утилиты версии обещана еще большая скорость работы эмулятора и реали­
зованы дополнительные возможности. Набор утилит Visual Studio Remote Tools В состав Visual Studio 2005 входят несколько утилит, которые мо­
гут пригодиться разработчикам программ для мобильных уст­
ройств. Если открыть группу программ Visual Studio Remote Tools, то можно увидеть, что в ней расположены утилиты, перечисленные в следующем списке: a Remote File Viewer; • Remote Heap Walker; • Remote Process Viewer; Q Remote Registry Editor; О Remote Spy; Q Remote Zoom-in. Если вы собираетесь серьезно посвятить себя программированию для мобильных устройств, то необходимо изучить все эти програм­
мы. Но сейчас будут рассмотрены три наиболее важные утилиты. Remote Zoom-in Утилита Remote Zoom-in позволяет получить снимок экрана уст­
ройства или эмулятора на компьютере разработчика. Большинство иллюстраций к данной книге были сделаны с помощью данной про­
граммы. Использовать эту утилиту очень просто. Перед ее запуском необ­
ходимо подключить к компьютеру устройство или эмулятор. При запуске программа предложит вам список доступных устройств, с которых можно получить снимок экрана (рис. 8.11). После нажатия кнопки 0К программа автоматически установит связь с выбранным устройством и загрузит текущее изображение экрана. Если необходимо получить новое изображение с экрана устройства, то нужно выполнить команду меню File • New Bitmap. Утилита Remote Zoom-in сохраняет экранные снимки в формате ВМР-
файлов. Функциональные возможности программы сильно урезаны, пользователь может вырезать часть изображения, изменить его раз­
мер или сделать копию. Но от этой программы и не требуется боль-
Remote Registry Editor 165 ших возможностей. Сохранив изображение в файле, при необходимо­
сти можно редактировать его в любом графическом редакторе. Рис. 8.11. Выбор устройства для получения снимка экрана Remote File Viewer Утилита Remote File Viewer является аналогом стандартного Про­
водника, входящего в состав Windows ХР. С помощью этой утили­
ты пользователь может просматривать содержимое папок устрой­
ства или эмулятора, а также копировать файлы из устройства на настольный компьютер и наоборот. При запуске утилита сначала отображает список доступных уст­
ройств, а затем устанавливает связь с выбранным устройством. Remote Registry Editor Еще одной полезной утилитой является редактор реестра Remote Registry Editor. С помощью данного редактора пользователь может изменять, удалять и создавать новые записи в реестре. В одной из следующих глав будет рассказано о программном изменении зна­
чений реестра с помощью функций Windows API. С помощью этой утилиты можно контролировать работу этих функций. Глава 9 Программирование для смартфонов Особенности программирования для смартфонов В этой главе мы научимся создавать приложения для смартфонов под управлением системы Windows Mobile 5.0. Так получилось, что в России смартфоны под управлением Smartphone 2003 поначалу не получили широкого признания. Признанными лидерами на рынке «умных» телефонов были такие марки, как Nokia, Siemens и Sony Ericsson, которые использовали в телефонах операционную систему Symbian. И если на рынке КПК компании Microsoft удалось потес­
нить своего вечного конкурента PalmOs, то в сфере мобильной связи основная борьба еще впереди. Небольшие изменения начались, когда в продаже появились смарт­
фоны под управлением Windows Mobile 5.0, выпускаемые азиатски­
ми компаниями. В этой главе мы только познакомимся с основными особенностями программирования для смартфонов, а в следующей главе более подробно изучим платформу Windows Mobile 5.0. Если .NET Compact Framework можно считать подмножеством пол­
ной версии .NET Framework, то смартфоны можно считать подмно­
жеством карманных компьютеров. Причем разница между ними стремительно стирается. Но, тем не менее, между двумя типами мобиль- ных устройств есть существенные различия. Во-первых, смартфоны являются прежде всего мобильными телефонами, пред­
назначенными для телефонных разговоров. Во-вторых, размер эк­
рана у смартфонов меньше, чем у стандартных карманных компью­
теров, и составляет 176 х 220 пикселов, тогда как у КПК размер экрана 240 х 320 пикселов. ПРИМЕЧАНИЕ В последнее время все чаще выпускаются смартфоны с разрешени­
ем 240 х320 пикселов. А КПК стали все чаще стали делать с экра­
ном 480 х 640 пикселов. Создание приложения для смартфона 167 Еще одной отличительной и, пожалуй, главной чертой смартфонов является отсутствие стилуса. Следовательно, тип экрана также от­
личается от экрана карманного компьютера. Пользователь может взаимодействовать с приложением только при помощи кнопок теле­
фона. И хотя «умные» телефоны используют такую же версию .NET Compact Framework, эти различия заставляют применять совсем иные приемы программирования. В состав Visual Studio 2005 уже входят эмулятор для Smartphone 2003 и необходимые шаблоны проектов. Пора приступить к созда­
нию приложения для смартфона. Самый первый пример будет сде­
лан для устройства Smartphone 2003, а остальные — для Windows Mobile 5.0. И хотя речь о Windows Mobile 5.0 пойдет только в сле­
дующей главе, я решил сразу тестировать программы именно для этой платформы. В этом случае вам придется скачать дополнитель­
ный пакет Windows Mobile 5.0 SDK Smartphone, который содер­
жит дополнительные эмуляторы для этого класса устройств. Создание приложения для смартфона В главе 7 уже создавался проект, рассчитанный на работу с смарт­
фоном. Мы тогда немного забежали вперед. Настало время вернуть­
ся к истокам и начать изучение с самого начала. Запустите Visual Studio 2005 для создания нового проекта. Надо выбрать тип проекта Smartphone 2003. Для этого типа применяется только .NET Compact Framework 1.0. Сразу после создания надо за­
пустить эмулятор при помощи команды меню Debug • Start Debugging. На экране будет отображено окно Deploy со списком имеющихся эму­
ляторов. Нужно выбрать эмулятор и нажать кнопке Deploy. Если все прошло нормально, то эмулятор будет загружен с пустой формой. В первом упражнении надо лишь проверить возможности работы с эмулятором. Поэтому надо закрыть приложение (но не эмулятор!) и продолжить работу с приложением в режиме проектирования. ПРИМЕЧАНИЕ Так как у приложений для смартфонов нет кнопки закрытия окна, то непонятно, как можно закрыть программу. Можно нажать кнопку Stop Debugging. Если на эмуляторе нажать кнопку с крас­
ным телефоном, то окно программы будет свернуто, а не закрыто, и все равно придется воспользоваться первым способом для закры­
тия приложения. 168 Глава 9. Программирование для смартфонов Создание меню Практически все программы для смартфонов работают при помо­
щи команд меню. Поэтому надо получить базовые навыки работы с этим элементом управления. В режиме проектирования формы сле­
дует щелкнуть мышью в левой части голубой полоски, которая рас­
положена в нижней части экрана. Эта полоска является элементом меню, которое вызывается нажа­
тием кнопки Soft Keyl, находя­
щейся под экраном. На форме по­
явится текст Туре Неге (рис. 9.1). В этой области нужно ввести сло­
во Привет и нажать клавишу Enter. Введенный текст появится в ле­
вой части формы, и будет активи­
рована Soft Key 2 с той же надпи­
сью Туре Неге. В этой области нужно ввести слово Закрыть. Те­
перь можно вводить текст для подменю. Новый пункт меню по­
лучит заголовок Выход. Перед словом Выход появится единица. Среда разработки Visual Studio 2005 автоматически вставляет цифры в создаваемое меню. Эти цифры являются номерами кно­
пок-клавиш телефонов. С помо­
щью этой подсказки пользователь может быстро активировать нуж­
ный пункт меню нажатием соот­
ветствующей кнопки (рис. 9.2). Теперь нужно перейти на форму и дважды щелкнуть на пункте созданного меню Выход. В результате будет открыт редактор кода с заготовкой функции menuItem3_Cl ick. Ее код приведен в листинге 9.1. Листинг 9.1 privat e voi d menuItem3_Click(objec t sender, EventArg s e) { this.Closet): } Рис. 9.1. Создание меню Э -гементы управления 169 ,.Oto^..^^J .^ ^.Р^' f ormi.c s [Desig n Рис. 9.2. Создание подменю После запуска приложения следует нажать правую серую кнопку под экраном. При этом будет активирована правая часть меню и появится пункт 1 Выход. Для выполнения этой команды можно нажать клавишу 1 или большую кнопку в центре телефона, кото­
рая выполняет функцию клавиши подтверждения. Если все было сделано правильно, то приложение закроется. Итак, только что мы создали приложение для смартфона, добавили в него меню, запус­
тили приложение и закрыли его. В отношении меню приложения для смартфонов установлено спе­
циальное правило. Клавиша Left Softkey может иметь только один пункт меню, а клавиша Right Softkey может иметь разветвленное меню. Любопытно отметить, что при попытке создания подменю для левой клавиши предыдущая версия среды разработки Visual Studio 2003 выводила ошибку. Теперь такой ошибки не вы­
водится, и, теоретически, ничто не мешает нарушить установив­
шуюся традицию. Например, при использовании примеров для смартфонов под управлением Windows Mobile 5.0 программист может создавать вложенные меню для левой кнопки. Элементы управления Так как пользователь лишен возможности пользоваться стилусом, то многие элементы управления смартфонами не поддерживаются. 170 Глава 9. Программирование для смартфонов В этом легко убедиться, достаточно лишь взглянуть на панель ин­
струментов проекта для смарфтонов, чтобы увидеть, как резко уменьшилось число поддерживаемых объектов. Поначалу количество не поддерживаемых элементов управления приводит в замешательство. Как же писать приложения, если смарт­
фон не поддерживает такой распространенный элемент, как кноп­
ка? Так как в смартфонах не используется стилус, то применение кнопок просто бессмысленно. Управление объектами в приложе­
ниях для смартфонов осуществляется при помощи реальных кно­
пок-клавиш. Кроме стандартных кнопок с цифрами у смартфона имеются еще так называемые softkey-клавиши. Это две дополнительные кнопки под экраном, которые выполняют очень важные функции в прило­
жениях. Именно с помощью этих кнопок осуществляется работа с меню. Также надо помнить, что внешний вид элементов управления зача­
стую отличается от вида аналогичных элементов на КПК. Возьмем, к примеру, текстовое поле. Текстовое поле TextBox в смартфонах не имеет окантовки. Она появляется только в том случае, когда тек­
стовое поле получает фокус. В этом нетрудно убедиться на простом примере. Следует добавить на форму два текстовых поля. Одно из них авто­
матически получит фокус при загрузке приложения (рис. 9.3). Если с помощью клавиши навигации перейти на второе поле, то оно по­
лучит окантовку, а у первого поля, соответственно, окантовка про­
падет. textaox l textJ3cx2 Рис. 9.3. Окантовка у первого текстового поля, имеющего фокус Чтобы не путать текстовые поля с элементами Label, в надписях используют более жирный текст. На форме надо расположить два элемента Label. На рис. 9.4 видно, что строки label 1 и label2 выде-
Элементы управления 171 ляются более жирным начертанием текста. На этом различия не за­
канчиваются. Стоит расположить на форме еще одно текстовое поле и для его свой­
ства Multiline указать значение True. В поле надо ввести какой-ни­
будь длинный текст. После запуска проекта будет видно, что тексто­
вое поле не в состоянии уместить весь текст, а показывает только несколько первых слов с завершающим маленьким треугольником. labeli I extSox l labet t ta&8cK 2 В «су родилась елочи > Рис. 9.4. Различия внешнего вида некоторых элементов управления Если установить фокус ввода на этом текстовом поле и нажать кноп­
ку Enter, то текст полностью будет показан в новом окне (рис. 9.5). J лесу родилась елсм», • пе су ста росла Рис. 9.5. Полный текст в текстовом поле Пользователь может самостоятельно дописать слова песни в новом окне и выбрать команду Done или отказаться от подтверждения вво­
да с помощью команды Cancel То же самое касается и элемента ComboBox. Данный элемент получа­
ет окантовку при получении фокуса и отображает уже два треуголь­
ника. Чтобы раскрыть список элементов, хранящихся в комбини­
рованном окне, необходимо сначала установить фокус и нажать на кнопку Enter. При этом будет открыто новое окно, в котором с по­
мощью клавиш навигации пользователь может выбрать необходи­
мый элемент и выполнить команду меню Done. "*&*Ф*?фР?Щ 172 Глава 9. Программирование для смартфонов Существует также альтернативный способ выбора элемента из ComboBox. Для этого нужно опять установить фокус на комбиниро­
ванном окне и прокручивать имеющиеся записи при помощи кно­
пок навигации Влево или Вправо. Режимы ввода Первые модели сотовых телефонов для отправки сообщений име­
ли только один режима ввода. Пользователь нажимал на кнопки телефона в определенном порядке, вводя тот или иной символ. Затем появились другие режимы. В частности, сейчас поддержи­
вается числовой режим, так называемый режим Т9 и символь­
ный режим. Поначалу библиотека .NET Compact Framework не имела поддержки режимов ввода. Поэтому для установки необ­
ходимого режима программистам приходилось использовать механизм P/Invoke для вызова функций API, как показано в ли­
стинге 9.2. Листинг 9.2 [D1 1 Import("coredll.dlT. EntryPoIn t - "SendMessage") ] privat e stati c exter n irin t SendMessagedntPt r hWnd. uin t msg. uin t wParam. uin t IParam); // Сообщение для ретина ввода cons t uin t EM_SETINPUTMOD E - OxOODE; // Перечисление режинов ввода publi c enu m InputModeAP I { Spel l - 0. T9 - 1. Number s - 2. Tex t = 3 } publi c stati c voi d SetInputModeCContro l Ctrl. InputModeAP I mode ) { SendHessage(ctrl.Handle. EM_SETINPUTMODE. 0. (uint)mode): } privat e voi d mnuT9_Click(objec t sender. EventArg s e) { Режимы ввода 173 SetInputMode(textBox3. InputModeAPI.Т9); } privat e void ranuSpen_Click(object sender, EventArgs e) { SetInputMode(textBox3. InputModeAPI.Spell): } privat e voi d mnuNumeri c Click(objec t sender, EventArg s e) { SetInputMode(textBox3. InputModeAP I .Numbers ) -. } privat e voi d mnuText_Click(objec t sender. EventArg s e) { SetInputMode(textBox3. InputModeAPI.Text): } В данном примере нужный режим ввода указывается для текстово­
го поля textBox3 с помощью системы меню (рис. 9.6). ь&ттт- **»ш*ъ 1ик*ви Рис. 9.6. Выбираем режим ввода ПРИМЕЧАНИЕ Режим Т9 в эмуляторе не работает, поэтому надо проверять код на реальном устройстве. В библиотеке .NET Compact Framework 2.0 появилась возмож­
ность контролировать режим ввода текста с помощью класса InputModeEditor. Данный режим распространяется только на тек­
стовые поля. Предположим, что в программе есть два текстовых поля. В одном поле пользователь должен ввести свое имя, а во втором — номер телефона. В первом случае пользователь будет использовать бук­
вы, а во втором случае ему необходимы только цифры. Поэтому 174 Глава 9. Программирование для смартфонов можно заранее задать нужный режим ввода текста для разных тек­
стовых полей. Для этого надо указать ссылку на сборку Mi его-
soft.WindowsCE.Forms и задействовать класс InputModeEditor, как по­
казано в листинге 9.3. Листинг 9.3 Управление режимами ввода с помощью управляемого кода privat e void Forml_LoadСobject sender, EventArgs e) { // Устанавливаем текстовый режим ввода текста InputModeEd i tor.SetInputMode(txtName, InputMode.AlphaCurrent): // Устанавливаем числовой режим ввода текста InputModeEditor.SetInputMode(txtPhone. InputMode.Numeric): } Переопределение клавиш Soft Key На смартфонах клавиши Soft Key 1 и Soft Key 2 используются для управления меню. Если попробовать переопределить эти клави­
ши для других задач, то у вас ничего не получится. Дело в том, что события Key_Down не распознаются системой для этих клавиш, если на форме присутствует компонент Mai nMenu. Но если удалить этот компонент, устанавливаемый по умолчанию, то с этими кнопка­
ми можно будет связать собственные команды, как показано в листинге 9.4. Листинг 9.4 privat e voi d Forml_KeyDown(objec t sender. KeyEventArg s e) { if ((e.KeyCod e —• System.Windows.Forms.Keys.Fl) ) { // Sof t Ke y 1 lblTest.Tex t - "Вы нажали на клавишу Sof t Ke y Г: } if ((e.KeyCod e — System.Windows.Forms.Keys.F2) ) { // Sof t Ke y 2 lblTest.Text * "Вы нажали на клавишу Sof t Key 2"; } } Прокручивание формы 17 J Прокручивание формы Если форма не умещается на экране целиком, то пользователь мо­
жет прокрутить ее стилусом с помощью полос прокрутки. Особен­
но это полезно, если учесть, что .NET Compact Framework 2.0 те­
перь поддерживает свойство AutoScroll. Но смартфоны не имеют сенсорного экрана, реагирующего на стилус. Для прокрутки фор­
мы надо искать другой вариант. Например, можно воспользоваться обработкой события KeyDown. В тестовом проекте надо растянуть форму так, чтобы нижнюю часть не было видно на экране смартфона. На форме надо разместить не­
сколько надписей, причем одна из них должна располагаться в ниж­
ней части формы. Для свойства формы AutoScrol 1 надо задать значе­
ни е True. В листинге 9.5 приведен пример кода для прокрутки формы. Листинг 9.5 private void FormlKeyDownCobject sender. KeyEventArgs e) { if ((e.KeyCod e = System.Windows.Forms.Keys.Up) ) { // U p this.AutoScrollPositio n - ne w Point(-this. AutoScrollPosition.X,-this.AutoScrollPosition.Y - 16); } if ((e.KeyCod e — System.Windows.Forms.Keys.Down) ) { // Dow n this.AutoScrollPositio n « ne w Point(-this. AutoScrollPosition.X,-this.AutoScrollPosition.Y + 16); } if ((e.KeyCod e = System.Windows.Forms.Keys.Left) ) { // Lef t this.AutoScrollPositio n = ne w Point(-this. AutoScrollPosition.X - 16.-this.AutoScrollPosition.Y): } if ((e.KeyCod e = System.Windows.Forms.Keys.Right) ) { // Righ t this.AutoScrollPosition = new Poi nt(-thi s. AutoScrollPosition.X + 16.-this.AutoScrollPosition.Y); } } 176 Глава 9. Программирование для смартфонов После запуска приложения можно нажимать на клавиши навигации. Написанный код позволит прокручивать форму в выбранном направ­
лении. Но здесь на с подстерегает одна опасность. Код будет работать лишь тогда, когда форма имеет фокус. Если форма содержит элемен­
ты управления, то фокус может находиться у данного элемента. И тогда нажатия на клавиши навигации не принесут желаемого ре­
зультата. Это ограничение легко обходится добавлением соответству­
ющего обработчика события, как показано в листинге 9.6. Листинг 9.6 privat e voi d FormlJ_oad(objec t sender, EventArg s e) { p i ctureBoxl.Focus(); this.pictureBoxl.KeyDow n += new KeyEventHandler (FormlKeyDown); Теперь, даже если фокус находится не у формы, пользователь все равно сможет прокручивать форму при помощи клавиш навигации. Глава 10 Windows MobHe 5.0 Первый взгляд Устройства под управлением Windows Mobile, к которым относятся КПК и смартфоны, все глубже вторгаются в нашу жизнь. Эти уст­
ройства очень быстро эволюционируют, обзаводятся более совершен­
ными экранами, увеличивают размер своей дисковой памяти, снаб­
жаются фотокамерами и получают поддержку новых сетевых технологий. Операционная система Windows Mobile 5.0 сделала еще один шаг в развитии этих маленьких, но умных устройств. В новой платформе появилась поддержка двухмерных и ЗБ-изоб-
ражений, появилось больше возможностей обработки мультиме­
дийных файлов, намного проще стало взаимодействовать с фото­
камерами и устройствами позиционирования GPS. Причем эта поддержка осуществлена на программном уровне с помощью но­
вых классов и расширения функциональности старых классов. Компания Microsoft уделяет большое внимание данной платфор­
ме, предоставляя разработчикам подробнейшую документацию, примеры и инструменты разработки. Главная страница для Window Mobile 5.0 находится на сайте Windows Mobile по адресу msdn.micro-
soft.com/mobility/windowsmobiLe/default.aspx. На сайте можно скачать необходимые пакеты SDK, позволяющие работать с устройствами под управлением Windows Mobile 5.0. В этой главе будут рассматриваться новые возможности, заложен­
ные в систему Windows Mobile 5.0, которые будут интересны про­
граммистам. Улучшенная продуктивность В системе Windows Mobile 5.0 появились нововведения, которые увеличивают продуктивность труда программиста. Основные воз­
можности перечислены в следующем списке. Q Появились новые API, связанные с отображением графики, уп­
равлением контактами и взаимодействием с GPS. 178 Глав а 10. Windows Mobile 5.0 u Продолжено стирание граней между КПК и мобильными теле­
фонами. Код программы, написанный для КПК, легко портиру-
ется на смартфоны. а Улучшена и добавлена поддержка технологий передачи данных, в том числе прием и посылка SMS и телефонных звонков. Q В Visual Studio 2005 добавлена поддержка устройств под управ­
лением новой платформы с помощью SDK. Работа с данными более прозрачна, улучшен отладчик ошибок, изменен дизайн гра­
фического интерфейса, который позволяет менять ориентацию экрана и его разрешения. Q Переработан эмулятор. Поддержка мультимедиа Теперь программисты могут использовать классы, взаимодействую­
щие с фотокамерами. Это позволяет расширить область приложе­
ния камер и использовать их в работе с изображениями и видеозапи­
сями в приложениях. Разработчики могут использовать возможности музыкального плеера Windows Media Player 10 Mobile в своих при­
ложениях. Технология Direct3D позволяет разработчикам создавать более совершенные игры, а библиотека DirectDraw позволяет рабо­
тать с графикой на более высоком уровне. Поддержка управляемого кода Система Windows Mobile 5.0 обеспечивает первоклассную поддерж­
ку программистов, работающих с управляемым кодом. Основные нововведения перечислены в следующем списке. Q Все устройства под управлением Windows Mobile 5.0 поставля­
ются с исполняемой средой .NET Compact Framework 1.0 Service Pack 3, которая записана в независимую память. а При помощи управляемого кода осуществляется работа с SMS-
сообщениями, контактами Outlook Mobile и телефонными воз­
можностями устройства. Windows Mobile 5.0 API Система Windows Mobile 5.0 обзавелась новыми функциями API. Программисты получили в свое распоряжение новое пространство имен Microsoft.WindowsMobile с множеством классов, перечислений Первый взгляд 17 9 и делегатов. Кроме того, появились такие пространства имен, как Configuration, Forms, PocketOutlook, PocketOutlook .Messagel interception, Status и Telephony. Устройства под управлением Windows Mobile постоянно улучша­
ются. И разработчики требуют новых возможностей для написания красивых и сложных игр. Поэтому в состав системы включена биб­
лиотека Direct3D Mobile. Ее можно считать аналогом библиотеки Direct3D API, которая используется в настольных компьютерах. Для доступа к памяти, улучшенной работе со сложной графикой и видеоматериалами система Windows Mobile 5.0 предлагает вос­
пользоваться DirectDraw API. Эта библиотека также может быть востребована для разработки игр, она является аналогом библио­
теки DirectDraw API настольного компьютера. Все больше устройств выпускается со встроенными камерами. Раз­
работчики могут воспользоваться библиотекой DirectShow API для доступа к возможностям камеры. С помощью соответствующих функций программист может управлять работой камеры, записы­
вать, а потом отображать и проигрывать картинки и видеоматериа­
лы. Библиотека поддерживает множество форматов и является ана­
логом DirectShow настольного компьютера. Кроме того, в устройствах все чаще стали использоваться прием­
ники GPS. Раньше писать приложения, работающие с технология­
ми GPS, было довольно трудно. Нужно было использовать для ра­
боты serial API, что требовало хорошего знания основ сетевого программирования. Система Windows Mobile 5.0 во многом облег­
чила эту задачу при помощи технологии GPS Intermediate Driver. Эта технология предоставляет набор простых функций для досту­
па к данным GPS. В следующем списке приведены основные функ­
ции для работы с GPS Intermediate Driver; Q GPSOpenDevice — соединение с GPS Intermediate Driver; P 6PSC1 oseDevi ce — разрыв связи от GPS Intermediate Driver; • GPSGetPosition — получение текущих координат; Q GPSGetDeviceState — получение информации о текущем состоя­
нии устройства. Взаимодействие с ActiveSync Разработчики теперь могут запускать и останавливать процесс синхронизации ActiveSync, используя методы ActiveSyncStart и ActiveSyncStop. 180 Глава 10. Wi ndows Mobile 5.0 Новые возможности системы После выхода системы Windows Mobile 2005 разработчикам стали доступны многие системные возможности, что намного упростило разработку приложений с использованием передовых технологий. В этом разделе были упомянуты некоторые новые возможности, которые появились в Windows Mobile 5.0. Но их надо рассмот­
реть на примерах, чтобы лучше понять преимущества новой плат­
формы. Подготовка к работе Прежде чем создавать приложения для устройств под управлени­
ем системы Windows Mobile 5.0, нужно установить необходимые пакеты SDK. Компания Microsoft предлагает заказать специальный диск с набором всех необходимых пакетов, сделав заказ на страни­
це их сайта, которая располагается по адресу msdn.microsoft.com/ mobility/windowsmobile/howto/resourcekit/default.aspx, или самостоя­
тельно скачать эти пакеты с сайта (рис. 10.1). Wsioaws Ut)b4» S 0 De.'»fep*r Я*-, -i^i«» kl Л и оо\*« <~к& <w* E*pwf*f ^:::::::::::к:::::?::в:::?^пгк;?:^ MSDN нот е Developer Centers Library \ Downloads How to Buy : Subscribers f Wor1dw| Mobile Devtloper Center WebC&st Getting Started Understanding Mobil e .NET Compact Framework Windows Mobil e SQL Mobile Other Technologies Semoles MSDM Home > Mobile Deveioper Center > Windows Mobile > Help and How 1.1.9.L> WiHdfflre,jMqbHe S:0,Pjftvej^er Resource Kit EVERYTHING YOU NHEO all in one place. Рис. 10.1. Веб-страница Windows Mobil e 5.0 Подготовка к работе 181 Следует обратить внимание на то, что существуют отдельные версии SDK для карманных компьютеров и смартфонов. После завершения установки пакеты автоматически интегрируются в среду разработки Visual Studio 2005. В результате разработчик получает новые эмуля­
торы под Windows Mobile 5.0, в систему будут добавлены новые клас­
сы, а справочная система пополнится новыми статьями и примерами. Также можно скачать локализованные версии эмуляторов. К при­
меру, страница для загрузки локализованной версии Windows Mobile 5.0 Pocket PC Emulator Images находится по адресу www-microsoft.com/down loads/details.aspx?familyid=EEC33AE3-C129-
4C25-ABAA-18E8E842178F&displaylang=en. Чтобы воочию увидеть особенности Windows Mobile 5.0, надо раз­
работать соответствующее приложение. Для этого следует запус­
тить среду разработки Visual Studio 2005 и создать новый проект. При этом надо выбрать пункт Smart Device project, чтобы создать приложение для мобильного устройства. Затем надо выбрать плат­
форму Pocket PC под управлением Windows Mobile 5.0 (рис. 10.2). После создания проекта Visual Studio 2005 отобразит пустую фор­
му с установленными реальными размерами устройства. New Prpj ec t suaieasrc • Windows • Smart Device Database Starter tots -ther Languages r'! VsuafC* i-WMdows & Smart Device • Pocket PC 2003 . • Smenpfton* 2003 Windows CE 5.C Ш 1 * Ш г & Ш 1":,- ».» ?ШГЪ Vfeua) Studi o ki st sl ed templates ВевсеЛ: ctss» Library Control Library Console Empty Project Я Device Class Library Console Empty Project *pp*cab... (1.C) Appscab... (1.C) Wndows Mobiie 5.0 Smertp jj My Templates Database Starter Kits Щ | * pnrjea:rv o-eeflmj « .«ET Compact ir?em**«Ktil,9 farrn»appfccaBo«ft>rwmoemt-M .::: _ v :•.ш,:•: ,• & ^. Рис. 10.2. Выбор типа проекта для Windows Mobil e 5.0 Система Windows Mobile 5.0 предоставляет разработчику множе­
ство новых пространств имен, классов, свойств и событий, с помо-
182 Глава 10. Windows Mobile 5.0 щыо которых он может получить доступ ко многим возможностям, которые ранее приходилось реализовывать только при помощи очень сложного и громоздкого кода на C++ с применением Windows API. Теперь разработчики могут для этих целей применять управ­
ляемый код .NET Compact Framework. Имеет смысл поближе по­
знакомиться с этими возможностями. Microsoft. WindowsMobtte.PocketOutlook С помощью пространства имен Mi crosof t. Wi ndowsMobi 1 e. PocketOutl ook разработчик получает доступ к модели Pocket Outlook Object Model (РООМ). А имея доступ к РООМ, можно легко получить данные из объектов Контакты, Встречи и Задачи. Также можно получить электронные адреса из адресной книги, номера отправки SMS и со­
общения. В следующем списке указаны наиболее часто используе­
мые классы. a OutlookSession — представляет собой объект Pocket Outlook для работы с контактами, встречами и задачами. Также можно по­
лучить доступ к учетным записям электронной почты и SMS. • Appointment— класс, отвечающий за работу с назначенными встречами. С помощью данного класса можно редактировать данные записи и тип применяемого сигнала. • Contact — класс для работы с контактами. Данный класс поддер­
живает более 60 свойств. Q Task — класс для работы с задачами. В следующих разделах применение пространства имен Micro­
soft . Wi ndowsMobi 1 е. PocketOutl ook будет рассматриваться на конкрет­
ных примерах. Встречи (Appointment) При помощи объектной модели Pocket Outlook Object Model разра­
ботчик может добавить новую запись в список намечаемых встреч. Сначала надо создать тестовый проект. Чтобы получить доступ к объектам Pocket Outlook, нужно добавить ссылку на соответству­
ющие сборки. Для этого следует выполнить команду Project • Add Reference. В диалоговом окне Add Reference нужно выбрать строки Microsoft.WindowsMobUe. Forms и Microsoft.WindowsMobile.PocketOutlook (рис. 10.3). Встречи (Appointment) 183 аЖЗ Rdferetx* HFT ; project s Browse5 Recent ^ ."V?"'' СипрепасйНвдае •* Verstoo CustomMerstiaiefs 2.0.O.O InsteilerActtons 1.0.0.0 Mtcrosoft.VtebalBasi c 8.0.0.0 Mterosoft.WirKtevwCEJOnris _A-_?.?;?. ;ЯьйЬй%». i.c.c.o «2.0.50727 «2.0.5O727 2.O.O.0 MkTosoftVw'irdovvsMoblle.ConSgtiratHjn Mlcrc5uft,V«'mdo*vsMcbite.DirectX 1.0.0.C 2Л0.О vl .1.4322 "vt.1.432 2 2.0.0-0 Ci\fivgram ffresVMi |i СУгодгет «езуэ*| СЛРгоогап» Ff et\t t | CcxProgrefn RSesNJWJ C.-'Prcgram Ftjetfo?^ C:«Prograrr> FK*s\Wi CVTocrem f testy*-
MK7C*oftWWKJo«sMoblte.S!aius Microsof t Wr^ev/sMobite.Telephony mscodi b OpenNETCP OpenNETCF.Data OpeobETCF.Orevvlog I-O.O C i.o.e.o 2.0.ОЛ 2.0.0.0 2.0O.O 2.0.0.0 «1.1.4322 «1.1.4322 2.0.0.0 V2.0.50727 V2JJ.50727 v2.0.50727 CVnwr wn FCesWVl" CtVrogrefn WesW* C:\Pr09raro Ries\0f C'Vroeram FiKs\Of ...л С Vrogrero ПМ«\РрШ-
* Ш •|~...,..Ж-у lll...,-,5^^!.::. Рис. 10.3. Подключение к проекту сборок После нажатия кнопки ОК выбранные ссылки должны появиться в списке ссылок Solution Explorer, как показано на рис. 10.4. ЗШШПЕХЙОШ $3 Appoiatment_.CS * Ш Properties Ф Ш References | -v» MfcTOSoftWrxtowsMobte.f4>rrm «»HkyosoftVWrxJc*»sf4ot*e.Poda«CXtfc>ok «omscortb -a System - о System.Deta •О System. Dtdtwg -сэ System.YAKtows.Forms ^ э System.Xmi Si Ш ^3 Program.cs Рис. 10.4. Окно Solution Explorer Теперь в редакторе кода следует добавить объявления для про­
странств имен Microsoft.WindowsMobile.Forms и Microsoft.WindowMo-
bile.PocketOutlook сразу после существующих объявлений. В этом случае появляется возможность работы с различными классами Pocket Outlook. Например, чтобы получить доступ к настройкам 184 Глава 10. Windows Mobile 5.0 для встреч, используется класс Appointment, как показано в лис­
тинге 10.1. Листинг 10.1 us i n g M i crosoft.W i ndowsMobi1e.Forms; us i n g M i croso f t. W i ndowsMobi1e.PocketOut1ook; privat e voi d buttonl_Click(objec t sender. EventArg s e) { // Создаем встречу и устанавливаем детали Appointmen t app t - ne w AppointmentO: // Тема для встречи appt.Subjec t = "Встреча с тещей"; // Время встречи - 8 марта 200 7 в 22 часа appt.Star t = ne w DateTime(2007. 03. 08, 22. 00, 00); // Продолжительность встречи - 3 минуты appt.Duratio n * ne w TimeSpan(00. 03, 00); // Использовать виброзвонок для напоминания appt.ReminderVibrat e = true; // Повторять напоминание, пока пользователь не отреагирует appt.ReminderRepea t « true; // Создаем сессию Outloo k // добавляем встречу в папку встреч Outloo k usin g (OutlookSessio n sessio n - ne w OutlookSessionO ) { session.Appointments.Iterns.Add(appt); session. DisposeO; > MJttj W m J m I Встреч* с тецей 22:00-22:0 3 Рис. 10.5. Календарь с установленной записью встречи Работа с адресной книгой 18 5 Нужно запустить программу и нажать кнопку Добавить встречу. Пос­
ле этого можно закрыть приложение, так как свою работу он о закон­
чило. Теперь следует открыть программу Календарь, которая встрое­
на в систему. В календаре нужно найти дату, которая использовалась в программе. В текущем примере встреча была запланирована на 8 марта 2007 года. Если все сделано правильно, то в указанной дате должна присутствовать запись о новой встрече (рис. 10.5). Работа с адресной книгой В этом разделе будет рассмотрен пример, в котором будет добавле­
на новая запись в объект Контакты. Для этого надо, как и прежде, добавить в проект ссылки на соответствующие сборки Mi crosoft. Wi n-
dowsMobi 1 e. Forms и Mi crosoft. Wi ndowsMobi 1 e. PocketOutl ook. А в редак­
торе кода надо добавить объявления для пространств имен Micro­
soft . Wi ndowsMobi 1 е. Forms и Mi crosoft. Wi ndowMobi 1 e. PocketOut 1 oo k сразу после существующих объявлений. Теперь можно обращаться к Контактам через объект Outl ookSessi on. Чтобы добавить новый контакт в коллекцию Контакты, надо раз­
местить на форме кнопку с именем butAddContact и написать код, приведенный в листинге 10.2. Листинг 10.2 privat e OutlookSessio n session; publi c Forml O { InitializeComponentC); // Создаем экземпляр сессии Pocke t Outloo k sessio n = ne w OutlookSession(); } privat e voi d butAddContact_Click(objec t sender, EventArg s e) { Contac t contac t • ne w ContactO; contact.FirstNam e = "Билл"; contact.LastNam e = "Гейтс"; contact.Eraai1lAddres s = "bi11gates@m i crosoft.com"; contact.Birthda y = ne w DateTime(1955.10.28); contact.CompanyNam e - "Microsoft"; contact.WebPag e = ne w Uri("http://www.raicrosoft.com"); session.Contacts.Items.Add(contact): } 186 Глава 10. Windows Mobile 5.0 Код очень прост и практически не требует комментариев. В начале работы создается переменная contact, в которой можно задавать самые различные параметры. В этом примере использовались толь­
ко основные свойства. Были указаны имя, фамилия, электронный адрес, день рождения, имя компании и ее веб-страница. После того как новый контакт будет добавлен в список, нужно закрыть сессию при помощи метода Di spose(). После запуска приложения следует нажать кнопку Добавить в Кон­
такты. В результате этого в списке Контакты появится новая запись (рис. 10.6). шш» < Просмотр веС-страницы 23.1055 Дм.рскд. Рис. 10.6. Просмотр списка контактов Но разработчик может не только добавлять, но и получать информа­
цию из имеющегося элемента списка. Для этого на форму надо по­
местить список IstContacts и кнопку butGetlnfo. Прежде чем полу­
чить информацию о нужном нам человеке, нужно сначала получить сам список контактов. И только потом, выбрав из этого списка нуж­
ную запись, можно получить дополнительную информацию. Для получения полного списка контактов нужно добавить код в обработ­
чик события Form Load, как это показано в листинге 10.3. Листинг 10.3 privat e voi d FormlLoadCobjec t sender, EventArg s e) { // Получаеи список контактов IstContacts.DataSourc e • session.Contacts.Items: Теперь при загрузке формы список автоматически будет заполнен. Пользователь может выбрать любую запись и получить дополни-
Работа с адресной книгой 18 7 тельную информацию о выбранном контакте. Для этого в событии butGetlnfoCl ick создается код, приведенный в листинге 10.4. Листинг 10.4 privat e voi d butGe t I nfoC l ic k (objec t sender, EventArg s e) { // Получим информацию о выбранном контакте session.Contacts.Items[lstContacts.SelectedIndex].ShowDialogO; } Когда пользователь выделит интересующую его запись и нажмет кнопку Получить информацию, на экран будет выведено стандартное диалоговое окно с информацией о выбранной записи. Удалить контакт из списка еще проще, чем создать его. На форму надо добавить еще одну кнопку butDel Contact, с которой будет свя­
зан код, приведенный в листинге 10.5. Листинг 10.5 privat e voi d butDe l ContactC l ic k (objec t sender, EventArg s e) { // Удаляем выбранный контакт session.Contacts.Items[lstContacts.SelectedIndex].Delete(); } Также из приложения можно вызвать стандартное окно выбора контакта, используемое программой Pocket Outlook. Теперь совсем не обязательно закрывать нашу программу и открывать окно кон­
тактов, как это было сделано при добавлении новой записи в спи­
сок контактов. Стандартное окно имеет некоторые дополнительные возможности, которые могут пригодиться разработчикам. Доступ к данному окну осуществляется через класс ChooseContactDialog, как показано в ли­
стинге 10.6. Листинг 10.6 privat e voi d butShowContactsC l ic k (objec t sender, EventArg s e) { ChooseContactDialo g contactDialo g - ne w С hooseContactD i al ogO; // Прячем пункт меню Новый контакт contactDialog.HideNe w - true; // Выводим диалоговое окна на экран продолжение & 188 Глава 10. Windows Mobile 5,0 Листинг 10.6 {продолжение) contactDialog.ShowDialogO; // Показываем выбранный контакт Mess ageBox.Show(contactDialод.SelectedContactName. "Выбранный контакт"); } Электронная почта Кроме получения доступа к списку контактов и добавления новых встреч, разработчик может также отсылать сообщения п о электрон­
ной почте или через SMS. Для этих целей используются соответствующие пространства имен Mi crosoft.WindowsMobil е. PocketOut look. Email Account и Microsoft.Win-
dowsMobi 1 e. PocketOut 1 ook. SmsAccount. Классы и з этих пространств имен позволяют легко интегрировать отправку сообщений в ваши прило­
жения. Например, класс Email Account позволяет создавать электрон­
ные письма и присоединять к ним файлы. В следующем примере демонстрируется вызов диалогового окна ChooseContactDialog для выбора нужного адресата из списка контак­
тов, которому будет отправлено электронное письмо. Приложение создаст сообщение, в коде будет указана и тема письма. Также в письмо будет добавлен вложенный файл, после чего сооб­
щение будет отправлено выбранному ранее лицу. Все эти действия проиллюстрированы листингом 10.7. Листинг 10.7 privat e voi d butSendEraail_Click(objec t sender. EventArg s e) { ChooseContactDialo g contactDialo g - new ChooseContactDialog(); contactDialog.Titl e - "Выберите контакт для отправки email"; i f (contactDialog.ShowDialogO — DialogResult.OK) { EmailMessag e messag e - ne w Emai1Message(),• message.To.Add C ne w Rec i p i ent(contactD i alog.SelectedContact.EmaillAddress)); message.Subjec t - "С дней рождения"; SMS-сообщения 18 9 message.BodyTex t = "Уважаемый Владимир Владимирович! Поздравляю вас с дней рождения! Посылаю ван открытку с видами Петербурга. Ваша Люда".: me s sage.At t achmen t s.Add ( ne w Attachment(@"\M y DocumentsVpiter.jpg")); usin g (OutlookSessio n sessio n = ne w OutlookSessionO ) { session.EmailAccounts[0].Send(message); session.DisposeO: } } } После запуска программы и нажатия кнопки Послать письмо будет открыто стандартное окно Контакты, где можно выбрать адресата. После выбора получателя по его электронному адресу будет отправ­
лено электронное письмо с заданным содержанием. Если надо отправить электронное письмо адресату, который не вне­
сен в адресную книгу, то пример надо переработать. Новый код при­
веден в листинге 10.8. Листинг 10.8 privat e voi d butSendEmail2_C1ick(objec t sender. EventArg s e) { Recipien t recipien t - ne w Recipient("alexander.klimoff@gmai1.com"); EmailMessag e ms g =• ne w Emai1MessageО; // Кому письмо msg.To.Add<recipient): // Тема письма msg.Subjec t - "О вашей книге"; // Текст письма msg.BodyTex t = "Спасибо за книгу"; msg.Send("Act i veSync"); } SMS-сообщения Отправка SMS-сообщения с помощью новых возможностей тоже очень и очень проста. Эти сообщения весьма популярны у владель­
цев мобильных телефонов. Раньше для отсылки и приема SMS 190 Глава 10. Windows Mobile 5.0 приходилось использовать неуправляемый код, очень сложный для восприятия неопытным программистом. Теперь создать код, отсы­
лающий сообщение, не сложнее, чем написать само сообщение, что иллюстрирует листинг 10.9. Листинг 10.9. privat e voi d butSendSMSClickCobjec t sender. EventArg s e) { ChooseContactDialo g contactDialo g = new ChooseContactDi alog(): contactDialog.Titl e = "Выберите получателя"; i f (contactDialog.ShowDialogO = DialogResult.OK) { // Создаем SMS-coo6uteHM e SmsMessag e messag e - ne w SmsHessage C co n tactD i a 1 og. Se l e c tedContact. Mob i 1 eTe l ephoneNumber, "Купи хлеба. Жена"): message.RequestDeliveryRepor t = true; // Посылаем сообщение mes s age. SendO; } } В этом примере SMS-сообщение отсылалось адресату, чья запись уже имелась в адресной книге. Если же требуется отправить сооб­
щение, не используя окно Контакты, то придется воспользоваться другим кодом. Здесь я хочу сделать небольшое отступление и открыть вам боль­
шой секрет. Разработчик может посылать SMS-сообщения самому себе при помощи эмулятора! Если послать SMS-сообщение из эму­
лятора на телефонный номер 4250010001, то оно вернется на эму­
лятор (рис. 10.7). Итак, необходимо отправить SMS-сообщение человеку, чья учет­
ная запись не отражена в списке Контакты. Для этого используется код, приведенный в листинге 10.10. ПРИМЕЧАНИЕ При отладке приложения в эмуляторе надо использовать ирлевое ус­
тройство типа «Phone Edition*. Если проверять пример в обычном эмуляторе, то будет отображено сообщение об ошибке «Could not load sms.dll». Впрочем, это не удивительно. Если эмулятор не имеет телефонных оУункций, то как можно отправлять SMS-сообщение? Прием и обработка SMS-сообщений 191 Листинг 10.1 0 privat e voi d butSendSMS2_Click<objec t sender. EventArg s e) { SmsMessag e messag e = ne w SmsMessageO; // Номер получателя message.To.Add(ne w Recipient("4250010001")); // Текст сообщения message.Bod y = "Позвони домой"; // Посылаем сообщение message. SendO; } Результат выполнения этого кода приведен на рис. 10.7. г И \ •• -
ПкптЯМЁ': : Пооцт^ЗМв-г йаалечча-* •"%• w 425001000 1 Поясни домой O.nwMN. Рис. 10.7. Прием SMS-сообщения Прием и обработка SMS-сообщений Итак, м ы научились отправлять SMS-сообщения из своей програм­
мы. Но было бы неплохо научить приложения принимать подоб­
ные сообщения. Для приема сообщений существует пространство имен Messagelnterception, которое находится в сборке Microsoft .Win-
dowsMobi 1 е. PocketOut 1 ook. Следует заметить: можно организовать прием сообщений таким об­
разом, что запущенное приложение будет перехватывать нужные сообщения, содержащие ключевые слова. Причем система даже не покажет окно, уведомляющее о прибытии перехваченного события. С этой возможностью стоит познакомиться ближе. После создания нового проекта Intercept!onSMS_CS, нужно добавить на форму кноп­
ку для отсылки SMS-сообщения, флажок chkAlert для установки 192 Глав а 10. Windows Mobile 5.0 флага срочности и текстовое поле, в котором будет содержаться текст SMS-сообщения. Затем надо задать ссылки на уже применяв­
шиеся ранее сборки Microsoft.WindowsMobile и Mi с rosoft. Windows-
Mobile.PocketOutlook. Код, отвечающий за обработку принятых со­
общений, приведен в листинге 10.11. Листинг 10.1 1 usiri g M i crosoft. Wi ndowsMob i 1 e; us i n g M i crosoft.W i ndowsMobi1e.PocketOutlook; usin g Mi crosoft.W i ndowsMobi1e.PocketOutlook.Messagelntercept i on; // Объявляем переменную privat e Messagelntercepto r smsInterceptor: privat e voi d smsInterceptor_MessageReceived(objec t sender, MessagelnterceptorEventArg s e) { // Обработка входящего сообщения MessageBox.ShowC'K вам пришло срочное сообщение"); } privat e voi d butSendSM S Click(objec t sender, EventArg s e) { SmsMessag e messag e - ne w SmsMessage(): // Номер получателя message.To.AddCne w RecipientC"4250010001")); // Текст сообщения if (chkAlert.Checked ) { // Если взведен флажок, то добавляем слово Срочно! message.Bod y - "Срочно! " + txtSMSText.Text; } els e { message.Bod y • txtSMSText.Text; } // Посылаем сообщение message. SendO; } privat e voi d Forml_Load(objec t sender, EventArg s e) { Прием и обработка SMS-сообщений 19 3 smslntercepto r - new Messagelnterceptor(Intercept i onAct i on.Not i fyAndDelete. true): smslnterceptor.MessageConditio n = new MessageCondition ( MessageProperty.Body. MessagePropertyCompar i sonType.Sta rtsWi th, "Срочно", true); smslnterceptor.NessageReceive d += new MessagelnterceptorEventHandle r (smsInterceptorMessageRece i ved); При помощи ключевого слова us i ng было объявлено несколько про­
странств имен, также была добавлена переменная smslnterceptor, после чего можно было объявлять функцию обработки сообщения smsInterceptor_MessageReceived. При получении SMS-сообщения с определенным текстом эта функция выводит соответствующую строку. Но самое интересное происходит в событии Form_Load. Как только устройство принимает SMS-сообщение, оно перехватывается при­
ложением для дальнейшей обработки. Если сообщение начинается словом «Срочно», то пользователь предупреждается о прибытии важного сообщения, после чего это сообщение удаляется. Подоб­
ное поведение обеспечивает параметр Noti fyAndDel ete. После запуска приложения на форме будут отображены текстовое поле и флажок. После ввода текста сообщения нужно нажать кноп­
ку Послать SMS. Код отправки сообщения позаимствован из преды­
дущего примера. Система должна отреагировать на прибытие но­
вого сообщения с помощью специального уведомления, которое мы видели при разборе предыдущего примера (см. рис. 10.7). Теперь следует повторить операцию. Только на этот раз надо взвес­
ти флажок Пометить как срочное. В этом случае при отправке сообще­
ния в начало текста вставляется дополнительное слово «Срочно». После нажатия кнопки приложение должно перехватить прибытие SMS-сообщения, так как теперь оно содержит ключевое слово, кото­
рое определялось в параметре StartWith. Как только это произойдет, сообщение будет удалено, а пользователь получит уведомление о прибытии срочного сообщения (рис. 10.8). Но следует помнить, что для перехвата сообщения приложение должно быть запущено. Данный пример предоставляет разработчику весьма широкие воз­
можности. Представьте себе, что ваша компания рассылает своим 7-287 3 194 Глава 10. Windows Mobile 5.0 сотрудникам особым образом отформатированные сообщения. Про­
грамма может обработать эти сообщения и автоматически создать новые записи в списках Контакты или Встречи. И теперь сотруд­
нику достаточно только взглянуть на экран, чтобы увидеть прият­
ную новость, что сегодня компания выдает премию, за которой нуж­
но подъехать в офис. Л «»ч yflfi Д » ' Ц | 0 Пометить как cpowoe Рис. 10.8. Прием срочного сообщения ВНИМАНИЕ Для примеров, связанных с SMS-сообщениями, нужно использовать эмуляторы и устройства, имеющие возможность работы с SMS. Телефония Разработчик может использовать возможности телефонии при по­
мощи класса Mi с rosof t. Wi ndowsMobi le. Tel ephony. Phone. Используя ме­
тод этого класса Talk, можно программно набрать нужный телефон­
ный номер. При использовании класса Phone перед началом работы нужно установить ссылку на сборку Mi crosof t. Wi ndowsMobi 1 e. Tel ephony. Пример использования этого метода приведен в листинге 10.12. Листинг 10.1 2 us i n g M i crosoft.W i ndowsMobi1e.Telephony; // Объявляем переменную Phon e phon e = ne w PhoneO; // Набираем номер // Перед набором запрашиваем подтверждение phone.Talk("4255551212". true); State and Notifications Broker 195 Обратите внимание на набираемый номер. С помощью данного но­
мера разработчик может делать звонок на эмуляторе. Эмулятор сначала запросит подтверждение набора номера (рис. 10.9) и затем будет звонить по указанному номеру. &е«Ши Наврать 42SS551212? И ,<** * fSa1[HPt | .; button*' Рис. 10.9. Запрос набора указанного номера После подтверждения будет установлено соединение с неизвест­
ным абонентом 4255551212 (рис. 10.10). т<4-&т Щь - - ** •ЙМЙКЙЙШ. Рис. 10.10. Соединение с абонентом State and Notifications Broker В Windows Mobile 5.0 появилась новая технология, получившая название State and Notifications Broker. Использование данной тех­
нологии позволяет управлять состоянием устройства. Раньше для доступа к системным настройкам приходилось использовать неуп­
равляемый код. Но теперь можно использовать возможности тех­
нологии State and Notification Broker. 7 * 196 Глав а 10. Windows Mobile 5.0 При помощи этой технологии можно обрабатывать информацию о различных состояниях устройства и постоянно отслеживать изме­
нения этих состояний. Если будут обнаружены какие-либо изме­
нения, то система сообщит об этом приложению. Использование данных технологий открывает широкие возможности для увеличе­
ния функциональности программ. Например, разработчик сможет определять силу сигнала от сотовой станции, значение текущего заряда батареи, узнать, подключен ли крэдл, отслеживать состоя­
ние ActiveSync, определять наличие подключаемой клавиатуры, фотокамеры и гарнитуры. Также с помощью этой технологии мож­
но определять число Bluetooth-соединений, отображать список те­
кущих сетевых соединений, получать информацию об ориентации экрана. Разработчик может получать информацию о следующей назначенной встрече (Appointment), обрабатывать информацию о проигрываемом музыкальном файле, получать информацию о контактах и количестве не прочитанных электронных писем, SMS-
сообщений и пропущенных телефонных звонков. Чтобы использовать возможности State and Notifications Broker в приложениях, надо добавить ссылку на сборку Microsoft.Win-
dowsMobilе.Status. Также необходимо добавить ссылку на сборку Microsoft .WindowsMobi 1 е. После этого программа готова использовать классы пространства имен Mi crosoft.Wi ndowsMobi 1 e. Status. Конечно, без наглядного примера обойтись просто нельзя. Предпо­
ложим, что нас интересует информация о владельце устройства и необходимо отслеживать изменение этой информации. Для этого надо создать новый проект и добавить на форму элемент Label. Этого вполне достаточно для работы примера. Также необходимо доба­
вить ссылки на сборки Mi crosoft.Wi ndowsMobi le и Mi crosoft. Win­
dowsMobi 1 e. Stat us при помощи команды меню Project • Add Reference. Нас интересует изменение электронного адреса владельца устрой­
ства. Для этого используется Код, приведенный в листинге 10.13. Листинг 10.1 3 us i n g M i crosoft.W i ndowsMobi1e.Status; privat e SystemStat e sysState; sysStat e - ne w SystemStateCSystemProperty.OwnerEmail, true); sysState. Change d + = ne w ChangeEventHandler(sysStateChanged): privat e voi d sysState_Changed(objec t sender. ChangeEventArg s args ) { lblOwnerEmail.Tex t я SystemState.OwnerEmai1; State and Notifications Broker 19 7 Протестируем пример. После запуска приложения с ним не нужно ничего делать. Следует нажать кнопку Пуск и выбрать пункт меню Настройка. На вкладке Личные нужно активировать пиктограмму Данные о владельце. В соответствующем текстовом поле Эл.почта следует изменить указанный адрес электронной почты. Если теперь вернуться к приложению, то можно будет увидеть, что изменилось содержимое надписи IblOwnerEmail. Таким образом, программа ав­
томатически отреагировала на изменение данных в настройках си­
стемы. Конечно, можно получать данные об электронном адресе владельца в принудительном порядке. Для этого используется код, приведенный в листинге 10.14. Листинг 10.1 4 privat e voi d butGetEmai l Click(objec t sender, EventArg s e) { //Получим email владельца устройства IblOwnerEmail.Text - SystemState.OwnerEmail; Да, с помощью этого кода можно получить интересующие данные, но в этом случае нельзя узнать, когда эти данные изменятся. При­
дется через определенные промежутки времени проверять, не из­
менился ли адрес у владельца устройства. Но стоит вернуться к примеру уведомления об изменении элект­
ронного адреса владельца устройства. Отслеживанием изменений в системе занимается класс SystemState. Данный класс содержит множество статичных свойств для получения различных настроек системы. Но кроме этого класс SystemState содержит очень важное событие Changed. Для обработки данного события нужно сначала создать экземпляр класса SystemState и передать ему соответствую­
щее свойство: sysState = new SystemState(SystemProperty.OwnerEmail, true); Затем нужно присоединить делегат к новому экземпляру события Changed: sysState.Changed += new ChangeEventHandler(sysState_Changed); А уже после этого можно перехватывать изменение состояния нуж­
ных параметров: private void sys$tate_Changed(object sender. ChangeEventArgs args) { IblOwnerEmail .Text • SystemState. Owner Email; } г •'•••j 198 Глава 10. Windows Mobile 5.0 Пример с электронным адресом был приведен лишь для ознаком­
ления. На самом деле, с помощью соответствующих свойств можно получить доступ более чем к ста системным настройкам. Наиболее внимательные читатели могут заметить, что State и Notifications Broker порой дублируют функциональность, которую можно вос­
произвести при помощи других средств. Например, текущую ори­
ентацию экрана можно узнать с помощью функции API Get-
SystemMetrics или с помощью вызова Screen.PrimaryScreen.Bounds. А информацию о заряде батареи можно узнать с помощью функ­
ции GetSystemPowerStatusEx. Но зачем понадобилось создавать еще одну дополнительную воз­
можность извлечения информации? Причин для такого шага было несколько. Прежде всего, новые возможности удобны и просты. В предыдущем примере было показано, что для получения элект­
ронного адреса владельца устройства достаточно вызвать одно со­
ответствующее свойство. Для получения других значений также вы­
зываются соответствующие свойства. Причем названия этих свойств говорят сами за себя и не требуют наличия под рукой спра­
вочной литературы. Для закрепления материала нужно дополнить программу еще не­
сколькими примерами получения различных свойств. Можно до­
бавить отображение уровня заряда батареи, текущего состояния батареи, наличия радиоприемника и фотокамеры, названия сото­
вого оператора и определение текущей ориентации экрана. Все это делает код, приведенный в листинге 10.15. Листинг 10.1 5 privat e voi d butGetInfo_Click(objec t sender, EventArg s e) { lstlnfo.Items.Add("Название оператора: " + SystemState.PhoneOperatorName); lstlnfo.Items.Add("Наличие радио: " + SystemState.PhoneRadioPresent); 1stInfo.Iterns.Add("Наличие камеры: " + SystemState.CameraPresent); lstlnfo.Items.Add("Ориентация экрана " + SystemState.D i splayRotat i on); } privat e voi d butBatter y Click(objec t sender, EventArg s e) State and Notifications Broker 19 9 // Уровень заряда батареи BatteryLeve l batteryLeve l = SystemState.PowerBatteryStrength: BatteryStat e batteryStat e - SystemState.PowerBatteryState; strin g strBatteryLeve l = "Уровень заряда"; switc h (batteryLevel ) { cas e BatteryLevel.VeryLow: strBatteryLeve l - "Уровень заряда: Очень низкий (О-
20* ) break; cas e BatteryLevel.Low: strBatteryLeve l = "Уровень заряда: Низкий (21-40*)": break; cas e BatteryLevel.Medium: strBatteryLeve l =» "Уровень заряда: Средний (41-60*)"; break: cas e BatteryLevel.High: strBatteryLeve l = "Уровень заряда: Высокий (61-80)"; break; cas e BatteryLevel.VeryHigh: strBatteryLeve l - "Уровень заряда: Очень высокий (81 -
100*)"; break; > // Состояние батареи strin g strBatteryStat e = "Состояние батареи: ": if ((batteryStat e & BatteryState.Normal ) — BatteryState.Normal ) strBatteryStat e + = "Нормальное "; if ((batteryStat e & BatteryState.NotPresent ) •= BatteryState.NotPresent ) strBatteryStat e + = "Батарея отсутствует "; if ((batteryStat e & BatteryState.Charging ) = = BatteryState.Charg i ng ) strBatteryStat e + = "Заряжается "; if ((batteryStat e & BatteryState.Low ) — BatteryState.Low ) strBatteryStat e + = "Низкий заряд "; if ((batteryStat e & BatteryState.Critical ) — BatteryState.Critical ) StrBatteryStat e •+ - "Критическое"; MessageBox.Show(strBatteryLeve l + "\n" + strBatteryState); } 200 Глав а 10. Windows Mobile 5.0 Мультимедиа Система Windows Mobile 5.0 обеспечивает еще более глубокую под­
держку мультимедиа, чем предыдущие версии операционных сис­
тем. Теперь разработчики имеют возможность напрямую работать с фотокамерой, встраивая в свои программы взаимодействие с ка­
мерой и обработку картинок и видеороликов. Технология Microsoft DirectShow дает возможность управлять потоковыми мультиме­
дийными материалами. Программа Microsoft Windows Media Player 10 Mobile позволяет интегрировать функциональность музыкаль­
ного плеера в собственные приложения. Технология Microsoft DirectDraw предоставляет доступ к графической системе на более высоком уровне, а библиотека Microsoft Direct3D позволяет созда­
вать очень сложные динамические игры, используя управляемый код. Эти возможности стоит рассмотреть подробнее. Выбор изображения В операционной системе Windows Mobile 5.0 стало поразительно легко работать с коллекцией фотографий и рисунков. При помощи стандартного диалогового окна выбора рисунка можно легко выб­
рать нужный рисунок. Доступ к стандартному окну выбора рисунка осуществляется при помощи класса Mi с rosof t. Wi ndows Mobi 1 e. Forms. Se-
lectPictureDialog. Но лучше работу с диалоговым окном выбора картинки рассмот­
реть н а примере. На форме надо разместить метку 1 bl Se I ectedPi cture и графическое поле pi cSelectImage. Не забудьте перед началом со­
здания приложения установить ссылку на пространство имен Mi с rosof t. Wi ndowsMobi 1 e. Forms. Соответствующий код приведен в ли­
стинге 10.16. Листинг 10.16 privat e voi d butSelectPicture_Click(objec t sender, EventArg s e) { SelectPictureDialo g selectPictureDialo g * ne w SelectP i ctureD i alog(): // Задаем фильтр selectPictureDialog.Filte r -
иРисунки(*.ВМР;*.JPG)|*.BMP;*.JPG"; // Задаем папку для обзора selectPictureDialog.Initia l Director y = @"\Windows"; Выбор изображения 201 // Заголовок для диалогового окна selectPictureDialog.Titl e - "Выберите рисунок"; i f (selectPictureDialog.ShowDialogO = DialogResult.OK && selectPictureDialog.FileName.Lengt h > 0) { // Получим расширение выбранного файла strin g fileExtensio n -
Path.GetExtens i on(selectP i ctureD i a!og.Fi1eName); // Выводим путь выбранного файла 1ЫSelectedPicture.Text - "Выбранный файл: " + selectPictureD i alog.Fi1eName; // Если выбран файл JPG. то выводим на экран if (fileExtension.ToLower O = H.jpg") picSelectedlmage.Imag e = ne w Bitmap(selectPictureDialog.FileName); } > В начале работы создается объект SelectPi ctureDi а 1 og, а затем для него задаются нужные свойства. С помощью свойства F i Iter огра­
ничивается выбор файлов. Пользователь может загружать изоб­
ражения с расширениями .BMP и .3PG. Затем указывается старто­
вая папка. Строго говоря, в Windows Mobile для хранения картинок используется папка Мои картинки. Но приложение, ра­
ботающее с изображениями, может использовать свою собствен­
ную папку. и *"i <i ш с Оыбраншй файл: \Му Оос1ЛвгИ$\Мои pMCyH*u\Waterial.jpg Рис. 10.11. Выбор изображения Потом в заголовке диалогового окна выводится текст, поясняю­
щий пользователю дальнейшие действия Это был минимально необходимый при использовании класса Sel ectPi ctureDi al og код. 202 ^ —- Глава 10. Windows Mobile 5.0 Если пользователь выбрал картинку и нажал на кнопку 0К, то надо распознать выбранный файл. С помощью метода Path .GetExtension можно получить расширение файла. В текстовой метке lblSelec-
tedPi cture отображается полный путь к выбранному файлу, а в гра­
фическом поле picSel ectedlmage размещается сама картинка. Но для этого она должна иметь расширение .JPG (рис. 10.11). Следует обратить внимание на то, что диалоговое окно выбора рисунка позволяет выбирать картинки из любой папки устрой­
ства. Работа с фотокамерой Мобильные устройства все чаще снабжаются фотокамерами. При­
чем многие пользователи отсутствие камеры на смартфоне счита­
ют очень большим недостатком. Система Windows Mobile 5.0 пред­
лагает поддержку работы с камерой, чтобы разработчики могли использовать ее возможности в своих приложениях. Диалоговое окно захвата изображения позволяет интегрировать фотографии и видеоматериал в приложения. При этом разработ­
чик получает возможность управлять поведением камеры. Доступ к возможностям камеры осуществляется при помощи класса Mi crosof t. Wi ndowsMobi 1 e. Forms. CameraCaptureDi al og. Класс CameraCap-
tureDialog очень похож на класс SelectPictureDialog. Свойство Mode позволяет управлять режимом съемки. Камера мо­
жет работать, как обычный фотоаппарат, что задается значением CameraCaptureMode.Still, или записывать видеоролик. Диалоговое окно вызывается методом ShowDi a log, который возвращает значение, показывающее, как было закрыто окно. Если пользователь выбрал кнопку 0К, то возвращается значение DialogResu 11.ОК. Имя выбран­
ной картинки записывается в свойство FileName. В листинге 10.17 приведен пример работы с фотокамерой. Листинг 10.1 7 privat e voi d butPhotoMake_Click(objec t sender. EventArg s e) { CameraCaptureDialo g cameraCaptureDialo g - ne w CameraCaptureDialog(); CameraCaptureDialog.Owne r » this; CameraCaptureDialog.Titl e - "Фотограф"; CameraCaptureDialog.Mod e - CameraCaptureMode.Still; Работа с фотокамерой 20 3 1f (cameraCaptureDialog.ShowDialogO — DialogResult.OK && cameraCaptureDialog.FileName.Length > 0) { pictureBox.Imag e = ne w Bitma p (cameraCaptureDialog.FileName); MessageBox.Show("Снято!"); } } Для записи видеоролика используется аналогичный способ, н о надо поменять режим съемки. Так, для записи видеоматериала вместе со звуком используется режим VideoWithAudio. Пример записи ви­
деоролика приведен в листинге 10.18. Листинг 10.1 8 privat e voi d butCaptur e Click(objec t sender, EventArg s e) { CameraCaptureDialo g cameraCaptur e = ne w CameraCaptureDialogO; cameraCapture.Owne r * null; cameraCapture. Initia l Director y = @"\M y Documents"; earneraCapture.DefaultFileNam e - @"test.3gp"; cameraCapture.Titl e • "Камера - Демонстрация"; cameraCapture.VideoType s -
CameraCaptureV i deoTypes.Messag i ng: cameraCapture.Resolutio n в ne w Size(176. 144); // Лимит в 1 0 секунд для видео earneraCapture.VideoTimeLimi t - ne w TimeSpanCO. 0, 10); cameraCapture. Mod e = CameraCaptureMode.VideoWithAudio: if (DialogResult.O K — cameraCapture.ShowDialogO ) { MessageBox.ShowC"Картинка или видео успешно записаны в:\п{0}\ cameraCapture.FileName): } } Легко заметить, что эти два примера практически идентичны. Су­
ществует еще режим записи видео без звукового сопровождения. В этом случае для свойства Mode задается значение CameraCap­
tureMode. VideoOnl у. Если перед вызовом метода ShowDialog использо­
вать свойство Def aul tFi 1 eName, то указанное имя будет использовать­
ся как имя файла для записи новых 4ютографий или видеоматериа­
ла. Свойство Initial Directory позволяет указать папку, в которой будут сохраняться отснятые материалы. Свойство Resoluti on позво-
204 Глава 10. Windows Mobile 5.0 ляет задать разрешение снимаемого материала, что иллюстрирует следующая строка кода: cameraCaptureDialog.Resolutio n = new Size(320. 240): Свойство St ill Quality позволяет установить качество сжатия для фотографий при помощи перечисления CameraCaptureSti 11 Quality. Используемые значения перечислены в следующем списке: • High — указывает на наилучшее качество картинки с минималь­
ным сжатием; • Normal — среднее качество картинки; • Low — высокая степень сжатия, плохое качество. Свойство VideoTimeLimit позволяет установить максимальную про­
должительность записи видеоматериала. По умолчанию использу­
ется нулевое значение, что означает отсутствие временного огра­
ничения. В этом случае запись съемки будет вестись до тех пор, пока позволяют ресурсы системы. Свойство VideoTypes позволяет выбрать тип видеоматериала. На устройствах под управлением Windows Mobile 5.0 используется видеоматериал двух типов — Multimedia Messaging Service (MMS) и Windows Media Video (WMV). Повторение пройденного Примеры доступа к объектам Pocket Outlook рассматривались при­
менительно к карманным компьютерам. Но теперь надо воссоздать их, опираясь уже на смартфоны. Сам код примеров останется прак­
тически неизменным. Но при этом изменится логика управления программой. Как уже говорилось ранее, управление в смартфонах сводится к обработке событий для пунктов меню. Встречи Сначала рассмотрим пример с использованием объекта Pocket Outlook. На этот раз надо получить доступ к списку встреч (Appoint­
ment). Перед началом изучения примера вам нужно убедиться, что список событий имеет хотя бы одну запись. Если там ничего нет, то следует создать несколько записей самостоятельно. После создания нового проекта на форме надо разместить элемент Li stView. Свойство Vi ew должно получить значение Detai 1 s. В коллек­
ции Col umns надо задать заголовки Дата, Время и Тема (рис. 10.12). Преж­
де всего потребуется задать переменную для экземпляра сессии Out-
Встречи 205 look. Сразу же после вызова метода Initia-
1 i zeComponent в конструкторе формы объяв­
ляем экземпляр для сессии PocketOutl ook, как показано в листинге 10.19. Листинг 10.1 9 privat e OutlookSessio n session; publi c Forml O { InitializeComponent(): // Создаем экземпляр сессии Pocket Outlook session - new OutlookSessionO; Теперь программист получил доступ к коллекции событий через объект Out-
1 ookSession. Для коллекции Appointment со­
здается соответствующая переменная, при помощи которой можно получить каждый элемент коллекции, что иллюстрирует код, приведенный в листинге 10.20. Contact s CS - Microsof t Viiua! SI ш т v*w, ища Bu«r & Formi .cs* ?Forml.cs [Design)*?' Рис. 10.12. Внешний ви д приложения Листинг 10.2 0 privat e voi d menuAppointments_Click(objec t sender, EventArg s e) { AppAppt s = session.Appointments.Items; // Проходим через все элементы коллекции foreac h (Appointmen t app t i n AppAppts ) { // Создаем объект ListViewIte m lvltem s * ne w ListViewItemO; // Разделяем полученные результаты по колонкам lvltems.Tex t - appt.Start.ToShortDateStringO; 1 v l terns. Sub l terns. Ad d (appt. S t art. ToShortTimeStringO); lvltems.SubItems.Add(appt.Subject): // Добавляем в ListVie w IvContacts.Iterns.Add(lvltems); } // H e забываем закрыть сессию PocketOutloo k session.DisposeO; } 206 Глава 10. Windows Mobile 5.0 Также мы можем получить информацию об имеющихся контактах. Но в этом случае рассматривать код не нужно, так как он полнос­
тью повторяет пример для КПК. Отсылка письма Рассматриваемый пример покажет, как можно посылать электрон­
ное письмо любому человеку, чья запись присутствует в списке Контакты. При этом разработчик может присоединять к отправля­
емому сообщению файл. В этом примере будет применен другой подход к дизайну програм­
мы. Так как средства навигации в смартфоне довольно скудны, же­
лательно сводить к минимуму число нажатий на клавиши. Напри­
мер, одна и та же клавиша может запускать разные функции. После создания нового проекта SendEmai lSmartphone_CS на форме надо разместить текстовое поле txtContact, в котором будет отображать­
ся выбранный электронный адрес. Также потребуется изменить код из предыдущего примера. Нужно переместить код из обработчика события menuSoftKeylClick в от­
дельный метод SelectContactC). Это делается для того, чтобы мож­
но было более гибко настраивать программу под свои нужды. Со­
ответствующий код приведен в листинге 10.21. Листинг 10.21 privat e void SelectContactC ) { // Создаем экзнемпляр окна выбора контактов ChooseContactDialo g contactDia l - ne w ChooseContactDialogO; // а также убираен возможность создания новых контактов contactDial.HideNe w - true: // выводим диалоговое окно на экран if (contactDial.ShowDialog O — DialogResult.OK ) { selContac t • contactDial.SelectedContact; txtContact.Tex t = selContact.FileAs: menuSoftKeyl.Tex t = Послать; } } Следует обратить особое внимание на строку menuSoftKeyl.Tex t = "Послать"; Отсылка письма 20 7 Когда пользователь выберет пункт Контакты, а затем нужный кон­
такт, то текст в пункте меню menuSoftKeyl меняется на строчку По­
слать. Также надо добавить новый пункт меню для очистки тексто­
вых полей. Это позволит пользователю выбрать новый контакт для отправки письма. Надо открыть файл Forml.cs в дизайнере формы. На правой стороне меню к уже имеющемуся пункту Выход следует добавить новый пункт меню Очистить. Созданный пункт получит имя mnuClear. Ко д для метода mnuClearClick приведен в листинге 10.22. Листинг 10.22 privat e voi d mnuClear_Click(objec t sender, EventArg s e ) { txtContact.Tex t = string.Empty; menuSoftKeyl.Tex t = "Контакты"; Это позволить очистить текстовое поле и в пункте меню menuSoftKeyl отобразить строку Контакты. Теперь можно писать функцию, отправляющую электронное пись­
мо. В примере сообщение будет отправляться с вложенными фай­
лами. Для примера можно использовать одну из картинок, входя­
щих в состав Windows Mobile 5.0. Для отправки письма используется класс EmailMessage. Чтобы ис­
пользовать этот класс в нашем примере, надо сначала установить ссылку на пространство имен System.Messaging, выполнив команду меню Project • Add Reference. После этого можно пользоваться дан­
ным пространством имен при помощи ключевого слова using: using System.Messaging; Код метода SendEmai 1 (), который будет отвечать за отправку пись­
ма, приведен в листинге 10.23. Листинг 10.23 privat e void SendEmai К ) { // Создаем экземпляр класса EmailMessag e EmailMessag e messag e = ne w EmailMessageO; // Тена письна message.Subjec t * "Поздравление"; // Текст письна message.BodyTex t - "Поздравляю с Дней Варенья!н; // Выбираем адресата Recipien t clien t - ne w RecipientCselContact.EmaillAddress); message.To.Add(client): a ^ продолжение ту 208 Глава 10. Windows Mobile 5.0 Листинг 10.2 3 (продолжение) II добавляем в письмо вложенный файл Attachment image = ne w Attachment(@"\My Documents\My Pictures\Flower.jpg"): message.Attachments.Add(image); message.Send("ActiveSync"): txtContact.Tex t - string.Empty; menuSoftKeyl.Tex t - "Контакты"; } Итак, в методе SendEmai 1 объявляется и создается экземпляр класса Email Message. В свойствах Subject и BodyText задаются тема и текст письма. Электронный адрес из выбранного контакта записывается в свойстве Email Mess age. То. Для этого создается экземпляр класса Recipient и передается свойство selContact.EmaillAddress. Теперь можно добавить в письмо вложенный файл. Для этого со­
здается экземпляр класса Attachment, которому в конструктор пере­
дается полное имя выбранного файла. После этого свойству Emai 1 Message .Attachment передается значение экземпляра. Теперь для отправки письма все готово. Следует вызвать метод message.Send и очистить текстовое поле. Также надо восстановить в меню строку Контакты. Так как для menuSoftKeyl используются два метода, SendEmai 1 и SelectContact, то нужно определиться, когда ка­
кой метод следует использовать. Для этого нужно получить значе­
ние свойства menuSoftKeyl .Text, как показано в листинге 10.24. Листинг 10.2 4 privat e voi d menu$oftKeyl_Click(objec t sender. EventArg s e) { if (menuSoftKeyl.Tex t — "Послать") SendEmailO; els e SelectContactO; } Настало время проверить нашу программу. После запуска приложе­
ния надо выбрать адресата, которому предназначено письмо. Для отображения окна выбора контакта следует нажать кнопку Soft Ke y 1. Можно выбрать любой контакт из имеющегося списка. После этого в текстовом поле появится выбранный контакт. При этом пункт меню обретет название Послать. Затем надо снова нажать кнопку Soft Key 1. Кнопка Soft Keyl примет первоначальный вид, в меню будет отображаться строка Контакт, Мелочь, а приятно 20 9 а текстовое поле будет очищено. Выходим из программы. Но нам надо убедиться, что письмо было отправлено. Поэтому следует пе­
рейти на экран Сегодня и нажать кнопку Пуск, после чего активиро­
вать пиктограмму Сообщения. В появившемся списке надо выбрать пункт Эл.п. Outlook, а из пункта Меню перейти в подменю Папки. За­
тем осталось перейти в папку Исходящие. В ней должно находиться новое сообщение. Мелочь, а приятно В блоге bLogs.msdn.com/anthonywong/, который ведет Энтони Вонг (Anthony Wong), я нашел несколько интересных заметок, расска­
зывающих об исправленных ошибках или улучшенных возможно­
стях, которые стали доступны в Windows Mobile 5.0. Метод Directory.Exists На устройствах под управлением Windows СЕ 4.Х метод Di recto­
ry. Exists О по-разному обрабатывал имена путей, которые закан­
чивались обратным слэшем. В качестве примера можно рассмот­
реть следующее выражение: Directory.ExistsCWtemp"); Это выражение возвращает значение True, если папка temp суще­
ствует. Добавим в предыдущее выражение символ обратной черты. Di rectory. Exi sts( "WtempW") Теперь данный метод возвратит Fal se, даже если папка существует. На устройствах под управлением Windows Mobile 5.0 платформа .NET Compact Framework исправила это противоречие, и теперь метод Di rectory. Exi sts () возвращает True вне зависимости от нали­
чия замыкающего обратного слэша. Метод Bitmap.SaveO На старых устройствах также отсутствовали конвертеры графичес­
ких изображений, что не позволяло сохранять изображения в фор­
матах GIF, JPG или PNG. Разработчикам приходилось довольство­
ваться только форматом BMP. Причем при написании программы среда разработки позволяла писать неправильный код, выводя со­
ответствующие подсказки. Однако при вызове этого метода про­
грамма выводила сообщение об ошибке. В Windows Mobile 5.0 те­
перь поддерживаются все четыре формата. Глава 11 Создание игр Игры на мобильных устройствах Создание игр — одно из самых любимых занятий для программис­
тов. При создании новой игры автору приходится быть и художни­
ком, и композитором, и дизайнером, и бухгалтером. Естественно, в данном случае речь идет о программисте-одиночке. Разработка игр для мобильных устройств не требует больших финансовых зат­
рат, которые имеют место при создании игр для настольных компь­
ютеров крупными компьютерными фирмами. На мой взгляд, кар­
манные компьютеры идеально подходят для логических и аркадных игр, с помощью которых можно скоротать время во время путеше­
ствия, долгого ожидания в очередях или при поездке на работу. MSDN Ноте > MSPN Ufrrary > MoWte *Г!<* Ftnbedtfa l Ряуе1р[;сг:спЕ > .NET Compac t FrarTKWQfk > Technical Arcjclgs > • Graphics, Audio, and Gaming a Dancing Particles; Adding Points, Lines, Fonts, о Dancing Rectangles: Using GAPI to Create a Ma d Dancing zombies: Adding Bitmaps to the Mar»: • Focus Point: An Image Scaling Game for Smart G;Games Programming with-Cheese:Part Onej a Games Programming with Cheese: Part Two a Games Programming with Cheese: Part Three • Games Programming with Cheese: Part Four D Gaming with the .NET Compact Framework | 13 Gaming w*h the .NET Compact Framework: AI a Pocket Sots; Writing a Battle Game for Smarter a Pocket Jack: Wr*ng a Card-Playing Application a Recording and Playing Sound with the wavefor D Saving a Control Image to a Bitmap File • Starlight: Writing a Space-Shooter Game for S p Writing МоЫе Games Using the Microsoft .NET Game s Programmi n g wi t h Cheese: ни***** Part one r&pmmp** j An Introductio n t0' U» **?.!*.;...;;; Games Programming for Windows Mobile-based Smartphones Rob Miles Department of Computer Science, University of Hull Updettd: March 2006 Applies to: Windows Mobile 5.0 for Smartphones Рис. 11.1. Раздел MSDN, посвященный играм Продуктовая аркада 21 1 Если в вашей коллекции уже есть игры для настольных компьюте­
ров, написанные с использованием .NET Framework, то в большин­
стве случаев вам не составит труда портировать их для мобильных устройств. Я хочу познакомить вас с играми, которые уже написа­
ны для КПК и смартфонов. Надо сказать, что существует опреде­
ленная категория программистов, которые не читают документа­
цию и ищут материалы по заданной теме в книгах и на сайтах. Но это не самое правильное поведение. Компания Microsoft очень час­
то размещает примеры написания игр в своих справочных систе­
мах. Очень много статей на тему разработки игр можно найти в MSDN. В этой коллекции статей и документации есть целый раз­
дел, посвященный созданию игр, под названием «Graphics, Audio and Gaming» (рис. 11.1). Продуктовая аркада Для начала имеет смысл рассмотреть игру Bouncer, которую мож­
но найти на веб-странице по адресу msdn.microsoft.com/library/ defauLt.asp?url-/h'brary/en-us/dnnetcomp/html/gamesprogwithcheese.asp. Автор игры Роб Майлз (Rob Miles) написал четыре большие ста­
тьи об этой игре, которая использует в качестве игровых объектов кусочки сыра, батон хлеба и яблоки. Интересно, что сначала статьи писались о версии игры для .NET Compact Framework 1.0 для смар­
тфонов под управлением Win- dows Mobile 2003 с использованием Visual Studio .NET 2003. Но к настоящему моменту игра была пере­
писана для смартфонов под управлением Windows Mobile 5.0. ПРИМЕЧАНИЕ К слову сказать, когда я читал эти статьи в 2004 году, у меня еще не было смартфона. И тогда я переписал игру для своего карманного компьютера, чтобы поиграть в аркаду на настоящем устройстве. Автор шаг за шагом раскрывает перед программистом тонкости процесса разработки игры, начиная с создания пустого проекта и заканчивая написанием полноценной игровой модели. Роб Майлз любезно разрешил использовать исходный код игры на страни­
цах книги, а в архиве программ вы найдете установочный файл, содержащий исходные коды игры. Когда вы запустите установоч­
ный файл, то программа установки скопирует файлы с примера­
ми в папку C:\Program Fites\Windows Mobile Developer Samptes\Games Programming With Cheese Part 1. В этом каталоге будут расположены 212 Глав а 11. Создание игр еще семь папок с проектами, которые шаг за шагом ведут програм­
миста к написанию игры. Так как сама статья написана на английском языке, то придется приложить некоторые усилия для понимания текста. Но автор так понятно и доходчиво объясняет материал, сопровождая его иллюс­
трациями и строчками кода, что разобраться в нем сможет даже начинающий программист. Начало работы Итак, прежде всего нужно создать новый проект для смартфона под управлением Windows Mobile 5.0 с использованием платформы .NET Compact Framework 2.0. Этот проект должен получить имя Bouncer. Добавление изображения в программу Наше приложение будет использовать графические изображения. Картинки, используемые в игре, хранятся в файле самой програм­
мы в виде ресурсов. Сначала надо подготовить сами рисунки для игры. Автор программы решил использовать для игры различные виды продуктов. Возьмем, к примеру, изображение кусочка сыра. Файл с изобра­
жением сыра надо скопировать в папку, в которой хранятся фай­
лы проекта. Затем следует щелкнуть правой кнопкой мыши на названии проекта Bouncer в окне Solution Explorer, выбрать пункт контекстного меню Add, а затем перейти к пункту подменю Add Existing Item. В диалоговом окне Add Existing Item надо выбрать файл cheese.gif. После этого остается нажать кнопку Add. Кар­
тинка теперь добавлена в проект, но еще не является частью про­
граммы. Необходимо указать, что графический файл будет храниться в виде встроенного ресурса. Нужно щелкнуть правой кнопкой мыши на значке графического файла в окне Solution Explorer и выполнить ко­
манду контекстного меню Properties. В разделе Build Action по умол­
чанию используется пункт Content. Но в данном случае нужно ука­
зать пункт Embedded Resource. Теперь картинка является частью сборки, и для распространения программы нам понадобится единственный исполняемый файл, в котором будут содержаться все необходимые изображения. Продуктовая аркада 21 3 Использование встроенных ресурсов При работе программы необходимо получить доступ к графичес­
кому файлу из ресурсов и вывести изображение на экран. Для это­
го сначала необходимо получить ссылку на сборку. Соответствую­
щий код приведен в листинге 11.1. Листинг 11.1 // Полунин ссылку на сборку System.Reflection.Assembl y execAssem = System. Re f 1 ect i on. Assembl у. GetExecut i ngAssembl у (); Метод System. Reflection. Assembly. Get Executi ngAssembl у возвращает сборку, из которой выполняется текущий код. Получив в программе ссылку на сборку, можно получить доступ к встроенным ресурсам, в то м числе к изображению сыра. Метод GetMani festResourceStream по­
зволяет извлекать указанный ресурс из сборки. Для этого нам надо указать имя файла и название пространства имен. В нашем случае это будет Bouncer .cheese.gi f, как показано в листинге 11.2. Листинг 11.2 // <summary > /// Изображение сыра /// </summary > privat e Imag e cheeselmag e - null; publi c Forml O { In i t i al i zeComponent(); // Полунин ссылку на сборку System. Reflec t ion. Assemb l у execAsse m = System. Ref 1 ect i on. Assemb l у. GetExecut i ngAssemb l у (); // Полунин доступ к картинке с сырон cheeselmag e = ne w System.Drawing.Bitmap ( execAssem.GetMan i festResourceStream((?"Bouncer.cheese.g i f") ): } Вывод картинки на экран При запуске программа загружает из ресурсов картинку. Теперь надо вывести изображение на экран. Для этого нужно воспользо­
ваться событием Pai nt, как показано в листинге 11.3. ,.vAC -. -"?.j «ri *?. 214 Глава 11. Создание игр Листинг 11.3 privat e voi d Forml_Paint(objec t sender. PaintEventArg s e) { e.Graphics.Drawlmage(cheeselmage. 0. 0): } После запуска программы в левом углу экрана будет отображен кусочек сыра (рис. 11.2). Рис. 11.2. Вывод изображения на экран Создание анимации Теперь нужно научиться перемещать объект по экрану. Если это делать достаточно быстро, то у пользователя создается ощущение непрерывного воспроизведения анимации. Для этого следует соз­
дать метод updatePositions, который позволит перемещать изобра­
жение. Пока ограничимся движением вниз и вправо. Соответству­
ющий код приведен в листинге 11.4. Листинг 11.4 /// <summary > /// Координата X для рисования сыра /// </summary > privat e int e x •= 0; /// <summary > /// Координата Y для рисования сыра /// </summary > privat e int су - 0; privat e voi d updatePosition s О { cx++; cy++; } Продуктовая аркада 21 5 Переменные сх и су содержат текущие координаты кусочка сыра. Меняя значения этих координат, можно управлять расположени­
ем изображения на экране. Теперь нужно переписать ко д для собы­
тия FormlPaint, как это показано в листинге 11.5. Листинг 11.5 privat e voi d FormlPain t {objec t sender. System. Wi ndows. Forms. Pa i ntEventArg s e) { // Текущая позиция сыра e.Graphics.Drawlmage(cheeselmage. ex. су): } Теперь при каждом вызове метода Paint программа перерисовывает изображение сыра в указанном месте. Но программа должна само­
стоятельно перемещать изображение через определенные промежут­
ки времени. Также нужно иметь возможность управлять скоростью перемещения картинки. Для этой задачи подойдет объект Timer. Со­
ответствующий элемент нужно добавить на форму. Следует помнить, что во время работы таймера смартфон не может использовать сберегающий энергорежим, так как устройство счи­
тает, что программа находится в активном состоянии, даже если она свернута. Это негативно влияет на работу аккумуляторов, сокра­
щая срок работы без подзарядки. Поэтому нужно останавливать таймер, когда программа работает в фоновом режиме, и включать его снова при активации приложения. Но вернемся к настройкам таймера. Интервал срабатывания тай­
мера должен составлять 50 миллисекунд, а свойство Enabl ed долж­
но получить значение False. Когда таймер будет включен, код в методе Ti ck будет срабатывать 20 раз в секунду. Пр и создании тай­
мера нельзя для свойства Enable устанавливать значение True, так как метод timerl_Tick попытается отобразить изображения до того, как они будут загружены. Включать таймер можно только тогда, когда все необходимые картинки будут загружены, иначе програм­
ма выдаст сообщение об ошибке. В нашем примере таймер активи­
руется в конструкторе формы после загрузки изображения сыра, как это показано в листинге 11.6. Листинг 11.6 publi c FormlO { // // Require d fo r Window s For m Designe r support. ^ л r продолжение & 216 Глав а 11. Создание игр Листинг 11.6 {продолжение) II InitializeComponent{); // Получим ссылку на сборку System.Reflection.Assembl y execAssem = System.Ref1ection.Assemblу.GetExecutingAssemblу(): // Получим доступ к картинке с сыром cheeselmage = new System.Drawing.Bitma p (execAssem. GetMani festResourceStream(@"Bouncer. cheese. gi f") ): // Включаем таймер this.timer1.Enable d - true; } Теперь при запуске программы конструктор загружает картинку и включает таймер. Настало время создать код для события Tick. Система перерисовы­
вает содержимое экрана только при определенных условиях. Мы можем заставить систему перерисовать экран при каждом измене­
нии местоположения картинки с помощью метода Invalidate. Та­
ким образом, через определенные промежутки времени приложе­
ние меняет координаты изображения и обновляет экран, чтобы пользователь увидел картинку на новом месте. Соответствующий код приведен в листинге 11.7. Листинг 11.7 privat e voi d timerlJTic k (objec t sender, System.EventArg s e) { updatePositionsO: InvalidateO; } После запуска программы кусочек сыра по диагонали переместит­
ся в правый нижний угол экрана. Когда изображение достигнет края экрана, оно продолжит свое движение и скроется. При движении изображение сыра немного мерцает, что очень раздражает всех пользователей. В дальнейшем этот недостаток будет исправлен. Отражения Нужно запрограммировать обработку отражений объекта от стенок. Для этого надо отслеживать текущую позицию объекта и направ-
Продуктовая аркада 21 7 ление движения. Когда объект достигнет края стенки, нужно изме­
нить направление движения. Для начала упростим код программы, отвечающей за отражения. Пусть координаты объекта при движе­
нии увеличиваются на единицу, когда кусочек сыра движется впра­
во и вниз, и уменьшаются на единицу при движении влево и вверх. Новый код метода updatePositions приведен в листинге 11.8. Листинг 11.8 /// <summary > /// Направление движения по оси X /// </summary > privat e boo l goingRigh t = tru e : /// <summary > /// Направление движения по оси Y /// </summary > privat e boo l goingDow n - tru e ; privat e voi d updatePosition s () { if ( goingRigh t ) cx++: } els e { ex--: } if ( ( e x + cheeselmage.Widt h ) > = this.Widt h ) { goingRight • false; i f ( cx< - 0 ) { goingRigh t - true: } i f (goingDown) { СУ++; } else { продолжение & 218 Глав а 11. Создание игр Листинг 11.8 (продолжение) } if ( ( су + cheeselmage.Heigh t ) > = this.Heigh t ) { goingDown = false; i f ( cy <- 0 ) { goingDown = true; } } Обратите внимание на то, что в коде используются ширина и высо­
та изображения и экрана. Не прописывая жестко величины разме­
ров экрана и изображения, мы можем быть уверенными в том, что программа будет работать корректно в устройствах с любыми раз­
решением экрана и размерами картинки. После запуска приложения можно увидеть, что изображение сыра корректно отражается от краев экрана при перемещении. Управление скоростью движения объекта Рассматривая поведение программы, вам, вероятно, хотелось бы ускорить процесс движения объекта. Чтобы игра была динамичной и увлекательной, нужно постепенно увеличивать сложность игро­
вого процесса для пользователя. Одним из таких способов являет­
ся ускорение движения. На данный момент кусочек сыра проходит расстояние от одного угла до другого за 5 секунд. Увеличить ско­
рость перемещения картинки очень просто. Достаточно увеличи­
вать значение текущей позиции объекта не на один пиксел, а на несколько. Нужно объявить новые переменные xSpeed и ySpeed, ко­
торые будут отвечать за увеличение или уменьшение скорости дви­
жения объекта. Соответствующий код приведен в листинге 11.9. Листинг 11.9 /// <summary > /// Скорость движения сыра по горизонтали /// </summary > privat e in t xSpee d - 1; /// <summary > /// Скорость движения сыра по вертикали Продуктовая аркада • 219 /// </summaгу> privat e i nt ySpeed « 1; privat e void updatePositions O { if (goingRight ) { ex += xSpeed; > els e { cx -- xSpeed; } if ((c x + cheeselmage.Width ) >- this.Width ) { goingRigh t - false: } i f (cx <- 0) { goingRigh t - true: } if (goingDown ) { cy +- ySpeed; } els e { cy -- ySpeed: } if ((c y + cheeselmage.Height ) >= this.Height ) { goingDown - false; } i f (cy <= 0) { goingDown - true: Изменяя значения переменных xSpeed и ySpeed, мы можем по свое­
му желанию увеличивать или уменьшать скорость движения ку­
сочка сыра. Для этого надо создать новую функцию, код которой приведен в листинге 11.10. 220 Глав а 11. Создание игр Листинг 11.10 privat e void changespeed ( int change ) { xSpee d + = change; ySpee d -н= change; } Теперь можно вызывать этот метод для изменения скорости дви­
жения изображения. Для уменьшения скорости надо передавать в функцию отрицательные значения. Чтобы управлять скоростью во время игры, можно использовать клавиши Soft Key, расположен­
ные под экраном. Следует создать простое меню, содержащее команды Быстрее и Мед­
леннее. Если пользователь нажмет на левую кнопку, то скорость движения сыра будет увеличиваться. При нажатии на правую кноп­
ку скорость уменьшится. Соответствующий код приведен в листин­
ге 11.11. Листинг 11.11 privat e voi d menuIteml_Click(objec t sender, System.EventArg s e) { changeSpee d (1); } privat e voi d menuItem2_Click(objec t sender, System.EventArg s e) с changeSpeed (-1); } В данной ситуации значения в методе changeSpeed не отслеживают­
ся. Это может привести к ситуации, когда пользователь будет по­
стоянно уменьшать скорость и значение скорости может стать от­
рицательным. В этом случае движение объекта будет совсем не таким, как это планировал разработчик. А при значительном уве­
личении скорости движение изображения теряет гладкость. Добавляем новый объект Итак, в результате наших усилий по экрану движется кусочек сыра. Настало время добавить новый объект, которым пользователь бу­
дет отбивать сыр. Для наших целей вполне подойдет батон хлеба. Вспоминаем предыдущие упражнения, где мы выводили кусочек сыра на экран, и повторяем шаги в той же последовательности для батона хлеба. Продуктовая аркада 221 • Добавляем графический файл в проект в виде ресурса. Q Получаем в коде ссылку на файл из сборки. а Объявляем две переменные, содержащие координаты батона хлеба. Соответствующий код приведен в листинге 11.12. Листинг 11.1 2 /// <$unrnrary > /// Изображение, содержащее батон хлеба /// </summary > privat e Imag e breadlmag e = null; // Получаем изображение батона хлеба breadlmag e - ne w System.Drawing.Bitmap C execAssem.GetMan i festResourceStream(@"Bouncer.bread.g i f") ); /// <summary> /// Координата X для батона хлеба /// </summary> privat e i nt bx - 0; /// <summary> /// Координата Y для батона хлеба /// </summary> privat e i nt by = 0: На рис. 11.3 показан внешний вид программы на этом этапе. Рис. 11.3. Изображения хлеба и сыра Устранение мерцания Несмотря на то что мы проделали уже очень большую работу, наша программа по-прежнему не лишена недостатков. При запуске про-
222 Глав а 11. Создание игр граммы изображения постоянно мерцают, раздражая пользователя. Это связано с перерисовкой экрана через заданные интервалы вре­
мени. Каждые 50 миллисекунд экран закрашивается белым фоном, а затем на экран выводятся два объекта. Если не устранить этот не­
достаток, то никто н е захочет играть в игру. Решение проблемы лежит в использовании специальной техники, на­
зываемой двойной буферизацией. Двойная буферизация обеспечивает плавную смену кадров. Технология позволяет рисовать необходимые изображения в специальном буфере, который находится в памяти ком­
пьютера. Когда все необходимые изображения будут выведены в бу­
фере, то готовое окончательное изображение копируется на экран. Процесс копирования идет очень быстро, и эффект мерцания пропа­
дет. Для реализации этой идеи надо создать новый объект Bi tmap. Имен­
но на не м будут отображаться все рисунки, а потом останется только скопировать объект в нужную позицию. Также потребуется перепи­
сать метод Forml. Paint, как показано в листинге 11.13. Листинг 11.13 /// <summary > /// картинка-буфер /// </summary > privat e Bitma p backBuffe r = nul l ; privat e voi d Forml_Paint(objec t sender. System.W i ndows.Forms.Pa i ntEventArg s e ) { // Создаем новый буфер if (backBuffer—nu l 1 ) { backBuffe r - ne w Bitmap(this.CIientSize.Width. this.ClientSize.Height); } using ( Graphics g « Graphics.FromImage(backBuffer ) ) { g.Clear(Color.White): g.Drawlmage(breadImage.bx. by); g.DrawImage(cheeseImage. ex. cy); e.Graphics.DrawImagefbackBuffer. 0. 0): } При первом вызове метода Forml^Paint создается буфер для приема изображений, который объявлен как переменная backBuffer. Затем Продуктовая аркада 223 данный буфер использует контекст устройства для вывода изобра­
жений. И, наконец, метод Drawlmage из графического контекста фор­
мы копирует изображение из буфера и выводит его на экран. После запуска программы станет понятно, что окончательно изба­
виться от мерцания не удалось. Хотя улучшения есть, тем не менее, небольшое мерцание объектов все же осталось. Это связано с осо­
бенностью перерисовки на уровне системы. Когда Windows рисует объекты на экране, она сначала заполняет его цветом фона. Затем при наступлении события Paint система рисует игровые элементы поверх фона. Поэтому, несмотря на наши ухищрения, мы по-преж­
нему видим неприятный эффект мерцания. Нужно сделать так, чтобы система Windows не перерисовывала экран. Для этого следует переопределить метод On Paint Background, отвечающий за перерисовку экрана, причем новая версия метода вообще ничего не будет делать, что иллюстрирует листинг 11.14. Листинг 11.1 4 protecte d overrid e voi d OnPaintBackgroundCPaintEventArg s ре vent ) { /У H e разрешаем перерисовывать фон } После добавления этого метода в программу мерцание исчезнет. Кусочек сыра теперь движется без всякого мерцания. Но теперь появилась другая проблема. Когда кусочек сыра прохо­
дит через батон хлеба, то виден прямоугольник, обрамляющий изоб­
ражение сыра. Кроме того, по условиям игры, сыр не может прохо­
дить через батон, а должен при столкновении изменить свое направление и двигаться в другую сторону. Именно этой пробле­
мой и нужно заняться сейчас. Хлеб — всему голова Наша программа должна уметь перемещать батон хлеба таким об­
разом, чтобы игрок мог отбивать кусок сыра, как будто играя им в теннис. Для этой цели игрок будет использовать клавиши нави­
гации на телефоне. Чтобы управлять батоном хлеба, придется ис­
пользовать события KeyDown и KeyUp. Событие KeyDown наступает, когда пользователь нажимает на заданную кнопку. Событие KeyUp ини­
циируется при отпускании кнопки. 224 Глав а 11. Создание игр Необходимо установить флаг, который будет отслеживать нажатия и отпускания клавиш. Когда флаг будет активирован, это будет оз­
начать, что пользователь нажал на клавишу, и батон должен дви­
гаться в указанном направлении. Когда пользователь отпустит кла­
вишу, то флаг сбрасывается и объект прекращает движение. Обработчики событий используют перечисления Keys, показываю­
щие конкретные кнопки навигации. Соответствующий код приве­
ден в листинге 11.15. Листинг 11.1 5 /// <summary > /// Используем keyArg s в качестве флага /// </summary > privat e System.Windows.Forms.KeyEventArg s keyArg s я null; privat e voi d FormlJCeyDow n (objec t sender. System.Windows.Forms.KeyEventArg s e) { keyArg s = e; > privat e voi d FormlKeyU p (objec t sender. System.W i ndows.Forms.KeyEventArg s e) { keyArg s = nul1; } Когда программа получает вызов события Forml_KeyDown, флаг keyArgs ссылается на класс KeyEventArgs. При наступлении события FormlJCeyUp флаг keyArgs сбрасывается в null, и код нажатых кла­
виш игнорируется. Теперь надо переписать метод updatePositions, как показано в листинге 11.16. Листинг 11.1 6 privat e void updatePosition s О { // Код для кусочка сыра остался прежний // Для батона хлеба if ( keyArg s I - nul l ) { switc h ( keyArgs.KeyCod e ) Продуктовая аркада 22 5 cas e Keys.Up: by-^ySpeed; break; cas e Keys.Down: by+=ySpeed: break: cas e Keys.Left: bx-=xSpeed; brea k ; cas e Keys.Right: bx+«xSpeed: break: } } } В данном коде используется оператор switch, который определяет действия программы в зависимости от нажатой клавиши. Батон хлеба движется с той же скоростью, что и кусочек сыра. На этой стадии при запуске программы пользователь может перемещать батон хлеба по всему экрану, в то время как кусочек сыра по-преж­
нему самостоятельно двигается по экрану. Обнаружение столкновений Для контроля столкновений в играх используются прямоуголь­
ные области. Конечно, здесь далеко до реализма, так как предме­
ты не всегда имеют прямоугольную форму. Но в некоторых слу­
чаях пользователь может и не заметить этого. Ограничивающий прямоугольник вокруг изображения хлеба выглядит так, как по­
казано на рис. 11.4. (Ьх * batWidth. by + batHeight ) Рис. 11.4. Ограничивающий прямоугольник для объекта Две точки позволяют оперировать координатами верхнего лево­
го и нижнего правого углов прямоугольника. В .NET Compact Framework существует структура RECTANGLE, использующая эти координаты для реализации прямоугольника. Несколько мето-
8-2873 226 Глав а 11. Создание игр дов используют эту структуру для обнаружения пересечения двух прямоугольников. С их помощью и можно обнаружить столкнове­
ние объектов. Ранее использовавшиеся переменные надо заменить структурой RECTANGLE, в которой будет содержаться информация о местонахождении объекта. Соответствующий код приведен в ли­
стинге 11.17. Листинг 11.1 7 /// <summary > /// Позиция и ограничивающий прямоугольник для сыра /// </summary> privat e Rectangle cheeseRectangle; /// <summary> /// Позиция и ограничивающий прямоугольник для батона хлеба /// </summary> privat e Rectangl e breadRectangle; Сразу после загрузки изображений надо ввести код, приведенный в листинге 11.18. Листинг 11.1 8 // Получим координаты и ограничивающие прямоугольники cheeseRectangl e = new RectangleCO, О, cheeselmage.Width.cheeselmage.Height): breadRectangl e = new RectangleCO. 0. breadlmage.Width, breadlmage.Height); Теперь для вывода картинок на экран надо использовать в методе Forml_Paint код, приведенный в листинге 11.19. Листинг 11.1 9 g.Drawlmage(breadImage. breadRectangle.X. breadRectangle.Y): g.DrawImage(cheeseImage, cheeseRectangle.X, cheeseRectangle.Y); При помощи свойств X и Y этих прямоугольников можно переме­
щать объекты п о экрану. В методе updatePos i ti on надо заменить часть кода, отвечающую за движение сыра и батона, с учетом созданных переменных, как показано в листинге 11.20. Листинг 11.2 0 privat e voi d updatePositionsО { Продуктовая аркада 22 7 // Движение кусочка сыра i f (goingRight) { cheeseRectangle.X +« xSpeed; } else { cheeseRectangle.X -- xSpeed; } i f ((cheeseRectangle.X + cheeselmage.Width) >- this.Width) { ••"• goingRight • false: } i f (cheeseRectangle.X <= 0) { goingRight - true: t ) i f (goingDown) { cheeseRectangle.Y +=» ySpeed: else { cheeseRectangle.Y -- ySpeed; } if ((cheeseRectangle.Y + cheeselmage.Height ) >*» this.Height ) { goingDown = false; } i f (cheeseRectangle.Y <*• 0) ? { goingDow n • true; // Управление батоном if (keyArg s !« null ) { swi tch (keyArgs.KeyCode) продолжение & 228 Глав а 11. Создание игр Листинг 11.2 0 (продолжение) case Keys.Up: breadRectangle.Y -= ySpeed; break; cas e Keys.Down: breadRectangle.Y += ySpeed: break; cas e Keys.Left: breadRectangle.X - = xSpeed: break; cas e Keys.Right: breadRectangle.X + = xSpeed; break; } } /// и далее... Когда сыр ударяется о батон хлеба, он должен отскочить. Этого эффекта можно добиться, просто изменив направления движения по оси У в методе updatePosi tion, как показано в листинге 11.21. Листинг 11.2 1 // Проверка на столкновение i f ( cheeseRectangle.IntersectsWith(breadRectangle) ) { goingDown * IgoingDown; > Метод IntersectsWith принимает параметры прямоугольников. Если они пересекаются, то возвращается значение True, после чего меня­
ется направление движения сыра. Запустите программу и попытайтесь отбить батоном движущийся кусочек сыра. Вы увидите, как сыр отскочит после столкновения. Столкновения батона и мяча Хотя код вполне нормально работает, все-таки хочется больше реа­
лизма. Отвлечемся на минутку и рассмотрим пример столкнове­
ний мячей с круглым предметом (рис. 11.5). Когда мяч ударяется о круглый объект, он отскакивает обратно, как показано на рисунке. Программа должна уметь определять вид столкновения для каждого мяча. По схожему принципу должна работать и наша программа. Продуктовая аркада 22 9 8АТ Рис. 11.5. Столкновение круглых объектов На рис. 11.6 показаны использующиеся три вида столкновений. Первое столкновение происходит при наезде правой нижней части сыра на прямоугольник батона. Во втором случае оба нижних угла изображения сыра одновременно пересекаются с прямоугольником батона. И третий случай реализуется, когда изображение сыра ле­
вой частью попадает на блокирующий прямоугольник. 1 2 з Рис. 11.6. Виды столкновений Нужно снова переписать код метода updatePosition для новой реа­
лизации модели столкновений, как показано в листинге 11.22. Листинг 11.2 2 i f (goingDown) { // если сыр движется вниз if (cheeseRectangle.IntersectsWith(breadRectangle) ) { // столкновение boo l rightl n - breadRectangle.Contains(cheeseRectangle. Ri ght, cheeseRectangle.Bottom); boo l leftl n - breadRectangle.Contains(cheeseRectangle. Left, cheeseRectangle.Bottom): // способ отражения if (rightl n & leftln ) { // отражается вверх - > л r r продолжение & 230 Глав а 11. Создание игр Листинг 11.22 (продолжение) goingDown = false; } else { // отражается вверх goingDow n = false; // в зависимости от вида столкновений if (rightIn ) { goingRigh t = false: } i f (l ef t l n ) { goingRigh t = true: > > } Обратите внимание на то, что сыр отскакивает только при движе­
нии в нижнюю часть экрана. Используя подобный подход, можно создать игру, в которой пользователь будет стараться не дать сыру упасть на дно экрана, отбивая его батоном. Новые объекты Продолжим улучшать игру. Теперь в игру будут введены и поми­
доры. Их изображения тоже надо ввести в состав проекта, как по­
казано в листинге 11.23. > Листинг 11.23 /// <summary > /// Изображение, содержащее лонидор /// </summary > privat e Imag e tomatolmag e - null; // Получаем изображение помидора tomatolmag e =» ne w System.Drawing.Bitmap C execAssem.GetMan i festResourceStream(@"Bouncer.tomato.g i f") ); Следует нарисовать несколько помидоров в верхней части экрана. Помидоры будут использоваться в качестве мишеней, которые нуж­
но уничтожать, сбивая их кусочком сыра. Продуктовая аркада 23 1 Для отслеживания попаданий нужно знать позицию каждого по­
мидора и определять момент столкновения. Можно было создать массив, содержащий координаты каждого помидора, но лучше вос­
пользоваться структурой, приведенной в листинге 11.24. Листинг 11.24 /// <summary > /// Позиция и состояние понидора /// </summary > struc t tomat o { publi c Rectangl e rectangle: publi c bool vi si bl e: Использование структуры позволит хранить позицию помидора и определять его видимость. При столкновении сыра с помидором овощ должен исчезнуть, позволяя тем самым игроку заработать очки. Размещение помидоров Нужно создать массив помидоров для размещения на экране, как показано в листинге 11.25. Листинг 11.25 /// <summary > /// Расстояние между понидорани. /// Устанавливаем один раз для игры /// </summary > privat e in t totnatoSpacin g - 4: /// <summary > /// Высота, на которой рисуется помидор /// Высота может меняться в процессе игры /// Начинаем с верхней части экрана /// </summary > privat e in t tomatoDrawHeigh t = 4; /// <summary > /// Количество помидоров на экране. /// Устанавливается при старте игры /// методом initialiseTomatoes. /// </sumrnary > . л продолжение & 232 Глав а 11. Создание игр Листинг 11.2 5 (продолжение) privat e i nt noOfTomatoes; /// <summary> /// Позиции всех понидоров на экране /// </sumntary> privat e tomato[ ] tomatoes; При усложнении игры помидоры должны отображаться все ниже и ниже, заставляя пользователя действовать интуитивно. Перемен­
ная tomatoDrawHeight будет отвечать за эту задачу. Для инициализа­
ции местоположения помидоров нужно создать функцию initialiseTomatos, которая использует размеры помидоров и экра­
на. Ее код приведен в листинге 11.26. Листинг 11.2 6 /// <summary > /// Вызывается один раз для установки всех понидоров /// </summary > privat e voi d initialiseTomatoes O { noOfTomatoe s - (this.CIientSize.Widt h - tomatoSpacing ) / (tomatolmage. Widt h + tomatoSpacing); // создаем массив, содержащий позиции понидоров tomatoe s - ne w tomato[noOfTomatoes]; // Координата x каждого помидора in t tomato X - tomatoSpacin g / 2: fo r (in t i =0; i < tomatoes.Length: i++ ) { tomatoes[i].rectangl e = ne w Rectangle(tomatoX, tomatoDrawHeight. tomatolmage.Width, tomatolmage.Height); tomato X = tomato X + tomatolmage.Widt h + tomatoSpacing; } > Вызов этого метода следует разместить в конструкторе формы. Метод подсчитывает количество помидоров, создает массив струк­
тур и задает прямоугольники, определяющие позицию каждого по­
мидора на экране. Теперь их надо разместить на форме в один ряд. Код, отвечающий за эти действия, приведен в листинг 11.27. Листинг 11.2 7 /// <summary > /// Вызывается для создания ряда помидоров. Продуктовая аркада 2вЗ^ /// </summary> privat e voi d placeTomatoesO { fo r (i nt i = 0; i < tomatoes.Length; i++) { tomatoes[i].rectangle.Y = tomatoDrawHeight; tomatoes[i].visibl e = true; } } Этот метод вызывается один раз при старте игры, а после этого он запускается после уничтожения очередного ряда томатов. Метод обновляет высоту с новым значением и делает изображения тома­
тов видимыми. Вызов данного метода также размещается в конст­
рукторе формы. Итак, сейчас позиции всех томатов определены. Нужно вывести их изображения помидоров на экран. Код, приведенный в листинге 11.28, встраивается в обработчик события FormlPaint. Листинг 11.28 fo r ( in t i = 0 ; i < tomatoes.Lengt h ; i++ ) { i f (tomatoes[i].visible ) { g.DrawImageCtomatoImage. tomatoes[i].rectangle.X, tomatoes[i].rectangle.Y ); } } Каждый раз, когда страница перерисовывается, этот код перерисо­
вывает все видимые томаты. Естественно, для отображения всех томатов используется одно и то же изображение. Чтобы сделать игру реалистичнее, нужно переместить началь­
ную высоту батона чуть ниже, чтобы игрок мог сразу играть в игру с более подходящей позиции. Этот код приведен в листин­
ге 11.29. Листинг 11.29 breadRectangl e - ne w Rectangle C (this.ClientSize.Widt h - breadImage.Width ) / 2. this.CIientSize.Heigh t - breadlmage.Height, breadImage.Width, breadlmage.Heigh t ); 23 4 Глава 11. Создание игр Теперь игра выглядит так, ка к показано на рис. 11.7. 4 "ЙЗЗЖ ^ioM* Рис. 11.7. Внешний ви д игры Уничтожение томатов К сожалению, в данный момент при столкновении сыра с помидо­
рами ничего не происходит. Ситуацию надо исправить при помо­
щи кода, добавленного в метод updatePosition, который приведен в листинге 11.30. Листинг 11.3 0 // Уничтожаем помидоры при столкновении с сырой fo r ( in t i = 0 : i < tomatoes.Lengt h ; i+ + ) { if ( !tomatoes[i].visibl e ) { continue; I f ( cheeseRectangle.IntersectsWith(toniatoes[i].rectangle ) ) { // прячем тоиат tomatoesCi].visibl e = false; // отражаемся вниз goingDow n - tru e : // только удаляем помидор break; } } Код выполняется, когда сыр двигается вверх. При этом проверяют­
ся позиции каждого помидора и куска сыра при помощи метода IntersectsWith. Если произошло столкновение сыра с томатом, то томат делается невидимым, для чего свойству Visible присваивает-
Продуктовая аркада 235 ся значение False. При следующей перерисовке экрана этот томат не появится на экране. Сыр должен отскакивать от помидора, как от стенок или от батона. Счет игры Итак, это уже похоже на игру. Но пока ей не хватает увлекатель­
ности. Нужно добавить подсчет результатов. Отображение резуль­
татов игры — не самая сложная задача. Мы можем выводить текст на экран с помощью метода DrawStri ng. Но при этом потребуется указать шрифт, кисть и координаты вывода текста. Начать стоит со шрифта. Его надо инициализировать в конструкторе формы при помощи кода, приведенного в листинге 11.31. Листинг 11.3 1 /// <summary> /// Шрифт для вывода счета /// </$ummary > privat e Font messageFont - nul l; // Создадим шрифт для показа набранных очков messageFont - new FontCFontFamily.GenericSansSerif. 10. FontStyle.Regular); Теперь необходимо выбрать прямоугольник, в котором будет ото­
бражаться текст. Нужно зарезервировать 15 пикселов в верхней части экрана для отображения текущего счета. При этом потребу­
ется модифицировать игру, чтобы двигающиеся объекты не попа­
дали в эту область. Используя переменную для хранения этой высоты, можно легко изменить размеры информационной панели, если понадобится. Прямоугольник инициализируется при загрузке формы, как пока­
зано в листинге 11.32. Листинг 11.3 2 /// <summary > /// Прямоугольник, в которой будет отображаться счет игры /// </summary > privat e Rectangl e messageRectangl e ; /// <suranary > /// Высота панели для счета. продолжение * 236 Глав а 11. Создание игр Листинг 11.3 2 (продолжение) III </summary> privat e i nt scoreHeigh t - 15; // Устанавливаем размеры прямоугольника для счета messageRectangl e = new Rectangle(0. 0. this.ClientSize.Width, scoreHeight): Если прямоугольник будет слишком мал для текста, то текст будет обрезаться при отображении. После того как будут заданы шрифт и область для отображения тек­
стовой информации, пора позаботиться о кисти. Выбирая тип кис­
ти, одновременно указывайте цвет и узор для рисования, как пока­
зано в листинге 11.33. Листинг 11.3 3 /// <summary> /// Кисть, используемая для отображения сообщений /// </summary* privat e SolidBrus h messageBrush; // Выбираем красную кисть messageBrush • new SolidBrush(Color.Red); Текст счета игры на экране будет отображаться красным цветом. Чтобы вывести сообщение на экран, понадобится вызвать метод DrawString в событии Forml_Paint, как показано в листинге 11.34. Листинг 11.3 4 /// <summary > /// Строка для вывода сообщений /// </summary > privat e string messageStrin g = "Нажмите Старт для начала игры"; g.DrawString(messageString. messageFont. messageBrush, messageRectangle); m Созданная переменная messageStri ng применяется для вывода со­
общений на экран во время игры. Ведение счета Теперь нужно научиться обновлять счетчик столкновения томатов в методе updatePosi tion. Код для этого приведен в листинге 11.35. Продуктовая аркада 23 7 Листинг 11.3 5 /// <summary > /// Счет в игре /// </summary > privat e in t scoreValu e = 0: privat e voi d updatePositions O { if (cheeseRectangle.IntersectsWith(tomatoes[i].rectangle) ) { // прячем томат tomatoes[i].visibl e * false; // отражаемся вниз goingDow n - true: // обновляем счет scoreValu e - scoreValu e + 10; messageStrin g - "Счет: " + scoreValue; break; } } За каждый уничтоженный томат начисляется 10 очков. Эти дан­
ные постоянно обновляются и выводятся на экран. Звуковые эффекты Неплохо бы добавить в игру звуковые эффекты. К сожалению, биб­
лиотека .NET Compact Framework пока не поддерживает воспро­
изведение звуковых файлов при помощи управляемого кода. По­
этому придется воспользоваться механизмом Platform Invoke (Р/ Invoke). В главе, посвященной вызовам функций Windows API, эта тема будет освещаться подробнее. Для воспроизведения звуков можно встроить звуковой файл в саму программу, как это делалось с изображениями, либо проиг­
рывать сам звуковой файл, который расположен где-то в файло­
вой системе. В этом проекте требуется создать отдельный класс для воспроизве­
дения звуков. Нужно щелкнуть правой кнопкой мыши на проекте Bouncer в окне Solution Explorer и выполнить команду контекстного меню Add • Ne w Item... В открывшемся окне нужно выбрать элемент Class и задать имя Sound.cs. После нажатия кнопки Add новый класс будет добавлен в проект. 238 Глав а 11. Создание игр Класс Sound будет иметь два метода. Один метод создает экземп­
ляр класса Sound, читая данные из заданного файла. Второй метод предназначен для проигрывания звука. Также в составе класса будет находиться свойство, позволяющее настраивать громкость звука. В начале файла Sound.cs надо расположить строки для подклю­
чения используемых пространств имен, как показано в листинге 11.36. Листинг 11.3 6 usi n g System. Runti me. I nteropServ i ces; using System.10; Наш пример со звуком просто хранит в памяти байтовый массив с аудиоматериалом. Для обращения к этому блоку используется функция операционной системы, способная производить звуки. В классе Sound блок памяти объявляется так, как показано в лис­
тинге 11.37. Листинг 11.3 7 /// <summary> /// иассив байтов, содержащий данные о звуке /// </summary> privat e byte[ ] soundBytes; Эта конструкция не создает массив, а только объявляет его. Мас­
сив будет создан при конструировании экземпляра класса, ведь из­
начально размер звукового файла неизвестен. Код Конструктора приведен в листинге 11.38. Листинг 11.3 8 /// <summary > /// Создание экземпляра soun d и хранение данных о звуке /// </summary > /// <para m name s5,,soundStream">noTO K для чтения звука</рагаш> publi c Sound(Strea m soundStream ) { // создаем иассив байтов для приема данных soundByte s - ne w byte[soundStream.Length]; // читаем данные из потока soundStream.Read(soundBytes, 0. (int)soundStream.Length); } Продуктовая аркада 23 9 Поток связывается с файлом или другим источником данных. Он имеет свойство Length, определяющее размер массива. Метод Read применяется для получения информации, после чего прочитанные байты сохраняются в массиве. Звуковые файлы хранятся в виде ресурсов, как и изображения. В проект надо добавить звуковые файлы click.wav и burp.wav и для их свойства Build Action задать значение Embedded Resources. Теперь доступ к звуковым файлам получить очень просто, что иллюстри­
рует код, приведенный в листинге 11.39. Листинг 11.3 9 /// <summary > /// Звук, воспроизводимый при столкновении с батоном хлеба /// </summary > privat e Soun d batHitSound; /// <summary > /// Звук, воспроизводимый при столкновении с помидором /// </summary > privat e Soun d tomatoHitSound: // Полунин звук при столкновении с батонои хлеба batHitSoun d = ne w Soun d (execAssera.GetMan i festResourceStream(@ nBouncer.click.wav")); // Получим звук при столкновении с помидором tomatoHitSoun d - ne w Soun d (execAssem.GetMan i festResourceStream(@"Bouncer.burp.wav")); Для воспроизведения звука в класс Soun d надо добавить метод Play, ка к показано в листинге 11.40. Листинг 11.40 /// <summary > /// Управление звуком в игре (Включать или выключать) /// </summary > publi c stati c boo l Enable d = true; /// <summary > /// Проигрываем звук /// </summary > publi c voi d Play O { 240 Глав а 11. Создание игр Листинг 11.40 (продолжение) i f (Sound.Enabled) { WCEP1aySoundBytes ( soundBytes. IntPtr.Zero. (int)(Flags.SND_ASYN C | Flags.SND_MEMORY)); } } Метод PI ay проверяет флаг переменной Enabl ed. С его помощью мож­
но легко включать или выключать звук в игре. Воспроизведение зву­
ка обеспечивается вызовом функции Windows API WCEPl aySoundBytes, что иллюстрирует код, приведенный в листинге 11.41. Листинг 11.4 1 pri vat e enum Flags { SNDSYNC - 0x0000, SNDASYNC = 0x0001. SNDN0DEFAULT - 0x0002. SND_MEM0RY - 0x0004. SND_L00P = 0x0008, SNDNOSTOP - 0x0010. SNDNOWAIT - 0x00002000, SND_ALIAS = 0x00010000. SND_ALIAS_ID - 0x00110000. SNDJILENAME = 0x00020000. SND_RESOURCE - 0x00040004 } /// <sufranary > /// Функция Window s AP I для воспроизведения звука. /// </summary > /// <para m name="szSound',>MaccM B байтов, содержащих данные /// </param > /// <param пате="пМс<Г>Дескриптор к модулю, содержащему звуковой /// pecypc</param > /// <param name-"flags">Onar M для управления звуком</рагаш> /// <returns></returns > [Dl l Import("CoreDl l.DLL\ EntryPoint = "PlaySound". SetLastErro r - true) ] privat e exter n stati c in t WCE_P1aySoundBytes ( byte[ ] szSound, Продуктовая аркада 24 1 IntPt r hMod. i n t fl ags); Теперь, когда создан экземпляр класса Sound, можно воспроизво­
дить звук при столкновении сыра с батоном хлеба. Соответствую­
щий код приведен в листинге 11.42. Листинг 11.4 2 // если сыр движется вниз i f (cheeseRectangle.IntersectsWith(breadRectangle) ) { // столкновение It воспроизводим удар batHitSound.PlayO; } Можете запустить проект, чтобы проверить работу звука. Также можно добавить звук при столкновении сыра с помидорами. Этот код приведен в листинге 11.43. Листинг 11.4 3 if CcheeseRectangle.IntersectsWith(tomatoes[i].rectangle) ) { // воспроизводим звук столкновения сыра с помидором tomatoH i tSound.P I ay(); } Дальнейшие улучшения Но игру все еще можно улучшить. В следующем списке указаны дополнительные возможности, которые необходимо реализовать. • Режим «attract», включающийся, когда пользователь не играет. • Потеря жизни, если сыр ударился о нижнюю границу экрана. D При уничтожении всех томатов они должны появиться чуть ниже, и скорость игры должна возрасти. • Добавление в игру случайных элементов. В программу надо ввести булеву переменную gameLi ve, которая име­
ет значение True, когда пользователь ведет игру. Если значение пе­
ременной равно False, то сыр будет двигаться по экрану, но ника­
ких игровых действий производиться не будет. Для этого потребуется изменить метод, выполняющийся при стар­
те игры. Новая версия приведена в листинге 11.44. 242 Глав а 11. Создание игр Листинг 11.4 4 /// <summary > /// True, если игра запущена на экране. /// </summary > privat e boo l gameLiv e - false: /// <summary > /// Число оставшихся жизней. /// </summary > privat e in t livesLeft; /// <summary > /// Число жизней, доступных для игрока. /// </summary > privat e in t startLive s - 3; privat e voi d startGam e О { // Устанавливаем число жизней, счет и сообщения livesLef t = startLives; scoreValu e - 0; messageStrin g - "Счет: 0 Жизнь: " + livesLeft; // Располагаем помидоры наверху экрана tomatoDrawHeigh t - tomatoLevelStartHeight; placeTomatoesO; // Поместим батон в центре экрана breadRectangle.X • (this.ClientSize.Width-breadRectangle.Width ) / 2: breadRectangle.Y - this.CIientSize.Heigh t / 2; // Поместим сыр над батоном в центре экрана cheeseRectangle.X = (this.CIientSize.Width-cheeseRectangie.Width ) / 2; cheeseRectangle.Y = breadRectangle.Y • cheeseRectangie. Height: // Установим начальную скорость xSpee d * 1; ySpee d - 1; // Установим флаг, позволяющий начать игру gameLiv e - true; } Продуктовая аркада 243 Этот код возвращает все объекты на исходные позиции и начина­
ет новую игру. Батон располагается в середине экрана, а сыр чуть выше него. Этот метод связан с пунктом меню, позволяющим на­
чать игру. Теперь надо добавить код, который проверяет, не коснулся ли сыр нижней границы экрана. В этом случае вызывается метод 1 oseLi f е, который уменьшает количество жизней у игрока. Соответствующий код приведен в листинге 11.45. Листинг 11.4 5 i f ( ( cheeseRectangle.Y + cheeselmage.Heigh t ) >= this.Heigh t ) { // сыр достиг нижней границы экрана loseLifeO: goingDown « false; } Метод loseLi fe подсчитывает количество оставшихся жизней и за­
канчивает игру, если все жизни были израсходованы. Также метод может показывать лучший достигнутый счет игры. Его код приве­
ден в листинге 11.46. Листинг 11.4 6 privat e voi d loseLifeO { i f (IgameLive) { return; } // Потеряли еще одну жизнь livesLeft--; i f (livesLef t > 0) { // обновим сообщение на экране messageStrin g = "Счет: " + scoreValue + " Жизнь: " + l i vesLeft; } else { // Останавливаем игру gameLive - false; продолжение # 244 Глав а 11. Создание игр Листинг 11.4 6 (продолжение) II сравниваем с лучший результатом i f (scoreValue > highScoreValue) { highScoreValu e = scoreValue; > // меняем сообщение на экране messageStrin g - "Лучший результат: " + highScoreValue; } } Этот код не выполняется, если игра не запущена. При вызове ме­
тод уменьшает количество жизней и подсчитывает оставшееся чис­
ло. Пока есть жизни, игра продолжается. В противном случае об­
новляется счет и игра выключается. Последний метод в нашей игре отвечает за перерисовку томатов, когда он и все уничтожены. Чтобы отследить эту ситуацию, в метод FormlPaint добавлен очень простой код, который приведен в лис­
тинге 11.47. Листинг 11.4 7 bool gotTomat o - false ; fo r ( i nt i = 0 ; i < tomatoes.Lengt h ; i++) { i f (tomatoe s [ i ]. vi si Ы e) { gotTomat o s true; g.DrawImage(tomatoImage, tomatoes[i].rectangle.X, tomatoes[i].rectangle.Y ); } } i f ( !gotTomat o ) { newLevel 0; } Если пользователь выбил все томаты, то вызывается метод newLevel. Метод просто перерисовывает томаты и увеличивает скорость, как показано в листинге 11.48. Листинг 11.4 8 privat e void newLevel О { Продуктовая аркада 24 5 if ( 'gameLiv e ) { return; } // Рисуен понидоры чуть ниже tomatoDrawHeigh t +- tomatoSpacin g ; if ( tomatoDrawHeigh t > ( ClientSize.Height -
(breadRectangle.Height+tomatoImage.Height ) ) ) { // Рисуен понидоры снова в верхней части экрана tomatoDrawHeigh t - tomatoLevelStartHeight: } placeTomatoesO; // Увеличиваен скорость i f ( xSpeed < maxSpeed ) { xSpeed++; ySpeed++; } } Метод перемещает томаты все ниже и ниже. Когда они почти достиг­
нут края экрана, то будут снова перемещены в верхнюю часть экрана. Тестирование Игра практически готова. Теперь нужно протестировать ее. Чтобы не играть самому несколько часов, надо поручить эту работу ком­
пьютеру. Достаточно лишь изменить метод updatePosition, как по­
казано в листинге 11.49. Листинг 11.4 9 Тестирование программы в автоматическом режиме /// <summary> /// Тестирование лрограмны. Батон автоматически отслеживает /// движение сыра /// </summary> privat e bool testingGame - tru e ; i f ( testingGame ) breadRectangle.X - cheeseRectangle.X; продолжение & 246 Глав а 11» Создание игр Листинг 11.49 (продолжение) breadRectangle.Y « ClientSize.Heigh t -
breadRectangle.He i ght: Булева переменная testingGame может принять значение True. В этом случае позиция батона всегда будет соответствовать позиции сыра. В этом состоянии игра будет действовать сама, без участия пользова­
теля и без потери жизней. Можно откинуться на спинку кресла и от­
дыхать. И опять добавляем новые объекты На данный момент игра довольно прямолинейна. Надо добавить ей сложности для повышения зрелищности. В игру нужно ввести допол­
нительный бонус в виде кусочка ветчины, который будет периодичес­
ки появляться на экране. Если игрок сумеет коснуться его батоном, то заработает несколько дополнительных очков. Но при этом игрок не должен забывать отбивать сыр, чтобы не потерять жизнь. Ветчина по­
является на экране на короткое время, и игрок должен сам решить, нужно ему охотиться за ветчиной или отбивать сыр. Сначала надо добавить графическое изображение ветчины в про­
грамму как ресурс. Затем потребуется создать несколько перемен­
ных, с помощью которых можно контролировать свойства нового объекта. Соответствующий код приведен в листинге 11.50. Листинг 11.50 /// <summary > /// Изображение ветчины /// </summary > privat e Imag e bonusHamlmag e - null: /// <summary > /// Позиция и ограничивающий прямоугольник для ветчины /// </summary > privat e Rectangl e bonusHamRectangle; /// <summary > /// Звук, воспроизводимый при столкновении с ветчиной /// </summary > privat e Soun d bonusHamSound; // Получим изображение ветчины Продуктовая аркада 24 7 bonusHamlmag e - new System.Drawing.Bitmap ( execAssem.GetMan i festResourceStream(@"Bouncer.ham.g i f") ); // Создадим прямоугольник для ветчины bonusHamRectangl e - new RectangleC 0. 0 r bonusHamlmage.Wi dth.bonusHamlmage.Heigh t ); // Получим звук при столкновении с ветчиной bonusHamSoun d = new Sound( execAs sem. GetMani f estResourceStream ( @n Bouncer. pi g. wav")); Для управления изображением ветчины надо создать новый метод, код котрого приведен в листинге 11.51. Листинг 11.5 1 /// <summary > /// True, если ветчина на экране /// </summary > privat e boo l hamPresen t - false; /// <summary > /// Интервал от 0 до 10. Чем выше значение. /// тем чаще ветчина появляется на экране /// </summary > privat e in t hamLikelihoo d - 5: /// <summary > /// Отчет времени перед исчезновением ветчины. /// Устанавливаем случайное число при появлении ветчины. /// </summary > privat e in t hamTimerCount; /// <summary > /// Случайное число. /// </summary > privat e Rando m randomNumbers; /// <summary > /// Вызывается для активизации ветчины /// </summary > privat e voi d startHa m 0 { // не продолжать, если ветчина уже есть на экране продолжение & 248 Глав а 11. Создание игр Листинг 11.51 (продолжение) i f (hamPresent ) { return ; } // решаем, как часто выводить ветчину на экран if ( randomNumbers.Next(10 ) > hamLikelihoo d ) { // не выводить ветчину на экран return: } // позиция ветчины в случайной позиции на экране bonusHamRectangle.X = randomNumbers.Next(ClientSize.Widt h - bonusHamRectangle. Widt h ); bonusHamRectangle.Y = randomNumbers.Nex t (ClientSize.Heigh t -
bonusHamRectangle. Heigh t ); // как долго держится изображение ветчины на экране // (по крайне нере 5 0 тиков) hamTimerCoun t = 5 0 + randomNumbers.Next(100); // делаен ветчину видимой hamPresen t - true; } На первый взгляд код кажется сложным. Но все очень просто. Ме­
тод вызывается каждый раз при столкновении сыра с томатом. Если ветчина уже отображается на экране, то метод ничего не делает. Если ветчины на экране нет, то программа использует случайное число для принятия решения, нужно ли показывать на экране изоб­
ражение. Генерируется случайное число в промежутке от 0 до 10. Ветчина не выводится, если это число больше, чем заданная пере­
менная. В нашем случае значение hamLikelihood равно 5. Это означает, что ветчина будет появляться в половине случаев. При помощи этой переменной можно регулировать частоту появления изображения ветчины на экране. Если метод решит вывести ветчину на экран, он выбирает случайную позицию и устанавливает расположение кар­
тинки. Также метод инициализирует счетчик таймера для отчета длитель­
ности присутствия ветчины на экране. Программа использует ми­
нимальное время вкупе со случайным периодом. Таким образом, Продуктовая аркада 24 9 пользователь никогда не будет знать, как долго ветчина будет ви­
дима. Каждый раз при обновлении игры программа должна обнов­
лять состояние куска ветчины. Если игрок коснулся изображения ветчины, то надо увеличить счет и удалить изображение. Соответ­
ствующий код приведен в листинге 11.52. Листинг 11.5 2 /// <summary > /// Обновляем состояние ветчины /// </summary > privat e voi d hamTick O { // ничего не делаем, если ветчина невидима if ( !hamPresent ) { retur n ; } i f ( breadRectangle.IntersectsWith(bonusHamRectangle ) ) { // при касании игроком куска ветчины // прибавляем 100 очков scoreValue - scoreValue + 100; messageStrin g - "Счет: " + scoreValue + " Жизнь: " + l i vesLeft: // звук касания ветчины bonusHamSound.PI ay(); // прячем ветчину с экрана hamPresent - false: els e { // Отчитываем время назад hamTimerCount--; if ( hamTimerCoun t *= 0 ) { // вреня вышло - удаляем ветчину hamPresent - false; } } } Также надо изменить код методов Forml_Pai n t и updatePosi ti on. Если изображения батона и ветчины пересекаются, то нужно увеличить счет и удалить изображение ветчины. В ином случае надо умень-
250 Глав а 11. Создание игр шить время отображения ветчины или удалить это изображение, если соответствующий период времени уже закончился. Соответ­
ствующий код приведен в листинге 11.53. Листинг 11.53 //(Forml_Paint ) // Выводим на экран кусок ветчины if ( hamPresen t ) { g.DrawImageCbonusHamlmage. bonusHamRectangle.X, bonusHamRectangle.Y); } //(updatePosition ) // Активизируем ветчину startHamO; //(ti merTi ck ) hamTickO; Но мы можем продолжить улучшение игры, добавляя в нее новые возможности. Все изменения по-прежнему будут происходить в про­
екте Bouncer. Теперь предстоит создать таблицу лучших результатов, улучшить работу графики и разобраться с применением спрайтов. Управление таблицей результатов Созданная программа может показывать лучший результат, достиг­
нутый пользователем. Но после ее перезапуска лучшие результаты теряются, и все приходится начинать сначала. Для устранения этого недостатка нужно добавить возможность сохранения имени пользо­
вателя при достижении высокого результата. Также следует сохра­
нять результат и имя пользователя при выходе из программы. Поэтому понадобится еще одна форма для отображения имен иг­
роков. При достижении лучшего результата эта форма будет пока­
зана на экране, чтобы пользователь мог ввести свое имя. Новую форму надо добавить в проект и задать для нее имя High-
Score.cs. На созданной форме следует разместить текстовое поле для ввода имени и меню, которое сигнализирует об окончании ввода. Созданная форма будет отображаться при достижении высокого ре­
зультата. В этом случае игрок-рекордсмен вводит свое имя и нажи­
мает на пункт меню 0К для закрытия формы и сохранения имени. Продуктовая аркада 25 1 Переключение между формами Программа должна выводить форму с результатами поверх основ­
ной формы игры, чтобы позволить игроку ввести имя, а затем вер­
нуться к игре. Когда форма с лучшими результатами появляется на экране, основная форма должна быть скрыта. И наоборот, при за­
крытии окна с результатами основная форма восстанавливается. При загрузке формы генерируется событие Load. При закрытии формы генерируется событие Closing. Программа должна контро­
лировать эти события для реализации поставленной задачи. При старте программы создается экземпляр формы HighScore. Дан­
ный экземпляр имеет ссылку на родительскую форму. При дости­
жении высокого результата форма HighScore выводится на экран. При этом выполняется метод HighScore_Load, который скрывает ро­
дительскую форму. На экране появляется форма, отображающая лучшие результаты, игрок вводит свое имя и выполняет команду меню ОК. При этом срабатывает обработчик события для меню ОК, которое закрывает форму HighScore. При закрытии формы выпол­
няется метод HighScore_Closi ng. Основное окно формы снова появ­
ляется на экране. Код главной формы извлекает имя игрока из фор­
мы HighScore. Итак, метод HighScoreJ-oad должен скрыть родительскую форму. Для этого метод должен использовать ссылку на главное окно. Ссылка на родительское окно передается в форму HighScore при ее созда­
нии, как показано в листинге 11.54. Листинг 11.5 4 /// <summary > /// Родительское окно, из которого вызывается данное окно. /// Используется при закрытии данного окна. /// </summary > privat e For m parentForm; publi c HighScore(For m inParentFonn ) { // Сохраняем родительское окно при закрытии окна лучших // результатов. parentFor m - inParentForm; I rri t i alizeComponentC): } Этот код является конструктором формы HighScore. Когда идет соз­
дание формы, то передается ссылка на родительскую форму. 252 Глав а 11. Создание игр Код метода HighScore_Load приведен в листинге 11.55. Листинг 11.5 5 privat e voi d HighScore_Load(objec t sender. System.EventArg s e) { parentForm.HideO: } При загрузке формы родительское окно автоматически прячется. При закрытии формы надо вернуть родительскую форму на экран. Для этого применяется код, приведенный в листинге 11.56. Листинг 11.5 6 privat e voi d HighScoreClosingCobjec t sender, System.ComponentModel.CancelEventArg s e ) { parentForm.ShowO; } После ввода имени игрок выполняет команду меню О К для закры­
тия формы. Обработчик этого события приведен в листинге 11.57. Листинг 11.5 7 privat e voi d doneMenuItem_CHck(objec t sender. System.EventArg s e ) { CloseO; } После закрытия окна вызывается обработчик события, который выводит главное окно на экран. Отображение дочернего окна Программа должна получить имя игрока при достижении им высо­
кого результата. Для этого создается копия формы HighScore. Про­
грамма должна создать форму при старте и хранить ссылку на нее. Экземпляр формы Hi ghScore создается при старте основной програм­
мы, вызывая конструктор и передавая ссылку на родительскую фор­
му, в нашем случае на саму себя, как показано в листинге 11.58. Листинг 11.5 8 /// <summary > /// Форма для ввода имени игрока с лучшим результатом. Продуктовая аркада 25 3 /// </summary> privat e HighScor e highScore; // Создаем форму для лучших результатов highScor e = new HighScore(this): В этом коде ключевое слово this является ссылкой на текущий эк­
земпляр основной формы, который должен быть закрыт при откры­
тии формы highScore и восстановлен при закрытии формы hi ghScore. Код для отображения формы highScore приведен в листинге 11.59. Листинг 11.59 if < scoreValu e > highScoreValu e ) { timerl.Enabled=false; // Показываем форму для лучших результатов highScore.ShowDialog(): t i merl.EnableaVtruer } Если игрок побил текущий лучший результат, то программа оста­
навливается при помощи отключения таймера. Для отображения формы hi ghScore вызывается метод ShowDi а 1 од. Игра должна сделать паузу, пока игрок вводит свое имя. После этого игра продолжается. Получение имени игрока Игрок вводит свое имя в текстовое поле формы highScore. Чтобы получить доступ к имени пользователя во время игры, необходимо иметь доступ к экземпляру формы Hi ghScore. В классе Hi ghScore надо создать свойство, с помощью которого можно получить введенное пользователем имя. Этот код приведен в листинге 11.60. Листинг 11.60 /// <summary > /// Имя игрока, введенное в текстовом поле. /// </summaгу> publi c strin g PlayerNam e { ge t { retur n nameTextBox.Text: > > MiJk*.-. 254 Глав а 11. Создание игр Свойство Name извлекает имя из текстового поля nameTextBox и воз­
вращает его тому, кто вызывал данное свойство. Это свойство ис­
пользуется в программе, как показано в листинге 11.61. Листинг 11.6 1 /// <summary> /// Имя игрока, достигшего лучшего результата. /// </summary> privat e stri n g highScorePlaye r = "Rob"; i f ( scoreValue > highScoreValue ) { highScoreValue - scoreValue ; t i mer1.En abled=fa1se: h i ghScore.ShowDi alog(); timerl.Enabled-true; highScorePlayer - highScore.PIayerName; } Теперь с помощью переменной h i ghScorePl ауег можно выводить имя лучшего игрока во время игры. Хранение лучших результатов Теперь игроку может указывать свое имя пр и достижении хороше­
го результата. Но нужно как-то сохранять это имя и достигнутый результат. Эту информацию будем хранить в той же папке, где и саму программу. Значит, наша программа должна автоматически определять свое местонахождение в файловой системе, чтобы знать, где хранить эту информацию. За это отвечает код, приведенный в листинге 11.62. Листинг 11.6 2 /// <summary > /// Папка, в которой находится програииа. /// Используется как несто для хранения настроек игры. /// </summary > privat e strin g applicationDirectory: // Получим имя файла программы из текущей сборки strin g appFilePat h -
execAssem.GetModules()[0].Ful1yQual i f i edName; // Выделяем из полного пути имени файла только путь к файлу Продуктовая аркада 25 5 applicationDirector y = System.10.Path.GetD i rectoryName(appFi1ePath); // Обязательно должен быть разделитель в конце пути if (!applicationDirectory.EndsWith(e"\") ) { applicationDirector y += @"\и: С помощью данного кода можно получить ссылку на первый модуль в программной сборке. Затем с помощью свойства Fill lyQual i f iedName можно получить полный путь к файлу программы. Текущий каталог можно получить с помощью свойства GetDi rectoryName. Также нам нужно быть уверенным, что путь к файлу заканчивается обратным слэшем. Небольшой код с проверкой решит эту проблему. Метод сохранения информации очень прост. Он приведен в листинге 11.63. Листинг 11.63 /// <summary > /// Иня файла для хранения лучших результатов. /// </summary > privat e strin g highScoreFil e - "highscore.bin"; /// <summary > /// Сохраняем лучший результат в файле. /// </summary > publi c voi d SaveHighScore O { System.10.TextWrite r write r = null; tr y { writer = new System.10.StreamWriter C applicationDirector y + highScoreFile); writer.WriteLine(highScorePlayer); writer.WriteLine(highScoreValue); } catch {} f i nal l y { i f ( write r != nul l ) { writer.CloseO; } } } 256 Глав а 11. Создание игр Метод сохранения результата в файле вызывается при выходе из программы. Загрузка лучших результатов выполняется при старте программы с помощью метода LoadHighScore, код которого приве­
ден в листинге 11.64. Листинг 11.6 4 /// <summary > /// Загружаем лучший результат из файла. /// </summary > publi c voi d LoadHighScor e С) { System.10.TextReade r reade r - null; tr y { reader - new System.10.StreamReader C applicationDirector y + highScoreFil e ); highScorePlayer - reader.ReadLineO: stri n g highScoreStrin g - reader.ReadLineO: highScorevalue - int.ParseChighScoreString); } catch { } f i nal l y { i f ( reader !» nul l ) { reader.CloseO; } } } Улучшение графики На данный момент игра достаточно увлекательна, но графика ос­
тавляет желать лучшего. Когда объекты проходят друг через друга, можно увидеть ограничивающие прямоугольники объекта. Надо ис­
править эту ситуацию. Для решения проблемы можно использовать прозрачность. Прин­
цип работы с прозрачностью очень прост. Надо выбрать один или несколько цветов, после чего остается указать, что они объявляют­
ся прозрачными. В этом случае прозрачные пикселы не участвуют в отображении картинок. Когда картинка рисуется без прозрачных цветов, она просто ко­
пируется в память. Применение прозрачных цветов заставляет ма­
шину проверять каждый пиксел для перерисовки, что увеличива-
Продуктовая аркада 25 7 ет нагрузку на процессор. В полной версии библиотеки .NET Framework разработчик может несколько цветов делать прозрач­
ными. В библиотеке .NET Compact Framework это можно сделать с одним цветом. Использование прозрачности реализуется при помощи класса ImageAttri butes пространства имен System. Drawi ng. Нужно создать новую переменную transparentWhite, так как белый цвет в изобра­
жениях будет считаться прозрачным. Экземпляр класса создается при старте программы, как показано в листинге 11.65. Листинг 11.6 5 /// <summary> /// Маска для белого цвета, который будет считаться прозрачным /// </summary> privat e System.Drawing.Imaging.ImageAttribute s transparentWhite; // Задаем белую маску. transparentWhit e = new System. Drawing. Imaging. ImageAttr i butesO; transparentWhite.SetColorKey(Color.White. Color.White); Напомню, что в .NET Framework метод SetColorKey принимает ряд цветов, а в .NET Compact Framework один и тот же цвет дается дваж­
ды. Этот цвет будет прозрачным для всех картинок, отображаемых с помощью класса ImageAttri bute. Если в игре понадобятся белые цвета, то они не должны быть совершенно белыми. Объекты игры были созданы так, чтобы их фон был абсолютно бе­
лым. Значения атрибутов, используемых при рисовании кусочка сыра, реализованы так, как показано в листинге 11.66. Для других объектов код будет абсолютно таким же. Листинг 11.6 6 // Выводим на экран кусочек сыра g.DrawImage C cheeselmage, // Imag e cheeseRectangle. // Dest. rect. 0. // src X 0. // src Y cheeseRectangle.Width, // srcWidt h cheeseRectangle.Height. // srcHeigh t GraphicsUnit.Pixel. // srcUni t transparentWhite); // ImageAttribute s 9-2873 * 258 Глав а 11. Создание игр В ранней версии игры вызывалась другая версия метода Drawlmage. Теперь же задается прямоугольник и указывается прозрачный цвет. Чтобы прозрачность работала должным образом, сыр должен ри­
соваться на экране после отображения батона. Итак, мы рисуем прозрачные области для батона, куска сыра и вет­
чины. Мы обошли вниманием помидоры, которые пока не перекры­
ваются. Этот недостаток будет исправлен чуть позже. В качестве украшения надо добавить фоновую картинку в виде красочной ска­
терти (рис. 11.8). Рис. 11.8. Фон для игры Картинка должна иметь размер клиентской части экрана с белым пространством в верхней части для ведения счета. Добавить фон не так уж и трудно. Вместо заливки экрана белым цветом в каждом кадре надо просто отрисовать этот узор. Следует объявить новую переменную backgroundlmage для картинки-фона, загрузить изображение из ресурсов и изменить код в методе Forml_Paint, как показано в листинге 11.67. Листинг 11.6 7 /// <sunmary> /// Изображение, содержащее фон игры. /// </sunmary> privat e Image backgroundlmag e = nul l; // Полунин изображение фона игры backgroundlmag e - new System.Drawing.Bitmap C execAssem.GetMani festResourceStream(@"Bouncer.tableeloth.g i f") ): g.DrawImageCbackgroundlmage, 0, 0); Код загружает картинку как ресурс. Программа теперь может ис­
пользовать прозрачность для отображения томатов. Продуктовая аркада 259 Программа неплохо работает в эмуляторе, но не очень хорошо на настоящем КПК, так как процесс рисования все еще имеет некото­
рые недочеты. Для их устранения следует применять спрайты. Спрайты Предыдущие версии программы выводили на экран каждое имею­
щееся изображение не самым лучшим образом. Скатерть, помидо­
ры, хлеб, сыр и ветчина постоянно перерисовываются при обнов­
лении экрана. Однако проще использовать экран в виде ряда слоев, как показано на рис. 11.9. Нижний слой — это фоновая картинка. Этот слой рисуется один раз в начале загрузки программы. Библиотека спрайтов содержит класс Background для работы с фоном. Средний слой — это спрайты, которые неподвижны. Их не нужно постоянно перерисовывать. Они меняют свое состояние только при ударах кусочка сыра или при запуске нового уровня. За них отве­
чает класс BackSprite. I 0 Рис. 11.9. Структура экрана Верхний слой — это спрайты, которые постоянно перемещаются по экрану. Они должны постоянно перерисовываться. Данные спрай­
ты реализуются классом ForeSprite. Классы Background, BackSpri te и ForeSpri te находятся в базовом классе Sprite, который используется программой для хранения информа­
ции о картинках и их расположении на экране. Также библиотека содержит класс Play Field, который поддерживает список спрайтов и управляет их видом на экране. Нам придется переписать почти весь код с учетом нового добавленного класса. Основной движок игры просто управляет движением передних спрайтов, а также отслеживает состояние и позицию фоновых 9* 260 Глава 11. Создание игр спрайтов. Данная версия библиотеки спрайтов немного отличается от прежней версии игры. Сыр теперь уничтожает томаты пр и движе­
нии вниз к нижней части экрана. Сыр может застрять позади линии томатов, набирая тем самым призовые очки. Автор игры автор Роб Майлз предлагает изучить применение спрайтов на примере другой игры, «Salad Rescue». Вам придется самостоятельно изучить эту игру. Версия игры, использующая спрайты, располагается в папке BouncerSprite, которая входит в состав материалов для книги, рас­
положенных на сайте издательства «Питер». Другие игры Как уже говорилось ранее, в документации MSDN имеется множе­
ство примеров различных игр. Если вы проявите настойчивость, то самостоятельно найдете эти примеры и сможете разобрать их. Также стоит посетить сайт CodeProject, где по адресу www.codeproject.com/ netcf/#Games расположился специальный подраздел, посвященный играм для .NET Compact Framework (рис. 11.10). j « » - *»•* Ьваес с o8r end Tutefia U y<r*c+*-%'-**-~-t?*fi>B№t I Ш hatC;*v«.G>lee<4«a.c3<wiec»;»G*n« i &..Сй iv.v.v.v.-M№«to «ernes M wtfci* vn tv&mi PC 9*** m«*»p*iw; .:-'!:;.: .. IS>»«20M «дайка «ame far »>гч->«И*С -, &.*:-y*V%%k'. •»»«****«*and M&i5«et|!»itfte«s (а е1ь*й*й№г vwncn;i M« ь » fes'*к*йЮС :::, йх*»«»С ,KT e»wg«c> I hrtrfi ^; Наивная HinewMpw «мкптмдеЧаи J*iuftpi»ir«rt сем «> « ч tfMayM M *t>»«l|»f«tK«l**U«>*niS. f«.(»»>;:n"*M»»l»4-«wW. ';C:;: SB*"*'*** . . • - - '"'iKS-v™-' •A**»?t»>. К* и* ^^:«?WiiS»ijtle«ftie tMttn» iMb * .>* * Сеймы *»»«»а*. ;•:•;.-:••'"> • -:; !»*t^ieo« :: ;:-:'•••:•.. ( * i l в у * * » « и * l h .«(TWeeWK Urtl i/aW.a» I 8«i «**i »f t4 *Р€вчИй " f « W^ t o ^ ^ k l « t t l'l ( l i ™< * ™N * « « J « i » « «: «СТО ». j »-B^W « «--^^«Л-»: Я « | » 1 ЙГ -' :>.:,-,• • ........... *••*'*•»-•• -.•:•..,-• Й<* &I ^ M* * » P» C* * t#e .» :> -.vi; :p>:.> atN«v»fe •- .-•;•• > •:,- ••-•xv »*ffr*V rbOIui» ДО Й «''' Рис. 11.10. Сайт CodeProject, посвященный играм для .NET Compact Framework Глава 12 Связь Инфракрасное соединение Несмотря на растущую популярность Wi-Fi, Bluetooth и других беспроводных технологий, по-прежнему не сдает своих позиций и передача данных через инфракрасный порт. Например, все мы каждый день применяем инфракрасный порт при использовании дистанционного пульта телевизора! Вы можете использовать этот способ работы в своих приложениях для передачи разных типов данных. Так как в этой технологии для передачи данных используется свет, то необходимо прямое соединение устройств, чтобы между ними не было препятствий. Несмотря на подобное ограничение, соеди­
нение через инфракрасный порт по-прежнему широко использу­
ется в цифровых камерах, КПК и ноутбуках. В этой главе будет показано, как использовать инфракрасный порт при помощи клас­
са IrDACl ient, входящего в библиотеку классов .NET Compact Frame­
work. История и теория Основанная в 1993 году как некоммерческая организация, Ассо­
циация инфракрасной передачи данных (Infrared Data Association, или сокращенно IrDA) является международной ассоциацией (www.irda.org), создающей и продвигающей стандарты инфракрас­
ной связи, позволяющие пользователям соединять устройства для передачи данных. Стандарты Infrared Data Association поддержи­
вают огромное число устройств. На данный момент существует несколько версий технологии IrDA, которые различаются скоро­
стью передачи данных. Протокол IrDA позволяет соединяться с другим устройством без проводов при помощи ИК-излучения. Порт IrDA позволяет уста­
навливать связь на расстоянии до 1-2 метров. Интерфейс IrDA предполагает малую мощность потребления, что позволяет созда­
вать недорогую продукцию. 262 Глав а 12. Связь Класс bDAClient Практически все устройства под управлением Windows Mobile име­
ют встроенные инфракрасные порты. Библиотека NET Compact Framework имеет в своем составе классы, позволяющие работать с инфракрасной связью. Инфракрасная связь осуществляется между двумя устройствами по принципу «сервер-клиент». Устройство, работающее как сервер, предлагает другому компьютеру установить связь для передачи дан­
ных через инфракрасный порт. Для осуществления передачи необ­
ходимо передать идентификатор устройства и имя устройства. Кли­
ент ждет вызова необходимой службы и откликается на ее запрос. В результате между двумя компьютерами устанавливается связь. За инфракрасное соединение отвечает специальный класс I rDACl i ent, который может выступать и в роли сервера, и в роли клиента. Дан­
ный класс входит в библиотеку System.Net.IrDA.dlL Таким образом, при использовании класса I rDACl i ent необходимо добавить в проект ссылку на указанную библиотеку. Для чтения и передачи данных используется метод GetStream, рабо­
тающий с основным потоком данных. Компьютер-клиент должен знать имя устройства, с которым нужно установить связь. Програм­
ма может поочередно опросить все доступные устройства и выбрать нужное устройство для связи. Алгоритм подключения устройства к инфракрасному порту другого устройства приведен далее. 1. Создать новый экземпляр класса I rDACl i ent. 2. Получить список доступных устройств с помощью метода I rDACl ient. DiscoverDevic.es. Можно ограничить количество оп­
рашиваемых устройств при помощи параметра maxDevices. Ме­
тод Discover Devices возвращает массив объектов IrDADevicelnfo. 3. Нужно исследовать каждый объект IrDADevicelnfo из получен­
ного массива, чтобы найти необходимое устройство для связи. 4. Если подобное устройство найдено, то при помощи метода I rDACl ient. Connect производится соединение. При этом необхо­
димо указать имя службы Создание программы для работы с ИК-связью В этом разделе будет создано приложение, которое будет соединять­
ся с другим устройством и пересылать ему текстовый файл. Прежде Создание программы для работы с ИК-связью 263 всего нужно создать новый проект IrDACS. На форме надо размес­
тить три кнопки, список и строку состояния. Кнопка butFindDevs предназначена для поиска устройств, кнопка butSend — для отправки текстового сообщения, а кнопка butRecei ve служит для приема сообщения. В списке 1 i stBoxl будет отображать­
ся информация об обнаруженных устройствах, а в строке состояния будут отображаться сообщения о производимых операциях. Для пе­
редачи данных и работы с файлами нам необходимо импортировать несколько пространств имен, как это показано в листинге 12.1. Листинг 12.1 Import s System.Net Import s System.10 Import s System.Net.Socket s Для работы с инфракрасной связью необходимо подключить к про­
екту класс IrDACl ient. Для этого выполним команду меню Project • Add Reference и в диалоговом окне выберем пункт System. Net Л rDa. Теперь нужно объявить переменные на уровне класса, как показа­
но в листинге 12.2 Листинг 12.2 privat e IrDAListener i rLi sten; privat e IrDAClient i rCl i ent; privat e IrDAEndPoint irEndP; privat e IrDADeviceInfo[ ] irDevices: stri n g fileSend; strin g fileReceive; stri n g irServiceName; i nt buffersize; В конструкторе формы надо создать экземпляр класса IrDACl ient, задать имена файлов для приема и отправки сообщения, указать имя службы, установить размер буфера для передаваемого файла и так­
же временно сделать недоступными кнопки для отправки и посылки сообщения. Соответствующий код приведен в листинге 12.3. Листинг 12.3 publi c FormlO ,...,. -. ... продолжение & I m t i al l zeComponent ( ); 264 Глав а 12. Связь Листинг 12.3 {продолжение) i rCl i en t = new IrDAClientO; // Файлы, предназначенные для отправки и приема fileSend - \\\Му DocumentsWsend.txt"; fileReceive - н.\\Му DocumentsWreceive.txt"; // Задаем имя для службы IrDA // Это может быть любое слово // Другие устройства для примера должны использовать это же // слово irServiceNam e - "IrDAFtp": // Устанавливаем максимальный размер буфера для передаваемого // файла buffersiz e - 256; // Делаем недоступными кнопки отправки и посылки сообщений // до тех пор. пока не будут обнаружены устройства butSend.Enable d * false; butReceive.Enable d = false; } Обнаружение устройств Теперь надо написать код для кнопки butFirdDevs, предназначен­
ной для обнаружения устройств. При тестировании примера не­
обходимо направить инфракрасные порты устройств друг на дру­
га. Код, ответственный за выполнение этой задачи, приведен в листинге 12.4. Листинг 12.4 privat e voi d butFindDevs_Click(objec t sender. EventArg s e) { // Ищем доступные устройства с инфракрасной связью // и помещаем их в список // Поиск не более трех доступных устройств irDevice s - irClient.DiscoverDevices(2); // Если устройства не найдены, то выводим сообщение if (irDevices.Lengt h == = 0) { MessageBox.Show("Устройства с ИК-портами не обнаружены!"); return; Создание программы для работы с ИК-связью 265 > // Перечисляем массив IrDADevicelnf o // и выводим информацию о каждом устройстве в список strin g device: in t ID; 1i stBoxl.Iterns.CIearО; foreac h (IrDADevicelnf o irDevic e in irDevices ) { ID * BitConverter.ToInt32(irDevice.DeviceID. 0); devic e - ID.ToString O + " " + irDevice.DeviceNam e + " H + irDevice.CharacterSe t + " " + irDevice.Hints; 1i stBoxl.Items.Add(device); } listBoxl.Selectedlnde x - 0; if (irDevices.Lengt h > 0) statusBarl.Tex t = irDevices.Length.ToString O + " устройств(a)"; // Делаем доступными кнопки для отправки и посылки сообщения butSend.Enable d » true; butReceive.Enable d = true: } Передача данных Код для отправки и посылки файлов приведен в листинге 12.5. Листинг 12.5 privat e voi d butSend_Click(objec t sender. EventArg s e) { // Открываем файл для отправки и получаем его поток Strea m fileStream; tr y { fileStrea m = new FileStream(fileSend, FileMode.Open); } catch (Exception exFile) { MessageBox.ShowC'He могу открыть " + exFile.ToStringO); return; } // Создаем IrDA-клиент с установленным именем службы, продолжение *& 266 Глава 12. Связь Листинг 12.5 (продолжение) // которое должно совпадать с ииенен службы на другой // IrDA-клиенте tr y { irClien t - ne w IrDAClient(irServiceName); } catc h (SocketExceptio n exS ) { MessageBox.Show("Ошибка сокета: " + exS.Messag e + " - Вы щелкнули на кнопке Получить на другом устройстве?*): return; } // Получим поток Strea m baseStrea m = irClient.GetStreamO; // Получим размер отправляемого файла // и запишем это значение в поток byte[ ] lengt h = Bi tConverter.GetBytesC(int)fi1eStream.Length); basestream.Write(length. 0. length.Length): // Создаем буфер для чтения файла byte[ ] buffe r = ne w byte[buffersize]; // Показываем число отправленных байт in t fileLengt h - (int)fileStream.Length: statusBarl.Tex t - "Отправлено " + fileLengt h + " байт": // Читаем файловый поток в базовом потоке whil e (fileLengt h > 0) { in t numRea d - fileStream.Read(buffer, 0. buffer.Length); baseStream.Write(buffer. 0. numRead); fileLengt h - » numRead; } fileStream.CloseO; baseStream. С loseO; irClient.CloseO; statusBarl.Tex t s "Файл отправлен"; Создание программы для работы с ИК-связью 26 7 } privat e voi d butReceive_Click(objec t sender, EventArg s e) { // Создаем поток для записи файла Strea m writeStream; tr y { writeStream - new FileStream(fileReceive, FileMode. OpenOrCreate): } catc h (Exception ) { MessageBox.ShowCH e йогу открыть "+ fileReceiv e + " для записи"): return: } // Создаен соединение с помощью класса IrDAEndPoIn t // для выбранного устройства из списка // Начинаем прослушку входящих сообщений // из устройства с объектом IrDAListene r tr y { in t i - listBoxl.Selectedlndex: irEnd P - ne w IrDAEndPoint(irDevices[i].DeviceID, irServiceName); irListe n = ne w IrDAListener(irEndP); irListen.StartO; } catc h (SocketExceptio n exSoc ) { MessageBox.ShowCH e могу прослушивать на службе "+irServiceNam e + ": " + exSoc.ErrorCode); } // Показываем прослушивание выбранного устройства statusBarl.Tex t - "Прослушка " + 1istBoxl.Selectedltem.ToString(); // Создаем соединение // для службы, обнаруженной прослушкой продолжение J& 268 Глав а 12. Связь Листинг 12.5 (продолжение) IrDAClient i rCl i ent; t r y { irClien t = i r Listen. Accep t I rDAClientO; } catc h (SocketExceptio n exp ) { MessageBox.ShowCH e могу принять сокет "+ exp.ErrorCode); return: } // Показываем, идет ли передача файла if (irListen.Pending O =-= true ) statusBarl.Tex t = "Передача из " + i rClient.RemoteMach i neName; els e statusBarl.Tex t = "Нет передачи из " + i rClient.RemoteMachineName; // Получим поток из клиента Strea m baseStrea m = irClient.GetStreamO; in t numToRead; // Создаем буфер для чтения файла byte[ ] buffe r = ne w byte[buffersize]; // Читаем поток данных, который содержит // данные из передающего устройства numToRea d = 4; whil e (numToRea d > 0) { in t numRea d = baseStream.Read(buffer, 0. numToRead); numToRea d •= numRead; } // Получим размер буфера для показа // числа байт для записи в файл numToRea d - BitConverter.ToInt32(buffer. 0); statusBarl.Tex t = "Записываем "+ numToRea d + " байт"; // Записываем поток в файл до тех пор. // пока не будут прочитаны все байты whil e (numToRea d > 0) { in t numRea d = baseStream.ReadCbuffer, 0, buffer.Length); numToRea d - = numRead: Технология Bluetooth 26 9 writeStream.Write(buffer. 0, numRead); } // Сообщаем, что файл получен statusBarl.Tex t - "Файл получен"; baseStream.CloseO: writeStream.CIoseО: i rti sten.StopO; irClient.CloseO; } Итак, можно запустить приложение на двух устройствах и попро­
бовать отправить и принять файл. Перед тестированием программы нужно создать текстовый документ send.txt с любым содержанием. Затем нужно повернуть друг к другу инфракрасные датчики двух уст­
ройств и на первом устройстве нажать кнопку Искать. Если поиск за­
вершился успешно, то в списке отобразится имя второго устройства. Затем на втором устройстве надо нажать кнопку Принять, а на пер­
вом устройстве нажать кнопку Отправить. В результате ваших дей­
ствий текст сообщения из файла send.txt должен быть передан на другое устройство и сохранен в файле receive.txt. К сожалению, данный пример нельзя тестировать на эмуляторе. Для проведения эксперимента вам необходимо иметь два настоящих ус­
тройства. Так как у меня нет второго КПК, я решил воспользоваться в качестве второго устройства своим смартфоном под управлением Windows Mobile 2005. Поскольку графический интерфейс программ для смартфонов не поддерживает кнопки, мне пришлось добавить в решение новый проект IrDA_Smartphone_CS и частично переписать код программы. Вместо кнопок использовалось меню, а вместо элемента управле­
ния ListBox — элемент ComboBox. Но можно было обойтись и без со­
здания текстовых файлов, а просто считывать данные из потока. В этом случае наша программа приобрела бы черты чата. Также можно написать какую-нибудь игру, в которой участвуют два игро­
ка. С помощью инфракрасной связи вы можете передавать инфор­
мацию, например, о сделанном ходе в шахматах. Технология Bluetooth Несмотря на свою дешевизну и простоту, инфракрасное соедине­
ние имеет несколько существенных недостатков. К ним относятся •. : -!.-• 270 Глав а 12. Связь маленький радиус действия и возможность связи в пределах пря­
мой видимости. Этих недостатков лишено Bluetooth-соединение. Но и тут не обошлось без ложки дегтя в бочке меда. Во-первых, су­
ществует два различных подхода к реализации Bluetooth-соедине­
ний, которые не совместимы друг с другом. Во-вторых, пока не су­
ществует поддержки этой технологии в управляемом коде .NET Compact Framework. Примеры с Bluetooth-связью мы будем приво­
дить для устройств под управлением Windows Mobile 5.0, так как он и гарантированно используют одну и ту же реализацию Bluetooth-
технологии. Так как библиотека .NET Compact Framework не имеет в своем составе классов, работающих с Bluetooth, то придется вос­
пользоваться вызовами функций Windows API, как показано в лис­
тинге 12.6. Листинг 12.6 publi c enu m RadioMod e { Of f - 0. Connectabl e - 1. Discoverabl e • 2 } /// <summary > /// Получает текущий статус bluetoot h /// </summary > /// <para m пагпе=,,о\*Мск1еи>флаги</рагап1> /// <returns></returns > [Dl l Impor t CBthUtil.dir) ] publi c stati c exter n in t BthGetModeCou t RadioMod e dwMode); /// <summary > /// Устанавливает новый режим bluetoot h /// </summary > /// <para m пате="а\Мх1еи>флаги для установки режима</рагапг> /// <returnsx/returns > [DlllmportC"BthUtil.dll") ] publi c stati c exter n in t BthSetModeCRadioMod e dwMode): privat e voi d mnuOn_Click(objec t sender. EventArg s e) { BthSetMode(RadioMode.Connectable); 1Ы Stat us. Text * RadioMode. Connectable. ToStringO; } Несколько слов о связи 271 privat e void Fornil_Load( objec t sender, EventArgs e) { RadioMod e mode; in t re t = BthGetMod e (ou t mode); "I bl Status. Tex t = mode.ToStringO; } privat e voi d mnuOff_Click(objec t sender. EventArg s e) { BthSetMode(RadioMode.Off); IblStatus.Tex t - RadioMode.Off.ToStringO; } В этом примере после запуска приложения текущий режим Bluetooth определяется при помощи функции BthGetMode, а с помощью команд меню пользователь может включать или выключать Bluetooth-соеди­
нение, используя функцию BthSetMode. Несколько слов о связи Несомненно, маленькие мобильные устройства, будь то смартфон или КПК, идеально подходят на роль коммуникационных уст­
ройств. В этой главе были приведены только самые простые при­
меры использования связи между устройствами. В последнее вре­
мя набирают обороты такие виды связи, как Wi-Fi, GPS и GPRS. Кроме того, мобильные устройства имеют в своем составе браузе­
ры для путешествия по Всемирной паутине. Таким образом, серь­
езному разработчику необходимо освоить весь спектр технологий, связанных с обменом данными между устройствами. Глава 13 Использование неуправляемог о кода Несмотря на то что библиотека .NET Compact Framework имеет множество классов для выполнения самых разных задач, во мно­
гих случаях приходится прибегать к вызовам функций Windows API. А в некоторых случаях использование функций Windows API даже предпочтительнее, чем использование аналогичных методов управляемого кода, так как они позволяют оптимизировать и по­
высить производительность приложения. Тема применения функций Windows API в .NET Compact Framework практически неисчерпаема. В некоторых случаях ис­
пользование этих функций оправданно, так как других вариантов для выполнения тех или иных задач просто не существует. В то же время библиотека .NET Compact Framework постоянно развивает­
ся, и часть задач с успехом решается с помощью встроенных клас­
сов, добавляемых в каждой новой версии .NET Compact Framework. Поэтому разработчику придется постоянно проводить ревизию сво­
их программ, заменяя в случае необходимости трудный код с ис­
пользованием Windows API на код с использованием безопасного управляемого кода .NET Compact Framework. Вызов функций Windows API Для вызовов функций Windows API используется механизм P/Invoke. Большинство часто вызываемых функций находится в библиотеке coredlLdlL Разработчики, которые пользовались функциями API в настоль­
ной версии Windows, наверняка обратят внимание на то, что эта библиотека coredll.dU содержит множество знакомых функций из библиотек kernel32.dll, gdi32.dll и user32.dll. Поэтому во многих случаях довольно легко будет перенести свои наработки из про­
грамм для настольного компьютера в приложения для мобиль­
ных устройств. Определение платформы 273 Определение платформы Если нужно определить, на какой платформе запущено ваше при­
ложение, то здесь вам не обойтись без вызова функции Windows API SystemParametersInfo. Для начала нужно создать новый класс PlatformDetector, в кото­
ром следует объявить функцию SystemParametersInfo и методы оп­
ределения платформы. А в обработчике события Load основной формы надо вызвать метод GetPl atform, чтобы узнать платформу сразу же после загрузки приложения, как это показано в листин­
ге 13.1. Листинг 13.1 usin g System; usin g System.Collections.Generic; usin g System.Text; usin g System.Runtime.InteropServices; namespac e PIatformDetectorC S { clas s PlatformDetecto r { [DlllmportC'coredll.dir) ] privat e stati c exter n boo l SystemParametersInfo C in t uiAction. in t uiParam. StringBuilde r pvParam. in t fWinlni); privat e stati c int SPI_GETPLATFORMTYP E - 257; publi c stati c Platfor m GetPlatform O { Platfor m pla t = PIatform.Unknown: swite n (System.Environment.OSVersion.PIatform ) { case PlatformID.Win32NT: pla t - PIatform.Win32NT; break; case PlatformlD.WinCE: pi at - CheckWi nCEPlatform(); DreaK; продолжение & 274 Глава 13. Использование неуправляемого кода Листинг 13.1 (продолжение) у retur n pl at; } stati c Platfor m CheckWinCEPlatformO { Platfor m plat = Platform.WindowsCE: StringBuilde r strbui l d - new StringBuilder(200); SystemParametersInfo(SPI_GETPLATFORMTYPE. 200. strbui l d. 0); stri n g st r - strbuild.ToStringO; switch (str ) { case "PocketPC": pla t - Platform.PocketPC: break: cas e "SmartPhone": // Not e tha t th e strbuil d paramete r fro m th e // PInvok e return s "SmartPhone" wit h an // uppe r cas e P. Th e correc t casin g is // "Smartphone" wit h a lowe r cas e p. pla t • P I atform.Smartphone: break; } retur n plat; } publi c enu m Platfor m { PocketPC. WindowsCE. Smartphone, Win32NT. Unknow n } } usin g System: us i n g System.Co l 1ect i ons.Gener i с; usin g System.ComponentModel; usin g System.Data; usin g System.Drawing; usin g System.Text; us i n g System.W i ndows.Forms; namespac e PlatformDetectorC S Пароли 27 5 { publi c parti al class Forml : Form { publi c Forml0 { Ini t i al i zeComponent(): } privat e voi d Forml_Load(objec t sender, EventArg s e) { tr y { MessageBox.ShowC"Платформа: " + PIatformDetector.GetPlatform()); } catc h (Exceptio n ex ) { MessageBox.ShowCex.Message.ToString()); } } } Особое внимание следует обратить на комментарий. Параметр strbuild после вызова функции возвращает значение SmartPhone с большой буквой «Р», хотя более правильным вариантом считает­
ся слово с маленькой буквой «р». Пароли Как вы, вероятно, знаете, пользователь может установить пароль на свой карманный компьютер. Для этого ему нужно зайти в раз­
дел Password при помощи последовательности команд Start • Settings • Password и указать четырехсимвольный пароль. С помо­
щью четырех функций API можно получить сведения о пароле и даже попытаться угадать его! Для тестирования этой возможности на форме надо разместить че­
тыре кнопки и текстовое поле. Соответствующий код приведен в листинге 13.2. 276 Глава 13. Использование неуправляемого кода Листинг 13.2 // Функция для установления нового системного пароля [D11Import("coredl1.dl1") ] privat e stati c exter n boo l SetPasswordCstrin g IpszOldpassword, strin g IspzNewPassword); // Функция для активации или блокировки текущего пароля [Dlllmport("coredl1.dl1") ] privat e stati c exter n boo l SetPasswordActiveCboo l bActive, strin g IpszPassword); // Функция для определения текущего состояния пароля [DllImportC"coredl1.dll") ] privat e stati c exter n boo l GetPasswordActiveO; // Функция для проверки пароля [DlllmportCcoredl l .dll") ] privat e stati c exter n boo l CheckPassword(strin g IpszPassword); privat e voi d butCheckPas s CIickCobjec t sender, EventArg s e) { txtlnfo.Tex t ="Активность пароля: " + GetPasswordActive().ToStr i ng(); privat e voi d butNewPass_Click(objec t sender, EventArg s e) { MessageBox.Show("Установка нового пароля " + SetPasswordC"Активность пароля: False", txtlnfo.Text ) .ToStringO); } private void butSetStateCl ickCobject sender, EventArgs e) { MessageBox.ShowC"Отключение пароля: м + SetPasswordActiveCfalse. txtlnfo.Text ) .ToStringO); } private void butFindPass_ClickCobject sender. EventArgs e) { MessageBox.ShowC"Угадали пароль? " + CheckPasswordCtxtlnfo.Text).ToString()); } Перезагрузка КПК 27 7 ВНИМАНИЕ Будьте осторожны с данными функциями на реальном устрой­
стве. Если вы случайно установите новый пароль, не запомнив его, то вам придется применить жесткую перезагрузку с потерей всех данных! Перезагрузка КПК Для карманных компьютеров может применяться как жесткая, так и мягкая перезагрузка. Жесткая перезагрузка возвращает устрой­
ство в первоначальное состояние, удаляя все установленные про­
граммы. Делать жесткую перезагрузку без особой необходимости не следует. Мягкая перезагрузка является более безопасной опера­
цией, которую часто выполняют при появлении различных сбоев в работе программ. Если разработчику необходимо программно перезагрузить устрой­
ство, то необходимо воспользоваться функцией Kernel IoControl. В листинге 13.3 приведен небольшой пример, демонстрирующий мягкую перезагрузку. Листинг 13.3 publi c cons t uin t FILEDEVICEHA L = 0x00000101: publi c cons t uin t METHODBUFFERE D = 0; publi c cons t uin t FILE_ANY_ACCES S - 0; publi c uin t CTL_CODE(uin t DeviceType, uin t Function, uin t Method, uin t Access ) { retur n ((DeviceTyp e « 16 ) (Acces s « 14 ) | (Functio n « 2 ) | Method); } [DlllmportrCoredl l .dl l"> ] publi c exter n stati c uint KemelloContro l ( uin t dwIoControlCode. IntPt r lpInBuf. uin t nlnBufSize, IntPt r lpOutBuf, uin t nOutBufSize, продолжение & 278 Глава 13. Использование неуправляемого кода Листинг 13.3 (продолжение) re f uint 1pBytesReturne d ): privat e void butReset_Click(objec t sender. EventArgs e) { uint bytesRetume d = 0; uint I0CTL_HAL_REB00T = CTL_CODE(FILE_DEVICE__HAL, 15. METHODJUFFERED. FILE_ANY_ACCESS); KernelIoControl(IOCTL_HAL_REBOOT. IntPtr.Zero. 0. IntPtr.Zero, 0. ref bytesRetumed); } Еще раз о перезагрузке Для устройств, работающих под управлением Windows Mobile 5.0, существует более удобный способ перезагрузки. Он очень похож на код перезагрузки настольных компьютеров с использованием функции ExitWindowsEx. При этом надо обратить внимание на раз­
личия карманных компьютеров и смартфонов. Если КПК можно только перезагрузить, то смартфон можно и перезагрузить, и вы­
ключить. Соответствующий код приведен в листинге 13.4. Листинг 13.4 [DlllmportCaygshel l .dll") ] publi c stati c exter n System.Boolea n ExitWindowsE x (in t uFlags, in t dwReserved): cons t in t EWXREB00 T - 2; // перезагрузка privat e voi d butReboot_Click(objec t sender, EventArg s e) { ExitWindowsEx(EWX_REBOOT. 0); } Поворот экрана Начиная с версии операционной системы Pocket P C 2003 Second Edition, карманные компьютеры научились изменять ориента­
цию экрана на системном уровне. Эту возможность часто исполь­
зуют при создании игр, просмотре видеоматериалов или отобра­
жении текстов. Если вы планируете писать программу с учетом Поворот экрана 27 9 поворота экрана, то будет нужно проверить, поддерживает ли це­
левое устройство данную функциональность. Ведь многие пользо­
ватели еще владеют КПК на базе PocketPC 2000, PocketPC 2002 и PocketPC 2003. Для поворота экрана, а также для проверки возможности такого по­
ворота используется функция API ChangeDisplaySettingsEx. Данная функция использует структуру DEVMODE. В первую очередь, в этой структуре нас интересует поле Fi el ds, в котором хранится значение DisplayQueryOrientation. Этот флаг отвечает за поддержку смены ориентации экрана и передает значение в поле lpDevMode.dmDis-
playOMentation. Например, значение DMO_0 говорит о том, что пово­
рот экрана не поддерживается. В листинге 13.5 приведен код, который проверяет, поддерживается ли системой изменение ориентации, и в случае положительного ответа поворачивает экран на 90°. Листинг 13.5 // Флаг, определяющий поддержку поворота экрана privat e stati c Int3 2 DisplayQueryOrientatio n - 0x01000000; privat e stati c Int3 2 CDS_TES T = 2; // запоминаем настройки экрана ScreenOrientatio n initialOrientatio n = SystemSett i ngs.ScreenOrientat i on; [Dl 1 Import("coredl1.dl1", SetLastErro r = true) ] privat e exter n stati c Int3 2 ChangeDisplaySettingsEx ( Strin g deviceName, re f DeviceMod e deviceMode. IntPt r hwnd. Int3 2 flags. IntPt r param): struc t DeviceMod e { [MarshalAs(UnmanagedType.ByValTStr. SizeConst - 32)] publi c Strin g DeviceName; publi c I ntl 6 SpecVersion; publi c I ntl 6 DriverVersion: publi c I ntl 6 Size: publi c I ntl 6 DriverExtra; продолжение & 28 0 Глава 13. Использование неуправляемого кода Листинг 13.5 (продолжение) publi c Int3 2 Fields; publi c Intl 6 Orientation; publi c Intl 6 PaperSize; publi c Intl 6 PaperLength; publi c Intl 6 PaperWidth; publi c Intl 6 Scale; publi c Intl 6 Copies; publi c Intl 6 DefaultSource; publi c Intl 6 PrintQuality; publi c Intl 6 Color; publi c Intl 6 Duplex; publi c Intl 6 YResolution; publi c Intl 6 TTOption; publi c Intl 6 Collate; [MarshalAsCUnmanagedType.ByValTStr. SizeCons t - 32) ] publi c Strin g FormName: publi c Intl 6 LogPixels; publi c Int3 2 BitsPerPel; publi c Int3 2 PelsWidth; publi c Int3 2 PelsHeight; publi c Int3 2 DisplayFlags; publi c Int3 2 DisplayFrequency; publi c Int3 2 DisplayOrientation; } privat e voi d butCheckRotate_Click(objec t sender, EventArg s e) { // подготавливаем структуру DeviceMod e DeviceMod e devMod e - ne w DeviceModeO; devMode.Siz e - (Intl6)Marshal.SizeOf(devMode); devMode.Field s = DisplayQueryOrientation; // Проверяем, поддерживает ли система поворот экрана Int3 2 resul t = ChangeDisplaySettingsEx ( null. re f devMode, IntPtr.Zero. CDS_TEST. IntPtr.Zero); if (resul t — 0) { // Если вызов функции прошел успешно, Прячем кнопку Star t 28 1 // то проверяем поддержку поворота экрана // Если параметр DisplayOrientatio n имеет ненулевое // значение то поворот экрана возможен if (devMode.DisplayOrientatio n != 0) { MessageBox.ShowC"Поворот экрана поддерживается"); } } els e { MessageBox.ShowC"Поворот экрана не поддерживается"); } } privat e voi d butRot90_Click(objec t sender, EventArg s e) { SystemSett i ngs.ScreenOr i entatio n » ScreenOr i entat i on.Angle90; > privat e voi d butRestore_Click(objec t sender, EventArg s e) { i f (SystemSettings.ScreenOrientatio n != initialOrientation ) { try { SystemSettings.ScreenOrientatio n = initialOrientation; } catc h (Exception ) { // Unabl e t o chang e th e orientatio n bac k // t o th e origina l configuration. MessageBox.ShowC"Thi s sampl e wa s unabl e to se t th e " + "orientatio n bac k t o th e origina l state."); } } } Прячем кнопку Start Функция SHFul 1 Screen позволяет прятать и показывать кнопку Start и пиктограмму виртуальной клавиатуры SIP. Соответствующий код приведен в листинге 13.6. 282 Глава 13. Использование неуправляемого кода Листинг 13.6 /// <summary > /// Функция используется для изменения вида экрана. /// Вы можете модифицировать панель задач, панель ввода, значок /// Пуск /// </summary > /// <para m name-',hwndRequester">flecKpHnTo p окна</рагаш> /// <para m name= HdwState">Onpeflenfle T состояние окна</рагаш> /// <returns> B успешном случае возвращается True, иначе -
/// False</returns > [DlllmportCaygshel l .dll") ] stati c exter n uin t SHFullScreenCIntPt r hwndRequester. uin t dwState); cons t uin t SHFSSHOwTASKBA R = 0x0001; cons t uin t SHFSHIDETASKBA R « 0x0002; cons t uin t SHFS_SHOWSIPBUTTO N = 0x000 4 cons t uin t SHFS_HIDESIPBUTTO N = 0x000 8 cons t uin t SHFS_SHOWSTARTICO N = 0x001 0 cons t uin t SHFS_HIDESTARTICO N - 0x002 0 privat e voi d butHideStartClickCobjec t sender, EventArg s e) { IntPt r hwn d • this.Handle; //прячем кнопку Star t SHFullScreenChwnd, SHFSJUDESTARTICON); //прячем SI P //SHFullScreenChwnd, SHFSHIDESIPBUTTON); } privat e voi d butShowStart_ClickCobjec t sender. EventArg s e) { //показываем кнопку Star t IntPt r hwn d - this.Handle; SHFullScreenChwnd, SHFS_SH0WSTARTIC0N); //показываем SI P //SHFullScreenChwnd. SHFSJHOWSIPBUTTON); } В примере показано, как прятать кнопку Start. Если нужно спря­
тать пиктограмму SIP, то надо убрать комментарии при втором вызове функции. На рис. 13.1 показан внешний вид экрана со спря­
танной кнопкой Start. Панель задач 283 I KJJbl-ТИГ НИШЙГ. IV | 3 ^ | * < Рис. 13.1. Скрытие кнопки Star t Панель задач Очень часто программисты в качестве шутки создают программы, которые прячут привычные для пользователя элементы интерфей­
са. В предыдущем примере было показано, как можно скрыть кноп­
ку Start. Теперь нужно рассмотреть пример работы с панелью задач. Для создания тестового приложения на форме надо разместить две кнопки. Одна из них будет скрывать панель задач, а вторая — пока­
зывать ее. Соответствующий код приведен в листинге 13.7. Листинг 13.7 /// <summary> /// Скрывает одно окно и активирует другое /// </summary> privat e const i nt SWHIOE - 0; /// <summary> /// Активирует окно /// </summary> privat e const i nt SWSHOW = 5: [Dl l l mport rcoredl l.dl l") ] privat e stati c exter n IntPt r FindWindow(string ClassName, stri n g WindowName); [Dni mportC'coredl l.di r) ] privat e stati c exter n bool ShowWindow(IntPtr hwnd. i nt nCmdShow); /// <summary> продолжение & *,•>••- '*-
284 Глава 13. Использование неуправляемого кода Листинг 13.7 {продолжение) III Прячем панель задач, чтобы пользователь не мог /// нажать кнопку Star t /// </summary> publi c stati c void HideTaskbarO { IntPt r h - FindWindowCHHTaskBar". ""); ShowWindow(h. SW_HIDE); } /// <summary > /// Показывает панель задач /// </summary > publi c stati c voi d ShowTaskBar O { IntPt r h - FindWindow("HHTaskBar\ ""); ShowWindowCh. SW_SH0W); } privat e voi d butHideTaskbar_Click(objec t sender. EventArg s e) { HideTaskbarO; } privat e voi d bu t ShowTaskbar C lic k (objec t sender, EventArg s e) { ShowTaskBarO; } На самом деле с помощью функций FindWi ndow и ShowWi ndow можно показывать и скрывать не только панель задач, но и окна других приложений. Запуск других приложений Иногда требуется запустить из своей программы другое прило­
жение. В этом случае можно призвать на помощь функцию API CreateProcess. В листинге 13.8 приведен код примера, который может запустить калькулятор, календарь и даже послать файл через инфракрасное соединение мобильному телефону. Для за­
пуска всех этих функций на форме надо разместить всего три кнопки. Запуск других приложений 28 5 Листинг 13.8 publi c clas s Processlnf o { publi c IntPt r hProcess; publi c IntPt r hThread; publi c Int3 2 Processld: publi c Int3 2 Threadld; } [DllImport("CoreDll.DLL", SetLastErro r - true) ] privat e exter n stati c in t CreateProcess(Strin g imageName. Strin g cmdLine, IntPt r lpProcessAttributes, IntPt r lpThreadAttributes. Int3 2 boolInheritHandles, Int3 2 dwCreationFlags, IntPt r IpEnvironment. IntPt r IpszCurrentOir. byte[ ] si, Processlnf o pi); privat e voi d butCa1c_Click(objec t sender, EventArg s e) { //Запускаем калькулятор Processlnf o p i - ne w ProcessInfoO; CreateProcessrca1c.exe". "". IntPtr.Zero. IntPtr.Zero. 0, 0. IntPtr.Zero. IntPtr.Zero, ne w Byte[128]. pi): } privat e voi d butCalendar_Click(objec t sender, EventArg s e) { //Запускаем календарь Processlnf o p i - ne w ProcessInfoO: CreateProcess("calendar.exe". "". IntPtr.Zero, IntPtr.Zero. 0. 0, IntPtr.Zero. IntPtr.Zero, ne w Byte[128], pi); } privat e voi d butInfra_Click(objec t sender. EventArg s e) { //Посылаем файл через инфракрасное соединение _ А продолжение & «I# '- Г 286 Глава 13. Использование неуправляемого кода Листинг 13.8 {продолжение) Processlnf o pi = new Processlnfo(); CreateProcessCBeam.exe", "WwindowsWAlarml.wav". IntPtr.Zero, IntPtr.Zero. 0, 0. IntPtr.Zero, IntPtr.Zero. new Byte[128]. pi); } Приведенный код достаточно прост. Нужно вызвать функцию CreateProcess с именем исполняемого файла в первом параметре. В методе для отправки файла также используется второй параметр, в котором указываем имя отсылаемого файла. Названия специальных файлов В Windows существует ряд специальных папок, в которых содер­
жатся файлы определенной категории. Например, в папке Избран­
ное содержатся ссылки на любимые сайты пользователя. Проблема заключается в том, что в локализованных версиях Windows эти папки зачастую имеют разные названия. Так, в аме­
риканской версии Windows упомянутая папка имеет название Favorites. И если ваша программа ссылается на файл, находящий­
ся в специальной папке, то необходимо точно узнать, как назы­
вается эта папка на конкретном устройстве. Код проверки при­
веден в листинге 13.9. Листинг 13.9 // Константы /// <summary > /// Папка, содержащая файлы и папки, которые появляются на /// экране Сегодня /// </summary > cons t in t CSIDL_DESKTOPOIRECTOR Y - 0x0010; /// <summary > /// Папка Избранное /// </summary > cons t in t CSIDLJFAVORITE S - 0x0006; /// <sumrnary > /// Папка \Мои документы /// </summary > Названия специальных файлов 28 7 cons t in t CSIDL_PERSONA L = 0x0005; /// <summary > /// Папка Программы в папке Главное неню /// (\Windows\Star t Menu\Programs ) /// </summary > cons t in t CSIDL_PROGRAM S = 0x0002: /// <summary > /// Папка Recen t (содержит последние из открывавшихся /// документов) /// </summary > cons t in t CSIDL_RECEN T - 0x0008: /// <sunmiary > /// Папка Главное меню /// (\Windows\Star t Menu ) /// </summary > cons t in t CSIDLJTARTMEN U = 0x000b: /// <summary > /// Папка Автозагрузка для программ, /// которые автоматически загружаются при запуске Window s /// \Windows\StartU p /// </summary > cons t in t CSIDLJTARTU P = 0x0007: /// <summary > /// Папка, в которой хранятся шаблоны документов /// </summary > cons t in t CSIDLJEMPLATE S = 0x0015: /// <summary > /// Функция получения имен специальных папок /// </summary > [DnimportrCoredll.dH") ] stati c exter n in t SHGetSpecialFolderPat h (IntPt r hwndOwner, Stn'ngBuilde r IpszPath, in t nFolder, int fCreate): cons t in t HAX_PAT H = 260: privat e voi d Forml_Load(objec t sender. EventArg s e) { // Папка Избранное StringBuilde r strFavorite s - ne w StringBuilder(MAXPATH); продолжение •& 288 Глава 13. Использование неуправляемого кода Листинг 13.9 (продолжение) SHGetSpeci alPolderPath<this.Hand!e, strFavorites. CSIDLFAVORITES. 0); MessageBox.Show("Избранное: " + strFavorites.ToStringO): // Папка Программы StringBuilde r strPrograms = new StringBuilder(MAXPATH); SHGetSpecialFolderPath(this.Handle. strPrograms. CSIDL_PROGRAMS. 0); MessageBox.ShowC"Программы: " + strPrograms.ToStringO): // Мои документы StringBuilde r strMyDocs = new StringBuilder(MAXPATH); SHGetSpecialFolderPath(this.Handle. strMyDocs. CSIDL_PERSONAL. 0>: MessageBox.Show("Мои документы: " + strMyDocs.ToStringO): } Использование звуковых файлов Мир современных компьютеров трудно представить без мультиме­
дийных возможностей; однако проигрывание звуковых файлов не поддерживалось в библиотеке .NET Framework 1.0. Подобный под­
ход Microsoft удивил многих программистов. В этом случае прихо­
дилось использовать неуправляемый код с вызовом функции PlaySound. С выходом .NET Framework 2.0 ситуация изменилась в лучшую сто­
рону. Но легкая поддержка звуковых файлов остается прерогати­
вой настольных систем. В библиотеке .NET Compact Framework по-
прежнему отсутствует поддержка проигрывания звуковых файлов. А ведь для разработки игры наличие звуковых эффектов является обязательным условием, иначе игра будет просто неинтересна! Поэтому нужно устранить недоработку разработчиков из Microsoft. В новом примере будут использоваться два способа воспроизведе­
ния звуков. В первом случае программа будет извлекать звуковой фрагмент из ресурсов. Во втором случае программа будет проигры­
вать звук из обычного WAV-файла. Итак, нужно создать новый проект с именем PlaySound_CS. К проек­
ту надо добавить новый класс с именем Sound. Объявление функ­
ции PlaySound, необходимой для проигрывания звуков, нужно по­
местить в класс Sound, как показано в листинге 13.10. Использование звуковых файлов 289 Листинг 13.1 0 privat e enu m Flag s { SN D SYN C - 0x0000. SND_ASYN C - 0x0001. SNDJODEFAUL T - 0x0002. SND_MEMOR Y - 0x0004. SND_L0O P = 0x0008. SNDJOSTO P - 0x0010. SNDJJOWAI T = 0x00002000. SND_ALIA S = 0x00010000. SND_ALIAS_I D = 0x00110000. SND_FILENAM E =» 0x00020000. SND_RESOURC E - 0x0004000 4 } " [DllImport("CoreDll.DLL". EntryPoin t = "PlaySound", SetLastErro r = true) ] privat e exter n stati c in t PIaySoundCstrin g szSound, IntPt r hMod, in t flags); [DllImportC'CoreDll.DLL". EntryPoint - "PlaySound". SetLastErro r - true) ] privat e exter n stati c in t PlaySoundBytes(byte[ ] szSound. IntPt r hMod, in t flags); Данная функция использует для параметра f 1 ags несколько пре­
допределенных констант. Более подробную информацию о назна­
чении флагов этой функции можно найти в документации. После этого создаются два конструктора с разными параметрами, которые будут использоваться для разных методов воспроизведения звука, и метод PI ау. Теперь нужно перейти к основной форме и раз­
местить на ней две кнопки. Первая кнопка, butResource, будет проиг­
рывать звуковой фрагмент, который хранится в ресурсах приложе­
ния. Кнопка butFi 1 е запустит метод, который проигрывает аудиофайл. Для того чтобы пример работал, понадобятся два звуковых фай­
лов. В состав Windows ХР входит несколько звуковых файлов. Для данного примера использовался файл chimes.wav. Его нужно доба­
вить в проект. Чтобы включить файл chimes.wav в проект как ре­
сурс, надо в свойствах файла выбрать пункт Build Action и устано­
вить значение Embedded Resource. 10-2873 290 Глава 13. Использование неуправляемого кода В качестве внешнего аудиофайла будет использоваться файл alarm3.wav, входящий в состав Windows Mobile. Этот файл находит­
ся в папке Windows. При желании можно использовать свой файл, но при этом надо в коде указать путь к нему. Теперь достаточно прописать код для обработки события CI ick созданных кнопок, как показано в листинге 13.11, — и приложение готово. Листинг 13.11 usin g System; usin g System.Collections.Generic; usin g System.Text; usin g System.10; usin g System.Runtime.InteropServices; namespac e PlaySoundC S { publi c class Sound { privat e byte[ ] m__soundBytes; privat e stri n g mfileName; privat e enum Flags { SND^SYN C = 0x0000. SN D ASYN C = 0x0001. SNDJIODEFAUL T = 0x0002. SND__MEMOR Y = 0x0004. SNDJ-OO P - 0x0008. SND__N0ST0 P - 0x0010. SND_NOWAI T = 0x00002000. SN D ALIA S - 0x00010000. SND-ALIASJ D * 0x00110000. SND""FILENAM E - 0x00020000. SN D RESOURC E = 0x0004000 4 } -
[DllImportCCoreDll.DLL\ EntryPoin t =» "PlaySound". SetLastErro r - true) ] privat e exter n stati c in t PIaySound(strin g szSound. IntPt r hMod. in t flags); [Dl l ImportrCoreDll.DLL", EntryPoint - "PlaySound". SetLastError Использование звуковых файлов 291 - true) ] privat e exter n stati c in t PlaySoundBytes(byte[ ] szSound, IntPt r hMod. in t flags); /// <summary > /// Конструктор объекта Sound, который проигрывает звук из /// указанного файла /// </summary > publi c SoundCstrin g fileName ) { га fileName - fileName; } /// <summary > /// Конструктор объекта Sound, который проигрывает звук из /// ресурсов /// </summary > publi c Sound(Strea m stream ) // читаем данные из потока msoundByte s - ne w byte[stream. Length]; stream. ReadtmsoundBytes, 0, (int ) stream-Length); > /// <summary > /// Воспроизводим звук /// </summary > publi c voi d PIayО { // Если из файла, то вызываем PlaySound. .// если из ресурсов, то PlaySoundBytes. i f CmfileName != nul l ) PlaySound(m_fileName, IntPtr.Zero, (int)(Flags.SND_ASYNC I Flags.SND_FILENAME)); else PIaySoundBytes(msoundBytes, IntPtr.Zero, (int)(Flags.SND_ASYNC | Flags.SNDMEMORY)); } } 1С 292 Глава 13. Использование неуправляемого кода Теперь нужно перейти к самой форме. Код для нее приведен в листин­
ге 13.12. Листинг 13.12 usin g System; us i n g System.Col1ect i ons.Gener i с; us i n g System.ComponentModel: usin g System.Data: usin g System.Drawing; usin g System.Text: usin g System.Windows.Forms: usin g System.Reflection: namespac e PlaySoundC S { publi c partia l clas s Form l : For m { publi c Forml 0 { In i ti al i zeComponent(): InitializeComponentO: #i f DEBU G MinimizeBox - false: #els e MinimizeBox - true: #endi f } privat e void butResource_Click(objec t sender. EventArgs e) { Soun d soun d я ne w Souix K Assemb l у. GetExecut l ngAssemb l у (). GetMan i festResourceStream ( "PlaySound_CS.chimes.wav")): sound.PIay(): } privat e voi d butFile_Click(objec t sender, EventArg s e) { Soun d soun d - ne w SoundCWindowsWalarnO.wav"): sound.PIay(): } } } Системное время 29 3 Системные звуки Также разработчик может использовать функцию MessageBeep, по­
зволяющую проигрывать системные звуки. Код, использующий эту функцию, приведен в листинге 13.13. Листинг 13.13 [Dl l l mportrcoredl l.di r) ] extern stati c void MessageBeepCuin t BeepType); privat e void butBeep_Click(objec t sender. EventArgs e) { MessageBeep(O); } Системное время Чтобы получить или установить системное время на устройстве» нужно использовать функции GetSystemTiте и SetSystemTime. Следу­
ет учитывать, что функция GetSystemTi me возвращает время по Грин­
вичу, а не местное время. Код, иллюстрирующий применение этих функций, приведен в листинге 13.14. Листинг 13.14 usin g System.Runtime.InteropServices; [Dl 1 Import("coredll.dl1") ] privat e exter n stati c voi d GetSystemTime(re f SYSTEMTIM E IpSystemTime); [Dl l Impor t ("coredl l .dlD ] privat e exter n stati c uin t SetSystemTime(re f SYSTEMTIM E IpSystemTime); privat e struc t SYSTEMTIM E { publi c ushor t wYear; publi c ushor t wMonth; publi c ushor t wDayOfWeek; publi c ushor t wDay; publi c ushor t wHour: publi c ushor t wMinute; publi c ushor t wSecond; продолжение ifr 294 Глава 13. Использование неуправляемого кода Листинг 13.1 4 (продолжение) publi c ushor t wMi Hi seconds; } privat e voi d GetTimeO { // Получим системное время SYSTEMTIM E s t <= ne w SYSTEMTIMEO: GetSystemTimeCre f st); » DateTim e d t - DateTime.UtcNow.ToLocalTimeO; // Выводим сообщение MessageBox.ShowC"Текущее вреня: " + st.wHour.ToString O + ":" + st.wMinute.ToStringO); privat e voi d SetTimeC ) { // Сначала получим системное время SYSTEMTIM E s t - ne w SYSTEMTIMEO; GetSystemTimeCre f st); // А теперь прибавим один час st.wHou r - (ushort)(st.wHou r + 1 X 24); SetSystemTime(re f st); MessageBox.ShowC"Новое время: • + st.wHour.ToString O + ": + st.wMinute.ToStringO); privat e voi d butGetTimeClickCobjec t sender, EventArg s e) { GetTimeO: } privat e voi d butSetTime_Click(objec t sender, EventArg s e) { SetTimeC): } *. )••• -
Создание ярлыка В некоторых случаях программисту необходимо создать ярлык к какой-либо программе. В этом случае можно воспользоваться спе-
Количество строк в текстовом поле 295 циальной функцией SHCreateShortcut, применение которой демон­
стрируется в листинге 13.15. Листинг 13.1 5 /// <summary > /// Функция для создания ярлыка /// </suwary > /// <рагаш name ss"szShortcut">CTpOKa, содержащая /// путь и имя создаваемого ярлыка. ///</рагаш> /// <para m name="szTarget">CTpoKa, содержащая /// путь и аргументы для ярлыка. /// Размер строки ограничен 25 6 символами. /// </рагаш> /// <returns> B успешном случае возвращается TRUE, /// в случае ошибки возвращается FALS E /// </returns > [DllImport("coredll.dlГ. EntryPoin t * "SHCreateShortcut") ] privat e stati c exter n boo l SHCreateShortcut(strin g szShortcut. strin g szTarget): privat e voi d butCreateShortcut_C1ick(objec t sender. EventArg s e ) { // Создадим ярлык к калькулятору boo l succes s = SHCreateShortcutC'WM y DocumentsWShortcut. 1 nk". "WWindowsWcalc.exeX-): } В этом примере создается ярлык Shortcut.Ink для стандартного каль­
кулятора, чей исполняемый файл носит имя windows\calc.exe. Количество строк в текстовом поле Если у текстового поля свойство Mul ti 1 i пе имеет значение True, то свойство Li nes возвращает массив строк в текстовом поле. Но у дан­
ного свойства есть два недостатка. Во-первых, свойство Lines не поддерживается библиотекой .NET Compact Framework, а во-вто­
рых, это свойство не учитывает перенос слов. Для подсчета количе­
ства строк в многострочном текстовом поле можно использовать сообщение EM_GETL INEC0UNT. Соответствующий код приведен в лис­
тинге 13.16. 296 Глава 13. Использование неуправляемого кода Листинг 13.16 [DllImport("coredll.dll") ] stati c exter n in t SendMessagedntPt r hwnd, in t msg. In t wParam. in t lParam): cons t in t EM_GETLINECOUN T - OxOOBA; privat e voi d butGetNumberClic k (objec t sender, EventArg s e) { // Узнаем число строк в текстовой поле in t numberOfLine s - SendMessageCtextBoxl.Handle. EM_GETLINECOUNT. 0. 0): sblnfo.Tex t - "Число строк: " + numberOfLines.ToStringO; } Реестр Реестр является важной частью любой операционной системы се­
мейства Windows. Не является исключением и система Windows Mobile, в которой тоже имеется собственный реестр. Однако раз­
работчики компании Microsoft не стали включать редактор реест­
ра в состав Windows Mobile. Поэтому для доступа к разделам рее­
стра приходится устанавливать программы от сторонних произво­
дителей. Однако любой программист может написать свой редактор реестра, используя возможности .NET Compact Framework. При этом следу­
ет учитывать, что в библиотеке .NET Compact Framework 2.0 появи­
лись классы для работы с разделами реестра. Если же вы продолжаете писать программы с использованием .NET Compact Framework 1.0, то придется вызывать функции Windows API. В листинге 13.17 приведен код, который будет работать в любой версии .NET Compact Framework. Листинг 13.17 using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices: namespace RegistryCS { class Registr y { /// <summary > Реестр 29 7 /// Создает ключ /// </summary > /// <рагаш name*"keyName">Иня создаваемого ключа</рагат> /// <returns> B успешной случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t CreateKeyCUIntPt r root, strin g keyName ) { UlntPt r hke y = UlntPtr.Zero; uin t dispositio n = 0; tr y { retur n RegCreateKeyExCroot, keyName. 0, null. 0. KeyAccess.None, IntPtr.Zero. re f hkey. re f disposition); } finall y { if (UlntPtr.Zer o != hkey ) { RegCloseKey(hkey); } } } /// <summary > /// Удаляет ключ /// </summary > /// <para m пате="кеу№те">Имя ключа</рагат> /// <returns> B успешном случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t DeleteKeyCUIntPt r root, strin g keyName ) { retur n RegDeleteKeyCroot. keyName); } /// <summary > /// Создает строковой параметр в заданной ключе /// </summary > /// <para m name e"keyName">MMf l ключа</рагат> /// <para m name= BvalueName,,>HHf l параиетра</рагат> /// <para m name a"stnngData">3Ha4eHM e параиетра</рагат> /// <returns> B успешном случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t CreateValueStringCstrin g keyName, strin g valueName. продолжение ?> 298 Глава 13. Использование неуправляемого кода Листинг 13.17 (продолжение) strin g stringData ) { UlntPt r hke y - UlntPtr.Zero; tr y { in t resul t - RegOpenKeyEx(root. keyName. 0. KeyAccess.None. re f hkey); i f (ERRORJUCCES S != result ) retur n result; byte[ ] byte s « Encoding.Unicode.GetBytes(stringOata); retur n RegSetValueExChkey, valueName, 0. KeyType.String, bytes. Cu i nt)bytes.Length); } finall y { i f (UlntPtr.Zero != hkey) { RegCloseKey(hkey); } } } /// <summary > /// Создает параметр типа DWOR D в заданном ключе /// </summary > /// <para m name*"keyNameн>Имя ключа</рагат> /// <para m name e',valueNaine">HMf l параметра</рагат> /// <para m name= s"dwordData">3Ha4eHM e параметра</рагат> /// <returns> B успешном случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t CreateValueDWORD(UlntPt r root, strin g keyName, strin g valueName. uin t dwordData ) { UlntPt r hke y = UlntPtr.Zero; tr y { in t resul t - RegOpenKeyEx(root. keyName. 0, KeyAccess.None, re f hkey); if (ERR0R_SUCCES S !- result ) retur n result; bytef j byte s - BitConverter.GetBytes(dwordData); Реестр • • 29 9 retur n RegSetValueExChkey, valueName. 0. KeyType.Dword, '* bytes, (uint)bytes.Length): } f i nal l y if (UlntPtr.Zer o != hkey ) { RegCloseKey(hkey); •'4 1 , I /// <summary > /// Создает двоичный параметр в заданной ключе /// </summary > /// <para m name * "keyName"^ я ключа</рагат> /// <para m name= s"valueNanie n>MMf l паранетра</рагага> /// <para m name-'dwordData-^HaHeHM e паранетра</рагага> /// <returns> B успешной случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t CreateValueBinarydJIntPt r root, strin g keyName, strin g valueName, uin t binData ) { UlntPt r hke y - UlntPtr.Zero; tr y { in t resul t a RegOpenKeyExCroot. keyName. 0, KeyAccess.None, re f hkey); if (ERROR_SUCCES S != result ) retur n result; byte[ ] dat a - BitConverter.GetBytes(binData); retur n RegSetValueExChkey, valueName. 0. KeyType.Binary. data, (uint)data.Length); } finall y { if (UlntPtr.Zer o != hkey ) { RegCloseKey(hkey); } } } /// <summary > a л продолжение & 300 Глава 13. Использование неуправляемого кода Листинг 13.1 7 (продолжение) III Получает значение строкового параметра /// </summary> /// <рагаш пате="кеуМате">Имя ключа</рагаш> /// <param name=-"valueName">llMf l параметра</рагат> /// <param name="stringResult">CTpoKOBbi e данные</рагат> /// <returns> B успешном случае возвращается /// ERROR_SUCCESS</returns> publi c stati c i nt GetStringValueCUIntPt r root, stri ng keyName, stri n g valueName, ref stri ng stringResult) { UlntPtr hkey - UlntPtr.Zero; tr y { re f re f in t resul t « RegOpenKeyExCroot, keyName, 0. KeyAccess.None, re f hkey); 1f (ERROR_SUCCES S !« result ) retur n result: byte[ ] byte s - null; uin t lengt h » 0: KeyTyp e keyTyp e - KeyType.None; resul t = RegQueryValueEx(hkey. valueName, IntPtr.Zero. keyType, null, re f length); i f (ERROR_SUCCES S !- result ) retur n result; keyTyp e * KeyType.None; byte s - ne w byte[length]; resul t - RegQueryValueExChkey, valueName. IntPtr.Zero, keyType. bytes, re f length); if (ERROR_SUCCES S != result ) retur n result\ stringResul t - Encoding.Unicode.GetString(bytes, 0, bytes.Length): retur n ERRO R SUCCESS; Реестр 30 1 f i nal l y { i f (UlntPtr.Zer o !» hkey) { RegCloseKey(hkey): } } } /// <summary > /// Получает заданное значение типа DWOR D /// </summary > /// <para m name-,,keyName">HMf l ключа</рагат> /// <para m name»"valueName">Иня паранетра</рагат> /// <para m name-"dwordResult">3Ha4eHM e паранетра</рагат> /// <returns> B успешном случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t GetDWORDValue(UIntPt r root, strin g keyName. strin g valueName. re f uin t dwordResult ) { UlntPt r hke y - UlntPtr.Zero; tr y { re f re f in t resul t * RegOpenKeyExCroot, keyName, 0. KeyAccess.None. re f hkey): if CERROR_SUCCES S I - result ) retur n result; byte[ ] byte s - null; uin t lengt h =« 0; KeyTyp e keyTyp e - KeyType.None; resul t - RegQueryValueEx(hkey, valueName, IntPtr.Zero, keyType, null, re f length): byte s - ne w byte[Marshal.SizeOf(typeof(uint))]; lengt h e (uint)bytes.Length; keyTyp e - KeyType.None: resul t - RegQueryValueEx(hkey. valueName. IntPtr.Zero. продолжение & 302 Глава 13. Использование неуправляемого кода Листинг 13.1 7 (продолжение) bytes, ref length); i f (ERROR_SUCCESS 1= result) retur n resul t: dwordResul t - BitConverter.ToUInt32(bytes, 0); retur n ERROR SUCCESS; } f i nal l y { if (UlntPtr.Zer o != hkey ) { RegCloseKey(hkey); } } } I l l <summary > /// Удаляет заданный паранетр из раздела реестра /// </summary > /// <para m name="keyName">llMf l ключа</рагат> /// <para m name="valueName,,>MMf l параметра</рагат> /// <returns> B успешной случае возвращается /// ERROR_SUCCESS</returns > publi c stati c in t DeleteValuedJIntPt r root, strin g keyName, strin g valueName ) UlntPt r hke y = UlntPtr.Zero; *. t py ;u { in t resul t - RegOpenKeyEx(root, keyName, 0, KeyAccess.None. re f hkey); if (ERRORJUCCES S != result ) retur n result; retur n RegDeleteValue(hkey. valueName); } f i nal l y { - .•.'} * - > i f (UlntPtr.Zero !- hkey) { Реестр 30 3 RegCloseKey(hkey): } } } /// <summary > /// Типы ключей /// </summary > publi c enu m KeyTyp e : uin t { Non e • 0, Strin g == 1. Binar y = 3. Dwor d = 4, } /// <summary > /// Тип доступа /// </summary > publi c enu m KeyAcces s : uin t { Non e - 0x0000. QueryValu e - 0x0001. SetValu e - 0x0002. CreateSubKe y =• 0x0004. EnumerateSubKey s = 0x0008, Notif y - 0x0010. CreateUn k - 0x002 0 } /// <summary > /// HKEY_CLASSES_ROO T /// </suramary > publi c stati c UlntPt r HKC R - ne w UIntPtr(0x80000000): /// <sumraary > /// HKEY_CURRENT_USE R /// </summary > publi c stati c UlntPt r HKC U - ne w UIntPtr(Ox80000001): /// <suramary > /// HKEY_LOCAL_MACHIN E /// </summary > publi c stati c UlntPt r HKL M - ne w UIntPtr(Ox80000002): /// <SUmmary > продолжение & 304 Глава 13. Использование неуправляемого кода Листинг 13.1 7 {продолжение) 41 HKEYUSERS 41 </summary > publi c stati c UlntPt r HKU - new UlntPtrC0x80000003); // <summary > // Возвращаемое значение в случае успеха // </summary > publi c cons t int ERROR_SUCCESS = 0; // <summary> // Функция для создания заданного раздела реестра. Если раздел // уже существует, то функция открывает его. // </sunimary> // <param name="hkey">[in ] Дескриптор к открываемому разделу // или одна из ветвей реестра: // HKCR. HKCU. HKLM.</param > // <para m name="lpSubKey">[in ] Имя для нового раздела. Данный // раздел должен быть подразделом раздела, определенного в // параметре hKey. // </рагага> // <param name="Reserved">[in ] Зарезервированный параметр. // Установлен равным 0</рагаш> // <рагага name=',lpC1ass">[in ] Имя класса или типа объекта // Данный параметр игнорируется, если раздел уже существует //</param > // <рагагп name ss"dw0ptions">[in ] Игнорируется; установите // равным 0 // </рагат> // <рагат name="samDesirecT>[in ] Игнорируется; установите // равным 0 // </рагат> // <рагат name="lpSecurityAttributes">[in ] Установите в NULL. // </param> // <para m name- n phkResult,,>[out ] Переменная, получаемая от // дескриптора нового или открытого раздела // Если вы больше не нуждаетесь в дескрипторе, то вызовите // функцию RegCloseKe y для его закрытия. </param > // <para m name="lpdwDisposition">[out ] Переменная, которая // получает значение 1 (REG_CREATED_NEW_KEY). // если раздел был создан // и значение 2 (REG_OPENED_EXISTINGJ<EY). если был открыт уже //существующий раздел Реестр 30 5 /// </param > /// <returns>ERROR_SUCCES S сообщает об успешном вызове функции. ///В случае ошибки возвращается ненулевое значение /// </returns > [Dl 1 ImportС"coredl1.dl1 \ SetLastErro r = true) ] publi c stati c exter n in t RegCreateKeyE x ( UlntPt r hkey. Strin g IpSubKey. uin t Reserved. StringBuilde r lpClass. uin t dwOptions. KeyAcces s samDesired, IntPt r IpSecurityAttributes. re f UlntPt r phkResult. re f uin t lpdwDispositio n ); /// <summary > /// Функция для удаления раздела реестра /// </summary > /// <para m name="hkey">[in ] Дескриптор к удаляемому разделу или /// одна из ветвей реестра: HKCR. HKCU. HKLM. /// </рагат> /// <рагат name= nsubkeyName,'>[in 3 Имя удаляемого раздела. /// Нельзя использовать NUL L /// </param > /// <returns>ERROR_SUCCES S сообщает об успешном вызове функции. ///В случае ошибки возвращается ненулевое значение /// </retums > [DllImport("cored!1.dlГ. SetLastErro r - true) ] publi c stati c exter n in t RegDeleteKe y ( UlntPt r hkey. strin g subkeyNam e >: /// <summary > /// Функция для открытия заданного раздела реестра. /// </summary > /// <para m name-,*hkey">[in ] Дескриптор к открываемому разделу /// или одна из ветвей реестра HKCR. HKCU. HKLM.</param > /// <para m name-'*lpSubKey">[in ] Имя открываемого раздела продолжение & 306 Глава 13. Использование неуправляемого кода Листинг 13.1 7 {продолжение) /// </рагат> /// <рагага name="ulOptions">[in ] Зарезервированный параметр. ///Установлен равный 0</рагат> /// <para m name="samDesired">[in ] Не поддерживается. Установите /// в 0.</рагат> /// <para m name="phkResult">[out ] Переменная, получаемая от /// дескриптора открытого раздела. Если вы больше не нуждаетесь /// в дескрипторе, то вызовите функцию RegCloseKe y для его /// закрытия</рагаш> /// <returns>ERROR_SUCCES S сообщает об успешном вызове функции. /// В случае ошибки возвращается ненулевое значение /// </returns > [DmmportГcoredll.cnГ. SetLastErro r = true) ] publi c stati c exter n in t RegOpenKeyEx ( UlntPt r hkey. Strin g IpSubKey. uin t ulOptions. KeyAcces s samDesired, re f UlntPt r phkResul t ): /// <summary > /// Функция получает тип и данные из заданного раздела реестра /// </sunmiary > /// <para m name="hkey">[in ] Дескриптор к открываеному разделу /// или одна из ветвей реестра: HKCR. HKCU. HKLM.</param > /// <рагаш name»"lpValueName">[in ] Значение параметра. /// </рагаш> /// <рагаш name=!"lpReserved,,>[in ] Зарезервированный параметр. /// Установите в NULL.</param > /// <para m name="lpType">[out ] Тип данных /// </param > /// <para m name="lpData">[out ] Буфер, получающий данные. /// Данный параметр может быть NULL, если данные не требуются. /// </param > /// <para m name e"lpcbData">[in/out ] Размер буфера в байтах ///</рагат> /// <returns>ERROR_SUCCES S сообщает об успешном вызове функции. /// В случае ошибки возвращается ненулевое значение /// </returns > [DllImport("coredll.dll", SetLastErro r - true) ] Реестр 30 7 publi c stati c exter n in t RegQueryValueE x ( UlntPt r hkey, Strin g lpValueName, IntPt r lpReserved, re f KeyTyp e lpType, byte[ ] IpData. re f uin t lpcbDat a ): /// <summary > /// Функция создает параметр в разделе реестра. /// </summary > [DllImport("coredll.dll". SetLastErro r - true) ] publi c stati c exter n in t RegSetValueE x ( UlntPt r hkey. Strin g lpValueName, uin t Reserved. KeyTyp e dwType. byte[ ] IpData. uin t cbDat a ): [DllImport("coredll.dll". SetLastErro r « true) ] publi c stati c exter n in t RegDeleteValu e ( UlntPt r hkey, strin g valueNam e ); [DllImportC"coredll.dll". SetLastErro r = true) ] publi c stati c exter n in t RegCloseKe y ( UlntPt r hke y ): } } Наличие внешней клавиатуры С помощью класса Registry разработчик может получать или уста­
навливать значения параметров в реестре. Предположим, что нуж-
308 Глава 13. Использование неуправляемого кода но узнать, подключена ли к устройству внешняя клавиатура. За данную функцию отвечает параметр HasKeyboard в разделе HKEY_CUR-
RENT_USER\Software\Microsoft\Shell. Если данный параметр имеет единичное значение, то система работает с подключенной внешней клавиатурой. Если значение равно нулю, то клавиатуры нет. В лис­
тинге 13.18 приведен код, показывающий, как можно извлечь зна­
чение интересующего параметра. Листинг 13.18 privat e voi d butCheckKeyboard_Click(objec t sender, EventArg s e) { uin t chec k * 0: Registry.GetDWORDValue(Registry.HKCU. "SOFTWAREWMicrosoftWShell". "HasKeyboard". ref check); Ibllnfo.Tex t - Convert.ToBoo1ean(check).ToStn*ng(): > В этом примере используется функция-оболочка GetDWORDValue из класса Registry. Если же вы предпочитаете обходиться без функ­
ций-оболочек, а обращаться напрямую к функциям API, то пример можно переписать так, как показано в листинге 13.19. Листинг 13.19 privat e stati c boo! IsKeyboardO с uin t dwordResult: UlntPt r hke y - UlntPtr.Zero; tr y { in t resul t - Registry.RegOpenKeyEx(Registry.HKCU, "SOFTWAREWMicrosoftWSheir. 0. Reg i stry.KeyAccess.None. re f hkey): if (Registry.ERRORJUCCES S != result ) retur n false; byte[ ] byte s - nul1: uin t lengt h - 0; Reg i stry. KeyTyp e keyTyp e - Reg i stry. KeyType. None; resul t - Reg i stry. RegQueryValueEx ( hkey. "HasKeyboard", IntPtr.Zero, Реестр 30 9 re f keyType, null, re f length): if (Registry.ERR0R_SUCCES S != result ) retur n false; byte s • ne w byte[Marshal.SizeOf(typeof(uint))]; lengt h » (uint)bytes.Length; keyTyp e = Registry.KeyType.None: resul t = Registry.RegQueryValueEx(hkey. "HasKeyboard". IntPtr.Zero, re f keyType, bytes, re f length); 1f (Registry.ERROR_SUCCES S 1= result ) retur n false; dwordResul t =» BitConverter.ToUInt32(bytes. 0): retur n (dwordResul t = 1); } f i nal l y { i f (UlntPtr.Zer o !- hkey) { Reg i stry.RegCloseKey(hkey): } } } Теперь эту функцию можно вызвать в любом месте программы, как показано в листинге 13.20. Листинг 13.2 0 // Определяеи наличие внешней клавиатуры IMInfo.Tex t - IsKeyboardO.ToStringO; ВНИМАНИЕ Следует отметить, что при проверке примера на эмуляторе функ­
ция обнаруживает присутствие клавиатуры. Что, в общем-то, справедливо, так как с помощью обычной клавиатуры пользователь может вводить данные в программах, запущенных на эмуляторе. Информация о пользователе Также с помощью реестра можно узнать информацию о пользова­
теле устройства. За эту информацию отвечает параметр Owner в раз-
I 310 Глава 13. Использование неуправляемого кода деле HKEY_CURRENT_US?R\Control Panel \0wner. В листинге 13.21 приве­
ден код, который получает эту информацию. Листинг 13.2 1 privat e void butOwner_Click(objec t sender. EventArgs e) { stri n g strOwner - ""; Registry.GetStringValue(Registry.HKCU, "WControlPanelWOwner", "Owner". re f strOwner): lblInfo.Tex t - strOwner: } Наличие дополнительной клавиатуры Узнать о наличии в системе подключаемой клавиатуры можно с по­
мощью функции API или просмотрев значение соответствующего ключа в реестре. Использование реестра рассматривалось несколько раньше. В листинге 13.22 приведен код, который показывает, как ** "можно узнать о присутствии подключенной клавиатуры с помощью функции API GetKeyboardStatus. Листинг 13.2 2 /// <summary > /// Функция возвращает статус подключаемой клавиатуры и ее /// возможности. /// </summary > /// <returns>OyHKiiMf l возвращает битовую маску, /// показывающую присутствие клавиатуры и ее возможности /// </returns > [D1 1 Impor t ("coredll. dl 1 *') ] publi c stati c exter n uin t GetKeyboardStatusO: • /// <summary > - /// Показывает присутствие клавиатуры в системе /// </summary > publi c cons t uin t KBDI_KEYBOARD_PRESEN T - 0x0001; /// <summary > /// Показывает доступность клавиатуры. ?$ /// Данный бит может быть изменен функцией /// EnableHardwareKeyboar d /// </suromary > Виброзвонок 31 1 publi c cons t uin t KBDIJCEYBOARD_ENABLE D - 0x0002; /// <summary > /// Показывает наличие на клавиатуре клавиш ENTE R и ES C /// </summary > publi c cons t uin t KBDI_KEYBOARD_ENTER_ES C = 0x0004; /// <summary > /// Показывает наличие клавиш с буквами и цифрами /// </summary > publi c cons t uin t KBDI_KEYB0ARD_ALPHA_NU M = 0x0008; privat e voi d FormlJ_oad(objec t sender. EventArg s e) { MessageBox.Show("Наличие и доступность клавиатуры: " + IsKeyboard O .ToStringO); } privat e stati c tool IsKeyboardO { uin t flag s * KBDI_KEYBOARD_ENABLE D | KBDI_KEYBOARD_PRESENT: retur n ((GetKeyboardStatus O & flags ) = flags): } Виброзвонок Как правило, практически все современные модели мобильных те­
лефонов и смартфонов поддерживают функцию виброзвонка. Сле­
довательно, должны существовать функции для его включения и выключения. Для использования виброзвонка на смартфоне при­
меняются функции Vibrate, VibrateStop и VibrateGetDeviceCaps. При помощи этих функций разработчик может использовать воз­
можности виброзвонка в своих приложениях. Соответствующий код приведен в листинге 13.23. Листинг 13.23 /// <summary > /// Включает виброэвонок /// </summary > /// <returns>S_O K сообщает об успешном вызове функции. В случае /// ошибки возвращается E__FAI L ///</returns > [Dlllmportraygsheir) ] продолжение ?> 312 Глава 13. Использование неуправляемого кода Листинг 13.23 (продолжение) privat e stati c exter n in t Vibrate C in t cvn, IntPt r rgvn. uin t fRepeat. uin t dwTimeout); /// <summary > /// Останавливает виброзвонок /// </summary > /// <retums>S_O K сообщает об остановке виброзвонка. В случае /// ошибки возвращается EFAI L /// </returns > [DlllmportC-aygshell") ] privat e stati c exter n in t VibrateStopO: /// <summary > /// Получает сведения о возможности использования виброзвонка /// </summary > /// <para m name-"caps^Перечисление VIBRATEDEVICECAPS. /// определяющее возможности воспроизведения виброзвонка /// устройством. ///</param > [DlllmportC"aygshell") ] privat e stati c exter n in t VibrateGetDeviceCaps(VibrationCapabilitie s caps): /// <summary > /// Используется функцией VibrateGetDeviceCap s для определения /// возможности воспроизведения виброзвонка. /// </summary > publi c enu m VibrationCapabilitie s : int { VDC_Amplitude, VDC_Frequency. } privat e voi d mnuStopVibrateClickCobjec t sender. EventArg s e) { StopVibrateO; } privat e voi d mnuVibrate_Click(objec t sender, EventArg s e) { Виброзвонок 313 StartVibrateO; } /// <summary > /// Включаем виброзвонок /// </suiranary > .^ /// <returns> B успешном случае возвращается TRUE, в случае /// ошибки - FALSE.</returns > publi c stati c boo l StartVibrateC ) { in t resul t - VibrateCO, IntPtr.Zero. Oxffffffff. Oxffffffff): i f (resul t !* 0) { retur n false: } retur n true: } /// <summary > /// Останавливаем виброзвонок /// </summary > /// <returns> B успешной случае возвращается TRUE, в случае /// ошибки - FALSE.</returns > publi c stati c boo l StopVibrate O { in t resul t • VibrateStopO: if (resul t != 0) { retur n false: } retur n true; } ВНИМАНИЕ Приведенный код будет работать только на смартфонах. На фо­
румах можно найти сообщения, что на устройствах под управлени­
ем PocketPC Phone Edition этот пример не работает, даже если ука­
занное устройство поддерживает виброзвонок. Глава 14 Кирпичики .NET Compact Framework Итак, изучение .NET Compact Framework подходит к концу. Мы с вами рассмотрели различные аспекты программирования для карманных компьютеров и смартфонов. Напоследок я хочу предложить вам не­
сколько маленьких советов-кирпичиков, с помощью которых вы смо­
жете построить свое новое приложение. Часть этих советов уже встре­
чалась вам на страницах этой книги. Но, может быть, вы н е обратили на них внимания или не помните, где искать нужный вам кусок кода. Поэтому я отобрал часть этих советов и поместил их в отдельную гла­
ву. Эту главу можно рассматривать как справочный материал. Узнать версию .NET Compact Framework В папке Windows есть утилита CGACUTIL.EXE, которая выводит но­
мер версии установленной .NET Compact Framework. Если нужно программно узнать номер версии, то следует воспользоваться ко­
дом, приведенным в листинге 14.1. Листинг 14.1 // Уэнаеи версию установленной .NE T Compac t Framewor k txtAppDir.Tex t = Environment.Version.ToStringO; Узнать версию операционной системы Для получения версии операционной системы нужно вызвать уже свойство OSVersion, как показано в листинге 14.2. Листинг 14.2 // Узнаен версию операционной системы txtlnfo.Tex t - Environment.OSVersion.ToStringO; Получаемые значения приведены в следующем списке. Q 3.0 - соответствует Pocket PC 2000/2002. О 4.20 — соответствует Pocket PC 2003. Узнать имя устройства 31 5 • 4.21 - соответствует Pocket PC 2003 SE. Q 5.01 — соответствует Windows Mobile 5.0. Путь к запущенному приложению Иногда требуется узнать путь к файлу запущенного приложения. Для этого можно воспользоваться кодом из листинга 14.3. Листинг 14.3 using System.10; usi ng System.Ref1ect i on: txtAppDir.Tex t • Path. GetDi rectoryNameCAssemb l у .GetExecutingAssemblyC). GetModul e OEO]. FullyQuelJfiedName) .ToStringO; В этом примере после выбора соответствующего пункта в тексто­
вом поле будет отображен полный путь к файлу запущенного при* ложения. Специальные папки -г В главе, посвященной функциям Windows API, путь к специаль­
ным папкам отыскивался с помощью функции SHGetSpecialFol-
derPath. Сторонники управляемого кода могут воспользоваться ме­
тодом GetFolderPath, который появился в .NET Compact Framework 2.0. С помощью перечисления Environrnent .Sped al Folder можно по­
лучить пути к некоторым специальным папкам системы. Напри­
мер, чтобы получить путь к папке Start Up, можно воспользоваться кодом, приведенным в листинге 14.4. Листинг 14.4 txtInfo.Tex t = Envi ronrnent.GetFolderPath(Env i ronrnent.Spec i alFolder.Startup).T o t ri ngO; Узнать имя устройства Чтобы узнать имя устройства, на котором запущено приложение* достаточно вызвать метод GetHostName, как показано в листинге 14.5. л 1'Г i-
316 Глава 14. Кирпичики .NET Compact Framework Листинг 14.5 txtlnfo.Tex t - System.Net.Dns.GetHostNameO.ToStringO; Узнать ориентацию экрана Чтобы узнать, какой режим экрана установлен на данный момент, достаточно получить свойство Bounds, как показано в листинге 14.6. Листинг 14.6 txtlnfo.Tex t - Screen.PrimaryScreen.Bounds.Widt h + ":" + Screen.PrimaryScreen.Bounds.Height: Зная ширину и высоту экрана, уже не составит труда понять, какой режим отображения используется в данный момент. Открытие файлов по умолчанию Стоит обратить особое внимание на класс Process. С помощью дан­
ного класса очень удобно запускать любой файл, который будет открываться программой, сопоставленной с данным типом фай­
ла. Предположим, что необходимо воспроизвести музыкальный файл МРЗ, но при этом неизвестно, какая именно программа у пользователя отвечает за воспроизведение этих музыкальных файлов. В этом случае можно просто указать имя файла, и систе­
ма сама запустит соответствующую программу. Соответствующий код приведен в листинге 14.7. Листинг 14.7 System.Diagnostics.Process.Start("\\M y Music\\myfile.mp3"): ВНИМАНИЕ Класс System.Diagnostics.Process появился в .NET Compact Framework 2.0. Для версии .NET Compact Framework 1.0 нужно использовать функцию API ShellExecuteEx. Создание и отправка письма Существует очень легкий и быстрый способ создания и отправки письма с использованием технологии, применяемой на веб-страни­
цах. С помощью ключевого слова mail to создается заготовка пись-
Кнопки навигации 31 7 ма, в которой указываются автор сообщения, тема и текст письма. После этого запускается процесс, который в автоматическом режи­
ме запускает нужную почтовую программу и отсылает письмо, как показано в листинге 14.8. Листинг 14.8 privat e voi d butSendMail_Click(objec t sender. EventArg s e) { System.Diagnostics.Process.Star t ("mailto:alexander.klimoff@gmail.com?subject«Abou t Book". nul l ); } Кнопки навигации У карманных компьютеров есть кнопки навигации, позволяющие управлять объектами на экране. Это кнопки со стрелками и кнопка ввода. Чтобы узнать, на какую кнопку нажал пользователь, нужно пере­
определить событие OnKeyDown. Для создания тестового приложения нужно разместить на форме строку состояния, в которой будет ото­
бражаться название нажатой кнопки. Соответствующий код при­
веден в листинге 14.9. Листинг 14.9 protecte d overrid e voi d OnKeyDown(KeyEventArg s keyg ) { switc h (keyg.KeyData ) { cas e Keys.Left: sbaKeys.Tex t = "Left": break; cas e Keys.Right: sbaKeys.Tex t = "Right": break: cas e Keys.Down: sbaKeys.Tex t - "Down": break: cas e Keys.Up: sbaKeys.Tex t = "Up": break: cas e Keys.Return: sbaKeys.Tex t - "Return": break; default: break; > } % Послесловие Что дальше? Вот и подошла к концу книга о программировании для мобиль­
ных устройств с помощью .NET Compact Framework. Надеюсь, я смог рассказать об основных особенностях программирования в этой среде, и вам будет легко продолжить изучение этой техно­
логии. Жизнь не стоит на месте, и постоянно выпускаются новые релизы эмуляторов, обновлений SDK и новых утилит. Компания Microsoft уже работает над новой мобильной версией Windows, которая должна прийти на смену Windows Mobile 5.0, и обещает выпустить ее в конце 2006 года. Новая операционная система на­
зывается Crossbow. По заявлениям разработчиков, в ней будут представлены расширенные средства синхронизации с програм­
мами Office 2007 и Exchange 12. Также в состав операционной системы войдет новая программа Office Communicator, обладающая широкими возможностями об­
мена информацией через мгновенные сообщения, голосовую связь и видео. Также появилась информация, что после Crossbow будет выпущена еще одна новая платформа под кодовым названием Photon. Главная особенность этой системы заключается в том, что ее можно будет использовать как на смартфонах, так и на карман­
ных компьютерах. На сегодняшний день, по оценкам экспертов, компания Microsoft удерживает примерно 16% рынка мобильных операционных систем. Лидером в этом сегменте является операци­
онная система Symbian, на долю которой приходится 63%. Но есть все предпосылки, что в ближайшем будущем эти цифры могут из­
мениться в сторону увеличения доли Windows Mobile. Полезные ресурсы Напоследок хотелось бы привести несколько полезных ссылок на различные ресурсы в Сети, которые могут оказаться полезными для разработчиков. Полезные ресурсы 31 9 .NET Compact Framework 2.0 Redistributable Если вы пишете программы с использованием .NET Compact Framework 2.0, то при распространении программы надо либо вклю­
чать в состав вашего установочного файла все необходимые биб­
лиотеки, либо предложить пользователю самостоятельно уста­
новить .NET Compact Framework 2.0. В этом случае достаточно бу­
дет выкладывать на сайте только сам исполняемый файл програм­
мы. Загрузить последнюю версию .NET Compact Framework 2.0 можно по адресу www.microsoft.com/downloads/details.aspx?familyid= -9655156b~356b-4a2c-857c-e62f50ae9a55&displaytang-en. Microsoft ActiveSync 4.1 Программа синхронизации ActiveSync используется для передачи файлов между настольным и карманным компьютерами. Последнюю версию программы можно скачать по адресу www.microsoft.com/ downloads/details.aspx?FamilyId=3926AlE0-CABD-4A45-8E5B-
F938D9A69D8D&displaylang-ru. Русская версия эмулятора для Windows Mobile 5.0 Smartphone Помимо стандартного эмулятора для смартфона под управлением системы Windows Mobile 5.0 на английском языке, вы можете ска­
чать и локализованную версию, которая располагается по адресу www.microsoft.com/downloads/details.aspx?familyid=52FED581-8F8D-
-4C46-9966-4832098191B7&displaylang=rii. Русская версия эмулятора для КПК под управлением Windows Mobile 5.0 Pocket PC Также можно использовать русскую версию эмулятора для КПК под управлением операционной системы Windows Mobile 5.0, ко­
торую можно найти по адресу www.microsoft.com/downloads/ detaUs.aspx?FamilyID=eec33ae3-cl29-4c25-abaa-18e8e842178f&dis-
playlang=en&Hash=S3HN6BD. Сайт Роба Майлза На страницах книги я использовал примеры разработчика Роба Майлза (Rob Miles). Он является автором многих статей, которые Послесловие можно найти в документации MSDN. Также у него есть свой сайт www.robmiles.com, который стоит посетить. Сайт Кристиана Форсберга Кристиан Форсберг (Christian Forsberg), чьи примеры я использо­
вал в этой книге, тоже ведет свой сайт www.businessanyplace.net, на котором можно найти много полезной информации. OpenNETCF.org Один из самых популярных сайтов, посвященных программиро­
ванию при помощи .NET Compact Framework, расположился по адресу www.opennetcf.org. Особый интерес вызывают представлен­
ные на сайте статьи и исходные коды приложений. Также на сай­
те находятся очень интересные блоги опытных программистбв, ко­
торые делятся своими мыслями, разработками и примерами. Например, я частенько заглядываю на блог Алекса Яхнина по ад­
ресу blog.opennetcf.org/ayakhnin, на котором не раз находил инте­
ресные примеры. Часть этих примеров я также использовал в кни­
ге с разрешения автора. Кстати, Алексу можно задать вопрос на русском языке! Google К сожалению, я не могу упомянуть все сайты, которые посвящены программированию с помощью .NET Compact Framework. Для по­
иска новой информации можно воспользоваться услугами любой поисковой системы. Я рекомендую вам для этих целей использо­
вать поисковую службу Google (www.google.com), которая осуще­
ствляет поиск не только по сайтам, но и по группам новостей. АНТИВИРУС И Г О Р Я ДА Н ИЛ О ВА тт www.drweb.ru 111 1 ШИН! ИНГ С помощью этой книги вы научитесь создавать самые разнообразные приложения для карманных компьютеров и смартфонов. В издании рассмотрены принципы использования технологии программирования .NET Compact Framework, описан процесс создания приложений различного типа. Кроме того, здесь вы найдете подробные примеры и множество советов по использованию .NET Compact Framework. Тема: Общие вопросы программирования С^ППТЕР Заказ книг: 197198, Санкт-Петербург а/я 619 тел.: (812) 703-73-74, postbook@piter.co m 61093, Харьков-93, а/я 9130 тел/ (057) 712-27-05, piter@kharkov.piter.co m www.pi t er.co m — вся информация о книгах и веб-магазин Этот файл был взят с сайта http://all-ebooks.com Данный файл представлен исключительно в ознакомительных целях. После ознакомления с содержанием данного файла Вам следует его незамедлительно удалить. Сохраняя данный файл вы несете ответственность в соответствии с законодательством. Любое коммерческое и иное использование кроме предварительног о ознакомления запрещено. Публикация данного документа не преследует за собой никакой коммерческой выгоды. Эта книга способствует профессиональному росту читателей и является рекламой бумажных изданий. Все авторские права принадлежат их уважаемым владельцам. Если Вы являетесь автором данной книги и её распространение ущемляет Ваши авторские права или если Вы хотите внести изменения в данный документ или опубликовать новую книгу свяжитесь с нами по email. 
Автор
dima202
dima202579   документов Отправить письмо
Документ
Категория
Информатика
Просмотров
1 248
Размер файла
62 176 Кб
Теги
2007
1/--страниц
Пожаловаться на содержимое документа