Дронов В. Самоучитель silverlight 3 (2010)

461
Âëàäèìèð Äðîíîâ Ñàíêò-Ïåòåðáóðã «ÁÕÂ-Ïåòåðáóðã» 2010

Upload: aivar-filonov

Post on 27-Jan-2016

250 views

Category:

Documents


11 download

DESCRIPTION

Дронов В. Самоучитель Silverlight 3 (2010)

TRANSCRIPT

Page 1: Дронов В. Самоучитель Silverlight 3 (2010)

Âëàäèìèð Äðîíîâ

Ñàíêò-Ïåòåðáóðã

«ÁÕÂ-Ïåòåðáóðã»

2010

Page 2: Дронов В. Самоучитель Silverlight 3 (2010)

УДК 681.3.06 ББК 32.973.26-018.2 Д75

Дронов В. А.

Д75 Самоучитель Silverlight 3. — СПб.: БХВ-Петербург, 2010. — 464 с.: ил.

ISBN 978-5-9775-0514-7

Доступно описано создание клиентских Web-приложений на платформе Microsoft Silverlight 3. На практических примерах показано, как самостоя-тельно создавать приложения с богатой функциональностью и развитым интерфейсом, используя при этом исключительно бесплатные инструмен- ты. Кратко даны основы Web-программирования, подробно рассмотрены принципы Silverlight-программирования. Рассказано о среде разработки Microsoft Visual Web Developer 2008 Express Edition, языках программиро-вания XAML и C#, с помощью которых создаются, соответственно, интер-фейс и логика Silverlight-приложения. Перечислены основные компоненты Silverlight и объяснено их использование. Дан краткий курс работы с дан-ными, локальными и удаленными файлами и Web-службами, базами дан-ных. Описаны графические, анимационные и мультимедийные возмож- ности Silverlight. Приведены рекомендации по распространению готовых Silverlight-приложений.

Для Web-программистов

УДК 681.3.06

ББК 32.973.26-018.2

Группа подготовки издания:

Главный редактор Екатерина Кондукова

Зам. главного редактора Евгений Рыбаков

Зав. редакцией Григорий Добин

Редактор Екатерина Капалыгина

Компьютерная верстка Ольги Сергиенко

Корректор Зинаида Дмитриева

Дизайн серии Инны Тачиной

Оформление обложки Елены Беляевой

Зав. производством Николай Тверских

Лицензия ИД № 02429 от 24.07.00. Подписано в печать 30.12.09. Формат 70×100

1/16.

Печать офсетная. Усл. печ. л. 37,41.

Тираж 2000 экз. Заказ № "БХВ-Петербург", 190005, Санкт-Петербург, Измайловский пр., 29.

Санитарно-эпидемиологическое заключение на продукцию № 77.99.60.953.Д.005770.05.09 от 26.05.2009 г. выдано Федеральной службой

по надзору в сфере защиты прав потребителей и благополучия человека.

Отпечатано с готовых диапозитивов в ГУП "Типография "Наука"

199034, Санкт-Петербург, 9 линия, 12

ISBN 978-5-9775-0514-7 © Дронов В. А., 2010

© Оформление, издательство "БХВ-Петербург", 2010

Page 3: Дронов В. Самоучитель Silverlight 3 (2010)

Оглавление

Введение .................................................................................................................. 1

Интернет-программирование в массы! .............................................................................. 1

Silverlight как она есть ......................................................................................................... 2

Что будет в этой книге ......................................................................................................... 2

Что нам понадобится ........................................................................................................... 3

Типографские соглашения .................................................................................................. 4

Благодарности ...................................................................................................................... 5

ЧАСТЬ I. ВВЕДЕНИЕ В SILVERLIGHT. НАШЕ ПЕРВОЕ ПРИЛОЖЕНИЕ ..................................................................... 7

Глава 1. Что такое Silverlight .............................................................................. 9

Этапы развития WWW ........................................................................................................ 9

Этап первый: обычные Web-страницы ...................................................................... 9

Этап второй: серверные Web-приложения .............................................................. 13

Этап третий: клиентские Web-приложения ............................................................. 14

Программные платформы для создания клиентских Web-приложений ....................... 16

HTML+CSS+JavaScript .............................................................................................. 16

Adobe Flash ................................................................................................................. 18

Sun Java ....................................................................................................................... 19

Microsoft Silverlight ............................................................................................................ 20

Что дальше? ........................................................................................................................ 21

Глава 2. Основные понятия и принципы Silverlight .................................... 22

Интерфейс и логика приложения ...................................................................................... 22

Интерфейс Silverlight-приложения ................................................................................... 23

Страницы .................................................................................................................... 23

Как страницы Silverlight-приложения выводятся на Web-страницу ...................... 24

Компоненты ................................................................................................................ 25

Контейнеры ................................................................................................................. 26

Page 4: Дронов В. Самоучитель Silverlight 3 (2010)

IV Оглавление

Логика Silverlight-приложения .......................................................................................... 28

Как работает Silverlight-приложение. События ....................................................... 28

Объекты и классы. Свойства и методы .................................................................... 29

Классы — родители и потомки. Иерархия классов ................................................. 31

Классы, из которых состоит Silverlight-приложение .............................................. 32

Языки программирования для создания Silverlight-приложений................................... 33

Что дальше? ........................................................................................................................ 34

Глава 3. Наше первое Silverlight-приложение ............................................... 35

Microsoft Visual Web Developer 2008 Express Edition ..................................................... 35

Понятие проекта. Решение ................................................................................................ 39

Создание Silverlight-приложения ...................................................................................... 40

Окна документов ................................................................................................................ 42

Панель Solution Explorer .................................................................................................... 43

Создание интерфейса Silverlight-приложения ................................................................. 44

Введение в язык разметки XAML ............................................................................. 44

Помещение компонентов на страницу. Панель Toolbox ......................................... 48

Компиляция и запуск Silverlight-приложения .......................................................... 52

Работа с контейнером "таблица"............................................................................... 53

Создание логики Silverlight-приложения ......................................................................... 56

Имена компонентов ................................................................................................... 57

Привязка обработчиков к событиям компонентов .................................................. 58

Введение в язык программирования C# ................................................................... 59

Введение в язык программирования C#, продолжение .......................................... 62

Выявление ошибок ............................................................................................................. 65

Файловые операции в Visual Web Developer 2008 .......................................................... 66

Что дальше? ........................................................................................................................ 67

ЧАСТЬ II. СБОРКИ, ПРОСТРАНСТВА ИМЕН, СТРАНИЦЫ, КОМПОНЕНТЫ И РЕСУРСЫ ........................................................................ 69

Глава 4. Сборки и пространства имен ............................................................ 71

Файлы, из которых состоит проект .................................................................................. 71

Сборки ................................................................................................................................. 73

Библиотеки ......................................................................................................................... 74

Пространства имен............................................................................................................. 75

Понятие пространства имен ...................................................................................... 75

Полные имена пространств имен и классов............................................................. 77

Отображение пространств имен ............................................................................... 78

Пространства имен в XAML-коде. Префиксы ......................................................... 79

Что дальше? ........................................................................................................................ 81

Глава 5. Страницы и контейнеры ................................................................... 82

Контейнеры ........................................................................................................................ 82

Контейнер "таблица" .................................................................................................. 82

Page 5: Дронов В. Самоучитель Silverlight 3 (2010)

Оглавление V

Контейнер "стопка" .................................................................................................... 89

Контейнер "холст" ...................................................................................................... 89

Страница ............................................................................................................................. 91

Что дальше? ........................................................................................................................ 92

Глава 6. Основные компоненты ...................................................................... 93

Надпись TextBlock .............................................................................................................. 93

Использование компонента TextBlock для вывода форматированного текста ..... 97

Поле ввода TextBox ............................................................................................................ 99

Поле ввода пароля PasswordBox ..................................................................................... 102

Кнопка Button ................................................................................................................... 103

Флажок CheckBox ............................................................................................................. 105

Переключатель RadioButton ............................................................................................ 106

Список ListBox .................................................................................................................. 107

Раскрывающийся список ComboBox .............................................................................. 109

Календарь Calendar.......................................................................................................... 110

Всплывающий календарь DatePicker ............................................................................. 111

Регулятор Slider ................................................................................................................ 111

Индикатор прогресса ProgressBar .................................................................................. 112

Панель с прокруткой ScrollViewer .................................................................................. 113

Блокнот с вкладками TabControl .................................................................................... 114

Пример использования компонентов ............................................................................. 116

Что дальше? ...................................................................................................................... 119

Глава 7. Вывод графики и мультимедийных данных ............................... 120

Вывод графики ................................................................................................................. 120

Компонент Image ...................................................................................................... 121

Программная загрузка изображений ...................................................................... 122

Вывод мультимедийных данных .................................................................................... 124

Компонент MediaElement ........................................................................................ 124

Программная загрузка мультимедийных данных .................................................. 126

Что дальше? ...................................................................................................................... 127

Глава 8. Ресурсы сборки .................................................................................. 128

Понятие ресурсов сборки ................................................................................................ 128

Работа с ресурсами сборки .............................................................................................. 129

Включенные и невключенные ресурсы сборки ............................................................. 130

Как обрабатываются ресурсы сборки ............................................................................. 132

Использование папок для организации ресурсов .......................................................... 132

Что дальше? ...................................................................................................................... 134

ЧАСТЬ III. ЯЗЫК C# ....................................................................................... 135

Глава 9. Основные конструкции языка C# ................................................. 137

Выражения, переменные, операторы, операнды и ключевые слова............................ 137

Page 6: Дронов В. Самоучитель Silverlight 3 (2010)

VI Оглавление

Типы данных .................................................................................................................... 139

Типы данных C#, классы и структуры Silverlight .................................................. 140

Строковый ................................................................................................................. 140

Целочисленный ........................................................................................................ 142

Число с плавающей точкой ..................................................................................... 142

Логический ............................................................................................................... 143

Символьный .............................................................................................................. 143

Значимые типы ......................................................................................................... 144

Преобразование типов ............................................................................................. 144

Неявное преобразование типов ...................................................................... 144

Явное преобразование типов.......................................................................... 145

Переменные ...................................................................................................................... 146

Именование переменных ......................................................................................... 146

Объявление переменных. Строгая типизация ........................................................ 147

Доступность переменных ........................................................................................ 148

Переменные, хранящие значения параметров метода .......................................... 148

Операторы ........................................................................................................................ 148

Арифметические операторы .................................................................................... 149

Оператор конкатенации ........................................................................................... 150

Операторы присваивания ........................................................................................ 150

Операторы сравнения .............................................................................................. 151

Логические операторы ............................................................................................. 152

Условный оператор .................................................................................................. 153

Приоритет операторов ............................................................................................. 153

Сложные выражения ........................................................................................................ 155

Блоки ......................................................................................................................... 155

Условные выражения ............................................................................................... 155

Выражения выбора ................................................................................................... 157

Циклы ........................................................................................................................ 158

Цикл со счетчиком .......................................................................................... 158

Цикл с постусловием ...................................................................................... 160

Цикл с предусловием ...................................................................................... 161

Прерывание и перезапуск цикла .................................................................... 161

Безусловный переход ....................................................................................................... 162

Массивы ............................................................................................................................ 163

Цикл просмотра ................................................................................................................ 165

Комментарии .................................................................................................................... 166

Что дальше? ...................................................................................................................... 167

Глава 10. Сложные типы данных C# ............................................................ 168

Классы и объекты ............................................................................................................. 168

Элементы класса ...................................................................................................... 169

Поля .................................................................................................................. 169

Методы ............................................................................................................. 169

Свойства ........................................................................................................... 169

Page 7: Дронов В. Самоучитель Silverlight 3 (2010)

Оглавление VII

События ........................................................................................................... 170

Именованные константы ................................................................................ 171

Вложенные типы ............................................................................................. 171

Статические элементы класса ........................................................................ 171

Наследование ............................................................................................................ 172

Работа с объектами и классами ............................................................................... 172

Создание объектов .......................................................................................... 172

Ссылочные типы ............................................................................................. 173

Работа с элементами объекта и статическими элементами класса ............. 174

Операторы проверки типа и преобразования ссылочных типов ................. 176

Значение null .................................................................................................... 177

Уничтожение объектов ................................................................................... 177

Полезные встроенные классы Silverlight ................................................................ 177

Класс Object ..................................................................................................... 178

Класс String ...................................................................................................... 178

Класс Math ....................................................................................................... 179

Создание собственных классов ............................................................................... 180

Создание самих классов ................................................................................. 181

Создание полей ................................................................................................ 182

Создание методов ............................................................................................ 183

Создание конструкторов ................................................................................. 186

Создание свойств ............................................................................................ 187

Создание именованных констант................................................................... 189

Структуры ......................................................................................................................... 190

Работа со структурами ............................................................................................. 190

Полезные встроенные структуры Silverlight .......................................................... 191

Int16, Int32, Int64, UInt16, UInt32 и UInt64 ................................................... 191

Double и Single ................................................................................................. 191

Decimal ............................................................................................................. 192

DateTime ........................................................................................................... 193

TimeSpan .......................................................................................................... 195

Создание собственных структур ............................................................................. 196

Интерфейсы ...................................................................................................................... 196

Перечисления ................................................................................................................... 199

Что дальше? ...................................................................................................................... 199

Глава 11. Коллекции ........................................................................................ 200

Понятие коллекции .......................................................................................................... 200

Обобщенные типы ........................................................................................................... 201

Коллекция List .................................................................................................................. 201

Создание объекта коллекции List ............................................................................ 201

Получение сведений о коллекции ........................................................................... 202

Добавление и удаление элементов коллекции ....................................................... 202

Получение элемента коллекции .............................................................................. 203

Page 8: Дронов В. Самоучитель Silverlight 3 (2010)

VIII Оглавление

Поиск нужного элемента коллекции ...................................................................... 204

Коллекция наших собственных объектов .............................................................. 205

Словарь Dictionary ........................................................................................................... 207

Создание объекта словаря Dictionary ..................................................................... 207

Получение сведений о словаре ............................................................................... 207

Добавление и удаление элементов словаря ........................................................... 207

Получение элемента словаря ................................................................................... 208

Поиск нужного элемента словаря ........................................................................... 209

Специализированные коллекции .................................................................................... 209

Очередь Queue .......................................................................................................... 210

Стек Stack .................................................................................................................. 210

Свойства компонентов, являющиеся коллекциями ............................................... 211

Что дальше? ...................................................................................................................... 212

Глава 12. Исключения ..................................................................................... 213

Понятие исключения........................................................................................................ 213

Обработка исключений .................................................................................................... 214

Встроенные классы исключений .................................................................................... 215

Обработка исключений .................................................................................................... 216

Реагирование на само исключение ......................................................................... 216

Выполнение завершающих операций..................................................................... 218

Генерирование исключений ............................................................................................ 219

Что дальше? ...................................................................................................................... 220

ЧАСТЬ IV. ПРИВЯЗКА КОМПОНЕНТОВ К ДАННЫМ. LINQ ............ 221

Глава 13. Привязка компонентов к данным ............................................... 223

Понятие привязки............................................................................................................. 223

Привязка к свойству объекта .......................................................................................... 224

Помещение на Silverlight-страницу произвольных объектов. Ресурсы

страницы и ресурсы приложения ............................................................................ 226

Создание самой привязки ........................................................................................ 228

Уведомление компонента об изменении данных .................................................. 230

Проверка вводимых данных .................................................................................... 232

Привязка компонента к компоненту ....................................................................... 233

Использование конвертеров .................................................................................... 234

Привязка к коллекции ...................................................................................................... 236

Привязка к коллекции элементарных типов .......................................................... 236

Привязка к коллекции объектов .............................................................................. 237

Вывод в пункте списка сразу нескольких значений. Шаблоны ........................... 239

Отображение связанных данных............................................................................. 240

Использование таблицы DataGrid для вывода данных из коллекции ................. 241

Реализация правки данных в таблице DataGrid .................................................... 246

Использование шаблонов ввода в таблице DataGrid ............................................ 247

Что дальше? ...................................................................................................................... 248

Page 9: Дронов В. Самоучитель Silverlight 3 (2010)

Оглавление IX

Глава 14. LINQ ................................................................................................... 249

Введение в запросы и язык LINQ ................................................................................... 249

Выборка одного значения ............................................................................................... 250

Выборка нескольких значений. Анонимные типы ........................................................ 253

Фильтрация данных ......................................................................................................... 254

Сортировка данных .......................................................................................................... 255

Связывание данных .......................................................................................................... 256

Группировка данных ........................................................................................................ 258

Получение агрегатных данных ....................................................................................... 261

Использование подзапросов и вложенных запросов.

Временные переменные запроса .................................................................................... 262

Использование временных переменных запроса для хранения

произвольных данных ...................................................................................................... 264

Открытое связывание данных ......................................................................................... 265

Что дальше? ...................................................................................................................... 267

ЧАСТЬ V. ГРАФИЧЕСКИЕ ВОЗМОЖНОСТИ SILVERLIGHT. МНОГОСТРАНИЧНЫЕ ПРИЛОЖЕНИЯ .................................................. 269

Глава 15. Графика ............................................................................................. 271

Рисование элементарных геометрических фигур ......................................................... 271

Рисование полигонов ....................................................................................................... 274

Рисование сложных фигур. Пути .................................................................................... 276

Рисование путей в виде элементарных фигур ....................................................... 276

Комбинирование элементарных путей. Группы путей ......................................... 277

Рисование сложных путей ....................................................................................... 279

Компонент Border ............................................................................................................ 284

Работа с цветом ................................................................................................................ 285

Сплошные цвета ....................................................................................................... 285

Градиентные цвета ................................................................................................... 286

Графические цвета ................................................................................................... 291

Видеоцвет ................................................................................................................. 293

Цвета как ресурсы страницы и приложения .......................................................... 294

Что дальше? ...................................................................................................................... 294

Глава 16. Эффекты и преобразования .......................................................... 295

Эффекты ........................................................................................................................... 295

Обрезка компонента ................................................................................................. 295

Маска полупрозрачности ......................................................................................... 296

Настоящие эффекты — размытие и тень ............................................................... 298

Преобразования ................................................................................................................ 299

Двумерные преобразования .................................................................................... 299

Комбинирование двумерных преобразований. Группы преобразований ........... 303

Трехмерные преобразования ................................................................................... 304

Что дальше? ...................................................................................................................... 305

Page 10: Дронов В. Самоучитель Silverlight 3 (2010)

X Оглавление

Глава 17. Анимация .......................................................................................... 306

Основные понятия Silverlight-анимации ........................................................................ 306

Трансформационная анимация ....................................................................................... 308

Покадровая анимация ...................................................................................................... 313

Составная анимация ......................................................................................................... 317

Программное управление анимацией ............................................................................. 319

Что дальше? ...................................................................................................................... 320

Глава 18. Многостраничные приложения ................................................... 321

Принципы создания многостраничных приложений .................................................... 321

Простейшее многостраничное приложение .................................................................. 322

Создание фрейма ...................................................................................................... 323

Создание подстраниц ............................................................................................... 324

Навигация ................................................................................................................. 326

Передача данных между подстраницами ............................................................... 328

Компонент-гиперссылка (HyperlinkButton) .................................................................... 330

Навигация на другие Web-страницы .............................................................................. 331

Что дальше? ...................................................................................................................... 331

Глава 19. Вторичные окна............................................................................... 332

Диалоговые окна .............................................................................................................. 332

Введение в диалоговые окна ................................................................................... 332

Создание диалогового окна ..................................................................................... 334

Открытие и закрытие диалогового окна ................................................................ 336

Передача данных в диалоговое окно и из него ...................................................... 338

Окна-предупреждения ..................................................................................................... 340

Что дальше? ...................................................................................................................... 342

ЧАСТЬ VI. РАБОТА С ФАЙЛАМИ И WEB-СЛУЖБАМИ ..................... 343

Глава 20. Работа с локальными файлами .................................................... 345

Изолированное хранилище ............................................................................................. 346

Открытие изолированного хранилища ................................................................... 346

Создание папок ......................................................................................................... 347

Создание и открытие файлов .................................................................................. 347

Запись в файл ............................................................................................................ 349

Чтение из файла ........................................................................................................ 350

Закрытие потока и файла ......................................................................................... 352

Проверка существования файлов и папок .............................................................. 353

Удаление файлов и папок ........................................................................................ 354

Увеличение квоты изолированного хранилища .................................................... 354

Удаление изолированного хранилища ................................................................... 356

Закрытие изолированного хранилища .................................................................... 356

Полный код примеров работы с изолированным хранилищем ............................ 356

Page 11: Дронов В. Самоучитель Silverlight 3 (2010)

Оглавление XI

Работа со сторонними файлами ...................................................................................... 358

Сохранение данных в стороннем файле ................................................................. 359

Загрузка данных из стороннего файла ................................................................... 361

Что дальше? ...................................................................................................................... 363

Глава 21. Работа с удаленными файлами .................................................... 364

Использование невключенных ресурсов........................................................................ 364

Программная загрузка файлов по сети ........................................................................... 365

Класс WebClient ........................................................................................................ 365

Запуск загрузки файла ............................................................................................. 366

Окончание загрузки файла и его обработка ........................................................... 367

Отслеживание процесса загрузки файла ................................................................ 369

Прерывание загрузки файла .................................................................................... 370

Обработка ошибок ................................................................................................... 370

Пример простейшего просмотрщика изображений .............................................. 371

Что дальше? ...................................................................................................................... 374

Глава 22. Работа с Web-службами ................................................................. 375

Web-службы ..................................................................................................................... 375

Базы данных ..................................................................................................................... 376

Создание базы данных ..................................................................................................... 378

Создание самой базы данных .................................................................................. 378

Создание таблиц ....................................................................................................... 380

Создание связи ......................................................................................................... 383

Занесение данных в таблицы ................................................................................... 386

Создание Web-службы ..................................................................................................... 387

Создание решения и Web-сайта .............................................................................. 387

Создание модели данных ......................................................................................... 388

Создание самой Web-службы .................................................................................. 390

Создание клиентского приложения ................................................................................ 392

Особенности создания Silverlight-приложения, работающего с Web-службой .. 392

Подключение Silverlight-приложения к Web-службе ............................................ 394

Загрузка данных из Web-службы ............................................................................ 395

Особенности запуска Silverlight-приложения, работающего с Web-службой .... 398

Создание LINQ-запросов к Web-службе ................................................................ 399

Загрузка данных из вторичной коллекции ............................................................. 401

Реализация добавления, правки и удаления данных ............................................. 403

Добавление данных во вторичную коллекцию ...................................................... 406

Что дальше? ...................................................................................................................... 409

ЧАСТЬ VII. ПОСЛЕДНИЕ ШТРИХИ .......................................................... 411

Глава 23. Полезные мелочи ............................................................................ 413

Привязка к данным сразу нескольких компонентов ..................................................... 413

Всплывающие подсказки для компонентов ................................................................... 414

Page 12: Дронов В. Самоучитель Silverlight 3 (2010)

XII Оглавление

Реализация полноэкранного режима .............................................................................. 415

Хранение настроек приложения ..................................................................................... 418

Что дальше? ...................................................................................................................... 420

Глава 24. Распространение Silverlight-приложений ................................... 421

Версии Silverlight-приложения. Отладочная и распространяемая версии .................. 421

Создание распространяемой версии приложения ......................................................... 422

Файлы, составляющие приложение ................................................................................ 423

Параметры приложения ................................................................................................... 426

Вставка Silverlight-приложения в Web-страницу .......................................................... 429

Независимые Silverlight-приложения ............................................................................. 430

Создание независимых Silverlight-приложений ..................................................... 430

Установка и использование независимых Silverlight-приложений ...................... 432

Заключение ......................................................................................................... 435

Предметный указатель .................................................................................... 437

Page 13: Дронов В. Самоучитель Silverlight 3 (2010)

Введение

Внимание-внимание! Вышла Microsoft Silverlight 3! Новая версия известной

платформы для создания клиентских Web-приложений! Пользователи и раз-

работчики — это для вас!

Что за шум? Что вышло? Какая такая Silverlight? Кому, зачем она нужна?

И что это за клиентские Web-приложения?

Интернет-программирование в массы!

Интернет продолжает свое победное шествие по планете, проникая в самые

потаенные ее уголки. Гималаи, Антарктида, Огненная Земля, Острова Зеле-

ного Мыса, остров Кергелен, остров Пасхи, острова Гренландия и Новая

Гвинея уже подключены к Всемирной Сети. Опутанная проводами планета...

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

Технологии сменяют друг друга с такой быстротой, что не успеваешь запом-

нить их названия. Казалось, совсем недавно Web-страничка с зеленым тек-

стом на фиолетовом фоне и парой корявых картинок была последним писком

компьютерной моды, знаком принадлежности к сообществу интернет-гуру...

И вот — на тебе! — появились Web-приложения, настоящие программы, ра-

ботающие в Web-обозревателе, прямо на Web-странице. Web-дизайнеры

спешно осваивают профессию Web-программистов, программисты торопятся

приобщиться к Web-дизайну, фирмы-разработчики выпускают все более и

более мощные программные средства, чтобы помочь и тем, и другим. А поль-

зователи — используют, конечно (чем им еще заниматься...).

Программных платформ для создания Web-приложений сейчас довольно

много. Раздвинув могучими плечами конкурентов, шагает по планете гигант

Adobe Flash. За ним поспевает вечный догоняющий, крепыш Sun Java (ныне

принадлежит корпорации Oracle). Завернувшись в лоскутное, сшитое из не-

Page 14: Дронов В. Самоучитель Silverlight 3 (2010)

2 Введение

скольких традиционных интернет-технологий пончо, неутомимо шагает сле-

дом "абориген" интернет-просторов HTML+CSS+JavaScript. В самом конце,

прихрамывая, бегут менее популярные Mozilla XUL, Microsoft ActiveX и др.

Толчея и гвалт — как на ярмарке.

И вот под гром фанфар и треск фейерверков появляется амбициозный нови-

чок — Microsoft Silverlight. Появляется и сразу же начинает отвоевывать

у "старожилов" территории, симпатии разработчиков и внимание пользовате-

лей. За ним бежит толпа поклонников, рассматривают его издали и вблизи,

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

Один из этой пестрой толпы — автор данной книги. Пристроившись побли-

же, он пытается понять, что собой представляет новая программная платфор-

ма, что она может дать, где пригодиться, что в ней хорошо и что плохо.

Silverlight как она есть

Подробный разбор Silverlight "по косточкам" и сравнение ее с конкурентами

будет в главе 1. Сейчас же просто пробежимся по важнейшим ее достоинст-

вам.

� Быстрое и простое создание клиентских Web-приложений с богатым, раз-

витым интерфейсом.

� Большой набор встроенных компонентов и простота создания новых.

� Мощные средства работы с данными, как локальными (хранящимися на

компьютере клиента), так и удаленными.

� Поддержка графики, анимации, звука и видео.

� Компактность и высокое быстродействие готовых приложений.

� Широкая поддержка другими программными продуктами Microsoft, в ча-

стности SharePoint 2010.

� Для создания приложений можно использовать исключительно бесплат-

ные инструменты.

Последний пункт, наверно, стоило поставить первым. В самом деле — это

достоинство (по сравнению с тем же Flash).

Что будет в этой книге

В книге мы познакомимся с платформой Silverlight и научимся писать про-

стейшие Silverlight-приложения. Они будут обрабатывать данные, выводить

графику и видео, работать с файлами, локальными и удаленными, взаимодей-

Page 15: Дронов В. Самоучитель Silverlight 3 (2010)

Введение 3

ствовать с серверными приложениями (Web-службами) и делать еще много

чего.

А теперь — внимание! Самоучитель — книга небольшая и нетолстая. Поэто-му автор не будет касаться в ней наиболее сложных для реализации и не са-мых нужных в практике начинающего программиста возможностей Silverlight. Кроме того, описание некоторых моментов автор будет сознатель-но утрировать — ради простоты.

Что нам понадобится

Сразу проведем ревизию программного обеспечения, которое нам понадо-бится. К сожалению, его придется загружать отдельно — в состав Windows оно не входит.

1. Ни один Web-обозреватель не поддерживает выполнение Silverlight-приложений. Поэтому нам понадобится отдельная программа — среда исполнения Silverlight. Ее можно найти на Web-странице http://

go.microsoft.com/fwlink/?LinkId=128526.

2. Microsoft Visual Web Developer 2008 Express Edition. Среда разработки раз-личных Web-решений, в том числе и Silverlight-приложений.

Требуется английская версия Visual Web Developer 2008 с установленным паке-том обновления SP1. На любую другую его версию Silverlight 3 Tools установить не удастся.

На Web-странице http://www.microsoft.com/express/download/default.aspx можно найти как отдельно Visual Web Developer 2008, так и весь пакет Microsoft Visual Studio 2008 Express Edition, который содержит, в том чис-ле, и Visual Web Developer 2008. Все эти программные пакеты уже вклю-чают в себя пакет обновлений SP1. Какой из них выбрать — решать вам; автор предпочел загрузить Microsoft Visual Studio 2008 Express Edition це-ликом.

При установке Visual Web Developer 2008 спросит, устанавливать ли в до-полнение к нему Microsoft SQL Server 2008 Express Edition и Silverlight 2 Tools. Microsoft SQL Server 2008 Express Edition установить стоит, т. к. в главе 22 мы будем создавать базу данных, и он нам пригодится. А вот Silverlight 2 Tools нам совершенно не нужен — ведь мы все равно будем устанавливать Silverlight 3 Tools.

3. Silverlight 3 Tools. Дополнение к Visual Web Developer 2008, предназна-

ченное для создания приложений под платформу Silverlight 3. Найти его

можно на Web-странице http://go.microsoft.com/fwlink/?LinkID=128219.

Page 16: Дронов В. Самоучитель Silverlight 3 (2010)

4 Введение

Silverlight 3 Tools включает в себя отладочную версию среды исполнения Silverlight. Так что отдельно нам ставить ее не придется.

4. Документация по Silverlight 3. Содержит полное описание возможностей

платформы Silverlight 3 и набор примеров. В формате CHM ее можно най-

ти на Web-странице http://go.microsoft.com/fwlink/?LinkId=127106.

Все это можно загрузить абсолютно бесплатно! Даже без регистрации.

Типографские соглашения

В этой книге часто будут приводиться форматы различных языковых конст-

рукций, применяемых при Silverlight-программировании. Нам необходимо

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

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

Так, в угловые скобки (<>) заключаются названия параметров или фрагментов

программного кода, набранные курсивом. В код реального сценария, разуме-

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

символов <>. Например:

if (<условие>)

Здесь вместо подстроки условие должно быть подставлено реальное условное

выражение.

В квадратные скобки ([]) заключаются необязательные фрагменты кода. На-

пример:

[<список модификаторов, разделенных пробелами>] <тип поля> <имя поля>

Здесь список модификаторов может присутствовать, а может и отсутствовать.

Сами символы [], в которые заключается необязательный фрагмент, в реаль-

ном коде не ставятся.

Слишком длинный, не помещающийся на одной строке код автор разрывает

на несколько строк и в местах разрывов ставит знаки �. Например:

string[] sPlatforms = {"HTML+CSS+JavaScript", "Flash", "Java",

�"Silverlight"};

Приведенный ранее код разбит на две строки, но должен быть набран в одну.

Знаки � при этом в реальном коде также не ставятся.

Page 17: Дронов В. Самоучитель Silverlight 3 (2010)

Введение 5

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

Благодарности

Автор приносит благодарности своим родителям, знакомым и коллегам по

работе.

� Губине Наталье Анатольевне, начальнику отдела АСУ Волжского гума-

нитарного института (г. Волжский Волгоградской обл.), где работает

автор, — за понимание и поддержку.

� Всем работникам отдела АСУ — за понимание и поддержку.

� Родителям — за терпение, понимание и поддержку.

� Архангельскому Дмитрию Борисовичу — за дружеское участие.

� Шапошникову Игорю Владимировичу — за содействие.

� Евгению из Волгограда — за фильмы ужасов, как лучшее средство для

развития чувства юмора.

� Рыбакову Евгению Евгеньевичу, заместителю главного редактора изда-

тельства "БХВ-Петербург", — за неоднократные побуждения к работе, без

которых автор давно бы обленился.

� Издательству "БХВ-Петербург" — за издание моих книг.

� Всем своим читателям и почитателям — за прекрасные отзывы о моих

книгах.

� Всем российским программистам, занятым в разработке Visual Web

Developer 2008 и Silverlight 3, — за прекрасные программные продукты.

� Всем, кого я забыл здесь перечислить, — за все хорошее.

Page 18: Дронов В. Самоучитель Silverlight 3 (2010)

6 Введение

Page 19: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ I

Введение в Silverlight. Наше первое приложение

Глава 1. Что такое Silverlight

Глава 2. Основные понятия и принципы Silverlight

Глава 3. Наше первое Silverlight-приложение

Page 20: Дронов В. Самоучитель Silverlight 3 (2010)
Page 21: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 1

Что такое Silverlight

В самом деле, а что же такое Silverlight? Да, во введении уже говорилось, что

это платформа для создания клиентских Web-приложений... Но что же тогда

клиентское Web-приложение? И что такое платформа?

В двух строчках на эти вопросы не ответишь... Поэтому приготовьтесь к не-

большому теоретическому курсу.

Этапы развития WWW

Начнем мы издалека — с рассмотрения процессов, происходящих с совре-

менным Интернетом. Куда шагает Интернет? Что ждет нас впереди? И как,

наконец, "вскочить" на этот могучий "паровоз", не остаться прозябать на ка-

ком-нибудь богом забытом "полустанке"?

Опустим рассказ о самом Интернете (или, как его часто называют, Сети) и

о самом популярном его воплощении — WWW. Все это и так знают. И уж,

тем более, знают это читатели данной книги (уж раз они хотят заниматься

интернет-технологиями...).

Этап первый: обычные Web-страницы

World Wide Web — Всемирная паутина — изначально создавалась для рас-

пространения по сети обычных текстовых документов. Эти документы назы-

ваются Web-страницами, пишутся в обычных текстовых редакторах с ис-

пользованием особого языка HTML (Hypertext Markup Language — язык ги-

пертекстовой разметки) и отображаются в особых программах, называемых

Web-обозревателями. Совокупность Web-страниц, имеющих общее назначе-

ние и связанных друг с другом гиперссылками (особыми указателями, щелк-

Page 22: Дронов В. Самоучитель Silverlight 3 (2010)

10 Часть I. Введение в Silverlight. Наше первое приложение

нув на которые можно перейти на другую страницу), называется Web-сайтом

(или просто сайтом). Это все знают.

Язык HTML определяет набор особых команд, называемых тегами HTML.

Теги задают форматирование и назначение различных фрагментов текста;

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

для выделения фрагментов текста полужирным и курсивным шрифтом и пре-

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

форматировать фрагменты Web-страницы достаточно сложным образом.

Сама же Web-страница представляет собой обычный текстовый файл, кото-

рый может быть создан в любом простейшем текстовом редакторе, например

Блокноте, стандартно поставляемом в составе Microsoft Windows. Этот файл

содержит исходный код Web-страницы — своего рода предписание Web-

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

в отличие от хорошо нам знакомых текстовых файлов, файл Web-страницы

имеет расширение не txt, а htm или html; это нужно для корректной работы

Web-серверов — особых служебных программ, о которых мы поговорим чуть

позже.

Когда Web-обозреватель открывает Web-страницу, он сначала считывает ее

исходный код в память, после чего просматривает его содержимое на пред-

мет различных тегов HTML и выясняет, к каким фрагментам текста они от-

носятся. После этого он выводит присутствующий в исходном коде текст на

экран, предварительно применив к найденным ранее фрагментам соответст-

вующие им теги. И в результате мы видим на экране абзацы, заголовки, по-

лужирный и курсивный текст, таблицы, гиперссылки и прочее, чем богат

HTML.

Но где же Web-обозреватель находит эти чудные Web-страницы? О-о-о, это

весьма интересный процесс, который следует рассмотреть подробнее.

Прежде всего, если нам нужно увидеть на экране какую-либо Web-страницу,

мы должны набрать в особом поле ввода окна Web-обозревателя ее интер-

нет-адрес. Интернет-адрес однозначно идентифицирует компьютер в Сети,

где хранится нужная нам Web-страница, и файл самой этой страницы и вы-

глядит примерно так:

http://www.compression.ru/all_anns.htm

Знакомо, правда? Это интернет-адрес Web-страницы со списком новых по-

ступлений сайта "Все о сжатии", посвященного принципам и программам

сжатия данных. Строка http://www.compression.ru этого интернет-адреса

указывает на компьютер, хранящий эту страницу, а строка all_anns.htm — на

сам файл страницы (собственно, это имя файла, где она хранится). Разделяет

их символ слэша (/), третий по счету, если считать с начала интернет-адреса.

Page 23: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 1. Что такое Silverlight 11

Интернет-адрес может иметь и такой, несколько сокращенный, вид:

www.compression.ru/all_anns.htm

Так его, кстати, часто и пишут.

Получив от нас такой интернет-адрес, Web-обозреватель отправляет соответ-

ствующему ему компьютеру (в нашем случае — компьютеру с интернет-

адресом http://www.compression.ru) по Сети особый запрос. Этот запрос со-

держит имя файла, в котором хранится нужная Web-страница (all_anns.htm).

Вот еще один пример интернет-адреса:

http://www.compression.ru/video/index_ru.htm

Он указывает на Web-страницу списка статей в разделе сайта "Все о сжатии",

посвященном сжатию видео. Запрос Web-обозревателя будет в этом случае

содержать путь к файлу данной Web-страницы — video/index_ru.htm.

Однако чаще всего набираемый нами интернет-адрес содержит только стро-

ку, идентифицирующую компьютер, без указания имени файла Web-стра-

ницы. Например:

http://www.compression.ru/

Понятно, что в этом случае запрос Web-обозревателя не будет содержать

имени файла Web-страницы.

Хорошо, компьютер, хранящий нужную нам страницу, получил этот запрос.

Что дальше?

Дело в том, что на этом компьютере работает особая программа — Web-

сервер. Эта программа никак не взаимодействует с пользователем, а занима-

ется только тем, что принимает запросы от Web-обозревателей, запущенных

на других компьютерах, и пересылает им запрошенные файлы.

Здесь нужно сказать, что все файлы Web-страниц, составляющих Web-сайт,

должны находиться в папке, путь которой указывается в настройках Web-

сервера. Это так называемая корневая папка сайта. Именно в ней Web-сервер

будет искать файлы, которые запрашивают у него Web-обозреватели.

Если запрос Web-обозревателя содержит имя файла, Web-сервер будет искать

его в корневой папке. Так, в случае первого из рассмотренных ранее интер-

нет-адресов файл all_anns.htm должен находиться именно там, иначе Web-

сервер его не найдет.

Если запрос содержит путь файла, Web-сервер будет искать этот файл

в папках, вложенных в корневую папку. Например, получив путь

video/index_ru.htm (см. второй пример), Web-сервер будет искать файл

index_ru.htm, вложенный в папку video, которая, в свою очередь, вложена

в корневую папку.

Page 24: Дронов В. Самоучитель Silverlight 3 (2010)

12 Часть I. Введение в Silverlight. Наше первое приложение

Но что если в запросе не указаны ни имя, ни путь файла? Тогда Web-

обозреватель получит Web-страницу по умолчанию. Она хранится в файле с именем default.htm[l] или index.htm[l] (может быть изменено в настройках

Web-сервера) в корневой папке сайта.

Вот так, в общий чертах, и работает традиционный Интернет.

За все время существования языка HTML в нем появились пять значительных нововведений. Давайте вкратце рассмотрим четыре из них, а пятое отложим

на потом.

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

да Web-страницы ставятся особые теги, содержащие интернет-адреса этих файлов. Web-обозреватель, встретив такой тег, посылает Web-серверу еще

один запрос, получает в ответ графический файл и выводит его на Web-страницу.

Второе нововведение — поддержка каскадных таблиц стилей (Cascade Style Sheet, CSS). Они добавляют к языку HTML мощные возможности формати-рования текста и других элементов Web-страницы, приближающиеся к воз-можностям программ текстовых редакторов.

Третье нововведение — поддержка внедренных элементов Web-страниц. Внедренными элементами называются все элементы Web-страниц, содержи-мое которых не помещается в ее исходный код HTML, а хранится в отдель-ных файлах. Это аудио- и видеоклипы, документы Adobe PDF, Microsoft Word, Excel и PowerPoint, графика и анимация Adobe Flash и пр. В исходный код Web-страницы помещаются определенные теги, содержащие интернет-адреса соответствующих файлов; встретив такой тег, Web-обозреватель за-прашивает нужный файл у Web-сервера и выводит его содержимое на Web-страницу.

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

Четвертое нововведение — поддержка Web-сценариев. Web-сценариями на-

зываются небольшие программы, помещаемые прямо в исходный код HTML Web-страницы и выполняющие какие-либо действия над ней в ответ на ма-

нипуляции пользователя (щелчки мышью, перемещение мыши, нажатие кно-пок на клавиатуре и пр.). Такие программы пишутся на особом языке про-

граммирования JavaScript и могут быть сколь угодно сложными.

Благодаря этим нововведениям мы имеем сейчас совершенно умопомрачи-тельные Web-сайты наподобие YouTube (http://www.youtube.com/). В основе

лежит старый добрый HTML, обильно используются графические изображе-

Page 25: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 1. Что такое Silverlight 13

ния, для форматирования содержимого применяются каскадные таблицы

стилей CSS, видеоклипы представляют собой внедренные элементы, а для управления ими и реализации всяческих дополнительных "красивостей" соз-

даются Web-сценарии.

Этап второй: серверные Web-приложения

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

Они размышляли так. Вот написали мы, скажем, программу по складскому учету. Ее нужно установить на каждое рабочее место, настроить, чтобы она "нашла" рабочие данные, научить пользователей с ней работать, а при выходе новой версии — обновить, опять же, на всех рабочих местах. Морока!..

А что если установить эту программу на одном-единственном компьютере, где находятся и ее рабочие данные? А саму ее переписать так, чтобы она ра-ботала как Web-сервер, но в ответ на запросы Web-обозревателей не искала готовые Web-страницы на жестком диске, а сама создавала их на основе рабочих данных? Да тут такого можно наворотить — все коллеги обзавиду-ются!

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

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

Что ж, цель ясна! Назовем подобные программы серверными Web-приложе-ниями (или просто серверными приложениями) — и за работу!

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

Page 26: Дронов В. Самоучитель Silverlight 3 (2010)

14 Часть I. Введение в Silverlight. Наше первое приложение

Достоинство такого подхода — серверное приложение не нужно устанавли-вать на каждый компьютер каждого пользователя, который должен с ним ра-ботать, — достаточно иметь там Web-обозреватель (который там наверняка уже есть). Недостаток — писать серверные приложения несколько сложнее, чем обычные, "настольные". (Хотя эта проблема решается подбором хорошей среды разработки.)

Осталась мелочь — внести в язык HTML пятое по счету нововведение. Это Web-формы — особые элементы Web-страницы, предназначенные для ввода данных. Они служат вместилищем для элементов управления: полей ввода, флажков, переключателей, списков, кнопок и пр., а также занимаются фор-мированием данных для пересылки их серверному приложению.

Сначала серверные приложения были уделом корпоративных сетей, а потом вышли на "широкие просторы" Интернета. Почтовые Web-сервисы, интер-нет-магазины, поисковые системы — вот далеко не полный перечень облас-тей их применения. И, разумеется, широко распространились Web-формы (пример — на рис. 1.1).

Рис. 1.1. Web-форма на главной Web-странице почтового Web-сервиса Mail.ru

Этап третий: клиентские Web-приложения

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

А что если поместить прямо на Web-страницу для ввода сведений о списании позиции (применительно к складской программе, о которой речь шла ранее) небольшую программу, которая бы проверяла введенные данные на правиль-ность перед тем, как отправить их серверному приложению. Тогда непра-вильные данные не отправлялись бы по Сети, не отнимали бы время у сер-верного приложения, не занимали бы системные ресурсы компьютера, на ко-тором она работает, а пользователь сразу бы получил сообщение об ошибке ввода и смог бы ее быстро исправить.

Можно пойти дальше. Когда пользователь требует список позиций, относя-щихся к определенной категории, серверное приложение генерирует Web-страницу с этим списком и отправляет ему. А ведь эта Web-страница очень

Page 27: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 1. Что такое Silverlight 15

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

Теперь пользователь выполняет списание позиции. Серверное приложение

снова генерирует немалую Web-страницу с обновленным списком позиций,

Web-обозреватель неспешно вытягивает ее из сети, и пользователь снова

ждет и нервничает.

А мы поместим на Web-страницу другую программу, которая будет прини-

мать от серверного Web-приложения только данные, которые значительно

компактнее Web-страницы, сохранять их в памяти на будущее и выводить на

экран. Если же пользователь выполнит списание, эта программка отправит

данные серверному приложению, получит от него сигнал, что списание

успешно выполнено, просто-напросто исправит те данные, что получило ранее

и хранит теперь в памяти, и выведет их повторно на экран. Никакой долгой

загрузки — все произойдет моментально!

Рис. 1.2. Клиентское Web-приложение — просмотрщик каналов RSS

Page 28: Дронов В. Самоучитель Silverlight 3 (2010)

16 Часть I. Введение в Silverlight. Наше первое приложение

Да и выглядят эти Web-формы как-то непрезентабельно... Конечно, мы мо-

жем применить CSS, чтобы их немного разукрасить, но все равно не то...

А в многомудрую голову (которая рукам покоя не дает) лезут совсем уж кра-

мольные мысли — Web-страница с программкой, которая вообще не будет

работать ни с одним серверным приложением. Скажем, игра "15", чтобы со-

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

Такие программы, вставляемые прямо в Web-страницу, программисты назва-

ли клиентскими Web-приложениями. И тотчас засели за их написание. При-

мер такого приложения вы можете увидеть на рис. 1.2.

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

платформ для их создания и приспособили под эти нужды уже существую-

щие. Рассмотрением этих платформ мы сейчас и займемся.

Программные платформы

для создания клиентских Web-приложений

Программной платформой, или просто платформой, в мире Web-

программирования называется совокупность:

� языка программирования;

� набора правил написания на нем программ;

� библиотек (дополнительных модулей, расширяющих функциональность

данного языка);

� дополнительных программ, необходимых для создания программ на этом

языке;

� программы, с помощью которой выполняются написанные на этом языке

программы (так называемой среды исполнения). Впрочем, среда исполне-

ния присутствует не во всех платформах.

Платформ для создания клиентских Web-приложений довольно много. Сей-

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

недостатках.

HTML+CSS+JavaScript

Самый очевидный подход при создании клиентских Web-приложений — ис-

пользовать традиционные интернет-технологии: язык HTML, каскадные таб-

лицы стилей CSS и Web-сценарии, написанные на языке JavaScript. То есть

все то, что применяется для создания обычных Web-страниц.

Page 29: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 1. Что такое Silverlight 17

Все современные Web-обозреватели предоставляют Web-программисту воз-можность загрузки произвольных фрагментов данных с Web-сервера. Эта возможность используется для получения рабочих данных от серверного приложения. А язык JavaScript достаточно мощный, чтобы реализовать с его помощью довольно сложную обработку данных.

Зачастую клиентские Web-приложения имитируют пользовательский интер-фейс Windows-приложений, предлагая пользователю знакомые ему элементы управления: поля ввода, флажки, переключатели, кнопки, списки, таблицы, панели с вкладками, сворачивающиеся панели, панели инструментов и меню. Для этого обычно используются сторонние библиотеки, такие как, например, популярная библиотека Ext (http://www.extjs.com/). Кстати, на рис. 1.2 пока-зано приложение, написанное именно с использованием этой библиотеки.

Достоинства:

� для создания приложений достаточно знать только языки HTML, CSS и JavaScript, а их и так знают все Web-программисты;

� не требуется среда исполнения — HTML, CSS и JavaScript выполняются непосредственно Web-обозревателем;

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

Недостатки:

� необходимость изучения сразу нескольких интернет-технологий, а именно языков HTML, CSS и JavaScript; в случае использования сторонних биб-лиотек нужно также знать эти библиотеки;

� невысокое быстродействие готовых приложений;

� невозможность реализовать в них ноу-хау.

О последних двух пунктах стоит поговорить подробнее. Дело в том, что, как мы уже знаем, Web-сценарии помещаются прямо в исходный HTML-код Web-страниц. Также они могут быть сохранены в отдельных файлах. В лю-бом случае для грамотного интернетчика не составит труда открыть файл Web-страницы или файл, где хранится Web-сценарий, и посмотреть, что там написано. Это по поводу невозможности реализации ноу-хау.

Теперь по поводу невысокого быстродействия. JavaScript относится к интер-претируемым языкам программирования. Web-обозреватель читает очеред-ную команду написанного на этом языке Web-сценария, расшифровывает ее и выполняет, потом читает следующую команду, расшифровывает, выполня-ет и т. д. А процесс этот весьма небыстрый.

Надо сказать, исходный код современных решений на основе "связки" HTML+CSS+JavaScript столь велик, что вряд ли кто-то захочет в нем разби-

Page 30: Дронов В. Самоучитель Silverlight 3 (2010)

18 Часть I. Введение в Silverlight. Наше первое приложение

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

Adobe Flash

Популярнейшая платформа Flash была разработана фирмой FutureSplash еще в 90-х годах прошлого века. В 1996 году она была приобретена фирмой Macromedia, которая, в свою очередь, в 2006 году стала собственностью кор-порации Adobe.

Изначально Flash использовалась для создания Web-графики и анимации, но позднее в нее были добавлены средства написания клиентских Web-приложений. Современная версия платформы Flash — CS4 — позволяет соз-давать мощные приложения с развитым интерфейсом и богатыми возможно-стями вывода мультимедийных данных. Для написания программ использу-ется язык программирования ActionScript.

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

Достоинства:

� мощные средства для построения пользовательского интерфейса, вывода мультимедийных данных, обмена данными по сети и пр.;

� развитый язык программирования ActionScript;

� достаточно высокое быстродействие готовых приложений;

� возможность реализации ноу-хау.

Да-да, платформа Flash позволяет реализовать ноу-хау! В отличие от языка JavaScript, ActionScript относится к компилируемым языкам программирова-ния. Исходный код приложения Flash перед распространением преобразуется (компилируется) в исполняемый код, который и выполняется средой испол-нения. Исполняемый код, помимо того, что он значительно компактнее, чем исходный, представляет собой нечитаемую последовательность байтов, рас-шифровать которую очень трудно.

У исполняемого кода есть еще одно преимущество перед исходным — он выполняется значительно быстрее. Именно поэтому компилируемые языки программирования в плане быстродействия дадут изрядную фору интерпре-тируемым.

Page 31: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 1. Что такое Silverlight 19

Недостатки платформы Flash:

� она была изначально разработана как средство создания интерактивной Web-графики и анимации, а средства создания пользовательского интер-фейса были добавлены позднее и реализованы в виде библиотек. Эти биб-лиотеки имеют довольно большой размер, что, к сожалению, не лучшим образом сказывается и на размерах готового приложения Flash;

� необходимость изучения языка программирования ActionScript;

� перед распространением приложения Flash требуется его компиляция;

� для выполнения приложений Flash требуется среда исполнения — проиг-рыватель Flash (впрочем, он достаточно компактен, и найти его в Сети не проблема);

� для создания приложений Flash доступны исключительно платные средст-ва разработки, например, известный пакет Adobe Flash Professional CS4.

В настоящее время Adobe Flash — пожалуй, самая распространенная плат-форма для создания клиентских Web-приложений. Да вы их сами, наверно, неоднократно встречали!..

Sun Java

Платформа Java была представлена корпорацией Sun в 1995 году. Она весьма универсальна; с ее помощью можно создавать и обычные "настольные" при-ложения, и клиентские, и даже серверные Web-приложения. К сожалению, распространилась она не так широко, как хотелось бы корпорации Sun...

Для написания программ используется язык Java. Он также является компи-лируемым; исходный код приложения Java перед распространением компи-лируется в исполняемый (сама Sun предпочитает термин "байт-код"). Прило-жения Java работают в собственной среде исполнения — виртуальной маши-не Java.

Достоинства платформы Java, в целом, такие же, как у ее "коллеги" Flash:

� мощные средства для построения пользовательского интерфейса, обмена данными по сети и пр.;

� исключительно развитый язык программирования Java;

� готовые приложения очень компактны;

� достаточно высокое быстродействие готовых приложений;

� возможность реализации ноу-хау.

Недостатки:

� необходимость изучения языка программирования Java;

� перед распространением приложения Java требуется его компиляция;

Page 32: Дронов В. Самоучитель Silverlight 3 (2010)

20 Часть I. Введение в Silverlight. Наше первое приложение

� для выполнения приложений Java требуется среда исполнения — вирту-

альная машина Java, которая имеет достаточно большой размер.

Java так и не получила широкого распространения, проиграв своему ближай-

шему конкуренту — Flash. Приложения Java — до сих пор редкие гости на

Web-страницах.

Microsoft Silverlight

А теперь настала пора начать разговор о герое этой книги — платформе

Microsoft Silverlight. Этот амбициозный новичок грозится потеснить три тра-

диционные платформы, которые мы рассмотрели ранее. Дорогу молодым!

Первая версия Silverlight была представлена в 2007 году. Честно говоря, ее

возможности не впечатляли, и Web-программирующая общественность была

от нее не в восторге. Вышедшая в 2008 году вторая версия была уже значи-

тельно мощнее и, что называется, пошла в народ. А в текущем 2009 году вы-

шла третья версия. Вот о ней-то и пойдет речь в этой книге.

Silverlight является своего рода "подмножеством" популярной программной

платформы Microsoft .NET. Можно сказать, это ее Web-разновидность, пред-

назначенная именно для создания клиентских Web-приложений.

Достоинства:

� для создания приложений можно использовать любой язык программиро-

вания, поддерживающий платформу .NET, например, Visual Basic и C#;

� мощные средства для построения пользовательского интерфейса, вывода

мультимедийных данных, обмена данными по сети и пр.;

� для создания приложений можно использовать исключительно бесплат-

ные инструменты. Так, автор книги собирается применять для этого

Microsoft Visual Studio 2008 Express Edition, которую можно найти на сай-

те Microsoft (http://www.microsoft.com/);

� готовые приложения очень компактны;

� достаточно высокое быстродействие готовых приложений;

� возможность реализации ноу-хау.

Видно, что достоинства Silverlight — суть комбинация достоинств рассмот-

ренных нами ранее традиционных платформ. Что понятно, "новичок" обычно

старается брать самое лучшее у "старичков".

Недостатки:

� перед распространением приложения Silverlight требуется его компи-

ляция;

Page 33: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 1. Что такое Silverlight 21

� для выполнения приложений Silverlight требуется среда исполнения (ко-

торую также можно найти на сайте Microsoft).

Их всего два! И, надо сказать, оба не принципиальны.

Посторонитесь, HTML+CSS+JavaScript, Flash и Java! Silverlight идет в атаку!

Что дальше?

К атаке нужно как следует подготовиться. Этим мы займемся в следующей

главе, где изучим основные понятия как Silverlight-программирования, так и

программирования вообще. Собственно боевые действия начнутся в главе 3,

где мы напишем наше первое, совсем-совсем простое Silverlight-приложение.

Page 34: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 2

Основные понятия и принципы Silverlight

В предыдущей главе мы познакомились с текущим состоянием дел в Интер-

нете и WWW, выяснили, что такое клиентские Web-приложения, и кратко

рассмотрели программные платформы для их создания. И среди четырех

представленных платформ выбрали самую "молодую" и амбициозную —

Microsoft Silverlight 3.

В этой главе мы начнем более тесное знакомство с Silverlight. Мы рассмот-

рим основные понятия и принципы, согласно которым пишутся Silverlight-

приложения, и познакомимся с понятиями современного программирования:

объектами, классами и наследованием. А еще мы узнаем, какие языки про-

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

предназначен каждый из этих языков.

Все это пока что теория. Практика начнется потом...

Интерфейс и логика приложения

Любое приложение состоит из двух принципиально разных частей: интер-

фейса и логики. Сейчас мы их рассмотрим.

Интерфейс — это внешнее представление приложения, предназначенное для

"общения" с пользователем. У "настольных" Windows-приложений это окна,

где пользователь вводит данные, задает действия и наблюдает результат.

У Web-приложений это нечто, выполняющее функцию окон: отдельные Web-

страницы, страницы самого приложения или формы.

И "настольные", и Web-приложения используют различные элементы управ-

ления для ввода и вывода данных. Что такое элемент управления, мы уже

знаем — об этом рассказывалось в главе 1. Элементы управления Web-

Page 35: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 2. Основные понятия и принципы Silverlight 23

приложений аналогичны элементам управления "настольных" приложений за

некоторыми малозначительными исключениями.

Логика — это "внутренности" приложения, которые занимаются обработкой

данных и выполняют служебные задачи (открытие и закрытие окон, форма-

тирование данных перед выводом, сохранение и загрузка файлов и пр.).

Пользователь не работает с логикой напрямую — он только видит результат

ее работы через интерфейс.

Зачастую интерфейс и логика приложения создаются с помощью разных

средств. Так, в Web-приложении, созданном на платформе HTML+CSS+

JavaScript, интерфейс создается с использованием языков HTML и CSS, а ло-

гика пишется на языке программирования JavaScript. Интерфейс Web-

приложения Flash разрабатывается в визуальной среде, буквально рисовани-

ем мышью, а логика пишется на языке программирования ActionScript.

Silverlight здесь также не исключение, но об этом мы поговорим потом.

А пока что давайте рассмотрим отдельно интерфейс и логику типичного

Silverlight-приложения и изучим основные принципы их создания.

Интерфейс Silverlight-приложения

И начнем с интерфейса, как наиболее "приближенной" к пользователю части

приложения.

Страницы

В Windows-приложениях для представления пользовательского интерфейса

используются различные окна. В Web-приложениях Silverlight для этой цели

применяются страницы.

Не путайте страницы Silverlight-приложения и Web-страницы! Это совершенно разные вещи.

Окна — эта часть Windows-приложения, которую видит пользователь. Окна

служат для ввода и вывода данных, управления приложением, запуска раз-

личных действий и слежения за их выполнением. То же самое и страницы в

терминологии Silverlight; фактически это окна Silverlight-приложений.

Окно Windows, так сказать, определяет границы Windows-приложения. То,

что находится вне этого окна, данному приложению не принадлежит. Так и

страницы Silverlight — они указывают, что данная часть Web-страницы суть

"собственность" данного Silverlight-приложения; все, что находится за его

Page 36: Дронов В. Самоучитель Silverlight 3 (2010)

24 Часть I. Введение в Silverlight. Наше первое приложение

пределами, является содержимым самой Web-страницы и к Silverlight-

приложению не имеет ни малейшего отношения.

Простейшее Windows-приложение зачастую состоит всего из одного окна, которое представляет весь его интерфейс. Более сложные Windows-

приложения имеют несколько окон: рабочих, где представляются обрабаты-

ваемые данные, диалоговых, окон-предупреждений, вспомогательных и др. Эти окна могут как присутствовать на экране одновременно, так и выводить-

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

влекать пользователя.

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

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

пользователь сразу может начать работу с приложением.

А вот Silverlight-приложение содержит одну-единственную страницу, где и

представлен весь ее интерфейс. Эта страница называется главной. Если же

возникает потребность разбить интерфейс Silverlight-приложения на несколь-ко частей, следует использовать подстраницы и вторичные окна (о них пой-

дет речь в главах 18 и 19).

Бета-версия Silverlight 3 предоставляла возможность создания приложений, со-держащих несколько страниц. Одна из страниц, которая выводилась на экран изначально, сразу после запуска приложения, называлась главной.

В окончательной же версии Silverlight 3 можно создавать только одностранич-ные приложения. Так что теперь главная страница фактически стала единст-венной...

Как страницы Silverlight-приложения выводятся на Web-страницу

А теперь ненадолго отвлечемся от интерфейса Silverlight-приложений и рас-смотрим один важный момент.

Клиентское Web-приложение Silverlight представляет собой внедренный объ-

ект. (О внедренных объектах Web-страниц было рассказано в главе 1.) Это значит, что оно сохраняется в отдельном файле, а в исходном HTML-коде

Web-страницы ставится особый тег HTML, содержащий интернет-адрес этого файла. Встретив этот тег, Web-обозреватель загружает его с Web-сервера,

извлекает из него исполняемый код приложения, запускает его и выводит на Web-страницу содержимое его главной страницы.

Page 37: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 2. Основные понятия и принципы Silverlight 25

Так вот, страница Silverlight-приложения выводится в том месте, где в исход-

ном коде Web-страницы находится упомянутый ранее тег. (Собственно, это

справедливо для любых внедренных объектов: графических изображений,

аудио- и видеоклипов, документов Adobe PDF и пр.)

Из этого следует, что мы сами можем указать место на Web-странице, где

Web-обозреватель будет выводить страницу приложения Silverlight. Так мы

можем сделать приложение органичной частью Web-страницы.

Существует также возможность занять под Silverlight-приложение все окно

Web-обозревателя. То есть фактически Web-страница будет содержать только

приложение Silverlight — и больше ничего. Как это сделать, мы рассмотрим

потом, когда начнем практиковаться в Silverlight-программировании.

Компоненты

Пользовательский интерфейс любого приложения собирается из целого на-

бора "строительных блоков". Такими "строительным блоками" являются эле-менты управления (поля ввода, кнопки, флажки, переключатели, списки

и пр.), надписи, изображения, всяческие линии и рамки, которыми элементы

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

или на страницу и задает необходимые параметры (местоположение, разме-ры, текст, цвет текста и фона и др.).

Каждый такой "строительный блок" уже имеет определенную функциональ-ность. Поле ввода "умеет" обрабатывать нажатия клавиш на клавиатуре и вы-

водить набранный текст на экран, кнопка "умеет" нажиматься, а список —

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

"учить" их этому.

Такие "строительные блоки" для интерфейса приложения в терминологии

Silverlight называются компонентами. У них есть две особенности, которые

мы сейчас рассмотрим.

Во-первых, как и приложение, любой компонент состоит из интерфейса и

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

Во-вторых, любой компонент — "черный ящик". Его интерфейс и логика же-

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

рых пределах и именно добавить. Пределы, в которых мы можем менять ин-терфейс, и средства для добавления своей логики определяет, опять же,

разработчик компонента.

Page 38: Дронов В. Самоучитель Silverlight 3 (2010)

26 Часть I. Введение в Silverlight. Наше первое приложение

Однако набор компонентов, предлагаемый Silverlight, весьма велик, и мы все-гда сможем найти в нем подходящий "кирпичик" для нашего приложения.

Контейнеры

Казалось бы, все ясно: компоненты (давайте уж использовать этот термин) помещаются на страницы — и интерфейс приложения готов. Однако не все так просто...

Дело в том, что компоненты должны быть определенным образом располо-жены на странице. Это, собственно, понятно — кому нужно приложение, в котором все элементы управления свалены в одну кучу!.. Обычно их вы-страивают в линию один за другим или формируют из них что-то наподобие формуляра; существуют и более экзотические варианты, но мы их рассматри-вать не будем.

Но каким образом мы можем разместить на странице компоненты в нужном порядке? С помощью компонентов совершенно особого рода — контейнеров. Они:

� служат своего рода "вместилищем" для компонентов, которые в этом слу-чае называются дочерними;

� задают местоположение дочерних компонентов, выстраивая их в опреде-ленном порядке или помещая в заданную нами точку;

� задают размеры дочерних компонентов, если мы сами их не задали;

� переупорядочивают дочерние компоненты при их добавлении и удалении, изменении их размеров или изменении размеров самой страницы.

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

Часто бывает, что одного контейнера не хватает; это может случиться, если интерфейс страницы слишком сложен. В таком случае в главный контейнер помещают другие контейнеры (вложенные), а в них уже — сами элементы управления. В особо сложных случаях применяется многократно вложенные друг в друга контейнеры: в главный контейнер помещают другой, в него — третий и т. д.

Платформа Silverlight предоставляет в наше распоряжение три типа контей-неров.

� "Таблица". Располагает вложенные в него компоненты в ячейках вообра-

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

формуляр. Чаще других используется в качестве главного контейнера.

Page 39: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 2. Основные понятия и принципы Silverlight 27

� "Стопка". Выстраивает вложенные в него компоненты по вертикали свер-

ху вниз или горизонтали слева направо.

� "Холст". Помещает компоненты в точки с координатами, заданными раз-

работчиком.

На рис. 2.1—2.3 представлены примеры использования контейнеров перечис-

ленных ранее трех типов для создания формы ввода имени и пароля пользо-

вателя, состоящей из двух надписей, двух полей ввода и кнопки.

Имя

Пароль

ОК

Рис. 2.1. Контейнер "таблица"

Имя

Пароль

ОК

Имя

Пароль

ОК

Y1

Y2

Y3

X1

X2

X3

Рис. 2.2. Контейнер "стопка"

(задано вертикальное расположение

вложенных в него компонентов)

Рис. 2.3. Контейнер "холст"

На рис. 2.4 показан образец применения вложенных контейнеров. В главный

контейнер "стопка" вложены контейнер "таблица" с полями ввода для указа-

ния имени и пароля пользователя и другой контейнер "стопка" с кнопками.

На этом знакомство с интерфейсом Silverlight-приложения можно считать

законченным. По крайней мере, теоретическое знакомство...

Настала пора приняться за логику.

Page 40: Дронов В. Самоучитель Silverlight 3 (2010)

28 Часть I. Введение в Silverlight. Наше первое приложение

Имя

Пароль

ОК Отмена

Главный контейнер

"стопка"

Вложенный

контейнер "таблица"

Вложенный

контейнер "стопка"

Рис. 2.4. Пример использования вложенных контейнеров

Логика Silverlight-приложения

Логика любого приложения — это то, что скрыто от глаз пользователя. Поль-

зователь видит лишь результаты ее работы, посредством все того же интер-

фейса. Но это не значит, что логика — что-то второстепенное, нет! Наоборот,

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

особое внимание.

Как работает Silverlight-приложение. События

Предположим, что мы загрузили в Web-обозревателе Web-страницу с прило-

жением Silverlight. Web-обозреватель, встретив в исходном коде Web-

страницы соответствующий тег, загрузит файл Silverlight-приложения, запус-

тит его и выведет на экран главную страницу.

Что дальше?

А дальше приложение... ждет.

Чего?

Возникновения события.

Событие — это некое условие, возникающее в компоненте или в самом при-

ложении в результате действия пользователя, работы Web-обозревателя, опе-

рационной системы или самого приложения. Так, при щелчке на какой-

нибудь кнопке в ней возникнет событие "щелчок", при вводе данных в поле в

нем возникнет событие "правка содержимого", а при наведении курсора мы-

ши на компонент, выводящий графическое изображение, в нем возникнет со-

бытие "наведение курсора мыши". Даже при запуске и завершении работы

приложения возникают соответствующие события; возникают они прямо

в самом приложении.

Всевозможных событий платформа Silverlight предлагает настолько много,

что даже самый привередливый программист будет доволен. С их помощью

Page 41: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 2. Основные понятия и принципы Silverlight 29

можно очень точно отслеживать различные моменты в "жизни" Silverlight-

приложения.

И не только отслеживать, но и реагировать на них.

Каким образом? С помощью обработчиков событий. Это особые фрагменты

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

образом привязываются, во-первых, к нужному событию, во-вторых, к ком-

поненту, в котором должно произойти это событие. А когда данное событие

возникает в данном компоненте, обработчик событий выполняется, делая

свою работу.

Предположим, что мы собираемся написать Silverlight-приложение, которое

будет переводить величины, заданные в дюймах, в миллиметры. Мы созда-

дим два поля ввода и кнопку; при нажатии кнопки введенная в первое поле

ввода величина будет преобразована и выведена во второе поле ввода. Для

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

преобразования, и привязать его к событию "щелчок", которое будет возни-

кать в компоненте-кнопке.

Работа любого Silverlight-приложения заключается в виде реакции на собы-

тия с помощью их обработчиков. Даже завершение приложения — суть реак-

ции на событие!

Объекты и классы. Свойства и методы

Хорошо, с событиями и их обработчиками все ясно. Вернемся к компонен-

там, с которыми мы познакомились ранее, и рассмотрим их с точки зрения

программиста.

Возьмем обычное поле ввода. Это компонент, "черный ящик". Он имеет оп-

ределенный интерфейс и определенную логику, которые мы не можем изме-

нить. Однако мы можем получать от него и задавать для него некоторые дан-

ные (например, занесенное в него значение), можем выполнять над ним неко-

торые действия (скажем, запретить ввод данных пользователем) и можем

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

все это сделать, поле ввода должно предоставить нам средства для задания и

получения данных, выполнения действий и реагирования на события.

Кроме того, таких полей ввода на странице у нас может быть несколько.

А кроме них, множество других компонентов. И со всеми ими нам придется

"общаться" программно.

Как быть?

Дело в том, что среда исполнения Silverlight представляет каждый компонент

в памяти компьютера как особую структуру данных, называемую объектом.

Page 42: Дронов В. Самоучитель Silverlight 3 (2010)

30 Часть I. Введение в Silverlight. Наше первое приложение

Этот самый объект также является "черным ящиком" и занимается тем, что

хранит данные компонента, позволяет управлять им и реагировать на его со-

бытия.

Каждая единица данных компонента хранится в свойстве объекта. Свойство

можно представить в виде ячейки памяти, которая может хранить только од-

ну единицу данных.

Для манипуляции с объектом служат методы объекта. Метод — это фраг-

мент исполняемого кода, выполняющий определенные действия либо над

данными, которые могут храниться в данном объекте или быть сторонними

по отношении к нему, либо с самим объектом.

И, наконец, объект предоставляет доступ к событиям, которые могут в нем

возникать.

Давайте для примера рассмотрим приложение, выполняющее перевод значе-

ний размера из дюймов в миллиметры. Оно, как мы помним, содержит два

поля ввода и кнопку. Все эти поля ввода и кнопка будут представлены в про-

грамме своими собственными объектами; всего таких объектов будет три, и

каждый из них будет содержать свои данные и свои инструменты — свои

свойства, методы и события.

Так, в случае поля ввода представляющий его объект будет содержать:

� свойства — введенное в это поле ввода значение, значения цветов текста и

фона, признак, указывающий, доступно ли данное поле ввода для пользо-

вателя, и др.;

� методы — выделение всего содержимого поля ввода, установка в поле

ввода текстового курсора и др.;

� события — ввод или правка значения в поле ввода, щелчок мышью, наве-

дение и увод курсора мыши и др.

Объектами в Silverlight-приложении представляется все! Страница приложе-

ния является объектом. Само приложение является объектом, имеющим свои

свойства, методы и события. Настоящее царство объектов — эта платформа

Silverlight!

Единственное исключение — самые простые данные, числа и некоторые дру-

гие. Они настолько просты, что нет особого смысла их усложнять.

Любой объект создается на основе класса. Класс — это своего рода шаблон,

описывающий набор свойств, методов и событий, которые будет содержать

каждый созданный на его основе объект. Класс — это пустая "рыба", на ос-

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

В случае нашего приложения оба объекта, представляющие поля ввода, будут

созданы на основе класса "поле ввода" и, соответственно, будут иметь тип

Page 43: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 2. Основные понятия и принципы Silverlight 31

"поле ввода". Объект кнопки будет создан на основе класса "кнопка". А объ-

ект страницы создается на основе класса "страница".

Каждый класс должен иметь уникальное имя. По имени мы можем найти

нужный нам класс и создать на его основе объект.

Каждое свойство, каждый метод и каждое событие также должны иметь имя,

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

с одинаковыми именами; то же относится к методам и событиям). По этим

именам осуществляется доступ к нужному свойству, методу или событию.

Запуск на выполнение метода называется его вызовом. Метод может прини-

мать параметры — дополнительные данные, являющиеся сторонними по

отношению к данному объекту (данные, хранящиеся в самом объекте, он

возьмет прямо из него). Также метод может возвращать результат своих

действий; этот результат можно использовать в последующих вычислениях

или вывести на экран, а можно и проигнорировать.

Кстати, обработчики событий реализуются в виде методов, а именно методов

класса страницы, на которой находятся компоненты. Но об этом позже.

Пока закончим с объектами, свойствами, методами и событиями. Обратим

внимание на классы.

Классы — родители и потомки. Иерархия классов

Класс создается самим программистом путем описания его свойств, методов,

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

ляющие платформу Silverlight, написаны программистами Microsoft. Мы

тоже будем писать свои классы — это и есть суть Silverlight-программиро-

вания.

Но сначала давайте посмотрим на поля ввода и другие элементы управления

какого-нибудь Windows-окна. Видно, что они имеют во многом схожую

функциональность. Так, они "умеют" принимать строго заданные размеры,

принимать фокус ввода (особый признак того, что в данный момент они спо-

собны обрабатывать нажатия клавиш клавиатуры), становиться недоступны-

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

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

изменяют ее.

Понятно, что писать эту общую логику отдельно для каждого класса, реали-

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

для всех классов, представляющих компоненты, а в отдельных классах-

компонентах только дополнять и немного изменять ее?

Page 44: Дронов В. Самоучитель Silverlight 3 (2010)

32 Часть I. Введение в Silverlight. Наше первое приложение

Конечно можно. Дело в том, что классы обладают замечательным свойст-

вом — они могут создаваться на основе других классов.

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

заданные размеры, получать фокус ввода, становиться недоступным для вво-да и невидимым. После этого на основе данного класса мы напишем классы,

представляющие конкретные компоненты, — поля ввода, кнопки, списки и др. В этих новых классах мы соответствующим образом дополним логику;

поле ввода получит возможность выводить набранное значение на экран,

кнопка — нажиматься, а список — упорядочивать свои пункты по вертикали и прокручиваться.

Назовем класс, на основе которого создаются другие классы, родителем, а классы, порожденные на его основе, — потомками. Свойства, методы и со-

бытия, полученные классом-потомком в "наследство" от родителя, пусть на-зываются унаследованными. А сам процесс создания одних классов на основе

других — наследованием.

Логика классов-потомков изменяется путем добавления в них новых свойств, методов и событий и изменением унаследованных методов. Как это делается,

мы обязательно рассмотрим потом.

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

Это справедливо и для любого класса. Однако мы можем создать на основе данного класса новый и наделить его новыми "умениями", опять же, путем

добавления в него новых свойств, методов и событий и изменением унасле-дованных методов.

Платформа Silverlight представляет собой огромный набор классов, создан-

ных на основе друг друга, — иерархию классов. В самом ее "начале" находит-ся базовый класс с самой примитивной логикой; на основе его созданы все

остальные классы иерархии. Целое генеалогическое древо классов!

Классы, из которых состоит Silverlight-приложение

Теперь посмотрим, какие классищи, классы и классики мы применим в на-

шем Silverlight-приложении.

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

упорядоченных наборов однотипных значений (строк или чисел), коллекции, хранящие упорядоченные наборы объектов и более развитые по сравнению

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

Page 45: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 2. Основные понятия и принципы Silverlight 33

Потом, классы, представляющие компоненты. Здесь комментировать нечего.

Еще мы используем классы страниц, из которых состоит приложение. Для каждой такой страницы мы создадим свой собственный класс. Да-да, созда-дим, мы, сами (конечно, с помощью среды разработки)! И вот почему...

1. Нам, так или иначе, придется писать обработчики событий.

2. Обработчики событий реализуются в виде методов класса страницы.

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

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

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

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

Языки программирования для создания Silverlight-приложений

Настала пора выяснить, какие же языки программирования мы должны знать, чтобы успешно создавать клиентские Web-приложения на платформе Silverlight. И решить кое-какие попутные вопросы.

Для создания пользовательского интерфейса приложения используется язык разметки XAML (eXtensible Application Markup Language — расширяемый язык разметки приложений; его название произносится как "зэмл"). Этот язык основан на популярном языке описания данных XML (eXtensible Markup Language — расширяемый язык разметки). Отметим, что XAML не язык про-граммирования, а именно язык разметки; языки программирования описыва-ют, что должна делать программа, чтобы получить результат, а языки раз-метки описывают сам результат, который мы хотим получить, — пользова-тельский интерфейс. (В этом смысле XAML похож на HTML, тоже являющийся языком разметки.)

Для создания логики приложения можно использовать любой язык програм-мирования, поддерживающий компиляцию программ для платформы .NET, например, Visual Basic или C#. В этой книге мы используем язык C# (произ-носится как "си-шарп").

Page 46: Дронов В. Самоучитель Silverlight 3 (2010)

34 Часть I. Введение в Silverlight. Наше первое приложение

Исходный код XAML компилируется в некое внутреннее представление,

о котором корпорация Microsoft ничего толком не говорит. Исходный код C#

компилируется в исполняемый код на "промежуточном" языке MSIL

(MicroSoft Intermediate Language — промежуточный язык Microsoft). Все это

выполняется средой исполнения Silverlight.

У-ф-ф! Чересчур много теории, вы не находите? Не пора ли закончить теоре-

тический курс и наконец-то попрактиковаться?

Что дальше?

При написании любых технических книг самое главное — не дать читателю,

утомленному обилием теоретических сведений и малопонятных терминов,

уснуть. Для этого время от времени проводятся практические занятия, где

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

Поэтому в следующей главе мы займемся практикой. Мы познакомимся со

средой разработки Microsoft Visual Studio 2008 Express Edition, создадим свое

первое, пока еще совсем простое приложение Silverlight, и начнем изучать

языки XAML и C#. Вперед!

Page 47: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 3

Наше первое Silverlight-приложение

В предыдущей главе мы получили столько теоретических сведений, что го-

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

ры, и получили представление об объектах и классах. Также мы узнали, что

для создания Silverlight-приложений нам придется изучить два языка: язык разметки XAML и язык программирования C#.

В этой главе мы создадим наше первое Silverlight-приложение. Попутно мы познакомимся с пакетом Microsoft Visual Studio 2008 Express Edition, точнее, с одной из входящих в него программ — Microsoft Visual Web Developer 2008 Express Edition, — предназначенной для создания Web-приложений, и на- учимся в ней работать. И, разумеется, начнем знакомство с языками XAML и C#.

Microsoft Visual Web Developer 2008 Express Edition

Microsoft Visual Web Developer 2008 Express Edition, как уже говорилось, — программа, входящая в состав пакета Microsoft Visual Studio 2008 Express Edition и предназначенная для разработки Web-приложений. После установки дополнения Silverlight 3 Tools в ней можно разрабатывать, в том числе, и приложения Silverlight 3.

Чем мы и займемся.

Запустим Visual Web Developer 2008 (будем для краткости называть его или так, или просто программой). Для этого следует нажать кнопку Пуск (Start),

выбрать в появившемся на экране меню пункт Программы (Programs) или Все программы (All Programs), а в появившемся на экране подменю — пункт

Page 48: Дронов В. Самоучитель Silverlight 3 (2010)

36 Часть I. Введение в Silverlight. Наше первое приложение

Microsoft Visual Web Developer 2008 Express Edition. После недолгого ожи-

дания на экране появится главное окно этой программы (рис. 3.1).

Рис. 3.1. Главное окно программы Microsoft Visual Web Developer 2008 Express Edition

Здесь нам все, в принципе, знакомо. Главное окно Visual Web Developer 2008

имеет заголовок, главное меню под ним и строку статуса, вытянувшуюся

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

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

Изначально в клиентской области выводятся несколько небольших окон;

в частности, два из них можно видеть в правой части главного окна. Это па-

нели — особые окна, которые находятся в клиентской области главного окна

и содержат дополнительные инструменты для работы над приложением и

управления самой программой. На рис. 3.2 показана панель Solution

Explorer, которой мы будем часто пользоваться в дальнейшем; здесь она по-

казана заполненной, но изначально, сразу после запуска Visual Web

Developer 2008, всегда пуста.

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

рассмотрим.

� Панель может быть "приклеена" к краю главного окна.

� Несколько панелей могут быть организованы в виде "блокнота" и при-

клеены в таком виде к краю главного окна (такой "блокнот", кстати, пока-

Page 49: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 37

зан на рис. 3.2; в нем присутствуют панели Solution Explorer и Database

Explorer). В нижней части "блокнота" при этом будут присутствовать

вкладки, на которых написаны названия всех "сложенных" в него панелей;

для вывода нужной панели на экран следует щелкнуть мышью на соответ-

ствующей вкладке.

Рис. 3.2. Панель Solution Explorer (заполненная)

� Панель может свободно "плавать" по экрану в виде отдельного окна.

� Панель может быть скрыта (скрытая панель). В этом случае она отобра-

жается в виде небольшого ярлычка, находящегося на одной из сторон

главного окна. На рис. 3.3 показаны три таких ярлычка, вытянувшиеся

вдоль левого края главного окна и соответствующие панелям Toolbox,

CSS Properties и Manage Styles. Чтобы вывести такую панель на экран,

следует навести курсор мыши на соответствующий ей ярлычок (рис. 3.4);

если курсор мыши после этого убрать, панель снова скроется.

Состояния панелей можно переключать. Как это сделать, описано в интерак-

тивной справке по Visual Web Developer 2008.

Любая панель может быть закрыта. Чтобы закрыть панель, достаточно щелк-

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

части заголовка панели (он имеет вид серой полосы, находится в верхней

части панели и похож на заголовок обычного окна Windows).

Page 50: Дронов В. Самоучитель Silverlight 3 (2010)

38 Часть I. Введение в Silverlight. Наше первое приложение

Рис. 3.3. Ярлычки, соответствующие скрытым панелям Toolbox,

CSS Properties и Manage Styles

Рис. 3.4. Скрытая панель Toolbox, появившаяся на экране после наведения курсора мыши

на соответствующий ей ярлычок

Вдоль верхней части главного окна располагаются еще две панели. Они име-

ют вид длинных и узких серых полос и содержат, за редким исключением,

одни только кнопки. Это панели инструментов; они предоставляют быстрый

доступ к различным инструментам программы. Одна из таких панелей инст-

рументов показана на рис. 3.5.

Изначально в главном окне Visual Web Developer 2008 выводится так назы-

ваемая стартовая страница. Она содержит, в частности, список новостей,

Page 51: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 39

взятых с сайта Microsoft, и организована в виде Web-страницы; так, заголов-

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

перейти к более подробному описанию данной новости.

Рис. 3.5. Панель инструментов

Но новости нас не интересуют (тем более что большинство из них давно

устарели). Давайте все-таки создадим наше первое Silverlight-приложение.

Понятие проекта. Решение

Но сначала нам нужно кое-что прояснить. И сделать это прямо сейчас.

Как мы уже знаем из глав 1 и 2, Silverlight-приложение пишется на языках

XAML и C# (или любом другом языке, поддерживающем компиляцию в ис-

полняемый код MSIL; но мы выбрали C#). Эти языки являются компилируе-

мыми. Это значит, что перед распространением приложения мы должны вы-

полнить его компиляцию. И не только перед распространением, но и перед

любым тестовым запуском — ведь для этого будет использоваться среда ис-

полнения Silverlight, которая способна обрабатывать только исполняемый

код, но никак не исходный.

Исходный код приложения, который мы напишем, мы сохраним в особых

файлах (файлах исходного кода). На каждую страницу приложения (точнее,

определяющий ее класс) таких файлов будет два: один — с XAML-кодом,

другой — с C#-кодом. Кроме того, класс самого приложения, которое авто-

матически создаст Visual Web Developer 2008, потребует для хранения также

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

для работы файлы, которые также создаст Visual Web Developer 2008.

В итоге мы получим множество файлов, хранящих исходный код приложения

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

из этих файлов должно "на выходе" получиться нечто цельное. Как?

Именно поэтому все серьезные среды разработки программ (и Visual Web

Developer 2008 в том числе) вводят понятие проекта. Проект — это совокуп-

ность файлов, хранящих:

� исходный код программы;

� параметры программы (платформа, под которую она создается, версия,

сведения о разработчике и его авторских правах и т. п.);

Page 52: Дронов В. Самоучитель Silverlight 3 (2010)

40 Часть I. Введение в Silverlight. Наше первое приложение

� вспомогательные данные (так, для любого Silverlight-приложения создает-ся Web-страница, включающая это самое приложение и позволяющая от-крыть его в Web-обозревателе).

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

Все файлы, составляющие проект, часто сохраняются в отдельной папке. Так с ними проще работать. Visual Web Developer 2008 это учитывает и сам соз-дает такую папку.

Также Visual Web Developer 2008 позволяет создавать совокупности проек-тов — решения. Нам это пока что не нужно, но пригодится потом.

Создание Silverlight-приложения

Создать новое Silverlight-приложение (фактически — новый проект) проще всего, выбрав пункт New Project меню File или нажав комбинацию клавиш <Ctrl>+<Shift>+<N>. На экране появится диалоговое окно New Project (рис. 3.6), в котором мы зададим параметры создаваемого приложения (они же — параметры проекта).

Рис. 3.6. Диалоговое окно New Project

Page 53: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 41

Первым делом нам нужно указать Visual Web Developer 2008, что мы хотим

создать именно Silverlight-приложение, причем использовать при этом язык

C#. Так что найдем в иерархическом списке Project types "ветвь" Visual C#,

в ней — пункт Silverlight и выберем его. После этого выберем в большом

списке Templates пункт Silverlight Application, т. е. Silverlight-приложение.

Следующий шаг — указание имени проекта, а значит, и имени нашего при-

ложения. Введем его в поле ввода Name. Наше первое приложение будет

преобразовывать величины из дюймов в миллиметры, значит, назовем его

Convertor.

Поскольку мы не собираемся создавать решение (это нам пока совершенно не

нужно), отключим флажок Create directory for solution. Этот флажок, буду-

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

проект.

Осталось нажать кнопку OK. И все?

Нет. Visual Web Developer 2008, помимо всего прочего, позволяет создавать

Web-сайты и сейчас предложит нам заодно создать сайт и поместить в него

только что созданное Silverlight-приложение. Так и есть — на экране появи-

лось диалоговое окно New Silverlight Application (рис. 3.7).

Рис. 3.7. Диалоговое окно New Silverlight Application

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

Visual Web Developer 2008 не делать этого. Для чего просто отключим

флажок Host the Silverlight application in a new Web site. И нажмем кноп-

ку OK.

После нескольких секунд ожидания главное окно Visual Web Developer 2008

изменится — см. рис. 3.8. Новый проект создан!

Page 54: Дронов В. Самоучитель Silverlight 3 (2010)

42 Часть I. Введение в Silverlight. Наше первое приложение

Рис. 3.8. Главное окно программы Visual Web Developer 2008 после создания нового проекта

Окна документов

Отвлечемся от Silverlight-приложения, заготовку которого только что создал

для нас Visual Web Developer 2008, и посмотрим на саму эту программу. Что

в ней добавилось?

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

код на языке XAML, описывающий пока что единственную страницу нашего

приложения (эта же страница станет главной, выводящейся на экран сразу

после запуска приложения).

Но вот где выводится этот исходный код? О, это особое окно Visual Web

Developer 2008, называемое окном документа. Оно служит для вывода со-

держимого файла, с которым в настоящий момент идет работа, отображается

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

Visual Web Developer 2008 позволяет открыть одновременно сколько угодно

окон документа — лишь бы хватило ресурсов компьютера. При этом работа

может вестись только с одним окном документа, которое называется актив-

ным.

Если мы откроем несколько окон документа, мы должны будем как-то между

ними переключаться. Посмотрим на верхний край главного окна Visual Web

Page 55: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 43

Developer 2008. Там, ниже панелей инструментов, видны ярлычки; на каждом

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

Чтобы переключиться на нужное окно документа, сделав его активным, дос-

таточно щелкнуть на соответствующем ему ярлычке.

Панель Solution Explorer

Еще один важный инструмент Visual Web Developer 2008, который мы рас-

смотрим, — панель Solution Explorer (см. рис. 3.2). Эта панель представляет

все файлы, из которых состоит проект, и его дополнительные данные.

Если панель Solution Explorer отсутствует на экране, ее можно вывести, вы-

брав пункт Solution Explorer меню View или нажав комбинацию клавиш

<Ctrl>+<Alt>+<L>.

Всю панель Solution Explorer занимает иерархический список. Если в Visual

Web Developer 2008 не открыт ни один проект, он пуст. Но как только мы

откроем проект (или решение), этот список покажет нам все файлы и данные

проекта (как и представлено на рис. 3.2).

Название "корня" иерархического списка в панели Solution Explorer совпа-

дает с названием проекта. Из него "растут" четыре "ветви".

� "Ветвь" Properties предоставляет доступ к параметрам создаваемого при-

ложения. Мы рассмотрим их в главе 24.

� "Ветвь" References перечисляет все библиотеки, используемые в проекте.

О библиотеках мы поговорим в главе 4.

� "Ветвь" App.xaml представляет одноименный файл исходного кода на

языке XAML, описывающий класс самого приложения. Мы познакомимся

с этим классом в главе 13.

� "Ветвь" MainPage.xaml представляет одноименный файл исходного кода

на языке XAML, описывающий класс главной страницы приложения (ее

интерфейс).

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

развернем "ветвь" MainPage.xaml. В ней присутствует один-единственный

пункт — MainPage.xaml.cs. Он соответствует одноименному файлу исходно-

го кода на языке C#, описывающему класс главной страницы приложения (ее

логику).

Отсюда можно сделать несколько выводов.

Вывод первый: файлы, содержащие исходный код на языке XAML, имеют

расширение xaml, а файлы с исходным кодом C# — расширение xaml.cs.

Page 56: Дронов В. Самоучитель Silverlight 3 (2010)

44 Часть I. Введение в Silverlight. Наше первое приложение

Вывод второй: обоим файлам, описывающим класс главной страницы, Visual

Web Developer 2008 изначально дает имя MainPage. Соответственно, полные

имена этих файлов: MainPage.xaml и MainPage.xaml.cs.

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

имя App. Тогда полные имена этих файлов: App.xaml и App.xaml.cs.

Мы можем открыть любой файл из представленных в списке панели Solution

Explorer, дважды щелкнув на нем мышью. Файл будет открыт в отдельном

окне документа.

Как только мы создали новый проект Silverlight-приложения, Visual Web

Developer 2008 сам откроет файл, содержащий исходный код XAML главной

страницы этого приложения. Как мы уже выяснили, это файл MainPage.xaml.

Найдем в главном окне программы соответствующее ему окно документа.

И посмотрим, что оно нам показывает.

Создание интерфейса Silverlight-приложения

А показывает оно нам вот такой исходный код XAML (частично сокращен):

<UserControl x:Class="Convertor.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Grid x:Name="LayoutRoot">

</Grid>

</UserControl>

Что бы это значило?..

Введение в язык разметки XAML

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

Этим мы сейчас и займемся.

Язык XAML описывает пользовательский интерфейс страницы Silverlight-

приложения. Это мы уже знаем из главы 1. Фактически же он указывает, ка-

кие компоненты будет содержать страница и какие параметры будут иметь

эти компоненты.

Из главы 2 мы знаем, что компоненты с точки зрения логики программы —

объекты, созданные на основе определенных классов. А параметры компо-

нентов — суть их свойства. То есть написанное ранее можно перефразиро-

вать так: язык XAML указывает, какие объекты составят пользовательский

Page 57: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 45

интерфейс страницы, на основе каких классов они будут созданы, и какие

значения и для каких свойств этих объектов должны быть заданы.

А что нам требуется знать для создания нужного объекта? Во-первых, конеч-

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

создании. В-третьих, сами эти значения.

Кроме того, в приложении к интерфейсу Silverlight-приложения нам нужно

знать еще кое-что. В главе 2 мы выяснили, что для размещения компонентов

на странице используются другие компоненты — контейнеры, которые вы-

страивают их в определенном порядке и задают их размеры. Поэтому,

в-четвертых, нам нужно знать, какие компоненты в каких контейнерах нахо-

дятся.

Разумеется, язык XAML позволяет все это сделать.

Для определения классов, на основе которых будут созданы объекты, он пре-

доставляет так называемые теги XAML. Свойства объекта, которым при его

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

тегов. А уже в этих атрибутах указываются значения, которые будут занесе-

ны в соответствующие свойства.

Теги XAML представляют собой имена классов, заключенные в символы < и

>. (Как мы помним из главы 2, все классы имеют уникальные имена; это нуж-

но, чтобы мы смогли найти нужный класс и создать на его основе объект.)

При этом между начальным символом < и именем класса не должно быть

пробелов.

Любой тег XAML фактически состоит из двух тегов: открывающего и за-

крывающего; однако в любом случае он трактуется как единый парный тег.

Открывающий тег пишется так, как говорилось ранее: имя класса, заключен-

ное в символы < и >. В закрывающем теге между начальным символом < и

именем класса ставится символ слэша /; между ними не должно быть пробе-

лов. Отметим, что и в открывающем, и в закрывающем теге должно исполь-

зоваться одно и то же имя класса.

Так, для помещения на страницу поля ввода используется вот такой тег

XAML:

<TextBox></TextBox>

TextBox — класс, представляющий функциональность компонента "поля

ввода".

Обычно между открывающим и закрывающим тегами нет ничего (как пока-

зано в примере ранее). Поэтому зачастую используют сокращенную запись

вида:

<TextBox />

Page 58: Дронов В. Самоучитель Silverlight 3 (2010)

46 Часть I. Введение в Silverlight. Наше первое приложение

То есть убирают закрывающий тег, ставят после имени класса в открываю-

щем теге пробел и символ /.

К сожалению, сам Visual Web Developer 2008 не использует при формировании кода XAML такую сокращенную запись.

Теперь нам как-то нужно задать значения свойств создаваемого объекта. Как

мы уже знаем, для этого используются атрибуты тегов. Они:

� ставятся в теге XAML, формирующем компонент, свойство которого нуж-

но задать;

� ставятся после имени класса в открывающем теге и отделяются от него

пробелом;

� представляют собой пары вида <имя свойства>="<значение свойства>", ко-

торые отделяются друг от друга пробелами.

Так, если мы захотим, чтобы в нашем поле ввода изначально присутствовал

текст "Введите значение сюда", мы присвоим этот текст свойству Text созда-

ваемого объекта класса TextBox (т. е. поля ввода), используя вот такой код

XAML;

<TextBox Text="Введите значение сюда"></TextBox>

Запись, как видим, весьма наглядна и пояснений не требует.

Если мы захотим, чтобы пользователь не смог ввести в поле ввода текст

длиннее 60 символов, мы используем свойство MaxLength. Этому свойству мы

присвоим число 60.

<TextBox Text="Введите значение сюда" MaxLength="60"></TextBox>

Отметим, что в любом случае значение свойства заключается в кавычки.

Недавно мы узнали о существовании сокращенной формы записи тегов

XAML. Давайте сократим фрагмент кода, приведенный ранее.

<TextBox Text="Введите значение сюда" MaxLength="60" />

Здесь пробел и символ / ставятся после значения последнего атрибута тега.

Вообще, пробел между именем класса или значением последнего атрибута и

символом / ставить необязательно. Но лучше все-таки ставить — так код XAML

лучше читается.

Теперь нам как-то нужно поместить только что созданное поле ввода в кон-

тейнер. Как это сделать?

Page 59: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 47

Сначала нам нужно создать сам контейнер.

<Grid></Grid>

Этот код XAML создает объект класса Grid — контейнер "таблица" (подроб-

нее о нем — в главе 2). Никаких значений свойств для этого объекта мы зада-

вать не будем — они там особо не нужны.

Потом мы поместим код XAML, создающий поле ввода, "внутрь" тега

<Grid> — прямо между открывающим и закрывающим тегами.

<Grid>

<TextBox Text="Введите значение сюда" MaxLength="60" />

</Grid>

Здесь нужно сказать кое-что о форматировании исходного кода. Дело в том,

что исходный код для лучшей читаемости оформляют особым образом. Так,

теги, помещенные (вложенные) внутрь другого тега, "сдвигают" вправо, ста-

вя перед ними пробелы. На приведенном ранее примере мы так и сделали.

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

тов:

<Grid>

<TextBox Text="Введите значение сюда" MaxLength="60" />

<TextBox Text="Введите другое значение сюда" MaxLength="40" />

<TextBox Text="Введите третье значение сюда" />

</Grid>

Мы только что поместили в контейнер "таблица" три поля ввода.

А что же сама страница? С помощью каких тегов XAML она создается? Да-

вайте выясним.

Обратим внимание на код XAML, сгенерированный самим Visual Web

Developer 2008 при создании страницы и приведенный ранее, в начале этого

раздела. И начнем с тега <UserControl>.

<UserControl x:Class="Convertor.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

</UserControl>

Как мы знаем из главы 2, каждая страница Silverlight-приложения представ-

ляется объектом, созданным на основе уникального класса. А класс этот мы

пишем сами в процессе создания страницы.

Так вот, UserControl — это класс, который станет родителем класса нашей

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

Convertor.MainPage — его задает атрибут x:Class тега <UserControl>.

Page 60: Дронов В. Самоучитель Silverlight 3 (2010)

48 Часть I. Введение в Silverlight. Наше первое приложение

Атрибут тега x:Class весьма специфичен. Он не соответствует ни одному

свойству. Он только сообщает Visual Web Developer 2008 и среде исполнения

Silverlight имя класса, который создается на основе класса UserControl.

На остальные два атрибуты тега <UserControl>, присутствующие в приведен-

ном ранее примере кода, мы пока не будем обращать внимание. Займемся

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

ной работы Silverlight-приложения, и не будем трогать.

Вообще-то в теге <UserControl> присутствует значительно больше атрибутов.

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

В тег <UserControl> вложен тег <Grid>, создающий контейнер "таблица":

<Grid x:Name="LayoutRoot">

</Grid>

Это главный контейнер (подробнее — в главе 2). О том говорит присутствие

в теге <Grid> атрибута x:Name со значением LayoutRoot. Запомним его и пока

оставим в покое.

Помещение компонентов на страницу. Панель Toolbox

Откроем главу 2 и еще раз перечитаем разделы, посвященные компонентам и

контейнерам. Там сказано, что все компоненты помещаются в главный кон-

тейнер, присутствующий на странице в единственном экземпляре. Страница

у нас есть, и главный контейнер тоже есть. Начнем, пожалуй!

Проверим, открыт ли файл MainPage.xaml, где хранится исходный код XAML

единственной страницы нашего приложения. Если не открыт, дважды щелк-

нем на одноименной "ветви" иерархического списка в панели Solution

Explorer (см. рис. 3.2) — и он откроется в отдельном окне документа.

Найдем на левом крае главного окна ярлычок, соответствующий скрытой

панели Toolbox. Наведем на него курсор мыши — и данная панель появится

на экране. Если же эта панель вообще отсутствует на экране, выведем ее,

выбрав пункт Toolbox меню View или нажав комбинацию клавиш

<Ctrl>+<Alt>+<X>.

Если в активном окне документа отображается код XAML, панель Toolbox

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

Silverlight (как показано на рис. 3.4). В противном случае она будет пуста.

Page 61: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 49

Набор компонентов организован в виде иерархического списка из двух "вет-

вей": Silverlight XAML Controls и General. Нам понадобится только первая "ветвь", пункты которой соответствуют доступным в Visual Web

Developer 2008 классам компонентов; вторая же "ветвь" всегда пуста.

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

тель не поймет, зачем он нужен.

Функциональность надписи реализует класс TextBlock. Найдем в "ветви" Silverlight XAML Controls соответствующий ему пункт TextBlock, наведем

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

подождем, пока данная панель не скроется. После этого можно аккуратно

поместить курсор мыши в пространство между тегами <Grid x:Name=

"LayoutRoot"> и </Grid>, т. е. в главный контейнер, и отпустить кнопку мыши.

Это самый простой и быстрый способ поместить компонент на страницу. Visual Web Developer 2008 сам вставит в XAML-код нужный тег и, если тре-

буется, внесет в него другие необходимые изменения.

После вставки на страницу надписи XAML-код, создающий содержимое страницы, станет таким (тег, создающий саму страницу, убран ради компакт-

ности):

<Grid x:Name="LayoutRoot">

<TextBlock></TextBlock>

</Grid>

Следующий шаг — задание значений свойств только что созданного компо-

нента (в смысле, представляющего его объекта). Так, нам обязательно нужно

задать для надписи текст, иначе от нее никакого толку.

Текст для надписи задает свойство Text, определяемое классом TextBlock.

Этому свойству присваивается строка, содержащая данный текст. Значит,

нам нужно вставить в тег XAML <TextBlock> нужный атрибут.

Поставим между именем класса TextBlock и конечным символом > в откры-вающем теге текстовый курсор. Наберем пробел и... Нет, это надо видеть! Предупредительный Visual Web Developer 2008 предлагает нам выбрать нуж-

ное свойство из списка автоподстановки (рис. 3.9).

Этот список появится на экране, когда, по мнению Visual Web Deve-loper 2008, мы собираемся задать новый атрибут тега. Мы можем прокрутить

его мышью или клавишами-стрелками и выбрать пункт, соответствующий нужному нам свойству, щелчком мыши или нажатием клавиши <Enter>. Если

же этот список нам не нужен, мы можем закрыть его, нажав клавишу <Esc>

или щелкнув мышью на любом месте экрана.

Page 62: Дронов В. Самоучитель Silverlight 3 (2010)

50 Часть I. Введение в Silverlight. Наше первое приложение

Рис. 3.9. Список автоподстановки

Если нам известно начало имени свойства, мы можем набрать его. Список

прокрутится, и первое свойство, содержащее в начале набранные нами сим-

волы, будет выделено.

Выберем в списке автоподстановки пункт Text — нужное нам свойство Text.

Visual Web Developer 2008 сам вставит его в тег <TextBlock>, не забудет

поставить также знак равенства и кавычки и установит текстовый курсор как

раз между кавычками. Нам останется только ввести текст надписи —

"Дюймы".

Автоподстановка — замечательная вещь! Как мы выясним потом, она ис-

пользуется в Visual Web Developer 2008 где только можно.

Так, надпись готова. Вот XAML-код, создающий содержимое страницы:

<Grid x:Name="LayoutRoot">

<TextBlock Text="Дюймы"></TextBlock>

</Grid>

Правильно говорят, что аппетит приходит во время еды!.. Давайте зададим

для текста надписи выравнивание по правому краю. Для этого добавим атри-

бут тега <TextBlock>, задающий для свойства TextAlignment (выравнивание

текста) значение Right (вправо). Поставим после атрибута тега Text="Дюймы"

пробел и выберем в списке автоподстановки свойство TextAlignment. Когда

Visual Web Developer 2008 создаст атрибут и поставит текстовый курсор ме-

жду кавычками, мы увидим еще один список автоподстановки, в котором вы-

берем пункт Right. Готово!

<Grid x:Name="LayoutRoot">

<TextBlock Text="Дюймы" TextAlignment="Right"></TextBlock>

</Grid>

Так, надпись мы сделали. Теперь перетащим из панели Toolbox и поместим

точно под создающим ее XAML-кодом компонент TextBox — поле ввода.

Page 63: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 51

В нем создадим атрибут тега <TextBox>, задающий значение свойства Text,

равное 0. Пусть в этом поле ввода изначально присутствует ноль.

Кроме этого, нам обязательно нужно задать ширину и высоту поля ввода,

иначе среда исполнения Silverlight задаст их сама как ей вздумается. Ширина

поля ввода задается с помощью свойства Width (ширина), а высота — с по-

мощью свойства Height (высота) класса TextBox в пикселах. Зададим их рав-

ными 100 и 22 пикселам, для чего создадим соответствующие атрибуты тега

<TextBox>.

XAML-код нашего приложения после этого будет выглядеть так:

<Grid x:Name="LayoutRoot">

<TextBlock Text="Дюймы" TextAlignment="Right"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

</Grid>

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

ние в миллиметрах, и надписи для него. Проще всего это сделать, выделив

готовые теги XAML, создающие поле ввода, где задается значение в дюймах,

и его надпись, скопировав их в буфер обмена, вставив под этими тегами и

соответственно исправив их. Вот что мы получим:

<Grid x:Name="LayoutRoot">

<TextBlock Text="Дюймы" TextAlignment="Right"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

<TextBlock Text="Миллиметры" TextAlignment="Right"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

</Grid>

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

для вывода данных, мы захотим, чтобы пользователь не смог ввести туда но-

вое значение. Для этого зададим для второго поля ввода атрибут тега, зано-

сящий в свойство IsReadOnly (доступно ли поле ввода только для чтения) зна-

чение true (истина, или "да"). Сделаем это, а Visual Web Developer 2008 по-

может нам, выведя соответствующие списки автоподстановки.

Вот что у нас должно получиться (показан только XAML-тег, создающий

второе поле ввода):

<TextBox Text="0" Width="100" Height="22" IsReadOnly="true"></TextBox>

Осталось создать кнопку, после нажатия которой приложение преобразует

величину из дюймов в миллиметры. Кнопку представляет компонент Button.

Изловчимся и "воткнем" ее между первым полем ввода и второй надписью.

Для кнопки нам нужно задать надпись. Текст надписи хранится в свойстве

Content класса Button. Создадим соответствующий атрибут тега <Button>.

Page 64: Дронов В. Самоучитель Silverlight 3 (2010)

52 Часть I. Введение в Silverlight. Наше первое приложение

Вот готовый XAML-код страницы нашего приложения:

<Grid x:Name="LayoutRoot">

<TextBlock Text="Дюймы" TextAlignment="Right"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

<Button Content="Преобразовать"></Button>

<TextBlock Text="Миллиметры" TextAlignment="Right"></TextBlock>

<TextBox Text="0" Width="100" Height="22" IsReadOnly="true"></TextBox>

</Grid>

Сохраним его, нажав комбинацию клавиш <Ctrl>+<S>. И немного передох-

нем.

Сейчас, по-хорошему, нам нужно взглянуть на результат трудов и оценить,

все ли мы сделали так. К сожалению, Visual Web Developer 2008 не может

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

нить компиляцию приложения и запустить его в Web-обозревателе...

Компиляция и запуск Silverlight-приложения

К счастью, с Visual Web Developer 2008 сделать это совсем несложно.

При создании нового Silverlight-приложения Visual Web Developer 2008 соз-

даст для него небольшую тестовую Web-страничку, содержащую весь необ-

ходимый для загрузки и запуска приложения HTML-код. Когда мы запустим

приложение, он откомпилирует его, если оно еще ни разу не компилирова-

лось после последней правки, откроет Web-обозреватель и загрузит в нем

данную Web-страницу.

Чтобы запустить Silverlight-приложение в среде Visual Web Developer 2008,

мы можем:

� либо нажать кнопку Start Debugging ( ) в панели инструментов;

� либо выбрать пункт Start Debugging меню Debug;

� либо нажать клавишу <F5>, что, по мнению автора, проще всего.

После этого, скорее всего, придется ждать, и довольно долго. Компиляция

приложения, особенно самая первая, — процесс небыстрый...

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

М-да, совсем не то, что мы планировали. Куча мала, или вали валом — потом

разберем...

Закроем Web-обозреватель, чтобы завершить наше приложение. И разберем

эту кучу!

Page 65: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 53

Рис. 3.10. Наше первое Silverlight-приложение в окне Web-обозревателя

Работа с контейнером "таблица"

Из главы 2 мы знаем, что контейнер "таблица" выстраивает компоненты по

строкам и столбцам воображаемой таблицы. Таким образом, мы можем с его

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

данных (да так чаще всего и делается).

Но для этого нам нужно указать контейнеру "таблица", во-первых, количест-

во строк и столбцов этой самой воображаемой таблицы, а во-вторых, какой

компонент в какую ячейку таблицы должен быть помещен. Если мы этого не

сделаем, контейнер "таблица" будет иметь всего одну ячейку (т. е. одну стро-

ку и один столбец) и "свалит" в нее все компоненты. Что мы и видим на

рис. 3.10.

Для задания количества строк и столбцов такой воображаемой таблицы класс

Grid определяет свойства RowDefinitions и ColumnDefinitions соответственно.

Но значения, которые мы зададим для этих свойств, весьма любопытны. Это

не числа, а наборы однотипных (созданных на основе одного класса) объек-

тов; каждый такой объект представляет, соответственно, строку или столбец.

Такие наборы объектов называются коллекциями и также представляют собой

объекты.

С помощью атрибутов тега мы задать эти свойства не сможем. Нам придется

использовать объектную запись. Которую мы рассмотрим на примере свой-

ства RowDefinitions класса Grid.

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

вать. Для этого мы поместим внутрь тега <Grid>, прямо между открывающим

и закрывающим тегами, определяющий это свойство парный тег XAML вида

<<имя класса>.<имя свойства>></<имя класса>.<имя свойства>>

Page 66: Дронов В. Самоучитель Silverlight 3 (2010)

54 Часть I. Введение в Silverlight. Наше первое приложение

Как видим, в обоих тегах — открывающем и закрывающем — мы указываем и имя класса, и имя свойства, разделив их точкой.

Сделать это нам придется вручную. На панели Toolbox ничего подходящего для этого случая нет.

Вот что у нас должно получиться:

<Grid x:Name="LayoutRoot">

<Grid.RowDefinitions>

</Grid.RowDefinitions>

. . .

</Grid>

Обычно XAML-код, определяющий строки и столбцы контейнера "таблица", помещают перед кодом, создающим содержащиеся в нем компоненты. Так нагляднее.

Следующий шаг — задание значения для этого свойства. Таким значением

будет коллекция объектов, созданных на основе класса RowDefinition, кото-рый определяет функциональность строки воображаемой таблицы. Делается это уже знакомым нам способом — написанием соответствующих XAML-тегов. Нам понадобятся три таких тега, чтобы получить коллекцию из трех объектов — три строки таблицы.

<Grid x:Name="LayoutRoot">

<Grid.RowDefinitions>

<RowDefinition></RowDefinition>

<RowDefinition></RowDefinition>

<RowDefinition></RowDefinition>

</Grid.RowDefinitions>

. . .

</Grid>

Последний шаг — задание свойств объектов в коллекции. Для объектов-

строк мы можем указать высоту. Она задается с помощью свойства Height

класса RowDefinition в пикселах.

<Grid x:Name="LayoutRoot">

<Grid.RowDefinitions>

<RowDefinition Height="25"></RowDefinition>

<RowDefinition Height="25"></RowDefinition>

<RowDefinition Height="25"></RowDefinition>

</Grid.RowDefinitions>

. . .

</Grid>

Аналогично указывается количество столбцов воображаемой таблицы. Для

этого используется свойство ColumnDefinitions класса Grid; оно принимает

Page 67: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 55

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

тов-столбцов мы укажем ширину — с помощью свойства Width класса

ColumnDefinition в пикселах.

<Grid x:Name="LayoutRoot">

<Grid.RowDefinitions>

<RowDefinition Height="25"></RowDefinition>

<RowDefinition Height="25"></RowDefinition>

<RowDefinition Height="25"></RowDefinition>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="80"></ColumnDefinition>

<ColumnDefinition Width="100"></ColumnDefinition>

</Grid.ColumnDefinitions>

. . .

</Grid>

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

Grid.Row="<номер строки таблицы, начиная с нуля>" Grid.Column="<номер столбца таблицы, начиная с нуля>"

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

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

Так, если мы захотим поместить первую надпись в первую ячейку вообра-жаемой таблицы, мы поместим в создающий эту надпись тег такие атрибуты (выделены полужирным шрифтом):

<TextBlock Grid.Row="0" Grid.Column="0" Text="Дюймы"

TextAlignment="Right"></TextBlock>

Укажем для остальных компонентов номера строк и столбцов воображаемой таблицы, в которые хотим их поместить. У нас должен получиться такой XAML-код:

<TextBlock Grid.Row="0" Grid.Column="0" Text="Дюймы"

TextAlignment="Right"></TextBlock>

<TextBox Grid.Row="0" Grid.Column="1" Text="0" Width="100"

Height="22"></TextBox>

Page 68: Дронов В. Самоучитель Silverlight 3 (2010)

56 Часть I. Введение в Silverlight. Наше первое приложение

<Button Grid.Row="1" Grid.Column="1" Content="Преобразовать"></Button>

<TextBlock Grid.Row="2" Grid.Column="0" Text="Миллиметры"

TextAlignment="Right"></TextBlock>

<TextBox Grid.Row="2" Grid.Column="1" Text="0" Width="100" Height="22"

IsReadOnly="true"></TextBox>

Сохраним готовый код и запустим приложение на выполнение. Вот теперь все нормально — см. рис. 3.11. Есть, правда, некоторые шероховатости, но мы устраним их потом.

Рис. 3.11. Окончательный вид нашего первого Silverlight-приложения

На этом пока закончим с интерфейсом приложения и обратимся к логике. Ведь пока что наше приложение — всего лишь картинка на мониторе...

Создание логики Silverlight-приложения

Логика обычно создается после интерфейса. И мы не будем отступать от это-го правила.

Откроем файл MainPage.xaml.cs, где хранится исходный код C#, описываю-щий логику единственной страницы нашего приложения. Проще всего сде-лать это, развернув "ветвь" MainPage.xaml иерархического списка на панели Solution Explorer (см. рис. 3.2) и дважды щелкнув на пункте MainPage.xaml.cs. Файл будет открыт в отдельном окне документа.

Рассмотрим этот код. Вот фрагмент, который нас заинтересует:

public partial class MainPage : UserControl

{

public MainPage()

{

InitializeComponent();

}

}

Page 69: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 57

Первая строка описывает класс страницы MainPage — об этом говорит ключе-

вое слово class. (Ключевое слово в языке программирования имеет особое

значение, например, как в данном случае, определяет класс.) Он создан на

основе родительского класса UserControl — об этом говорит имя этого клас-

са, стоящее после имени класса страницы MainPage и отделенное от него

двоеточием.

Ниже этой строки мы видим фрагмент кода, заключенный в фигурные скоб-

ки. Он определяет все свойства, методы и события класса MainPage — всю его

логику. Когда мы будем писать обработчики событий, определяющий их код

поместим именно сюда. (Вспомним: обработчики событий реализуются в ви-

де методов страницы.)

В данный момент в классе MainPage реализован всего один метод, имеющий

то же имя, что и сам класс, — MainPage. Это так называемый конструктор —

особый метод, выполняющийся при создании любого объекта на основе дан-

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

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

Ну да бог с ним, с конструктором! Поговорим о нем потом. Сейчас мы напи-

шем обработчик события щелчка на кнопке, в котором извлечем значение,

введенное в первое поле ввода, преобразуем его и поместим во второе поле

ввода. И наше приложение наконец-то заработает!

Имена компонентов

И тут возникает вопрос: как нам получить доступ к нужному полю ввода,

чтобы получить или занести в него значение?

Сразу напрашивается ответ: как-то поименовать это поле ввода.

Имя любому компоненту (в том числе и полю ввода) можно дать с помощью

особого атрибута тега x:Name. Его поддерживают все теги XAML, создающие

компоненты. В качестве значения этого атрибута тега указывается собствен-

но имя компонента.

Имя компонента должно следовать определенным правилам.

� Оно должно содержать только латинские буквы, цифры и знаки подчерки-

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

� Оно должно быть уникально в пределах страницы, т. е. на странице не

должно быть компонентов с одинаковыми именами.

Имена даются тем компонентам, к которым необходимо иметь доступ из ло-

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

тальным компонентам давать имена излишне.

Page 70: Дронов В. Самоучитель Silverlight 3 (2010)

58 Часть I. Введение в Silverlight. Наше первое приложение

Давайте дадим компонентам нашего приложения такие имена:

� первому полю ввода — txtInches;

� кнопке — btnConvert;

� второму полю ввода — txtMillimetres.

Откроем файл MainPage.xaml, если уже его закрыли. Впишем в соответст-

вующие теги XAML атрибуты x:Name, задающие эти имена. XAML-код, фор-

мирующий эти компоненты, должен стать таким (добавленные атрибуты тега

x:Name выделены полужирным шрифтом):

<TextBox x:Name="txtInches" Grid.Row="0" Grid.Column="1" Text="0"

Width="100" Height="22"></TextBox>

<Button x:Name="btnConvert" Grid.Row="1" Grid.Column="1"

Content="Преобразовать"></Button>

. . .

<TextBox x:Name="txtMillimetres" Grid.Row="2" Grid.Column="1" Text="0"

Width="100" Height="22" IsReadOnly="true"></TextBox>

Кстати, если мы посмотрим на тег XAML, создающий главный контейнер, то

увидим, что этот контейнер имеет имя LayoutRoot (выделено полужирным

шрифтом):

<Grid x:Name="LayoutRoot">

. . .

</Grid>

Это имя дал ему сам Visual Web Developer 2008 при создании страницы при-

ложения. Оно необходимо для правильной работы приложения, так что ни

в коем случае не удаляйте его.

Пока не будем закрывать файл MainPage.xaml. Он нам еще понадобится.

Привязка обработчиков к событиям компонентов

Настала торжественная минута! Сейчас мы напишем наш первый обработчик

события в компоненте. Это будет обработчик события "нажатие" кнопки

btnConvert.

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

атрибутов тега. В данном случае они представляют собой пары вида

<имя события>="<имя обработчика — метода класса страницы>". Да-да, к одно-

му компоненту можно привязать сразу несколько обработчиков — к разным

его событиям, разумеется.

Класс Button поддерживает событие Click — "нажатие кнопки". Пожалуй, это

самое часто обрабатываемое событие кнопки.

Page 71: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 59

Впишем в тег <Button> атрибут Click, воспользовавшись списком автопод-

становки. Когда Visual Web Developer 2008 поставит текстовый курсор между

кавычками, он выведет на экран еще один список автоподстановки. Он пере-

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

обработчика этого события; в нашем случае таких методов еще нет. Кроме

того, этот список будет содержать пункт <New Event Handler>, позволяю-

щий создать новый обработчик события. Выберем именно этот пункт.

В ответ Visual Web Developer 2008 сделает две вещи:

� поместит в атрибут тега, задающий обработчик события, сгенерированное

им самим имя метода, который станет обработчиком;

� создаст в коде C# в файле MainPage.xaml.cs, формирующем класс

MainPage, "пустой" метод с данным именем.

Проверим, правильно ли мы все сделали. Вот XAML-код, формирующий

кнопку (добавленный атрибут тега, привязывающий обработчик к событию,

выделен полужирным шрифтом):

<Button x:Name="btnConvert" Grid.Row="1" Grid.Column="1"

Content="Преобразовать" Click="btnConvert_Click"></Button>

Как мы помним из главы 2, каждый метод (как и свойство и событие) должен

иметь уникальное в пределах класса имя. Visual Web Developer 2008 при за-

дании имени для метода, который станет обработчиком события, не оригина-

лен — он просто берет имя компонента, добавляет к нему знак подчеркива-

ния и имя события.

Сохраним файл MainPage.xaml. И откроем файл MainPage.xaml.cs, если уже

его закрыли.

Введение в язык программирования C#

Найдем C#-код, сформированный Visual Web Developer 2008 и создающий

метод, который станет обработчиком события. Вот он:

private void btnConvert_Click(object sender, RoutedEventArgs e)

{

}

Здесь мы сразу видим имя этого метода — btnConvert_Click. Также мы ви-

дим, что этот метод не возвращает значения, — об этом говорит ключевое

слово void. А еще мы видим, что этот метод принимает два параметра — они

перечислены в скобках после имени этого метода. (О значениях, которые мо-

гут возвращать методы, и параметрах, которые они могут принимать, говори-

лось в главе 2.)

Page 72: Дронов В. Самоучитель Silverlight 3 (2010)

60 Часть I. Введение в Silverlight. Наше первое приложение

Описание параметра, принимаемого методом, состоит из двух частей.

� Та часть, что находится слева, описывает тип значения этого параметра,

т. е. разновидность данных, к которой оно будет принадлежать (число,

строка или объект, созданный на основе какого-либо класса).

� Та часть, что справа, задает имя переменной — особой ячейки в памяти,

где хранится само значение параметра заданного типа.

Если метод принимает несколько параметров (как в нашем случае), их описа-

ния отделяются друг от друга запятой.

Как уже говорилось, наш метод принимает два параметра.

� Первый параметр хранится в переменной sender и имеет тип object.

object — это базовый класс иерархии Silverlight, на основе которого, пря-

мо или опосредованно, созданы все остальные классы. Сам же этот пара-

метр хранит объект, к которому подключен данный обработчик события

(в нашем случае это кнопка btnConvert).

� Второй параметр хранится в переменной e и в данном случае имеет тип

RoutedEventArgs. Он представляет собой объект класса RoutedEventArgs,

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

Мы не будем использовать значения этих параметров (их вообще редко ис-

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

Нам нужно извлечь из поля ввода txtInches значение, преобразовать его и

поместить в поле ввода txtMillimetres. Подумаем, что нам для этого понадо-

бится.

Прежде всего, нам понадобится еще одна переменная — для хранения извле-

ченного из поля ввода txtInches значения. Это значение имеет вид строки,

значит, нам нужна переменная строкового типа. Строку (строковый тип дан-

ных) в языке C# представляет ключевое слово string.

Посмотрим на приведенный ранее фрагмент C#-кода, описывающего метод

btnConvert_Click. Найдем фигурные скобки — именно в них мы будем писать

код этого метода. И напишем в них вот что:

string sInches;

Мы только что написали наше первое выражение на языке C#! (Выраже-

ние — это единица исходного кода, выполняет законченное действие.) То

есть сделали первый, совсем еще маленький шажок в программирование.

Это выражение выполняет объявление переменной типа string (строкового) с

именем sInches. Объявление указывает Visual Web Developer 2008, что мы

собираемся использовать в методе переменную заданного типа и с заданным

именем.

Page 73: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 61

Отметим, что в конце этого выражения стоит знак ;. Он должен стоять в кон-це каждого выражения и, собственно, обозначает его конец.

Теперь поместим в эту переменную значение, введенное в поле ввода

txtInches. Для этого напишем еще одно выражение, вот такое:

sInches = txtInches.Text;

Рассмотрим это выражение по частям. И начнем с той части, что находится

правее знака =.

Там мы видим объект поля ввода txtInches и его свойство Text, хранящее занесенное в это поле ввода значение. Они разделены точкой. Этим мы гово-

рим Visual Web Developer 2008, что хотим получить доступ к свойству Text

объекта txtInches.

Рассмотрим теперь сам знак = и ту часть выражения, что находится левее его. Там мы видим имя объявленной в предыдущем выражении переменной —

sInches. А сам знак = выступает здесь как оператор — команда языка C#, выполняющая элементарное действие в составе выражения. В данном случае это оператор присваивания, который помещает в переменную, чье имя стоит левее этого оператора, значение, стоящее правее его.

То есть выражение, приведенное ранее, присваивает переменной sInches зна-

чение свойства Text объекта txtInches.

Продолжим. Давайте ограничимся пока тем, что просто поместим в поле вво-

да txtMillimetres значение, заданное в поле ввода txtInches, добавив к нему справа символы "мм.". Само преобразование значения мы отложим на потом.

Последнее выражение обработчика события, которое и будет это выполнять, таково:

txtMillimetres.Text = sInches + "мм.";

Здесь знак + есть оператор конкатенации, или объединения строк. Он

объединяет строки, стоящие слева (значение переменной sInches) и справа

(строку "мм.") от него, в одну строку.

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

[<имя объекта>.]<имя метода> ([<список значений параметров, разделенных запятыми>])

Мы вызовем метод Focus объекта txtInches, который устанавливает на эле-мент управления фокус ввода, не принимает параметров и возвращает значе-ние, которое мы проигнорируем.

txtInches.Focus();

Page 74: Дронов В. Самоучитель Silverlight 3 (2010)

62 Часть I. Введение в Silverlight. Наше первое приложение

Комментировать здесь особо нечего. Обратим только внимание, что, даже

если метод не принимает параметров, после его имени при вызове обязатель-

но ставятся скобки.

Вот и все! Сохраним файл MainPage.xaml.cs и запустим приложение. Введем

в поле ввода Дюймы какое-либо число и нажмем кнопку Преобразовать.

Это число, с добавленным справа символом ', должно появиться в поле ввода

Миллиметры.

Введение в язык программирования C#, продолжение

В одном дюйме 25,4 мм. Значит, мы должны умножить значение, введенное

в поле ввода txtInches, на 25,4, чтобы получить значение в миллиметрах.

Одна проблема — свойство Text объекта TextBox может хранить только дан-

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

тать и делить. Если же мы попытаемся умножить строку на число, Visual Web

Developer 2008 сообщит об ошибке и откажется запускать приложение. Такие

операции можно проделывать только с данными числового типа — числами.

Значит, сначала нам нужно выполнить преобразование типов, в нашем слу-

чае — строки в число. Платформа Silverlight как раз предоставляет ряд под-

ходящих методов. Причем весьма необычных.

Эти методы вызываются не у объекта, а у класса (статические методы). То

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

<имя класса>.<имя метода> ([<список значений параметров, разделенных запятыми>])

Обычно в качестве статических методов реализуются части логики, манипу-

лирующие не свойствами объектов данного класса, а сторонними данными.

Как в случае с преобразованием типов данных.

Методы, выполняющие преобразование строк в числа, возвращают значение.

Это значение принадлежит к совершенно особому, чисто "компьютерному"

типу данных — логическому, или bool. Величина логического типа может

принимать всего два значения: true ("истина") и false ("ложь"). В данном

случае значение true означает, что преобразование удалось, а значение

false — что не удалось (например, из-за того, что пользователь ввел какую-

нибудь абракадабру вроде "абвгд", которую никак нельзя преобразовать

в число).

Для представления чисел мы используем тип double — число с плавающей

точкой. Ведь пользователь может ввести дробное число, не так ли?

Page 75: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 63

Что ж, пора писать новый обработчик события. Начнем, попутно рассматри-

вая его выражения.

string sInches;

double sgInches, sgMillimetres;

bool isConverted;

Объявляем четыре переменные:

� строковую sInches — для хранения значения, введенного в поле ввода

txtInches;

� с плавающей точкой sgInches и sgMillimetres — для хранения изначаль-

ного значения в дюймах и преобразованного в миллиметрах соответст-

венно;

� логическую isConverted — для хранения признака того, удалось ли преоб-

разование.

sInches = txtInches.Text;

Присваиваем переменной sInches значение, введенное в поле ввода

txtInches.

isConverted = double.TryParse(sInches, out sgInches);

Собственно преобразуем строковое значение в числовое с плавающей точкой.

Для этого используем статический метод TryParse класса double (вообще-то,

double — не класс, а структура, но пока будем считать, что это класс). Этот

метод принимает два параметра:

� преобразуемое строковое значение (оно у нас хранится в переменной

sInches);

� переменная, в которую будет помещено преобразованное значение, если,

конечно, преобразование удалось (sgInches). Ключевое слово out говорит

о том, что эта переменная получает значение, вычисленное в данном

методе, а не задает значение его параметра.

Если преобразование удалось, метод TryParse вернет логическое значение

true, в противном случае — значение false. Его мы присвоим переменной

isConverted и используем потом.

Далее идет очень большое выражение, занимающее несколько строк. Рас-

смотрим его по частям.

if (isConverted)

{

sgMillimetres = sgInches * 25.4;

}

Page 76: Дронов В. Самоучитель Silverlight 3 (2010)

64 Часть I. Введение в Silverlight. Наше первое приложение

Ключевое слово if говорит о том, что перед нами условное выражение. Это

выражение берет логическое значение, указанное после ключевого слова if

в скобках (значение переменной isConverted), и, если оно равно true, выпол-

няет нижеследующий код, заключенный в фигурные скобки. Если же это зна-

чение равно false, код в фигурных скобках не выполняется.

А код этот выполняет умножение значения, хранящегося в переменной

sgInches (уже преобразованное значение в дюймах), на 25,4 и помещает по-

лученный результат в переменную sgMillimetres. Поскольку мы уже прове-

рили, равно ли значение переменной isConverted true, т. е. успешно ли вы-

полнилось преобразование введенного в поле ввода txtInches строкового

значения в число, переменная sgInches гарантированно хранит числовое зна-

чение, которое можно без проблем умножить на другое число.

И обязательно обратим внимание на само число 25,4. Оно записывается

с точкой вместо запятой — 25.4. Таким образом в C# записываются все числа

с плавающей точкой.

else

{

sgMillimetres = 0;

}

Если условное выражение выяснит, что указанное после ключевого слова if

в скобках значение равно false, выполнится код, стоящий после ключевого

слова else в фигурных скобках. И переменная sgMillimetres получит значе-

ние 0.

txtMillimetres.Text = sgMillimetres + "мм.";

Ну, здесь почти нечего комментировать... Кроме одного. Если мы сложим

число (значение переменной sgMillimetres) и строку ("мм."), среда исполне-

ния Silverlight вполне резонно посчитает, что мы на самом деле хотим вы-

полнить конкатенацию строк, и сама при компиляции выполнит неявное пре-

образование числа в строку. Такая она умная!

Вот и весь код.

Удалим код предыдущего обработчика события и введем на его место код

нового. Сохраним файл MainPage.xaml.cs и запустим приложение. Введем в

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

поле ввода Миллиметры появится преобразованное в миллиметры значение.

Если же мы введем в поле ввода Дюймы нечисловое значение, то в поле вво-

да Миллиметры появится ноль.

А это значит, что наше первое Silverlight-приложение наконец-то заработало!

Page 77: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 65

Выявление ошибок

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

Но Visual Web Developer 2008 идет навстречу неряхам и торопыгам. Так, если мы ошибемся, набирая ключевое слово, он подчеркнет нашу ошибку волни-стой красной линией. Наведя на подчеркнутый код курсор мыши, мы полу-чим на экране подсказку, в чем именно мы ошиблись; правда, подсказка эта будет на английском языке...

Если мы запустили приложение, не исправив ошибку, Visual Web Developer 2008 выведет окно-предупреждение, предлагающее нам две воз-можности. Во-первых, мы можем отменить компиляцию и запустить скомпи-лированную ранее версию приложения, нажав кнопку Yes. Во-вторых, мы можем вообще отменить запуск приложения и исправить ошибку, нажав кнопку No. Второй вариант явно полезнее.

Если при компиляции в исходном коде были найдены ошибки, Visual Web Developer 2008 выведет на экран панель Error List (рис. 3.12). Она содержит организованный в виде таблицы список всех найденных ошибок; столбец Description показывает описание ошибки (по-английски), File — имя файла, в котором встретилась ошибка, Line — номер строки, а Column — номер столбца. Если дважды щелкнуть на какой-либо строке этого списка, соответ-ствующая ей ошибка будет найдена в исходном коде и выделена.

Рис. 3.12. Панель Error List

Исправив все ошибки, лучше всего закрыть панель Error List, чтобы она не занимала место, — все равно Visual Web Developer 2008 выведет ее снова, если обнаружит ошибки при следующей компиляции. Для этого достаточно щелкнуть мышью кнопку закрытия, расположенную в правой части заголовка этой панели; данная кнопка имеет вид крестика.

Здесь нужно сказать еще, что во время запуска приложения Visual Web Developer 2008 выводит панель Output, в которой показывает сообщения, описывающие различные стадии компиляции. Она похожа на панель Error List, поэтому автор решил не приводить ее рисунок. Пользы от этой панели не очень много, но она всегда остается на экране после компиляции. Так что ее тоже лучше закрыть.

Page 78: Дронов В. Самоучитель Silverlight 3 (2010)

66 Часть I. Введение в Silverlight. Наше первое приложение

Файловые операции в Visual Web Developer 2008

Осталось описать файловые операции и способы их выполнения в Visual Web

Developer 2008. Это не займет много страниц.

Время от времени файл, с которым идет работа, следует сохранять. Проще

всего это сделать, нажав комбинацию клавиш <Ctrl>+<S>. Также можно на-

жать кнопку Save <имя файла, открытого в активном окне документа>

( ) на панели инструментов или выбрать одноименный пункт меню File.

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

комбинацию клавиш <Ctrl>+<Shift>+<S>. Можно еще нажать кнопку Save

All ( ) на панели инструментов или выбрать одноименный пункт меню File.

Закрыть файл, открытый в активном окне документа, проще всего щелчком

на кнопке закрытия этого окна. Эта кнопка имеет вид крестика и расположе-

на в той же линии, в которой выводятся ярлычки окон документа, но справа

от них, в районе панели Solution Explorer. Закрыть файл также можно выбо-

ром пункта Close меню File.

Если требуется закрыть не один файл, а весь проект, следует выбрать пункт

Close Project меню File.

Открыть проект можно выбором пункта Open Project меню File или нажати-

ем комбинации клавиш <Ctrl>+<Shift>+<O>. На экране появится диалоговое

окно Open Project, похожее на стандартное диалоговое окно открытия файла

Windows. В нем нужно выбрать файл проекта (он имеет расширение csproj),

проверить, установлен ли переключатель Close Solution, и нажать кнопку

открытия.

Если в диалоговом окне Open Project установлен переключатель Close

Solution, Visual Web Developer 2008 перед открытием выбранного проекта за-кроет уже открытый. Если же в этом окне установить переключатель Add to

Solution, на основе уже открытого проекта будет создано решение, в которое

также будет добавлен открываемый проект. (О решениях говорилось в начале этой главы.)

Также Visual Web Developer 2008 позволяет открыть произвольный файл лю-

бого типа, поддерживаемого им. Для этого достаточно нажать кнопку Open

File ( ) панели инструментов, выбрать одноименный пункт меню File или

нажать комбинацию клавиш <Ctrl>+<O>. На экране появится диалоговое ок-

Page 79: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 3. Наше первое Silverlight-приложение 67

но Open File, похожее на стандартное диалоговое окно открытия файла

Windows. В нем нужно выбрать открываемый файл и нажать кнопку от-

крытия.

Закрыть Visual Web Developer 2008 проще всего щелчком на кнопке закрытия

его главного окна. Также можно выбрать пункт Exit меню File.

Что дальше?

Что ж, разговор о Visual Web Developer 2008 и работе в нем получился очень

долгим. Зато мы узнали много нового и наконец-то создали свое первое

Silverlight-приложение!

Создали-то создали, но у нас накопилась уйма вопросов, на которые мы не

получили ответы в этой главе. Ответам на некоторые из них будет посвящена

следующая глава.

Page 80: Дронов В. Самоучитель Silverlight 3 (2010)

68 Часть I. Введение в Silverlight. Наше первое приложение

Page 81: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ II

Сборки, пространства имен, страницы, компоненты

и ресурсы

Глава 4. Сборки и пространства имен

Глава 5. Страницы и контейнеры

Глава 6. Основные компоненты

Глава 7. Вывод графики и мультимедийных данных

Глава 8. Ресурсы сборки

Page 82: Дронов В. Самоучитель Silverlight 3 (2010)
Page 83: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 4

Сборки и пространства имен

В предыдущей главе мы наконец-то создали свое первое Silverlight-

приложение. Заодно "галопом по европам" познакомились с Visual Web

Developer 2008, языками XAML и C#, тегами и выражениями, типами данных

и их преобразованиями, коллекциями и статическими методами. Впечатляет!

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

самое, но полнее и подробнее. Торопиться не будем — у нас впереди еще

20 глав.

И начнем мы с того, что посмотрим на файлы, из которых состоит проект

нашего приложения. Позднее будет разговор о сборках, библиотеках, про-

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

Файлы, из которых состоит проект

Давайте запустим проводник или любую другую программу для управления

файлами и откроем в ней папку, где хранится проект нашего первого

Silverlight-приложения Converter. По умолчанию все проекты Visual Web

Developer 2008 сохраняет в папке <папка с документами текущего пользова-

теля>\Visual Studio 2008\Projects.

Первое, что мы обнаружим, — Visual Web Developer 2008 создал для проекта

отдельную папку и назвал ее так же, как и сам проект. Откроем эту папку.

Что мы видим? Три папки и множество файлов. Оставим папки на потом и

рассмотрим файлы.

� Файл проекта имеет то же имя, что и сам проект, и расширение csproj.

Этот файл — "сердце" любого проекта; можно даже сказать, что это и есть

сам проект.

Page 84: Дронов В. Самоучитель Silverlight 3 (2010)

72 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

� Файл дополнительных параметров проекта имеет то же имя, что и сам проект, и расширение csproj.user.

� Файл решения содержит список всех проектов (фактически файлов проек-та), входящих в решение, имеет то же имя, что и само решение, и расши-рение sln.

При создании проекта Visual Web Developer 2008 всегда создает решение, даже если мы его об этом не просим. Он дает этому решению то же имя, что и проекту.

� Файл дополнительных параметров решения имеет то же имя, что и само решение, и расширение suo.

� Файл исходного кода, содержащий XAML-код, имеет то же имя, что и имя определяемого в нем класса, и расширение xaml.

� Файл исходного кода, содержащий C#-код, имеет то же имя, что и имя определяемого в нем класса, и расширение xaml.cs.

Для каждого класса, определяющего функциональность страницы или самого приложения, Visual Web Developer 2008 создает два файла исходного кода; один содержит XAML-код, другой — C#-код. Эти файлы имеют одинаковое имя (о чем уже говорилось чуть ранее), но разные расширения.

Класс приложения всегда имеет имя App, а класс главной страницы —

MainPage. Стало быть, исходный код класса приложения хранится в файлах App.xaml и App.xaml.cs, а исходный код класса главной страницы — в фай-лах MainPage.xaml и MainPage.xaml.cs.

Больше ничего интересного для нас здесь нет. Так что обратим внимание на папки.

Папка Properties для нас особого интереса не представляет. Там хранятся файлы, описывающие параметры приложения, в частности, номер его версии. Эти параметры лучше задавать, пользуясь средствами Visual Web Developer 2008 (как их задать, будет описано в главе 24).

Папка obj хранит служебные файлы, созданные самим Visual Web De-veloper 2008 в процессе разработки и компиляции приложения. Мы заглянем туда потом, а сейчас оставим ее в покое.

Отметим только, что в этой папке хранятся, в том числе, и файлы App.g.cs и

MainPage.g.cs. Они содержат часть исходного кода C# классов App и MainPage соответственно; этот код сгенерирован Visual Web Developer 2008 и жизнен-но необходим для нормальной работы приложения.

А вот папка Bin и ее содержимое заметно интереснее. Она хранит папку Debug, где находятся файлы уже откомпилированного приложения. Откроем ее и посмотрим, что там.

Page 85: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 4. Сборки и пространства имен 73

А там мы видим следующее...

� Файл сборки (о сборках речь пойдет чуть позже), содержащий наше при-

ложение. Он представляет собой библиотеку DLL, чье имя совпадает

с именем приложения.

� Файл манифеста, хранящий сведения о нашем приложении; эти сведения

необходимы для его выполнения. Он имеет имя AppManifest.xaml.

Эти два файла фактически представляют собой наше приложение. Однако

к распространению оно еще не готово.

� Файл пакета. Он представляет собой архив ZIP, имеет имя, совпадающее

с именем приложения, расширение xap и содержит файл сборки нашего

приложения и файл его манифеста. Если мы откроем этот файл с по-

мощью любой программы-архиватора, сможем убедиться в этом сами.

� Файл тестовой Web-страницы, имеющий имя, совпадающее с именем

приложения. Этот файл служит для запуска нашего приложения в Web-

обозревателе.

А два этих файла мы уже можем распространять в Интернете.

� Служебный файл, имеющий имя, которое совпадает с именем приложе-

ния, и расширение pdb. Этот файл генерируется самим Visual Web

Developer 2008 и необходим для отладки приложения (возможности Visual

Web Developer 2008 по отладке описаны в его интерактивной справке).

Сборки

Но что же такое сборка?..

Сборка — это результат компиляции любого проекта в Visual Web

Developer 2008, файл, содержащий весь исполняемый код нашего прило-

жения.

Обычно файлы, содержащие исполняемый код приложения (запускаемые

файлы), имеют расширение exe. Но исполняемый файл Silverlight-при-

ложения имеет расширение dll, т. е. представляет собой динамическую биб-

лиотеку Windows. Таково требование платформы Silverlight.

Сборка может быть не приложением, а библиотекой. Библиотека (их также

называют библиотечными сборками) содержит классы (а возможно, и целые

компоненты), которые сторонние разработчики смогут использовать для на-

писания своих приложений, и также представляет собой динамическую биб-

лиотеку Windows. Visual Web Developer 2008 позволяет создавать библиотеки

так же легко, как и приложения.

Вот библиотеки стоят того, чтобы поговорить о них.

Page 86: Дронов В. Самоучитель Silverlight 3 (2010)

74 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Библиотеки

Что нужно, чтобы писать Silverlight-приложения? Классы. Огромное количе-

ство классов: компонентов, структур данных, вспомогательных и служебных. Эти классы предоставляет нам среда исполнения Silverlight.

Сама же среда исполнения Silverlight является ничем иным, как набором биб-

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

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

Visual Web Developer 2008 при создании нового проекта подключает к нему

все стандартные библиотеки. Для простых приложения (наподобие нашего

Converter) их обычно хватает.

Выяснить, какие библиотеки подключены к проекту, нам поможет знакомая по главе 3 панель Solution Explorer (см. рис. 3.2). Для этого достаточно раз-

вернуть "ветвь" References, что присутствует в иерархическом списке этой панели. Каждый пункт данной "ветви" соответствует одной из подключенных

библиотек.

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

ками или нами. Сторонних библиотек для платформ Silverlight и .NET сейчас достаточно много, и мы сможем найти среди них подходящую.

Все сторонние библиотеки перед использованием должны быть зарегистри-

рованы в Visual Web Developer 2008, чтобы он "узнал" об их существовании. Как это сделать, можно узнать из интерактивной справки.

Кстати, в составе Silverlight 3 Tools уже поставляется несколько сторонних

библиотек; они содержат множество полезнейших классов (в том числе и компонентов), без которых зачастую просто не обойтись. Эти библиотеки

уже зарегистрированы в Visual Web Developer 2008.

Если мы поместим на страницу любой компонент, определенный в сторонней

библиотеке, Visual Web Developer 2008 сам подключит ее к проекту. Но если мы собираемся использовать класс, не являющийся компонентом, то под-

ключать библиотеку к проекту нам придется самим.

Проще всего сделать это, щелкнув правой кнопкой мыши на "ветви" References иерархического списка панели Solution Explorer и выбрав в по-

явившемся контекстном меню пункт Add Reference. На экране появится диа-логовое окно Add Reference (рис. 4.1).

Здесь все просто: выбираем в большом списке, занимающем бо́льшую часть этого окна, нужную библиотеку и нажимаем кнопку OK. Имена сборок,

Page 87: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 4. Сборки и пространства имен 75

в которых реализованы библиотеки, отображаются в колонке Component

Name этого списка. Если мы передумали подключать библиотеку к проекту, нажмем кнопку Cancel.

Рис. 4.1. Диалоговое окно Add Reference

Отключить ненужную библиотеку от проекта еще проще — достаточно щелкнуть правой кнопкой мыши на соответствующем пункте в "ветви" References списка панели Solution Explorer и выбрать в контекстном меню пункт Remove.

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

Библиотеки — один из способов объединить классы сходного назначения для удобства доступа и управления ими. Второй способ сделать это — использо-вать пространства имен.

Понятие пространства имен

Пространство имен — это сущность, объединяющая классы, имеющие сходное назначение. Так, существуют пространства имен, объединяющие классы компонентов, классы базовых типов данных (строк и чисел), классы коллекций и пр.

Пространства имен обладают замечательной особенностью — они могут

вкладываться друг в друга (вложенные пространства имен). Обычно ее ис-

Page 88: Дронов В. Самоучитель Silverlight 3 (2010)

76 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

пользуют для создания своего рода "структуры" классов: общее пространство

имен для классов, скажем, компонентов, в нем — пространства имен для

классов компонентов, обеспечивающих ввод данных, компонентов для выво-

да графики и мультимедийных данных, сложных компонентов (таблица,

иерархический список и пр.). Такие структуры из многократно вложенных

друг в друга пространств имен встречаются в Silverlight сплошь и рядом. Так,

класс TextBox (поле ввода) определен в пространстве имен Controls, которое

вложено в пространство имен Windows, вложенное, в свою очередь, в про-

странство имен System.

Сборка должна содержать, по крайней мере, одно пространство имен. В жиз-

ни бывает по-всякому. Так, сборка-приложение практически всегда содержит

только одно пространство имен, а сборка-библиотека зачастую содержит

множество.

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

в нескольких сборках (например, пространство имен System.Windows.Controls).

Последний вариант, правда, встречается нечасто.

При создании проекта Visual Web Developer 2008 сам создает для него про-

странство имен, чье имя совпадает с именем проекта. И сейчас мы в этом

убедимся.

Откроем файл MainPage.xaml.cs и найдем в нем такую конструкцию:

namespace Convertor

{

. . .

}

Ключевое слово namespace определяет само пространство имен. За ним долж-

но следовать ее имя, а потом, в фигурных скобках, — код классов, опреде-

ляемых в этом пространстве имен.

Уже понятно, что пространство имен должно иметь имя. Это имя обязано

быть уникальным в том пространстве имен, в которое вложено данное про-

странство имен. Если же пространство имен никуда не вложено, его имя не

должно совпадать с именами пространств имен, определенных в используе-

мых библиотеках, стандартных и сторонних.

Чтобы создать вложенное пространство имен, его определение просто пи-

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

имен, вот так:

namespace Converter

{

namespace Converter_Inner

Page 89: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 4. Сборки и пространства имен 77

{

. . .

}

}

Здесь пространство имен Converter_Inner вложено в пространство имен

Converter. Хотя для нашего простенького приложения такая конструкция бу-дет явно лишней...

При подключении библиотеки к проекту все определенные в ней пространства имен становятся тотчас доступными в коде C#. Дополнительно подключать их не требуется.

Полные имена пространств имен и классов

При определении пространства имен в исходном коде мы пишем только его сокращенное имя. Но для использования пространства имен нам понадобится другое его имя — полное.

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

Давайте возьмем приведенный ранее пример кода:

namespace Converter

{

namespace Converter_Inner

{

. . .

}

}

Здесь полное имя пространства имен Convertor_Inner будет таким:

Convertor.Convertor_Inner. Фактически мы в полном имени перечислили все пространства имен, в которые оно вложено.

Полное имя самого "внешнего" пространства имен будет совпадать с его со-кращенным именем — оно ведь никуда не вложено. Так, полное имя про-

странства имен Converter в приведенном ранее примере будет таким же —

Converter.

Page 90: Дронов В. Самоучитель Silverlight 3 (2010)

78 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

А полное имя класса создается объединением полного имени пространства

имен, в котором он определен, и обычного, сокращенного, имени класса,

между которыми ставится точка.

namespace Converter

{

namespace Converter_Inner

{

class DataClass

{

}

}

}

В данном примере класс DataClass будет иметь полное имя

Convertor.Convertor_Inner.DataClass. Класс TextBox будет иметь полное имя

System.Windows.Controls.TextBox, а класс String — System.String.

Кстати! Помните XAML-тег <UserControl>, создающий страницу, и его атри-

бут x:Class, указывающий имя класса создаваемой страницы? Значение этого

атрибута — Convertor.MainPage. Полное имя класса страницы нашего прило-

жения!

Отображение пространств имен

По идее, Silverlight требует, чтобы обращение к любому классу выполнялось по его полному имени. Но, во-первых, полные имена классов зачастую очень

длинны (класс TextBox имеет еще далеко не самое длинное полное имя...), а во-вторых и в-последних, мы их никогда не использовали! Мы писали только сокращенные имена. Так, объявляя в главе 3 переменную строкового типа,

мы писали String вместо System.String. В чем же дело?

А в том, что Visual Web Developer 2008 хранит в памяти особый список ука-занных нами пространств имен. Встретив в коде сокращенное имя класса, он

просматривает в его поисках все пространства имен, перечисленные в этом списке, пока не найдет нужный класс.

А для занесения пространства имен в этот список применяется особое выра-

жение C#. В его начале стоит ключевое слово using, за ним через пробел —

полное имя пространства имен. Ну и в конце не забываем поставить знак ; — признак конца выражения.

Подобные выражения выполняют отображение пространства имен. Они должны находиться в самом начале исходного кода C#, перед всеми про-

странствами имен и классами. Это нужно, чтобы Visual Web Developer 2008 "увидел" их в первую очередь.

Page 91: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 4. Сборки и пространства имен 79

Эти выражения мы можем найти в начале любого файла исходного кода C#:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

Их создает для нашего удобства сам Visual Web Developer 2008. Так что мы

можем использовать в нашем коде только сокращенные имена классов.

Единственный случай, когда следует использовать только полные имена

классов — если сразу в нескольких пространствах имен определены классы

с одинаковыми именами. Тут уж нам придется явно указать класс, указав его

полное имя.

Пространства имен в XAML-коде. Префиксы

Мы много говорили о пространствах имен в C#-коде. А что же XAML-код?

В нем все одновременно сложнее и проще.

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

имен в XAML несколько иные, чем в C#. Это второе.

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

внутреннее представление, о котором разработчики Silverlight ничего не го-

ворят. Этот код обрабатывается особой подсистемой среды исполнения

Silverlight, которая зачастую "не в курсе" того, что творится в исполняемом

коде MSIL, в который компилируется исходный код C#. Поэтому мы должны

сами указать этой подсистеме пространства имен, где она будет искать клас-

сы компонентов.

Два основных пространства имен, правда, за нас подключит сам Visual Web

Developer 2008. Это выполняется с помощью специальных атрибутов xmlns

тега <UserControl>, создающего саму страницу.

Вот первый из них:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

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

выполнения кода XAML будет искать все классы, если не указано иное. Про-

Page 92: Дронов В. Самоучитель Silverlight 3 (2010)

80 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

странство имен по умолчанию содержит все классы компонентов, определен-

ных в стандартных библиотеках Silverlight.

Второй атрибут xmlns тега <UserControl>:

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

подключает дополнительное пространство имен. Оно определяет некоторые

дополнительные атрибуты тегов, перед именами которых стоит префикс x:

(x:Class, x:Name и некоторые другие). Если мы присмотримся к этому атрибу-

ту тега, то увидим в нем этот префикс (выделен полужирным шрифтом).

Если же мы собираемся использовать на странице компонент из пространства

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

вставить в тег <UserControl> соответствующий атрибут xmlns вида:

xmlns:<префикс>="clr-namespace:<полное имя пространства имен>;

�assembly=<имя файла сборки, в которой определено это пространство имен,

�без расширения>"

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

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

го вида:

<<префикс, определенный ранее>:<имя класса компонента>>

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

Проще всего это показать на примере. Давайте поместим на единственную

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

дату. Перетащим компонент Calendar из панели Toolbox в XAML-код стра-

ницы и посмотрим, что получится.

Прежде всего, в теге <UserControl> появится новый атрибут, созданный са-

мим Visual Web Developer 2008:

xmlns:controls="clr-namespace:System.Windows.Controls;

�assembly=System.Windows.Controls"

Он подключит "нестандартную" часть пространства имен System.Windows.

Controls, которая определена в сборке System.Windows.Controls.dll, не вхо-дящей в состав пространства имен по умолчанию, и присвоит ей префикс

controls. Этот префикс будет использоваться для создания компонентов,

определенных в этой, нестандартной, части пространства имен

System.Windows.Controls.

Page 93: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 4. Сборки и пространства имен 81

А вот тег, создающий сам компонент Calendar:

<controls:Calendar></controls:Calendar>

Обратим внимание, что перед именем класса Calendar стоит префикс

controls. Он дает Visual Web Developer 2008 знать, что данный класс опреде-

лен в сборке System.Windows.Controls.dll, которую мы подключили ранее.

Это, кстати, одна из причин, по которой лучше всего для помещения на стра-

ницу компонентов пользоваться панелью Toolbox. Visual Web Developer 2008

сам внесет в XAML-код нужные изменения: подключит пространство имен,

сборку и, возможно, библиотеку, задаст префикс и проставит его в тегах.

Что дальше?

В этой главе мы познакомились с ключевыми понятиями платформы

Silverlight: сборками, библиотеками, пространствами имен, полными имена-

ми и префиксами. То есть опять занимались теорией.

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

Silverlight: соответствующие им классы, их свойства, методы, события и

примеры использования. И начнем мы со страниц и контейнеров.

Page 94: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 5

Страницы и контейнеры

В предыдущей главе мы выяснили, что такое сборки, библиотеки и простран-

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

платформы Silverlight.

В этой главе мы приступим к изучению компонентов Silverlight. Начнем мы

с контейнеров как основополагающих единиц, из которых строится пользова-

тельский интерфейс Silverlight-приложений. Напоследок мы познакомимся со

страницами Silverlight; знакомство это будет весьма коротким.

Контейнеры

Silverlight предоставляет разработчикам три типа контейнеров. В главе 2, рас-

сматривая интерфейс Silverlight-приложений, мы с ними уже познакоми-

лись — это "таблица", "стопка" и "холст". Настала пора разобраться с ними

всерьез.

Контейнер "таблица"

Контейнер "таблица" организует воображаемую таблицу, в ячейках которой

размещаются компоненты.

Контейнеру "таблица" соответствует класс Grid. Этот класс определен в про-

странстве имен по умолчанию, так что ничего подключать к тегу

<UserControl> нам не нужно.

Контейнер "таблица" создается с помощью парного тега <Grid>, внутри кото-

рого помещаются теги, создающие различные компоненты: элементы управ-

ления, надписи, рисунки, другие контейнеры и пр.

Page 95: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 5. Страницы и контейнеры 83

<Grid>

<TextBlock Text="Дюймы"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

<Button Content="Преобразовать"></Button>

<TextBlock Text="Миллиметры"></TextBlock>

<TextBox Text="0" Width="100" Height="22" IsReadOnly="true"></TextBox>

</Grid>

Мы можем задать размеры компонента "таблица", воспользовавшись свойст-

вами Width и Height класса Grid. Первое из этих свойств задает ширину таб-

лицы, второе — высоту. Оба этих значения задаются в пикселах в виде чисел

с плавающей точкой (Double), но практически всегда задаются как целые чис-

ла (т. к. при выводе эти значения все равно округляются).

<Grid Width="400" Height="300">

Если ширина или высота не задана, то контейнер растянется, соответственно,

на всю ширину или высоту страницы или контейнера, в котором он находит-

ся ("резиновый" контейнер). Иногда это бывает полезно, но в остальных слу-

чаях лучше задавать размеры контейнера явно.

При изменениях размеров окна Web-обозревателя размеры "резинового" кон-

тейнера также будут меняться. В этом случае нам могут пригодиться сле-

дующие четыре свойства класса Grid:

� MaxWidth — максимальная ширина, до которой контейнер сможет увеличи-

ваться;

� MaxHeight — максимальная высота, до которой контейнер сможет увели-

чиваться;

� MinWidth — минимальная ширина, до которой контейнер сможет умень-

шаться;

� MinHeight — минимальная высота, до которой контейнер сможет умень-

шаться.

Значения этих четырех свойств задаются в пикселах и должны иметь тип

числа с плавающей точкой (Double), но практически всегда указываются

в виде целых чисел.

<Grid MaxWidth="600" MaxHeight="400" MinWidth="100" MinHeight="100">

Полезное свойство Margin задает отступы от границ области, выделенной

внешним контейнером или самой страницей для размещения контейнера, до

границ самого данного контейнера. Его значение задается в следующем фор-

мате:

<отступ слева>,<отступ сверху>,<отступ справа>,<отступ снизу>

Page 96: Дронов В. Самоучитель Silverlight 3 (2010)

84 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Все эти величины, опять же, указываются в пикселах как числа с плавающей точкой.

<Grid Margin="5,10,5,10">

Еще два полезных свойства: HorizontalAlignment и VerticalAlignment. Они задают выравнивание контейнера внутри выделенной ему области "внешне-го" контейнера или страницы, соответственно, по горизонтали и вертикали.

Эти свойства имеют тип перечисления. Перечисление — особая разновид-ность данных, набор предопределенных значений (элементов), каждый из которых имеет имя. Перечисления очень удобны для выбора какого-либо ва-рианта из нескольких возможных.

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

Так и есть! Свойство HorizontalAlignment имеет тип перечисления HorizontalAlignment. Он определяет четыре элемента:

� Left — выравнивание по левой стороне отведенного для данного компо-нента места в контейнере;

� Center — выравнивание по центру отведенного для данного компонента места в контейнере;

� Right — выравнивание по правой стороне отведенного для данного ком-понента места в контейнере;

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

Свойство VerticalAlignment имеет тип перечисления VerticalAlignment. Он определяет четыре элемента:

� Top — выравнивание по верхней стороне отведенного для данного компо-нента места в контейнере;

� Center — выравнивание по центру отведенного для данного компонента места в контейнере;

� Bottom — выравнивание по нижней стороне отведенного для данного ком-понента места в контейнере;

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

Если значение какого-либо из этих свойств не задано, среда исполнения

Silverlight предполагает, что оно равно Stretch (и создает "резиновый" кон-

Page 97: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 5. Страницы и контейнеры 85

тейнер). Программисты в таком случае говорят, что это значение по умолча-

нию.

<Grid Width="400" Height="300" HorizontalAlignment="Left"

�VerticalAlignment="Bottom">

Еще одно полезное свойство класса Grid — ShowGridLines. Оно имеет логиче-

ский тип; значение true указывает отобразить рамки ячеек воображаемой

таблицы, а значение false — не выводить их. Значение по умолчанию — ра-

зумеется, false.

<Grid ShowGridLines="true">

Свойство ShowGridLines, вероятно, будет полезно только при создании поль-

зовательского интерфейса, чтобы выяснить, находятся ли компоненты на

своих законных местах в контейнере. Перед окончательной компиляцией

приложения его лучше отключить.

И еще одно свойство, без которого в некоторых случаях не обойтись, —

Visibility. Оно позволяет сделать контейнер невидимым, до поры до време-

ни, разумеется.

Это свойство имеет тип перечисления Visibility, который содержит всего

два элемента: Visible (контейнер видим; значение по умолчанию) и Collapsed

(контейнер не видим). Понятно, что в последнем случае все содержащиеся

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

Свойство Visibility полезно в том случае, если мы собираемся управлять

видимостью контейнера из C#-кода.

<Grid x:Name=grdContainer"" Visibility="Collapsed">

. . .

grdContainer.Visibility = Visibility.Visible;

Обратим внимание, как в C#-коде указывается элемент перечисления. Снача-

ла пишется имя самого типа перечисления (в примере ранее — Visibility),

потом ставится точка, а за ней — собственно имя нужного элемента

(Visible).

По умолчанию контейнер "таблица" имеет всего одну ячейку, или, говоря

другими словами, одну строку и один столбец. В эту единственную ячейку он

помещает все присутствующие в нем компоненты. Конечно, это подойдет

нам только в том случае, если мы собираемся поместить в данный контейнер

только один компонент (так тоже делают). Но если у нас в контейнере мно-

жество компонентов, которые мы собираемся "раскидать" по ячейкам вооб-

ражаемой таблицы, нам придется принять некоторые меры. А именно, явно

задать количество строк и столбцов таблицы.

Page 98: Дронов В. Самоучитель Silverlight 3 (2010)

86 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Строки таблицы задаются с помощью свойства RowDefinitions. Этому свой-

ству присваивается объект класса RowDefinitionCollection; этот объект пред-

ставляет собой коллекцию объектов класса RowDefinition, представляющего

функциональность строки воображаемой таблицы. Каждый такой объект в

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

дать коллекцию из стольких объектов класса RowDefinition, сколько нам

нужно строк.

Мы не сможем задать значение свойства, являющееся объектом, с помощью

атрибутов тегов. Для этого случая XAML предоставляет нам объектную

запись. Она, собственно, и сообщает среде исполнения Silverlight, что значе-

нием данного свойства является объект.

Объектная запись для присвоения значения свойству включает в себя:

� парный тег XAML, определяющий свойство, значение которого мы долж-

ны задать, вида <<имя класса>.<имя свойства>>. Это тег помещается внутрь

тега, создающего сам объект, свойству которого присваивается значение;

� теги XAML, создающие сами объекты, которые станут значением этого

свойства.

Вот пример задания значения для свойства RowDefinitions:

<Grid>

<Grid.RowDefinitions>

<RowDefinition></RowDefinition>

<RowDefinition></RowDefinition>

<RowDefinition></RowDefinition>

<RowDefinition></RowDefinition>

</Grid.RowDefinitions>

. . .

</Grid>

Для указания свойства RowDefinitions класса Grid мы использовали тег

<Grid.RowDefinitions>, который поместили прямо в тег <Grid>. А внутрь тега

<Grid.RowDefinitions> мы вставили набор из четырех тегов <RowDefinition>,

создающих коллекцию из четырех объектов класса RowDefinition. Значит,

наша воображаемая таблица будет иметь четыре строки.

Класс RowDefinition поддерживает свойство Height, которое позволяет задать

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

� число с плавающей точкой — высота строки в пикселах;

� Auto — строка примет такую высоту, чтобы вместить содержимое всех ее

ячеек;

� * — строка займет все остальное свободное пространство контейнера.

Page 99: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 5. Страницы и контейнеры 87

Мы также можем задать значение высоты строки вида <целое число>*. В этом

случае все свободное пространство контейнера будет разделено на сумму

всех целых чисел, а строки получат высоту, равную произведению частного от

этого деления на соответствующее целое число.

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="25"></RowDefinition>

<RowDefinition Height="Auto"></RowDefinition>

<RowDefinition Height="*"></RowDefinition>

<RowDefinition Height="2*"></RowDefinition>

</Grid.RowDefinitions>

. . .

</Grid>

Столбцы таблицы задаются с помощью свойства ColumnDefinitions. Значение

этого свойства должно принадлежать типу ColumnDefinitionCollection —

коллекции объектов класса ColumnDefinition, представляющего функцио-

нальность столбца воображаемой таблицы.

Класс ColumnDefinition поддерживает свойство Width, которое позволяет за-

дать ширину столбца таблицы. Она может быть задана в виде значений тех

же трех типов, что поддерживает свойство Height класса RowDefinition.

<Grid>

. . .

<Grid.ColumnDefinitions>

<ColumnDefinition Width="100"></ColumnDefinition>

<ColumnDefinition Width="Auto"></ColumnDefinition>

<ColumnDefinition Width="*"></ColumnDefinition>

</Grid.ColumnDefinitions>

. . .

</Grid>

Для указания, какой компонент в какой ячейке воображаемой таблицы дол-

жен находиться, служат следующие атрибуты тега:

Grid.Row="<номер строки таблицы, начиная с нуля>" Grid.Column="<номер столбца таблицы, начиная с нуля>"

Эти атрибуты помещаются прямо в тег, создающий компонент, который

нужно установить в заданную ячейку таблицы.

<Grid>

. . .

<TextBlock Grid.Row="0" Grid.Column="0" Text="Дюймы"></TextBlock>

Page 100: Дронов В. Самоучитель Silverlight 3 (2010)

88 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

<TextBox Grid.Row="0" Grid.Column="1" Text="0" Width="100"

�Height="22"></TextBox>

<Button Grid.Row="1" Grid.Column="1" Content="Преобразовать"></Button>

<TextBlock Grid.Row="2" Grid.Column="0" Text="Миллиметры"></TextBlock>

<TextBox Grid.Row="2" Grid.Column="1" Text="0" Width="100" Height="22"

�IsReadOnly="true"></TextBox>

</Grid>

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

ячеек воображаемой таблицы, объединив их в одну. Для этого к нашим услу-

гам — атрибуты тега вида

Grid.RowSpan="<количество ячеек, объединяемых по вертикали>" Grid.ColumnSpan="<количество ячеек, объединяемых

�по горизонтали>"

Эти атрибуты тегов также помещаются в тег, создающий компонент, который

нужно установить в заданную ячейку таблицы, и используются совместно

с их "коллегами" Grid.Row и Grid.Column.

Атрибут тега Grid.RowSpan указывает количество ячеек, которые будут объ-

единены в одну по вертикали; при этом ячейка, чей номер указан атрибутами

тегов Grid.Row и Grid.Column, будет "прирастать" расположенными ниже ее

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

тикали.

А атрибут тега Grid.ColumnSpan указывает количество ячеек, которые будут

объединены в одну по горизонтали; ячейка, чей номер указан атрибутами

тегов Grid.Row и Grid.Column, будет "расти" вправо. Объединенная ячейка,

таким образом, будет вытянута по горизонтали.

<Grid>

. . .

<Button Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"

�Content="Преобразовать"></Button>

. . .

</Grid>

В данном примере кода кнопка займет ячейку, получившуюся в результате

объединения двух ячеек второй строки (имеющей номер 1) воображаемой

таблицы.

Вот, пожалуй, и все о контейнере "таблица".

Page 101: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 5. Страницы и контейнеры 89

Контейнер "стопка"

Контейнер "стопка", вероятно, самый простой из всех контейнеров Silverlight. Он выстраивает вложенные в него компоненты по вертикали сверху вниз или горизонтали слева направо.

Контейнеру "стопка" соответствует класс StackPanel. Этот класс определен в пространстве имен по умолчанию.

Контейнер "стопка" создается с помощью парного тега <StackPanel>, внутри которого помещаются теги, создающие различные компоненты: элементы управления, надписи, рисунки, другие контейнеры и пр.

<StackPanel>

<TextBlock Text="Дюймы"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

<Button Content="Преобразовать"></Button>

<TextBlock Text="Миллиметры"></TextBlock>

<TextBox Text="0" Width="100" Height="22" IsReadOnly="true"></TextBox>

</StackPanel>

Класс StackPanel поддерживает свойство Orientation, позволяющее указать направление, по которому контейнер выстраивает компоненты. Это свойство

имеет тип перечисления Orientation, который определяет два элемента:

� Vertical — контейнер выстраивает компоненты по вертикали сверху вниз (это значение по умолчанию);

� Horizontal — контейнер выстраивает компоненты по горизонтали слева направо.

Кроме того, класс StackPanel поддерживает свойства Width, Height, MinWidth,

MinHeight, MaxWidth, MaxHeight, Margin, HorizontalAlignment, VerticalAlignment

и Visibility, знакомые нам по контейнеру "таблица".

<StackPanel Width="200" Height="800" HorizontalAlignment="Right">

Контейнер "холст"

Контейнер "холст" позволяет разработчику самому указать местоположение размещенных в нем компонентов. Это позволяет расставить компоненты произвольным образом.

Контейнеру "холст" соответствует класс Canvas. Этот класс определен в про-странстве имен по умолчанию.

Контейнер "холст" создается с помощью парного тега <Canvas>, внутри кото-

рого помещаются теги, создающие различные компоненты: элементы управ-

ления, надписи, рисунки, другие контейнеры и пр.

Page 102: Дронов В. Самоучитель Silverlight 3 (2010)

90 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

<Canvas>

<TextBlock Text="Дюймы"></TextBlock>

<TextBox Text="0" Width="100" Height="22"></TextBox>

<Button Content="Преобразовать"></Button>

<TextBlock Text="Миллиметры"></TextBlock>

<TextBox Text="0" Width="100" Height="22" IsReadOnly="true"></TextBox>

</Canvas>

Кроме того, класс Canvas поддерживает свойства Width, Height, MinWidth,

MinHeight, MaxWidth, MaxHeight, Margin, HorizontalAlignment, VerticalAlignment

и Visibility, знакомые нам по контейнеру "таблица".

<Canvas Width="400" Height="300" HorizontalAlignment="Center"

�VerticalAlignment="Center">

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

тега:

Canvas.Left="<горизонтальная координата>" Canvas.Top="<вертикальная координата>"

Горизонтальная координата отсчитывается от левой границы контейнера, а вертикальная — от верхней границы. Обе координаты указываются в виде

чисел с плавающей точкой (Double) в пикселах.

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

<Canvas>

<TextBlock Canvas.Left="20" Canvas.Top="20" Text="Дюймы"></TextBlock>

<TextBox Canvas.Left="100" Canvas.Top="20" Text="0" Width="100"

�Height="22"></TextBox>

<Button Canvas.Left="100" Canvas.Top="60"

�Content="Преобразовать"></Button>

<TextBlock Canvas.Left="20" Canvas.Top="100"

�Text="Миллиметры"></TextBlock>

<TextBox Canvas.Left="100" Canvas.Top="100" Text="0" Width="100"

�Height="22" IsReadOnly="true"></TextBox>

</Canvas>

Компоненты, находящиеся в контейнере "холст", могут перекрывать друг друга. По умолчанию компонент, определенный в XAML-коде позже, пере-крывает компонент, определенный раньше. Однако мы можем переопреде-лить порядок перекрытия компонентов, указав для них так называемый Z-индекс — своего рода третью координату компонента. Z-индекс указывает-ся в виде целого числа и обозначает своего рода "высоту" компонента над поверхностью "холста"; компоненты с бо́льшим Z-индексом перекрывают компоненты с меньшим Z-индексом.

Page 103: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 5. Страницы и контейнеры 91

Для указания Z-индекса компонентов в контейнере служит такой атрибут

тега:

Canvas.ZIndex="<Z-индекс>"

Как уже говорилось, Z-индекс указывается в виде целого числа. А сам атри-

бут помещается в тег, создающий компонент.

<TextBlock Canvas.Left="20" Canvas.Top="20" Canvas.ZIndex="2"

�Text="Дюймы"></TextBlock>

<TextBlock Canvas.Left="20" Canvas.Top="20" Canvas.ZIndex="1"

�Text="Миллиметры"></TextBlock>

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

Z-индекс (вот только кому нужны перекрывающиеся надписи...).

Страница

Мы уже знаем, что класс каждой страницы приложения уникален и создается

самим ее разработчиком. И создается он на основе класса UserControl, насле-

дуя все его свойства, методы и события.

Однако из всего этого богатства нам будет полезно только одно событие —

Loaded. Оно возникает сразу после вывода страницы со всем ее содержимым

на экран и сигнализирует о том, что страница готова к работе.

Обычно в обработчике этого события выполняются различные предуста-

новки.

private void UserControl_Loaded(object sender, RoutedEventArgs e)

{

txtInches.Text = "0";

}

Здесь мы написали метод — обработчик этого события, в котором присваи-

ваем свойству Text компонента txtInches строковое значение "0". После это-

го в поле ввода Дюймы нашего Silverlight-приложения появится ноль.

Обратим внимание, что этот метод принимает два параметра. (Собственно, об

этом уже шла речь в главе 3.)

� Первый параметр хранится в переменной sender, имеет тип object (базо-

вый класс иерархии Silverlight, на основе которого, прямо или косвенно,

созданы все остальные классы) и хранит указатель на объект, к которому

привязан данный обработчик события.

� Второй параметр хранится в переменной e и представляет собой объект

класса RoutedEventArgs. Этот класс предоставляет функциональность для

Page 104: Дронов В. Самоучитель Silverlight 3 (2010)

92 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

хранения сведений о самом событии; правда, обычно они никак не исполь-

зуются в обработчике.

Это справедливо для любого обработчика событий в Silverlight. Единствен-

ное: тип второго параметра различается у разных событий. И со временем мы

в этом убедимся.

Что дальше?

Разговор о контейнерах получился не очень длинным, а о странице — совсем

коротким. Зато мы узнали достаточно много, чтобы начать разбирать другие

компоненты Silverlight — элементы управления. Их довольно много, и речь

о них пойдет в следующей главе.

Page 105: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 6

Основные компоненты

В предыдущей главе мы начали знакомиться с компонентами Silverlight. Зна-

комство это ограничивалось контейнерами как основополагающими едини-

цами для "строительства" интерфейса Silverlight-приложений. Рассмотрение

всех прочих компонентов мы отложили на потом.

В этой главе мы займемся изучением компонентов-"неконтейнеров". Мы рас-

смотрим бо́льшую часть компонентов из предлагаемых нам средой исполне-

ния Silverlight и сторонними библиотеками, поставляемыми в составе

Silverlight 3 Tools. Также мы обязательно разберем самые полезные из

поддерживаемых ими свойств, методов и событий и научимся ими пользо-

ваться.

Основных компонентов, предоставляемых нам платформой Silverlight, весьма

много. Думается, мы можем написать уйму приложений, пользуясь только

ими и не привлекая сторонние разработки.

Надпись TextBlock

C компонентом TextBlock и соответствующим классом мы уже знакомы по

главе 3. Он позволяет вывести на страницу строку текста; это может быть

надпись к элементу управления, заголовок, пояснение или что-то еще. Класс

TextBlock определен в пространстве имен по умолчанию.

Как мы уже знаем, компонент TextBlock создается тегом <TextBlock>. (Это

справедливо для всех остальных компонентов.)

<TextBlock Text="Дюймы"></TextBlock>

Для указания самого текста надписи используется свойство Text. Строка,

присвоенная этому свойству, будет выведена на страницу.

Page 106: Дронов В. Самоучитель Silverlight 3 (2010)

94 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Также класс TextBlock поддерживает свойства Width, Height, MinWidth,

MinHeight, MaxWidth, MaxHeight, Margin, HorizontalAlignment, VerticalAlignment

и Visibility, знакомые нам по контейнеру "таблица" (см. главу 5).

<TextBlock Text="Дюймы" Width="100" Margin="5,0,5,0"></TextBlock>

Свойство TextAlignment задает выравнивание текста. Оно имеет тип перечис-

ления TextAlignment с тремя элементами:

� Left — выравнивание текста по левому краю (значение по умолчанию);

� Center — по центру;

� Right — по правому краю.

<TextBlock Text="Дюймы" TextAlignment="Center"></TextBlock>

Свойство TextDecorations задает дополнительное "украшение" текста надпи-

си. Его значение должно быть объектом класса TextDecorationCollection.

В данный момент Silverlight поддерживает всего одно такое "украшение" —

подчеркивание текста. Задать его в XAML-коде можно так:

<TextBlock Text="Дюймы" TextDecorations="Underline"></TextBlock>

А в C#-коде так:

lblInches.TextDecorations = TextDecorations.Underline;

Здесь lblInches — компонент текстовой надписи.

Тут мы столкнулись с так называемыми статическими свойствами классов.

Подобно знакомым нам по главе 3 статическим методам, эти свойства вызы-

ваются не у объектов, а у самих классов. Так, в представленном ранее выра-

жении TextDecorations — это класс, а Underline — его статическое свойство;

это свойство содержит объект класса TextDecorationCollection, задающий

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

Если нужно в C#-коде убрать подчеркивание у надписи, следует присвоить

свойству TextDecorations особое значение null. Оно обозначает отсутствие

всяких значимых данных.

lblInches.TextDecorations = null;

Если нам нужно, чтобы надпись выполняла перенос строк текста, мы исполь-

зуем свойство TextWrapping. Оно имеет тип перечисления TextWrapping с дву-

мя элементами: NoWrap (перенос строк не выполняется; значение по умолча-

нию) и Wrap (перенос строк выполняется по пробелам).

<TextBlock Text="Это очень-очень-очень-очень-очень длинная строка."

�TextWrapping="Wrap"></TextBlock>

Page 107: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 95

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

<отступ слева>,<отступ сверху>,<отступ справа>,<отступ снизу>

Все эти величины указываются в пикселах как числа с плавающей точкой.

<TextBlock Text="Дюймы" Padding="20,2,20,2"></TextBlock>

А сейчас очередь за свойствами, описывающими шрифт, которым будет на-бран текст надписи.

Свойство FontFamily задает название шрифта. Оно задается в виде строки, содержащей имя шрифта или сразу несколько имен, разделенных запятыми. Список имен всех поддерживаемых Silverlight шрифтов можно найти на странице Layout, Text, and Input / Text and Fonts документации по Silverlight.

<TextBlock Text="Дюймы" FontFamily="Courier New"></TextBlock>

<TextBlock Text="Дюймы" FontFamily="Verdana,Arial"></TextBlock>

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

Значение этого свойства по умолчанию — строка "Portable User Interface", представляющая некий универсальный шрифт, который среда исполнения Silverlight выбирает сама из имеющихся на компьютере.

Свойство FontSize задает размер шрифта в виде числа с плавающей точкой в пунктах.

<TextBlock Text="Хорошего шрифта должно быть много!"

�FontFamily="Courier New" FontSize="36"></TextBlock>

Значение по умолчанию — 11 пунктов.

Свойство FontStretch задает относительное расстояние между символами

текста. Оно имеет тип структуры FontStretch. (Структура — это особый объект с несколько "урезанной" функциональностью. Отличие структуры от полноценного объекта мы рассмотрим в главе 10.)

Для задания значения свойству FontStretch используются статические свой-

ства класса FontStretches. Вот они:

� UltraCondensed, ExtraCondensed, Condensed и SemiCondensed — задают сжа-тый текст, от сильно сжатого до почти не сжатого;

� Normal — задает обычный текст со стандартным межсимвольным расстоя-

нием (это значение по умолчанию);

Page 108: Дронов В. Самоучитель Silverlight 3 (2010)

96 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

� SemiExpanded, Expanded, ExtraExpanded и UltraExpanded — задают разрежен-

ный текст, от почти не разреженного до сильно разреженного.

Каждое из этих свойств содержит значение типа FontStretch, задающее соот-

ветствующее относительное расстояние между символами.

Значение свойства FontStretch задается в XAML-коде уже знакомым нам об-

разом:

<TextBlock Text="Дюймы" TextStretch="Expanded"></TextBlock>

Так же и в C#-коде:

lblInches.TextStretch = TextStretches.Expanded;

Здесь lblInches — компонент текстовой надписи.

Свойство FontStretch довольно странное — задание для него значения не

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

автора...

Свойство FontStyle позволяет задать начертание шрифта текста: обычное или

курсивное. Оно имеет тип структуры FontStyle. Значение ему задается с по-

мощью статических свойств класса FontStyles, которых всего два: Normal

(обычное начертание; значение по умолчанию) и Italic (курсивное начерта-

ние). Эти свойства содержат значения типа FontStyle, задающие соответст-

вующие начертания текста.

<TextBlock Text="Дюймы" TextStyle="Italic"></TextBlock>

lblInches.TextStyle = TextStyles.Italic;

Свойство FontWeight задает "жирность" шрифта. Оно имеет тип структуры

FontWeight. Значение ему задается с помощью статических свойств класса

FontWeights Thin, ExtraLight, Light, Normal, Medium, SemiBold, Bold, ExtraBold,

Black и ExtraBlack. Эти свойства содержат значения типа FontWeight, задаю-

щие соответствующие величины "жирности" текста, от самого "светлого" до

самого "жирного". Значение Normal соответствует обычному тексту (это зна-

чение по умолчанию), а значение Bold — полужирному.

<TextBlock Text="Дюймы" TextWeight="Bold"></TextBlock>

lblInches.TextWeight = TextWeights.Bold;

Осталось рассмотреть свойство LineHeight, задающее высоту строки текста.

Высота строки указывается в виде числа с плавающей точкой в пикселах.

По умолчанию это свойство имеет значение Double.NaN — своего рода "не-

число"; оно указывает среде исполнения Silverlight самой подбирать высоту

Page 109: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 97

строки. Мы можем сами присвоить это значение свойству LineHeight

в C#-коде, если хотим вернуть это поведение по умолчанию:

lblInches.LineHeight = Double.NaN;

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

использовать только в C#-коде.

� ActualWidth — содержит (или, как говорят программисты, возвращает)

значение текущей ширины компонента в виде числа с плавающей точкой

в пикселах. Доступно только для чтения, т. к. текущая ширина на самом

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

свойству.

� ActualHeight — содержит значение текущей высоты компонента в виде

числа с плавающей точкой в пикселах. Доступно только для чтения.

� Name — содержит имя компонента, заданное атрибутом тега x:Name.

Использование компонента TextBlock

для вывода форматированного текста

А теперь начинается самое интересное! С помощью компонента TextBlock мы

можем вывести на страницу текст, различные фрагменты которого набраны

разными шрифтами.

Дело в том, что класс TextBlock поддерживает свойство Inlines. Значение

этого свойства имеет тип InlineCollection — коллекцию объектов класса

Inline. Для задания значения этого свойства в объекте класса мы используем

знакомую нам по главе 5 объектную запись.

<TextBlock>

<TextBlock.Inlines>

. . .

</TextBlock.Inlines>

</TextBlock>

Сам класс Inline нас мало интересует, но вот два класса, созданные на его

основе, — Run и LineBreak — очень даже пригодятся.

Класс Run представляет фрагмент текста произвольной длины, к которому

применены единые правила форматирования. Для задания текста использует-

ся уже знакомое нам свойство Text, а для указания параметров шриф-

та — свойства FontFamily, FontSize, FontStretch, FontStyle, FonrWeight и

TextDecorations, которые нам также знакомы.

<Run FontFamily="Courier New" FontSize="18" Text="Компонент"></Run>

Page 110: Дронов В. Самоучитель Silverlight 3 (2010)

98 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Как мы видим, для создания объекта этого класса используется XAML-тег

<Run>.

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

шрифта прямо в теге <TextBlock> (для самого компонента TextBlock) или в

теге <Run> (для отдельного фрагмента текста в нем). При этом параметры, за-

данные для компонента, будут действовать на все фрагменты текста — объ-

екты класса Run, присутствующие в коллекции. А параметры, заданные для

фрагмента текста, будут действовать только в нем.

<TextBlock FontSize="18">

<TextBlock.Inlines>

<Run FontFamily="Courier New" Text="Компонент"></Run>

<Run FontFamily="Arial" Text="TextBlock"></Run>

</TextBlock.Inlines>

</TextBlock>

Здесь фрагмент текста "Компонент" будет выведен шрифтом Courier New, а

фрагмент "TextBlock" — шрифтом Arial. В обоих случаях размер шрифта со-

ставит 18 пунктов.

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

ды: и для компонента, и для фрагмента текста. В этом случае значение, за-

данное для фрагмента текста, заменит (как говорят программисты, перекроет)

значение, заданное для компонента.

<TextBlock FontFamily="Courier New" FontSize="18">

<TextBlock.Inlines>

<Run FontSize="12" Text="Компонент"></Run>

<Run FontFamily="Arial" Text="TextBlock"></Run>

</TextBlock.Inlines>

</TextBlock>

Здесь фрагмент "Компонент" будет выведен шрифтом Courier New размером

12 пунктов, а фрагмент "TextBlock" — шрифтом Arial размером 18 пунктов.

Все фрагменты текста, определенные объектами класса Run, выводятся в одну

строку. Так, приведенный ранее XAML-код выведет на экран строку

"КомпонентTextBlock". (Конечно, мы можем включить для надписи перенос

строк, но это дела не меняет.) Однако мы можем разбить выводимый текст на

отдельные строки, использовав объекты другого класса — LineBreak.

<TextBlock FontFamily="Courier New" FontSize="18">

<TextBlock.Inlines>

<Run FontSize="12" Text="Компонент"></Run>

<LineBreak></LineBreak>

<Run FontFamily="Arial" Text="TextBlock"></Run>

Page 111: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 99

</TextBlock.Inlines>

</TextBlock>

После этого фрагменты текста "Компонент" и "TextBlock" будут выведены в разных строках.

Поле ввода TextBox

C компонентом TextBox и соответствующим классом мы также знакомы по

главе 3. Он создает на странице поле ввода и определен в пространстве имен по умолчанию.

<TextBox Text="0"></TextBox>

Свойство Text задает или возвращает значение, указанное в поле ввода.

Класс TextBox поддерживает свойства Width, Height, MinWidth, MinHeight,

MaxWidth, MaxHeight, Margin, HorizontalAlignment, VerticalAlignment и

Visibility. Также он поддерживает свойства ActualWidth, ActualHeight,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Name, Padding,

TextAlignment, TextWrapping, знакомые нам по компоненту TextBlock.

<TextBox Text="0" Width="100" Height="22" TextAlignment="0"></TextBlock>

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

свойства указывается в виде целого числа. Если указано значение 0, длина вводимой строки не ограничена; это, кстати, значение свойства по умолча-нию.

Свойство SelectedText задает или возвращает строку, представляющую вы-деленный посетителем фрагмент значения, занесенного в поле ввода. Если

в поле ввода вообще ничего не выделено, свойство SelectedText содержит

пустую (ничего не содержащую) строку ""; это значение свойства по умолча-нию.

Данное свойство будет более полезно в C#-коде.

if (txtTest.SelectedText == string.Empty)

{

. . .

}

Здесь мы используем условное выражение, чтобы сравнить значение свойст-

ва SelectedText объекта txtTest класса TextBox со значением статического

свойства Empty класса string (которое представляет собой ту самую пустую

строку). Оператор сравнения == сравнивает значения, стоящие слева и справа

от него, и, если они равны, возвращает логическое значение true, а если не

Page 112: Дронов В. Самоучитель Silverlight 3 (2010)

100 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

равны — значение false. Если условие в данном выражении выполнится (т. е.

в свойстве SelectedText объекта txtTest содержится пустая строка — в поле

ввода txtTest ничего не выделено), выполнится код, стоящий ниже в фигур-ных скобках (не показан).

Свойства SelectionStart и SelectionLength задают или возвращают, соответ-ственно, номер первого символа и длину выделенного пользователем фраг-мента значения в поле ввода в символах. Оба эти значения задаются в виде целых чисел, причем нумерация символов в строке начинается с нуля. Если

в поле ввода вообще ничего не выделено, свойство SelectionStart содер- жит номер символа, на котором стоит текстовый курсор, а свойство

SelectionLength — 0.

Оба этих свойства также будут более полезны в C#-коде.

if (txtTest.SelectionLength > 0)

{ . . .

}

Оператор сравнения > сравнивает значения, стоящие слева и справа от него,

и, если левое значение больше правого, возвращает логическое значение true,

в противном случае — значение false.

Свойство IsEnabled позволяет сделать поле ввода временно недоступным для

пользователя. Его значение должно иметь логический тип; значение true де-

лает поле ввода доступным (значение по умолчанию), а значение false — недоступным. Как правило, это свойство также используется в C#-коде.

<TextBox x:Name="txtTest" IsEnabled="false"></TextBox> . . .

txtTest.IsEnabled = true;

Свойство IsReadOnly позволяет сделать поле ввода доступным только для

чтения, для чего ему достаточно присвоить логическое значение true. Логи-

ческое значение false, наоборот, делает его доступным и для чтения, и для ввода (это значение по умолчанию).

Свойство AcceptsReturn позволяет указать, может ли пользователь при вводе текста в данное поле разбивать его на абзацы нажатием клавиши <Enter> (это может пригодиться в полях ввода, вмещающих несколько строк по вертика-

ли). Его значение имеет логический тип; значение true позволяет это делать,

а значение false — не позволяет (значение по умолчанию).

<TextBox Width="200" Height="100" TextWrapping="Wrap"

�AcceptsReturn="true"></TextBox>

Свойства HorizontalScrollBarVisibility и VerticalScrollBarVisibility ука-

зывают, будет ли в поле ввода присутствовать, соответственно, горизонталь-

Page 113: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 101

ная или вертикальная полоса прокрутки. Эти свойства принимают значения

типа перечисления ScrollBarVisibility с четырьмя элементами:

� Auto — полоса прокрутки появляется только при необходимости;

� Hidden — полоса прокрутки не появляется, но пользователь может про-

кручивать содержимое поля ввода перемещением текстового курсора

(значение по умолчанию);

� Visible — полоса прокрутки присутствует всегда;

� Disabled — полоса прокрутки не появляется, и пользователь даже не мо-

жет прокручивать содержимое поля ввода перемещением текстового кур-

сора (очень странный вариант...).

<TextBox Width="200" Height="100" TextWrapping="Wrap"

�AcceptsReturn="true" VerticalScrollBarVisibility="Visible"></TextBox>

Многие знают, что перемещаться по элементам управления Windows-окна

можно нажатием клавиш; клавиша <Tab> перемещает фокус ввода на сле-

дующий элемент управления, а комбинация клавиш <Shift>+<Tab> — на

предыдущий. Этот же прием работает и в Silverlight-приложениях.

Изначально "обход" компонентов на странице Silverlight по нажатию клави-

ши <Tab> выполняется в том порядке, в котором они были созданы в XAML-

коде. Но мы сами можем задать этот порядок как нам заблагорассудится. Для

этого предназначено свойство TabIndex. Его значение в виде целого числа

указывает номер данного компонента в порядке обхода.

<TextBox x:Name="txtInches" Text="0" TabIndex="0"></TextBlock>

<TextBox x:Name="txtMillimetres" Text="0" TabIndex="1"></TextBlock>

А свойство IsTabStop позволяет вообще исключить данный компонент из по-

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

Значение true (которое является значением по умолчанию) возвращает ком-

понент в порядок обхода.

Теперь поговорим о методах класса TextBox. Их немного.

Метод Focus() устанавливает в поле ввода фокус ввода. Он не принимает па-

раметров и возвращает логическое значение true, если фокус ввода удалось

установить, и false, если не удалось. Практически всегда возвращаемое им

значение игнорируется.

txtInches.Focus();

Метод SelectAll() выделяет все содержимое поля ввода. Он не принимает

параметров и не возвращает результата.

Page 114: Дронов В. Самоучитель Silverlight 3 (2010)

102 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

А метод Select() позволяет выделить фрагмент содержимого поля ввода. Вот формат его вызова:

Select(<номер первого символа выделяемого фрагмента>,

�<длина выделяемого фрагмента в символах>)

Нумерация символов в строке всегда начинается с нуля — не забывайте об

этом. Значения данный метод не возвращает.

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

рассмотрим.

Событие SelectionChanged возникает, когда пользователь выделяет фрагмент

содержимого поля ввода или изменяет сделанное ранее выделение.

private void txtTest_SelectionChanged(object sender, RoutedEventArgs e)

{

string s;

s = txtTest.SelectedText;

}

Здесь мы написали метод — обработчик этого события, в котором объявляем

переменную s строкового типа и присваиваем ей строку, содержащую выде-

ленный в поле ввода txtTest фрагмент содержимого.

Событие TextChanged возникает, когда пользователь вводит в поле ввода но-

вое значение или изменяет уже существующее.

private void txtTest_TextChanged(object sender, TextChangedEventArgs e)

{

string s;

s = txtTest.Text;

}

В главе 3 мы выяснили, что вторым параметром в обработчик события переда-ется объект, чьи свойства хранят сведения о самом событии. Этот объект мо-жет иметь разный тип; так, у события SelectionChanged он имеет тип

RoutedEventArgs, а у события TextChanged — тип TextChangedEventArgs. Мы

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

Поле ввода пароля PasswordBox

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

вместо введенного в него текста отображаются точки. Такие поля ввода при-

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

Page 115: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 103

Для создания поля ввода пароля используется компонент PasswordBox. Его функциональность представляет одноименный класс, который определен в пространстве имен по умолчанию.

<PasswordBox Password="password"></PasswordBox>

Свойство Password задает или возвращает введенное в поле ввода значение в виде строки.

Свойство PasswordChar позволяет задать символ, которым будет "забиваться" реальное значение поля ввода.

<PasswordBox Password="password" PasswordChar="+"></PasswordBox>

Полезное событие PasswordChanged возникает, когда пользователь вводит в поле ввода новое значение или изменяет уже существующее; в этом оно

аналогичное событию TextChanged.

Класс PasswordBox также поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxLength,

MaxWidth, MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment,

Visibility и Width и методы Focus() и SelectAll().

Кнопка Button

И компонент Button с соответствующим ему классом нам знаком по главе 3. Он создает на странице кнопку и определен в пространстве имен по умол- чанию.

<Button Content="Преобразовать"></Button>

Для указания надписи для кнопки используется свойство Content.

<Button Content="Преобразовать" Width="100" Height="25"

�FontStyle="Italic" IsEnabled="false"></Button>

Свойство Content весьма интересно тем, что на самом деле принимает значе-

ние типа ContentControl. Класс ContentControl является родителем для всех классов компонентов Silverlight. А это значит, что мы можем поместить в кнопку любой компонент. Разумеется, использовав для этого объектную запись, о которой говорилось в главе 5 и в начале текущей главы.

<Button Width="100" Height="100">

<Button.Content>

<TextBlock Text="Это очень длинная надпись для кнопки."

�TextWrapping="Wrap"></TextBlock>

</Button.Content>

</Button>

Page 116: Дронов В. Самоучитель Silverlight 3 (2010)

104 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Мы только что поместили в кнопку большую многострочную надпись.

<Button Width="100" Height="120">

<Button.Content>

<StackPanel>

<TextBlock Text="Это очень длинная надпись для кнопки с флажком."

�TextWrapping="Wrap"></TextBlock>

<CheckBox></CheckBox>

</StackPanel>

</Button.Content>

</Button>

А здесь мы поместили в кнопку контейнер "стопка", а в него — многостроч-

ную надпись и — держитесь! — флажок (тег <CheckBox>)! Причем флажок

этот будет работать отдельно от самой кнопки.

Если же мы зададим значение свойства Content в виде строки, среда исполне-

ния Silverlight сама создаст на основе этой строки текстовую надпись (ком-

понент TextBlock). А, как мы уже знаем, все классы компонентов, в том числе

и TextBlock, порождены от класса ContentControl. Так что свойство Content

все равно получит значение типа ContentControl.

Еще одно свойство класса Button, которое может быть нам полезно, —

ClickMode. Оно позволяет указать, в какой именно момент произойдет нажа-

тие кнопки, и принимает значение типа перечисления ClickMode с тремя эле-

ментами:

� Release — только после отпускания нажатой ранее кнопки мыши или кла-

виши <Enter> или <Пробел> клавиатуры (значение по умолчанию);

� Press — сразу после нажатия кнопки мыши или клавиши <Enter> или

<Пробел> клавиатуры;

� Hover — сразу после наведения курсора мыши на кнопку.

<Button Content="Наведи мышь на кнопку — получишь результат!"

�ClickMode="Hover"></Button>

Событие Click возникает при щелчке на кнопке. Именно в его обработчик

помещается код, который должен выполняться в ответ на нажатие кнопки.

Класс Button также поддерживает знакомые нам свойства ActualHeight,

ActualWidth, FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Page 117: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 105

Флажок CheckBox

Компонент CheckBox (и соответствующий ему класс) создает на странице

флажок, который можно включить или отключить. Этот класс определен

в пространстве имен по умолчанию.

<CheckBox Content="Применить изменения"></CheckBox>

Для указания надписи для флажка используется свойство Content. Но, как и у

кнопки, его можно использовать для внедрения во флажок любого компонен-

та, т. к. оно имеет тип ContentControl.

<CheckBox Width="100" Height="120">

<CheckBox.Content>

<TextBlock Text="Это очень длинная надпись для флажка."

�TextWrapping="Wrap"></TextBlock>

</CheckBox.Content>

</CheckBox>

Здесь мы, используя все ту же объектную запись, поместили во флажок мно-

гострочную надпись.

Обычно флажок может находиться только в двух состояниях — установлен-

ном и сброшенном. Но можно сделать так, что он сможет принять и третье

состояние — неопределенное, в котором он будет закрашен серым цветом. За

это "отвечает" свойство IsThreeState. Логическое значение true указывает

среде исполнения Silverlight, что данный флажок может принимать третье,

неопределенное, состояние, а значение false — не может (это значение по

умолчанию).

<CheckBox Content="Применить изменения" IsThreeState="true"></CheckBox>

Свойство IsChecked задает или возвращает состояние флажка. Логическое

значение true обозначает, что флажок установлен, значение false — что

сброшен (значение по умолчанию), а значение null — что находится в неоп-

ределенном состоянии (если свойство IsThreeState установлено в true).

<CheckBox Content="Применить изменения" IsChecked="true"></CheckBox>

. . .

if (chkOption.IsChecked)

{

. . .

}

Как видим, свойство IsChecked можно использовать и в C#-коде. Как раз там

оно, в основном, и используется — должны же мы узнать, установлен флажок

или нет.

Page 118: Дронов В. Самоучитель Silverlight 3 (2010)

106 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Класс CheckBox поддерживает метод Focus() и событие Click.

Еще этот класс поддерживает четыре полезных для нас события, которые пе-

речислены далее.

� Click — возникает при щелчке на флажке.

� Checked — возникает при установке флажка.

� Unchecked — возникает при сбросе флажка.

� Indeterminate — возникает при установке флажка в неопределенное со-

стояние.

Они используются для выполнения каких-то действий при установке и сбросе

флажка, например, предоставление доступа к какому-либо другому компо-

ненту. Событие Click, по мнению автора, самое удобное — для отслеживания

переключения флажка можно использовать всего один обработчик.

private void chkOption_Click(object sender, RoutedEventArgs e)

{

if (chkOption.IsChecked)

{

. . .

}

else

{

. . .

}

}

Класс CheckBox также поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Переключатель RadioButton

Компонент RadioButton (и соответствующий ему класс) создает на странице

переключатель. Этот класс определен в пространстве имен по умолчанию.

<RadioButton Content="Перевести в миллиметры"></RadioButton>

Для указания надписи для переключателя используется все то же свойство

Content. Как и у кнопки и у флажка, его можно использовать для внедрения

в переключатель любого компонента, т. к. оно имеет тип ContentControl.

Page 119: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 107

<RadioButton Width="100" Height="120">

<RadioButton.Content>

<TextBlock Text="Это очень длинная надпись для флажка."

�TextWrapping="Wrap"></TextBlock>

</RadioButton.Content>

</RadioButton>

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

От одного переключателя толку никакого (в этом случае лучше использовать

флажок), поэтому переключатели объединяют в группы. В такой группе мо-

жет быть включен только один переключатель.

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

группы. Оно задается в свойстве GroupName в виде строки.

<RadioButton Content="Перевести в миллиметры"

�GroupName="Opt"></RadioButton>

<RadioButton Content="Перевести в дюймы" GroupName="Opt"></RadioButton>

Здесь мы создали группу Opt из двух переключателей.

Класс RadioButton поддерживает свойства ActualHeight, ActualWidth,

ClickMode, FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsChecked, IsEnabled, IsTabStop, IsThreeState, Margin,

MaxHeight, MaxWidth, MinHeight, MinWidth, Name, Padding, TabIndex,

VerticalAlignment, Visibility и Width, метод Focus() и события Checked,

Click, Indeterminate и Unchecked.

Единственное "но": неопределенное состояние у переключателя практически

никогда не используется. Слишком уж это непривычно для пользователя...

Список ListBox

Компонент ListBox (и соответствующий ему класс) создает на странице спи-

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

<ListBox></ListBox>

Для задания пунктов списка класс ListBox поддерживает свойство Items,

в качестве значения которого используется объект класса ItemCollection.

Данный класс представляет собой коллекцию объектов класса

FrameworkElement, от которого порожден уже знакомый нам класс

ContentControl, а от него, в свою очередь, — все классы компонентов. Но мы

будем наполнять ее объектами класса ListBoxItem, который определяет функ-

циональность пункта списка.

Page 120: Дронов В. Самоучитель Silverlight 3 (2010)

108 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Для создания пунктов списка мы, опять же, используем объектную запись.

<ListBox>

<ListBox.Items>

<ListBoxItem Content="Дюймы"></ListBoxItem>

<ListBoxItem Content="Миллиметры"></ListBoxItem>

<ListBoxItem Content="Сантиметры"></ListBoxItem>

<ListBoxItem Content="Метры"></ListBoxItem>

</ListBox.Items>

</ListBox>

Класс ListBoxItem поддерживает свойство Content, которое используется для

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

ContentControl, поэтому мы можем указать в качестве пункта как простую

строку (как в приведенном ранее примере; в этом случае среда исполнения

Silverlight создаст на основе этой строки надпись), так и любой компонент.

Еще класс ListBoxItem поддерживает свойство IsSelected. Значение true это-

го свойства говорит о том, что данный пункт выбран, значение false — что

не выбран. Значение по умолчанию — false.

<ListBoxItem Content="Миллиметры" IsSelected="true"></ListBoxItem>

Также класс ListBoxItem поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, Margin, MaxHeight, MaxWidth, MinHeight,

MinWidth, Name, Padding, VerticalAlignment, Visibility и Width.

Теперь вернемся к классу ListBox. Мы его так толком и не рассмотрели.

Свойство SelectedIndex этого класса задает или возвращает номер выбранно-

го пункта в виде целого числа. Если ни один пункт не выбран, возвращается

число -1. Обычно это свойство используется только в C#-коде.

if (lstTest.SelectedIndex > -1)

{

btnGo.IsEnabled = true;

}

Не забываем, что нумерация любых объектов в памяти компьютера начинается с нуля.

Здесь мы проверяем, выбрал ли посетитель какой-либо пункт в списке

lstTest (больше ли значение его свойства SelectedIndex, чем -1), и, если вы-

брал, делаем доступной для пользователя кнопку btnGo (присваиваем ее свой-

ству IsEnabled значение true).

Page 121: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 109

Событие SelectionChanged возникает, когда пользователь выбирает в списке другой пункт.

Также класс ListBox поддерживает свойства ActualHeight, ActualWidth, FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height, HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth, MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и Width и метод Focus().

Раскрывающийся список ComboBox

Компонент ComboBox (и соответствующий ему класс) создает на странице рас-крывающийся список. Этот класс определен в пространстве имен по умол- чанию.

Стандартный раскрывающийся список Windows может предоставить пользова-телю возможность ввести нужное значение вручную. Но раскрывающийся спи-сок Silverlight такой возможности не предоставляет.

Для задания пунктов списка класс ComboBox поддерживает свойство Items, в качестве значения которого используется объект класса ItemCollection. Данный класс представляет собой коллекцию объектов класса FrameworkElement. Мы же будем наполнять ее объектами класса ComboBoxItem, который определяет функциональность пункта раскрывающегося списка и полностью аналогичен уже знакомому нам классу ListBoxItem.

<ComboBox>

<ComboBox.Items>

<ComboBoxItem Content="Дюймы"></ComboBoxItem>

<ComboBoxItem Content="Миллиметры"></ComboBoxItem>

<ComboBoxItem Content="Сантиметры"></ComboBoxItem>

<ComboBoxItem Content="Метры"></ComboBoxItem>

</ComboBox.Items>

</ComboBox>

Как и класс ListBox, ComboBox поддерживает свойство SelectedIndex и собы-

тие SelectionChanged.

Свойство MaxDropDownHeight задает максимальную высоту раскрывающегося списка после его раскрытия. Это значение задается в виде числа с плавающей точкой.

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

� DropDownOpened — возникает при раскрытии списка.

� DropDownClosed — возникает при закрытии списка.

Page 122: Дронов В. Самоучитель Silverlight 3 (2010)

110 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Также класс ComboBox поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Календарь Calendar

Компонент Calendar (и соответствующий ему класс) создает на странице ка-лендарь, в котором можно выбрать дату. Этот класс определен в той части

пространства имен System.Windows.Controls, что находится в библиотечной

сборке System.Windows.Controls.dll.

<controls:Calendar></controls:Calendar>

Обычно Visual Web Developer 2008 для указания на это пространство имен оп-ределяет при его подключении префикс controls. Мы тоже всегда будем его

использовать.

Свойство SelectedDate задает или возвращает значение выбранной в календа-ре даты. С этим свойством нужно держать ухо востро!

� В XAML-коде его значение задается в формате <год>/<номер меся-

ца>/<число>, где год указывается в виде четырехзначного числа, а номер

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

� В C#-коде для этого используется особый тип данных — дата и время

(DateTime), о котором мы поговорим в главе 10.

<controls:Calendar SelectedDate="2009/09/08"></controls:Calendar>

Здесь мы указали календарю изначально выбрать дату 8 сентября 2009 года.

Свойство IsTodayHighlighted позволяет указать календарю, выделять ли те-

кущую дату. Логическое значение true указывает выделять текущую дату,

значение false — не выделять. Значение по умолчанию — true.

Событие SelectedDatesChanged возникает, когда посетитель выбирает в ка-лендаре другую дату.

Также класс Calendar поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Page 123: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 111

Всплывающий календарь DatePicker

Компонент DatePicker (и соответствующий ему класс) создает на стра-

нице поле ввода даты с кнопкой справа, при нажатии которой появля-

ется календарь. Этот класс определен в той части пространства

имен System.Windows.Controls, что находится в библиотечной сборке

System.Windows.Controls.dll.

<controls:DatePicker></controls:DatePicker>

Как и класс Calendar, DataPicker поддерживает свойства IsTodayHighlighted и

SelectedDate и событие SelectedDatesChanged.

Свойство Text представляет значение, введенное посетителем в поле ввода

вручную, в виде строки. Обычно оно используется только в C#-коде.

Еще три события могут быть нам полезны.

� CalendarOpened — возникает при открытии всплывающего календаря.

� CalendarClosed — возникает при закрытии всплывающего календаря.

� DateValidationError — возникает, если компонент не смог преобразовать

введенное пользователем вручную значение в правильную дату.

Также класс DatePicker поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Регулятор Slider

Компонент Slider (и соответствующий ему класс) создает на странице регу-

лятор с бегунком. Этот класс определен в пространстве имен по умолчанию.

<Slider></Slider>

Свойство Value задает или возвращает значение, заданное пользователем при

помощи регулятора. Это значение задается в виде числа с плавающей точкой.

Набор из следующих четырех свойств служит для указания размерности ре-

гулятора. Значения всех этих свойств задаются в виде чисел с плавающей

точкой.

� SmallChange — указывает величину, на которую значение регулятора будет

увеличиваться или уменьшаться при нажатии клавиш-стрелок. Значение

по умолчанию — 0.1.

Page 124: Дронов В. Самоучитель Silverlight 3 (2010)

112 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

� LargeChange — указывает величину, на которую значение регулятора будет увеличиваться или уменьшаться при щелчке мышью на шкале регулятора. Значение по умолчанию — 1.

� Minimum — указывает минимальное значение, которое может задать поль-

зователь. Значение по умолчанию — 0.

� Maximum — указывает максимальное значение, которое может задать поль-

зователь. Значение по умолчанию — 10.

Свойство Orientation позволяет расположить регулятор горизонтально или вертикально. Это свойство имеет тип перечисления Orientation, который оп-ределяет два элемента:

� Horizontal — регулятор располагается горизонтально (значение по умол-чанию);

� Vertical — регулятор располагается вертикально.

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

� false — значение регулятора увеличивается при перемещении бегунка вправо или вверх (значение по умолчанию).

� true — значение регулятора увеличивается при перемещении бегунка вле-во или вниз.

Событие ValueChanged возникает при изменении значения регулятора.

Еще класс Slider поддерживает свойства ActualHeight, ActualWidth, FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height, HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth, MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Индикатор прогресса ProgressBar

Компонент ProgressBar (и соответствующий ему класс) создает на странице индикатор прогресса. Этот класс определен в пространстве имен по умолча-нию.

<ProgressBar></ProgressBar>

Свойство Value задает значение текущего прогресса для отображения на ин-дикаторе. Оно задается в виде числа с плавающей точкой.

Следующие два свойства служат для указания размерности шкалы у индика-

тора прогресса. Значения этих свойств задаются в виде чисел с плавающей

точкой.

Page 125: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 113

� Minimum — указывает минимальное значение прогресса, которое может

задать пользователь. Значение по умолчанию — 0.

� Maximum — указывает максимальное значение прогресса, которое может

задать пользователь. Значение по умолчанию — 100.

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

бика, который "растет" слева направо и показывает текущий прогресс какого-

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

в неопределенном состоянии, показывая некий "бесконечный" прогресс. Для

этого служит свойство IsIndeterminate. Значение false указывает индикатору

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

по умолчанию), а значение true — отобразить "бесконечный" прогресс.

Также класс ProgressBar поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, Margin, MaxHeight, MaxWidth, MinHeight,

MinWidth, Name, Padding, VerticalAlignment, Visibility и Width.

Панель с прокруткой ScrollViewer

Компонент ScrollViewer (и соответствующий ему класс) незаменим, если нам

нужно поместить что-либо крупное на небольшом пространстве страницы.

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

Компонент ScrollViewer создает на странице прямоугольное пространство,

называемое панелью и содержащее полосы прокрутки. На эту панель мы мо-

жем поместить содержимое любых размеров; полосы прокрутки позволят

нам просмотреть его целиком.

<ScrollViewer Width="100" Height="100"

�HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">

<ScrollViewer.Content>

<TextBox Width="200" Height="200"></TextBox>

</ScrollViewer.Content>

</ScrollViewer>

Здесь мы поместили на панель с прокруткой большое поле ввода.

Для размещения содержимого на панели с прокруткой используется свойство

Content типа ContentControl. Любой компонент можно поместить на панель.

Если же нам нужно разместить там несколько компонентов, мы воспользуем-

ся любым подходящим контейнером.

<ScrollViewer Width="100" Height="100"

�HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">

Page 126: Дронов В. Самоучитель Silverlight 3 (2010)

114 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

<ScrollViewer.Content>

<StackPanel>

<TextBlock Text="Большое поле ввода"></TextBlock>

<TextBox Width="200" Height="200"></TextBox>

</StackPanel>

</ScrollViewer.Content>

</ScrollViewer>

Здесь мы воспользовались контейнером "стопка", чтобы поместить на панель

с прокруткой большое поле ввода вместе с надписью.

Свойства HorizontalScrollBarVisibility и VerticalScrollBarVisibility ука-

зывают, будет ли на панели присутствовать, соответственно, горизонтальная

или вертикальная полосы прокрутки. Эти свойства принимают значения типа

перечисления ScrollBarVisibility с четырьмя элементами:

� Auto — полоса прокрутки появляется только при необходимости;

� Hidden — полоса прокрутки не появляется, но пользователь может про-кручивать содержимое поля ввода перемещением текстового курсора;

� Visible — полоса прокрутки присутствует всегда;

� Disabled — полоса прокрутки не появляется, и пользователь даже не мо-жет прокручивать содержимое поля ввода перемещением текстового кур-сора.

Для свойства HorizontalScrollBarVisibility значение по умолчанию —

Hidden, а для свойства VerticalScrollBarVisibility — Visible.

Также класс ScrollViewer поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus.

Блокнот с вкладками TabControl

Компонент TabControl (и соответствующий ему класс) создает на странице

знакомый нам по многим Windows-приложениям блокнот с вкладками; каж-

дая вкладка при этом содержит свой набор компонентов. Этот класс опреде-

лен в той части пространства имен System.Windows.Controls, что находится

в библиотечной сборке System.Windows.Controls.dll.

<controls:TabControl></controls:TabControl>

Для задания создания вкладок блокнота класс TabControl поддерживает свой-ство Items, в качестве значения которого используется объект класса

Page 127: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 115

ItemCollection. Как мы уже знаем, этот класс представляет собой коллекцию объектов класса FrameworkElement; в нашем случае это будут объекты класса

TabItem, который определяет функциональность вкладки.

<controls:TabControl Width="300" Height="100">

<controls:TabControl.Items>

<controls:TabItem Header="Первая вкладка">

<TextBlock Text="Это пример блокнота"

�TextWrapping="Wrap"></TextBlock>

</controls:TabItem>

<controls:TabItem Header="Вторая вкладка">

<StackPanel>

<TextBlock Text="Введите сюда что-нибудь"></TextBlock>

<TextBox></TextBox>

</StackPanel>

</controls:TabItem>

</controls:TabControl.Items>

</controls:TabControl>

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

Кроме того, класс TabItem поддерживает свойство Header, предназначенное для создания заголовка вкладки. Оно имеет тип object (самый базовый класс иерархии Silverlight, от которого порождены абсолютно все остальные клас-сы), что значит, что мы можем использовать в качестве заголовка как обыч-ный текст (как на приведенном ранее примере), так и любой компонент. На практике, однако, почти всегда используется обычный текст, много реже — текст с изображением.

Также класс TabItem поддерживает свойства ActualHeight, ActualWidth, FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height, HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth, MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Вернемся к классу TabControl. Он поддерживает полезное свойство SelectedIndex, которое задает или возвращает номер выбранной в данный момент вкладки в виде целого числа. Если ни одна вкладка не выбрана, воз-вращается число -1 (хотя трудно представить себе блокнот, в котором не вы-брана ни одна вкладка...).

Свойство TabStripPlacement позволяет указать блокноту, где выводить яр-

лычки вкладок с их заголовками. Значение этого свойства имеет тип пере-

числения Dock с четырьмя элементами:

Page 128: Дронов В. Самоучитель Silverlight 3 (2010)

116 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

� Top — ярлычки располагаются вдоль верхней границы блокнота (значение

по умолчанию);

� Bottom — вдоль нижней границы;

� Left — вдоль левой границы;

� Right — вдоль правой границы.

Обычно используется значение Top, заметно реже — Bottom. Значения Left и

Right применяются крайне редко.

Событие SelectionChanged возникает, когда пользователь выбирает в блокно-

те другую вкладку.

Еще класс TabControl поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus().

Пожалуй, на этом разговор об основных компонентах Silverlight можно за-

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

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

Пример использования компонентов

Теперь настала пора закрепить полученные знания. Попрактиковаться.

Давайте создадим новую версию нашего первого Silverlight-приложения

Convertor. Новая версия позволит переводить величины не только из дюймов

в миллиметры, но из миллиметров в дюймы, из футов в метры и из метров в

футы. Если мы уж живем рядом с англичанами и американцами с их едини-

цами мер, то должны же с ними как-то ладить!

Создадим новый проект Silverlight-приложения на языке C# и дадим ему имя

Convertor2. (Как это делается, было описано в главе 3.) Сразу же откроем

файл MainPage.xaml и заменим контейнер "таблица", используемый в качест-

ве главного, на контейнер "стопка", зададим для него горизонтальное распо-

ложение компонентов и высоту в 25 пикселов — этого достаточно для всех

компонентов, которые мы в него поместим. (Значения ширины мы зададим

Page 129: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 117

отдельно для каждого компонента.) В результате мы получим вот такой

XAML-код (тег, формирующий саму страницу, опущен)

<StackPanel x:Name="LayoutRoot" Orientation="Horizontal" Height="25">

</StackPanel>

В этом контейнере создадим следующие компоненты:

� раскрывающийся список cboMode с пунктами Дюймы в миллиметры, Миллиметры в дюймы, Футы в метры и Метры в футы;

� поле ввода txtToConvert для ввода преобразуемого значения;

� кнопку btnConvert с надписью Преобразовать;

� поле ввода txtConverted для вывода преобразованного значения.

В раскрывающемся списке cboMode первый пункт (Дюймы в миллиметры)

сделаем изначально выбранным. Кнопку btnConvert сделаем изначально не-

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

У нас должен получиться примерно такой XAML-код (теги, формирующие саму страницу и главный контейнер, опущены):

<ComboBox x:Name="cboMode" Width="180" Margin="10,0,5,0">

<ComboBox.Items>

<ComboBoxItem Content="Дюймы в миллиметры"

�IsSelected="true"></ComboBoxItem>

<ComboBoxItem Content="Миллиметры в дюймы"></ComboBoxItem>

<ComboBoxItem Content="Футы в метры"></ComboBoxItem>

<ComboBoxItem Content="Метры в футы"></ComboBoxItem>

</ComboBox.Items>

</ComboBox>

<TextBox x:Name="txtToConvert" Width="100" Margin="5,0,5,0"></TextBox>

<Button x:Name="btnConvert" Content="Преобразовать" IsEnabled="false"

�Margin="5,0,5,0"></Button>

<TextBox x:Name="txtConverted" IsReadOnly="true" Width="100"

�Margin="5,0,10,0"></TextBox>

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

Настала пора приняться за логику приложения.

Привяжем к кнопке btnConvert обработчик события Click. Вот его код:

double toConvert, converted;

if (double.TryParse(txtToConvert.Text, out toConvert))

Page 130: Дронов В. Самоучитель Silverlight 3 (2010)

118 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

{

if (cboMode.SelectedIndex == 0)

{

converted = toConvert * 25.4;

}

else

{

if (cboMode.SelectedIndex == 1)

{

converted = toConvert / 25.4;

}

else

{

if (cboMode.SelectedIndex == 2)

{

converted = toConvert * 0.3;

}

else

{

converted = toConvert / 0.3;

}

}

}

txtConverted.Text = converted.ToString();

}

else

{

txtConverted.Text = string.Empty;

}

Сначала мы выполняем преобразование введенного в поле ввода txtToConvert

строкового значения в число с плавающей точкой (тип double). Если преобра-

зование прошло неуспешно, мы помещаем в поле ввода txtConverted пустую

строку (ее содержит статическое свойство Empty класса string). Здесь все нам

знакомо.

Если же преобразование прошло успешно, мы выясняем, какой пункт рас-

крывающегося списка cboMode выбран. Нам придется последовательно срав-

нить значение свойства SelectedIndex компонента cboMode, хранящее номер

выбранного в списке пункта, с номерами всех пунктов этого списка (0, 1, 2

и 3). Для этого мы используем несколько вложенных друг в друга условных

выражений; каждое из таких выражений сравнивает значение свойства

SelectedIndex с одним из номеров пунктов и, в случае успеха этого сравне-

Page 131: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 6. Основные компоненты 119

ния, выполняет соответствующее этому пункту преобразование. Оператор /

делит значение, стоящее слева, на значение, стоящее справа.

Напоследок мы помещаем готовое значение в поле ввода txtConverted, пред-

варительно выполнив явное преобразование типов вызовом метода

ToString(). Этот не принимающий параметров метод возвращает строковое

представление числа.

Теперь добавим нашему приложению немного "дуракоустойчивости" — сде-

лаем так, чтобы пользователь не смог нажать кнопку btnConvert, пока не вве-

дет что-либо в поле ввода txtToConvert. Для этого привяжем к полю ввода

txtToConvert такой обработчик события TextChanged:

if (txtToConvert.Text == string.Empty)

{

btnConvert.IsEnabled = false;

}

else

{

btnConvert.IsEnabled = true;

}

Ну, здесь совсем нечего комментировать! Мы сравниваем занесенное в поле

ввода txtToConvert значение с пустой строкой и, в зависимости от успешно-

сти или неуспешности сравнения, делаем кнопку btnConvert доступной или

недоступной.

Собственно, на этом разговор о компонентах закончен. На время.

Что дальше?

В этой главе мы очень много узнали об основных компонентах, предостав-

ляемых платформой Silverlight и чаще всего используемых для создания

пользовательского интерфейса. Да, богата Silverlight компонентами...

Но это еще не все! В следующей главе мы изучим еще два компонента, ис-

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

дийных данных. Так что по-быстрому переведем дух и продолжим.

Page 132: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 7

Вывод графики

и мультимедийных данных

В прошлой главе мы изучили множество компонентов Silverlight, которые

пригодятся нам для создания приложений. Глава эта была большой и насы-

щенной новыми сведениями.

Однако Silverlight — это платформа для создания интернет-приложений.

А какой ныне Интернет без графики и мультимедиа! Поэтому данная глава

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

на страницы графики и мультимедийной информации (звука и видео). Ком-

понентов всего два, но зато каких!

Вывод графики

Для испытания возможностей Silverlight по выводу на страницы графических

изображений нам понадобится хотя бы одно изображение. Найдем подходя-

щее изображение в формате JPEG.

Создадим новый проект с именем Image. Назовем файл с найденным изобра-

жением image.jpg и поместим его в папку, где хранятся файлы этого проекта.

А теперь — внимание! Мы поместим этот файл прямо в сборку, которая ста-

нет результатом наших трудов, в виде так называемого ресурса сборки. (Под-

робнее о ресурсах сборки речь пойдет в главе 8.) Для этого на панели Solution

Explorer (см. рис. 3.2) щелкнем правой кнопкой мыши на "корне" иерархиче-

ского списка (он имеет то же имя, что и проект создаваемого нами приложе-

ния) и выберем пункт Existing Item подменю Add появившегося на экране

контекстного меню. Когда на экране возникнет диалоговое окно Add Existing

Item, выберем в нем файл, где хранится наше изображение, и нажмем кнопку

открытия. После этого в иерархическом списке панели Solution Explorer

Page 133: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 7. Вывод графики и мультимедийных данных 121

появится новый пункт, чье имя совпадает с именем выбранного нами фай-

ла, — это значит, что при компиляции Visual Web Developer 2008 включит

его прямо в эту сборку.

Теперь можем приступать к экспериментам с выводом графики.

Компонент Image

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

нент — Image. Он определен в пространстве имен по умолчанию.

<Image Source="image.jpg"></Image>

Класс Image, соответствующий данному компоненту, поддерживает свойство

Source, с помощью которого задается имя графического файла, чье содержи-

мое должно быть выведено на страницу. Это содержимое в XAML-коде зада-

ется в виде строки с именем файла.

Компонент Image Silverlight поддерживает только изображения в форматах

JPEG и PNG, при этом накладывая на них некоторые ограничения. За подроб-ностями обращайтесь к странице .NET Framework Class Library for Silverlight / System.Windows.Media.Imaging Namespace / BitmapImage Class документа-ции по Silverlight.

Мы можем отобразить на странице изображение, взятое из Интернета. Для это-го достаточно выяснить интернет-адрес нужного изображения и присвоить его свойству Source компонента Image. Однако при загрузке файлов из Интернета

среда исполнения Silverlight учитывает некоторые ограничения, о которых мы поговорим в главе 21.

Еще одно полезное свойство класса Image — Stretch, с помощью которого мы

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

все отведенное ему пространство на странице, или нет. Оно принимает зна-

чение типа перечисления Stretch с четырьмя элементами:

� None — изображение не растягивается в любом случае, сохраняя свои из-

начальные размеры;

� Fill — изображение растягивается, занимая все отведенное ему простран-

ство, при этом соотношение сторон может не сохраняться;

� Uniform — изображение растягивается с соблюдением соотношения сто-

рон, но отведенное ему пространство может быть занято не полностью

(значение по умолчанию);

Page 134: Дронов В. Самоучитель Silverlight 3 (2010)

122 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

� UniformToFill — изображение растягивается с соблюдением соотношения

сторон, занимая все отведенное ему пространство; при этом изображение

может быть обрезано по краям.

<Image Source="image.jpg" Stretch="None"></Image>

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

� ImageOpened — возникает при успешной загрузке файла изображения перед

его выводом.

� ImageFailed — возникает, если при загрузке файла изображения возникла

ошибка (например, если данный файл отсутствует в ресурсах сборки или

среда исполнения Silverlight не смогла опознать его формат).

Класс Image также поддерживает свойства ActualHeight, ActualWidth, Height,

HorizontalAlignment, Margin, MaxHeight, MaxWidth, MinHeight, MinWidth, Name,

VerticalAlignment, Visibility и Width.

<Image Source="image.jpg" Width="480" Height="640"></Image>

Программная загрузка изображений

Да, вывести на страницу изображение — это здо́рово. Но куда интереснее

загрузить его программно. Как? Совсем не сложно.

Мы уже знаем, что свойство Source класса Image в XAML-коде принимает

значение в виде строки, содержащей полное имя графического файла. На са-

мом деле тип его значения — класс ImageSource, но реально используется его

класс-потомок BitmapImage. Эти классы позволяют хранить сведения о файле-

источнике графического изображения, которое должно быть выведено на

страницу.

Когда мы в XAML-коде указываем имя файла в качестве значения свойства

Source компонента Image, подсистема Silverlight, выполняющая обработку

откомпилированного кода XAML, сама создает соответствующий объект

класса ImageSource. В C#-коде нам придется создать его самим.

Давайте поместим весь код, загружающий изображение, в обработчик собы-

тия Loaded страницы. Привяжем этот обработчик прямо к тегу <UserControl>,

как показано далее:

<UserControl . . . Loaded="UserControl_Loaded">

Удалим из тега <Image>, создающего данный компонент, атрибут Source и да-

дим ему имя, скажем, imgSample. После чего откроем файл MainPage.xaml.cs

и найдем созданную Visual Web Developer 2008 "заготовку" для метода —

обработчика события UserControl_Loaded. Сейчас мы напишем этот метод.

Page 135: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 7. Вывод графики и мультимедийных данных 123

Первое, что нам нужно, — создать объект класса Uri, хранящий сведения об имени (или интернет-адресе) нужного нам графического файла. Для этого сначала объявим переменную типа Uri, которая будет хранить этот объект.

Uri uFileName;

После этого можно создать сам объект.

uFileName = new Uri("image.jpg", UriKind.Relative);

Оператор new выполняет создание объекта на основе класса, имя которого указано правее его. За именем класса в скобках через запятую перечисляются параметры создаваемого объекта.

Еще в главе 3 мы выяснили, что в любом классе присутствует как минимум один метод, который выполняется при создании на основе этого класса объ-екта, имеет то же имя, что и сам класс, и называется конструктором. Так вот, то, что стоит правее оператора new, суть вызов этого самого метода-конструктора с параметрами.

Параметров этих всего два. Первый представляет собой имя файла в виде строки. Значение второго имеет тип перечисления UriKind и поясняет, что именно задает первый параметр. В нашем случае следует использовать зна-чение UriKind.Relative или UriKind.RelativeOrAbsolute. (Подробнее о пере-числении UriKind и его элементах мы поговорим в главе 21.)

Так, объект класса Uri, хранящий сведения об имени файла, мы создали. Теперь создадим на его основе объект класса BitmapImage, хранящий сведения о самом графическом файле.

Сначала объявим переменную, которая будет хранить этот объект.

System.Windows.Media.Imaging.BitmapImage bimFile;

Класс BitmapImage определен в пространстве имен System.Windows.Media. Imaging, которое изначально не отображено. Поэтому мы должны либо ис-пользовать полное имя класса, либо отобразить данное пространство имен. (Подробнее о полных именах классов, пространствах имен и их отображении см. в главе 4.)

И создадим сам объект.

bimFile = new System.Windows.Media.Imaging.BitmapImage(uFileName);

Здесь мы также указали полное имя класса BitmapImage. А в качестве пара-метра передали объект класса Uri, хранящий сведения об имени загружаемо-го файла.

Вообще, объявлять переменные в середине кода — не очень хороший стиль про-граммирования. Обычно все объявления переменных идут в самом начале кода.

Page 136: Дронов В. Самоучитель Silverlight 3 (2010)

124 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Полученный объект мы присвоим свойству Source компонента imgSample.

imgSample.Source = bimFile;

Все! Запустим приложение и полюбуемся на нашу картинку.

Вообще, программная загрузка изображений в Silverlight реализована

несколько громоздко и не особенно очевидно. По крайней мере, на взгляд

автора.

Вывод мультимедийных данных

Чтобы попрактиковаться в выводе на страницу Silverlight-приложения муль-

тимедийных данных, нам понадобятся сами мультимедийные данные.

Поэтому подберем какой-нибудь не очень большой аудио- или видеофайл,

который гарантированно воспроизводится на нашем компьютере.

Создадим новый проект с именем, скажем, Media. Дадим файлу с найденным

звуком или фильмом имя media и соответствующее формату расширение

(автор нашел подходящий аудиофайл и назвал его media.mp3). Поместим его

в папку, где хранятся файлы этого проекта. И включим этот файл в состав

сборки, как уже проделали это ранее с графическим файлом.

А теперь — внимание! Выделим в иерархическом списке панели Solution

Explorer пункт, соответствующий файлу, который мы только что включили

в состав сборки. Найдем в главном окне Visual Web Developer 2008 панель

Properties; если ее там почему-то нет, нажмем клавишу <F4>, чтобы ее вы-

вести. И выберем в раскрывающемся списке Build Action этой панели пункт

Resource. Просто сделаем все это, отложив подробности до главы 8.

Компонент MediaElement

Для вывода на страницу мультимедийных данных также используется специ-

альный компонент — MediaElement. Он определен в пространстве имен по

умолчанию.

<MediaElement Source="media.mp3"></MediaElement>

Класс MediaElement, соответствующий данному компоненту, поддерживает

свойство Source, с помощью которого задается имя мультимедийного файла,

чье содержимое должно воспроизводиться на странице. Это содержимое

в XAML-коде задается в виде строки с полным именем файла.

Компонент MediaElement Silverlight поддерживает только форматы MP3, MP4 и

Windows Media. Все подробности описаны на странице Graphic, Animation, and

Page 137: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 7. Вывод графики и мультимедийных данных 125

Media / Audio and Video / Supported Media Formats, Protocols, and Log Fields документации по Silverlight.

Класс MediaElement также поддерживает свойства ActualHeight, ActualWidth,

Height, HorizontalAlignment, Margin, MaxHeight, MaxWidth, MinHeight, MinWidth,

Name, Stretch, VerticalAlignment, Visibility и Width.

<MediaElement Source="media.mp3" Stretch="None" Width="400"

�Height="300"></MediaElement>

Свойство AutoPlay позволяет указать, должно ли содержимое мультимедий-ного файла начать воспроизводиться сразу после окончания загрузки этого

файла. Значение true предписывает компоненту начать воспроизведение

файла после его загрузки, значение false — не начинать. Значение по умол-

чанию — true.

Свойство Volume задает громкость воспроизведения звука в виде числа с пла-

вающей точкой от 0 до 1. Значение по умолчанию — 0.5.

Свойство Balance задает стереобаланс при воспроизведении звука в виде чис-

ла с плавающей точкой от -1 (звук слышен только в левом канале) до 1 (толь-

ко в правом канале). Значение по умолчанию — 0 (нормальный баланс).

Свойство IsMuted позволяет убрать звук совсем. Значение true убирает звук,

а значение false — включает его. Значение по умолчанию — false.

Свойство CurrentState возвращает значение, указывающее текущее состоя-

ние компонента. Это значение имеет тип перечисления MediaElementState из следующих элементов:

� Closed — мультимедийный файл не задан (значение по умолчанию);

� Opening — компонент пытается открыть файл;

� Individualizing — компонент пытается определить, защищен ли откры-

ваемый файл и есть ли у пользователя лицензия на его просмотр;

� AcquiringLicense — компонент пытается получить лицензию на воспроиз-ведение защищенного файла (если, конечно, он защищен);

� Buffering — компонент загружает файл и сохраняет в памяти перед нача-лом воспроизведения;

� Playing — компонент воспроизводит файл;

� Paused — воспроизведение файла приостановлено;

� Stopped — воспроизведение файла остановлено или файл воспроизведен до конца.

Это свойство доступно только для чтения и поэтому может использоваться

только в C#-коде.

Page 138: Дронов В. Самоучитель Silverlight 3 (2010)

126 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Полезное свойство Position задает или возвращает текущую позицию вос-

произведения файла. Его значение имеет тип TimeSpan — особой структуры,

предназначенной для хранения временны́х интервалов. Подробнее об этой

структуре и ее использовании мы поговорим в главе 10.

Поскольку компонент MediaElement не предоставляет никаких элементов

управления для контроля над воспроизведением файла, нам придется преду-

смотреть их самим. Для этого нам очень пригодятся перечисленные далее

методы.

� Play() — начинает или возобновляет воспроизведение файла.

� Pause() — приостанавливает воспроизведение файла. Возобновить вос-

произведение можно вызовом метода Play().

� Stop() — останавливает воспроизведение файла.

Еще нам могут пригодиться четыре события, которые перечислены далее.

� MediaOpened — возникает при успешном открытии мультимедийного файла.

� MediaFailed — возникает при ошибке во время открытия или загрузки

файла.

� MediaEnded — возникает по окончании воспроизведения файла.

� CurrentStateChanged — возникает при изменении значения свойства

CurrentState (было описано ранее).

На самом деле классы Image и MediaElement (особенно последний) поддержи-

вают куда больше свойств, методов и событий, чем мы здесь рассмотрели. Все они описаны в документации по Silverlight.

Программная загрузка мультимедийных данных

Да, и компонент MediaElement позволяет загружать мультимедийные файлы

программно. Причем в его случае это выполняется проще, чем у компонента

Image.

Свойство Source класса MediaElement в XAML-коде принимает значение в ви-

де строки, содержащей полное имя мультимедийного файла. На самом деле

тип его значения — Uri. Когда мы в XAML-коде указываем имя файла в ка-

честве значения свойства Source компонента MediaElement, подсистема

Silverlight, выполняющая обработку откомпилированного кода XAML, сама

создает соответствующий объект класса Uri.

C классом Uri мы уже имели дело, когда разбирались с программной загруз-

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

Page 139: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 7. Вывод графики и мультимедийных данных 127

Мы используем тот же прием, что и раньше, — поместим код, загружающий

наш файл, в обработчик события Loaded страницы. А наш компонент

MediaElement назовем melSample.

Сначала нужно создать объект класса Uri, хранящий сведения об имени (или

интернет-адресе) нужного нам мультимедийного файла, и переменную для

него.

Uri uFileName;

uFileName = new Uri("media.mp3", UriKind.Relative);

Потом присвоить полученный объект свойству Source компонента melSample.

melSample.Source = uFileName;

Вот и все!

Что дальше?

В этой главе мы познакомились с компонентами Silverlight, с помощью кото-

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

И даже организовать что-то наподобие кинотеатра.

А еще мы упомянули о каких-то ресурсах сборки. Не пора ли поговорить

о них подробнее?

Page 140: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 8

Ресурсы сборки

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

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

Настала пора внести ясность в этот вопрос!

Понятие ресурсов сборки

Еще в главе 4 мы узнали, что любой проект Visual Web Developer 2008 дает при компиляции сборку. Сборка может быть приложением (для среды Windows или платформы Silverlight) или библиотекой (для использования нами или нашими коллегами в других проектах).

А что находится внутри сборки? Во-первых, конечно же, исполняемый код MSIL, который выполняется средой исполнения Silverlight. Во-вторых, до-вольно объемистые служебные данные, несущие жизненно важную инфор-мацию о самой сборке и содержащемся в ней исполняемом коде. В-третьих...

А вот "в-третьих" — самое интересное! Silverlight позволяет поместить внутрь сборки любой файл с любым содержимым. Это может быть графиче-ский файл, мультимедийный файл, текстовый файл или даже файл с произ-вольными двоичными данными. В общем, данные, не являющиеся ни испол-няемым кодом, ни относящимися к нему служебными сведениями.

Так, в предыдущей главе мы поместили в сборку Image графический файл

image.jpg, а в сборку Media — мультимедийный файл с именем media. И не

просто поместили, а еще и использовали в своих приложениях!

Page 141: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 8. Ресурсы сборки 129

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

В главе 13, обсуждая привязку компонентов к данным, мы познакомимся с ре-сурсами страницы и ресурсами приложения. Так вот, не следует путать их с ресурсами сборки — это совершенно разные вещи!

Работа с ресурсами сборки

Работать с ресурсами сборки в Visual Web Developer 2008 очень просто. Сей-час мы сами в этом убедимся.

Поместить файл в сборку, превратив его в ресурс сборки, можно тремя спо-собами.

� Выбрать пункт Add Existing Item меню Project.

� Щелкнуть правой кнопкой мыши на "корне" иерархического списка на панели Solution Explorer (см. рис. 3.2) и выбрать пункт Existing Item подменю Add появившегося на экране контекстного меню. ("Корень" спи-ска имеет то же имя, что и проект, с которым мы работаем.)

� Нажать комбинацию клавиш <Shift>+<Alt>+<A>.

В любом случае на экране появится диалоговое окно Add Existing Item, по-хожее на стандартное диалоговое окно открытия файла Windows. Выбираем в нем нужный файл и нажимаем кнопку открытия.

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

Ресурсы сборки выводятся прямо в иерархическом списке панели Solution Explorer, в корневой "ветви". Они имеют то же имя, что и файлы, в которых они хранятся.

Мы можем переименовать ресурс, чье имя нас почему-то не устраивает. Для этого достаточно либо выбрать пункт Rename контекстного меню соответст-вующего ресурсу пункта в иерархическом списке панели Solution Explorer, либо выбрать этот пункт и нажать клавишу <F2>. Вместо имени пункта по- явится небольшое поле ввода, содержащее старое имя ресурса; введем в него новое имя и нажмем клавишу <Enter> для его сохранения или <Esc> для от-каза от переименования ресурса.

Если нам понадобится удалить из сборки ненужный ресурс, мы выберем пункт Exclude From Project его контекстного меню. Ресурс будет сразу же удален.

Page 142: Дронов В. Самоучитель Silverlight 3 (2010)

130 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Если выбрать пункт Delete контекстного меню какого-либо ресурса в списке па-нели Solution Explorer или выделить его и нажать клавишу <Del>, не только

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

Включенные и невключенные

ресурсы сборки

Ранее мы говорили, что ресурсы сборки включаются в сам файл сборки. Но

это не всегда верно. Ресурсы могут храниться в отдельных файлах, при этом

фактически оставаясь неотъемлемой частью сборки.

Ресурсы, которые при компиляции помещаются в сам файл сборки, называ-

ются включенными. Ресурсы, которые хранятся во внешних по отношению

к сборке файлах, носят название невключенных.

У включенных ресурсов всего одно преимущество — они хранятся внутри

сборки, распространяются и загружаются вместе с ней. Недостаток у них

также один — они заметно увеличивают размеры файла сборки. Поэтому

включенными ресурсами обычно делают небольшие графические изображе-

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

кнопок, эмблемы разработчиков и пр.).

Преимущество невключенных ресурсов также одно — они не увеличивают

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

один — их нужно распространять с приложением в виде отдельных файлов.

Невключенными ресурсами делают большие изображения, аудио- и видео-

файлы, причем пользователь обычно предупреждается о том, что придется

ждать, пока они будут загружены.

Когда мы добавляем в проект ресурс сборки, Visual Web Developer 2008 дела-

ет его включенным или невключенным по своим собственным правилам. На-

сколько удалось выяснить автору, он при этом руководствуется расширением

файла; графические файлы делаются включенными ресурсами, а мультиме-

дийные — невключенными. Выяснить, включенный это ресурс или невклю-

ченный, можно только с помощью панели Properties, и с помощью этой же

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

Обычно панель Properties (рис. 8.1) всегда присутствует на экране. Она пока-

зывает параметры выбранного на панели Solution Explorer файла. Если она

все-таки отсутствует на экране, ее можно вывести выбором пункта Properties

Window меню View или нажатием клавиши <F4>.

Page 143: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 8. Ресурсы сборки 131

Рис. 8.1. Панель Properties

Как видим, на панели Properties присутствуют несколько элементов управ-

ления, в основном, полей ввода и раскрывающихся списков. Нам понадобятся

раскрывающиеся списки Build Action и Copy to Output.

Раскрывающийся список Build Action позволяет указать тип ресурса, выде-

ленного в списке панели Solution Explorer. Он содержит довольно много

пунктов, но нам будут полезны только два: Resource (превратить выбранный

файл во включенный ресурс) и None (в невключенный ресурс).

Раскрывающийся список Copy to Output полезен только в случае невклю-

ченных ресурсов. Он содержит три пункта, перечисленных далее.

� Do not copy — ничего не делать с файлом, в котором хранится содержи-

мое ресурса.

� Copy always — всегда копировать файл с содержимым ресурса в папку,

где находится откомпилированное приложение (это папка Debug или

Release, находящаяся в папке bin, которая, в свою очередь, хранится в

папке проекта).

� Copy if newer — копировать файл с содержимым ресурса в папку, где на-

ходится откомпилированное приложение, только если он новее, чем файл,

уже находящийся в этой папке.

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

все файлы, хранящие невключенные ресурсы. Это очень удобно при распро-

странении готового приложения — нам не придется собирать эти файлы

самим.

Page 144: Дронов В. Самоучитель Silverlight 3 (2010)

132 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Как обрабатываются ресурсы сборки

Встретив в XAML- или C#-коде приложения имя файла, среда исполнения

Silverlight начинает его поиск в ресурсах сборки.

Сначала выполняется поиск этого файла в самой сборке, т. е. среди включен-

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

ется.

В противном случае среда исполнения Silverlight предполагает, что искомый

файл является невключенным ресурсом. В этом случае возможны два ва-

рианта.

� Если Web-страница с приложением Silverlight была открыта с локального

диска, выполняется поиск файла в папке, где хранится файл данной Web-

страницы.

� Если Web-страница с приложением Silverlight была загружена с Web-

сайта, через Web-обозреватель Web-серверу отправляется запрос на за-

грузку данного файла, опять же, из папки, где хранится файл данной Web-

страницы.

Если таковой файл там присутствует, он будет загружен. В противном случае

среда исполнения Silverlight выведет сообщение об ошибке и, возможно, пре-

кратит работу приложения.

Использование папок

для организации ресурсов

Чтобы как-то организовать файлы по формату или назначению, используются

папки. Для организации ресурсов сборки Silverlight, как включенных, так и

невключенных, мы также можем использовать папки.

Чтобы создать новую папку для хранения ресурса, достаточно выбрать пункт

New Folder подменю Add контекстного меню "корня" в списке панели

Solution Explorer. Новая папка появится сразу же после этого; введем ее имя

в небольшое поле ввода прямо в списке и нажмем клавишу <Enter> для его

сохранения.

Новая папка может быть создана в уже существующей папке. Для этого сле-

дует использовать все тот же пункт New Folder подменю Add, но уже кон-

текстного меню папки, в которой мы хотим создать новую папку.

Перемещать файлы в папки и из папок мы можем точно таким же способом,

как в Проводнике, — перетаскиванием мышью. Точно так же мы можем пе-

ремещать сами папки. На рис. 8.2 показаны последовательно вложенные друг

Page 145: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 8. Ресурсы сборки 133

в друга папки medias и sounds; в последней находится аудиофайл media.mp3,

использованный автором в предыдущей главе.

Рис. 8.2. Вложенные друг в друга папки в списке панели Solution Explorer

Самое интересное, что данные папки представляют собой реальные папки

файловой системы; открыв папку проекта, мы сможем в этом сами убедиться.

Так, для представленного на рис. 8.2 случая Visual Web Developer 2008 соз-

даст в папке проекта папку medias, в ней — папку sounds, в которую помес-

тит файл media.mp3.

Мы можем исключить ненужную папку из проекта и вообще удалить ее. Де-

лается это таким же образом, как и в случае с файлами.

Теперь — очень важный момент! Сейчас мы выясним, каким образом запи-

сываются имена файлов ресурсов, помещенных в папки. Точнее, уже не име-

на, а пути.

Пути этих файлов отсчитываются от папки, в которой хранится Web-

страница с Silverlight-приложением. Данная папка выступает своего рода

корневой папкой для отсчета путей файлов с ресурсами.

Сначала перечисляют имена всех папок, в которые последовательно вложен

данный файл, начиная от папки с Web-страницей; эти имена разделяются

символами слэша (прямого, не обратного!). За последним символом слэша

ставят имя файла. Можно сказать, что получается "урезанный" путь, начи-

нающийся не в корневой папке диска, а в папке с Web-страницей, которая

загружает Silverlight-приложение.

Например, для показанного на рис. 8.2 случая путь файла media.mp3 будет

таков:

medias/sounds/media.mp3

Если мы превратили какие-то файлы, помещенные в папки, в невключенные

ресурсы и указали при компиляции копировать их в папку с откомпилиро-

ванным приложением, Visual Web Developer 2008 воссоздаст в этой папке

всю структуру папок, в которые вложены файлы с невключенными ресурса-

ми. Нам останется только не забыть о них, когда мы начнем распространять

наше приложение.

Собственно, о ресурсах сборки автору сказать больше нечего...

Page 146: Дронов В. Самоучитель Silverlight 3 (2010)

134 Часть II. Сборки, пространства имен, страницы, компоненты и ресурсы

Что дальше?

В этой главе мы узнали о ресурсах сборки и научились с ними работать. Соб-

ственно, учиться было особо нечему — все за нас делала среда исполнения

Silverlight...

Думается, настала пора немного отвлечься от самой Silverlight и посвятить

какое-то время языку программирования C#. А то мы уже написали на нем

довольно много кода, а так толком его и не узнали. Непорядок!

Page 147: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ III

Язык C#

Глава 9. Основные конструкции языка C#

Глава 10. Сложные типы данных C#

Глава 11. Коллекции

Глава 12. Исключения

Page 148: Дронов В. Самоучитель Silverlight 3 (2010)
Page 149: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 9

Основные конструкции

языка C#

В предыдущих двух частях книги мы вовсю пользовались языком програм-мирования C# для написания логики приложений. Но так и не познакомились с ним подробно. Настала пора восполнить пробелы в наших знаниях.

Эта часть книги будет полностью посвящена языку C#. Язык этот весьма сложен (в отличие от довольно-таки простого XAML, в котором и изучать-то, собственно, нечего), и в одном разделе по ходу дела о нем не расскажешь. Так что приготовимся к долгому обстоятельному разговору.

И начнем мы разговор с рассмотрения самых базовых конструкций C#. Тех, без которых не напишешь и строчки кода.

Выражения, переменные, операторы, операнды и ключевые слова

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

Вот пример выражения:

sgMillimetres = sgInches * 25.4;

Это выражение извлекает из переменной sgInches числовое значение, умно-

жает его на число 25.4 и присваивает его переменной sgMillimetres. Здесь нам все знакомо еще из главы 3.

В приведенном ранее выражении мы использовали две переменные с имена-

ми sgInches и sgMillimetres. Переменная — это фрагмент оперативной памя-

Page 150: Дронов В. Самоучитель Silverlight 3 (2010)

138 Часть III. Язык C#

ти компьютера, предназначенный для хранения какого-либо значения и

имеющий уникальное имя, по которому к нему можно обратиться. Это нам

также знакомо.

Переменная может хранить любое значение (имеющее тот тип, что был опре-

делен при объявлении переменной, но об этом позже). Причем значение она

может хранить только одно — если присвоить переменной другое значение,

старое будет безвозвратно утеряно.

В противоположность переменной, число с плавающей точкой 25.4 имеет

строго определенное значение, которое никогда не меняется. (Вообще, любое

число имеет строго определенное и неизменное значение.) Поэтому оно на-

зывается константой. Точно так же константами являются строка

"Silverlight", целое число 435, логическое значение true и другие подобные

им значения.

Еще мы использовали в приведенном ранее выражении оператор умноже-

ния *. Оператор — это команда языка C#, выполняющая элементарное дей-

ствие в составе выражения над одним, двумя или тремя операндами, которы-

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

гих операторов (а также результаты, возвращенные методами, но об этом —

в главе 10). Оператор умножения * в нашем выражении перемножает зна-

чение переменной sgInches и константу 25.4 и возвращает полученное произ-

ведение...

...которое мы присваиваем переменной sgMillimetres с помощью оператора

присваивания =. Этот оператор берет значение, стоящее справа от него, и по-

мещает его в переменную, чье имя стоит слева от него.

Давайте рассмотрим еще одно выражение:

sgMetres = sgInches * 25.4 / 1000;

Оно содержит уже два оператора: знакомый нам оператор умножения *

и оператор деления /. Эти операторы выполняются в следующей последова-

тельности.

1. Значение переменной sgInches умножается на константу 25.4.

2. Полученное произведение делится на константу 1000.

3. Полученное частное присваивается переменной sgMetres.

Видно, что операторы в выражении выполняются в порядке слева направо.

Это потому, что они имеют одинаковый приоритет. (О приоритете операто-

ров речь пойдет позже.)

А вот еще одно выражение:

x = x + 2;

Page 151: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 139

C точки зрения языка C# оно абсолютно правильно, хоть и выглядит неле-пым. В нем сначала выполняется сложение значения переменной x и числа 2, после чего результат сложения снова присваивается переменной x. Такие вы-ражения мы будем встречать довольно часто.

Помимо операторов, в выражениях часто используются ключевые слова язы-ка программирования, имеющие особые функции. Так, в приведенном далее выражении ключевое слово double используется для объявления переменной sgInches, имеющей тип числа с плавающей точкой (double).

double sgInches;

Ключевое слово class используется для объявления классов, а ключевое сло-во if — при написании условного выражения. Но об этом позже.

Напоследок давайте рассмотрим два правила, по которым пишутся выраже-ния языка C#.

� Разрешаются переносы слишком длинных строк для удобства чтения кода. При этом допускаются переносы между отдельными языковыми конст-рукциями: операторами, операндами, ключевыми словами, именами пере-менных и методов и т. п. Не допускаются переносы строк внутри языко-вых конструкций — это расценивается как ошибка в синтаксисе.

� В конце каждого выражения должен стоять символ точки с запятой (;). Этот символ, собственно, и обозначает конец выражения.

Так, предпоследнее из рассмотренных нами выражений —

sgMetres = sgInches * 25.4 / 1000;

мы можем записать так:

sgMetres = sgInches * 25.4 /

1000;

или даже так:

sgMetres =

sgInches *

25.4 /

1000;

хотя это, конечно, уже перебор...

Основные понятия о языковых конструкциях C# мы получили. Поговорим теперь о том, ради чего пишется любая программа, — о данных.

Типы данных

Каждое значение в C# имеет строго определенный тип. Тип указывает, к ка-

кой разновидности будет принадлежать хранящееся в переменной значение:

Page 152: Дронов В. Самоучитель Silverlight 3 (2010)

140 Часть III. Язык C#

строка, целое число, число с плавающей точкой, логическая величина или

объект какого-либо класса. Тип также определяет, какие операции мы можем

выполнить над данным значением.

Типы данных C#, классы и структуры Silverlight

Как мы уже знаем, платформа Silverlight содержит набор библиотечных сбо-рок, в которых определены типы данных, которые мы можем использовать в коде C#. В том числе и типы, представляющие элементарные данные: строки, числа, логические значения и пр.

Часть этих типов реализована в виде классов, а часть — в виде структур. Структуру можно рассматривать как "облегченный" аналог класса: он быст-рее обрабатывается средой исполнения Silverlight, но лишен некоторых дос-тоинств "полноразмерных" классов. Впрочем, элементарные типы данных слишком просты, чтобы усложнять их сверх меры.

Каждая из этих классов и структур Silverlight соответствует определенному

типу данных языка C#. Так, тип string (строка) C# соответствует классу

String платформы Silverlight, тип double (число с плавающей точкой) —

структуре Double, тип bool — структуре Boolean.

Когда Visual Web Developer 2008 компилирует приложение, он находит в ис-ходном коде ключевые слова C#, определяющие его типы данных, и преобра-зует их в имена соответствующих классов Silverlight. Эти имена уже исполь-зуются при выполнении приложения в среде исполнения Silverlight.

Что нам это дает? Одну забавную и полезную возможность — мы можем ис-пользовать для указания типа как ключевые слова C#, так и имена классов Silverlight. Например:

bool bFlag;

Boolean bFlag;

Эти два объявления логической переменной bFlag абсолютно идентичны,

просто в первом используется ключевое слово C# bool, а во втором — имя

структуры Silverlight Boolean. Выбирайте, как вам удобнее.

Строковый

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

"Silverlight 3"

"1234567"

"Строковые данные — это последовательности символов."

Page 153: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 141

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

ствует класс Silverlight String.

string sValue;

Строки могут иметь любую длину (определяемую количеством составляю-

щих их символов), ограниченную лишь объемом свободной памяти компью-

тера. Разумеется, теоретически существует предел в 2 Гбайт, но вряд ли

в нашей практике встретятся столь длинные строки.

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

символ в памяти компьютера представлен особым числовым кодом, одно-

значно его определяющим. Поэтому все строки фактически представляют

собой совокупный набор кодов составляющих их символов.

Набор всех доступных символов вместе с соответствующими им кодами на-

зывается кодировкой. Silverlight использует универсальную кодировку

Unicode. Эта кодировка содержит 65 535 символов — практически все сим-

волы почти всех языков мира. Это позволяет использовать в Silverlight-

приложениях строки, содержащие символы любых алфавитов.

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

символы, служащие для особых целей. Самые полезные для нас специальные

символы, поддерживаемые C#, приведены в табл. 9.1.

Таблица 9.1. Некоторые специальные символы, поддерживаемые языком C#

Символ Описание Код Unicode

\" Двойная кавычка 0x0022

\' Одинарная кавычка 0x0027

\\ Обратный слэш 0x005c

\r Возврат каретки 0x000d

\n Прогон строки 0x000a

\t Табуляция 0x0009

\xFFFF Любой символ по его коду Unicode (обозначен как FFFF) —

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

нужно записать ее так:

"\"Silverlight 3\" — платформа для создания интернет-приложений"

Часто применяется так называемая пустая строка "", не содержащая ни одно-

го символа.

sValue = "";

Page 154: Дронов В. Самоучитель Silverlight 3 (2010)

142 Часть III. Язык C#

Мы можем использовать статическое свойство Empty класса String, чтобы получить пустую строку.

sValue = String.Empty;

Целочисленный

Целочисленный тип C# представляет целые числа, над которыми можно про-

изводить различные арифметические действия. Примеры целых чисел: 45,

2563446, -74376.

Строго говоря, в C# целочисленных типов шесть. Все они перечислены в табл. 9.2.

Таблица 9.2. Целочисленные типы C#

Ключевое слово C#

Структура Silverlight

Название Диапазон значений

int Int32 Знаковое целое От –2 147 483 648 до 2 147 483 647

uint UInt32 Беззнаковое целое От 0 до 4 294 967 295

short Int16 Короткое знаковое целое

От –32 768 до 32 767

ushort UInt16 Короткое беззнаковое целое

От 0 до 65 535

long Int64 Длинное знаковое целое

От –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807

ulong UInt64 Длинное беззнаковое целое

От 0 до 18 446 744 073 709 551 615

Чаще всего используются типы int (знаковое целое) и uint (беззнаковое це-лое). В некоторых случаях, когда требуется хранить небольшие числа или

программист хочет сэкономить память, применяются типы short (короткое

знаковое целое) и ushort (короткое беззнаковое целое). "Длинные" типы при-меняются редко, в основном, в научных вычислениях.

Целые числа могут быть заданы, помимо десятичной, в шестнадцатеричной системе счисления. Запись шестнадцатеричного числа состоит из цифр от 0 до 1 и больших или маленьких букв латинского алфавита от A до F и в языке

C# должна начинаться с символов 0x. Пример шестнадцатеричного числа:

0x5f8c.

Число с плавающей точкой

Тип числа с плавающей точкой C# представляет дробные числа, над которы-ми можно производить различные арифметические действия. В таких числах

Page 155: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 143

целая и дробная части отделяются друг от друга точкой, а не запятой. Приме-

ры целых чисел: 25.4, 0.63465, -234.8.

Для записи чисел с плавающей точкой может быть использована экспоненци-

альная форма вида <мантисса>E<порядок>. Вот примеры чисел, записанных

в такой форме (в скобках дано традиционное математическое представление):

1E-5 (10–5

), 8.546E23 (8,546·1023

).

Собственно, в C# типов чисел с плавающей точкой три. Они перечислены

в табл. 9.3.

Таблица 9.3. Типы чисел с плавающей точкой C#

Ключевое слово C#

Структура Silverlight

Название Диапазон значений

double Double Число с плавающей точкой двойной точности

От ±5·10–324 до ±1,7·10308

float Single Число с плавающей точкой обычной точности

От ±1,5·10–45 до ±3,4·1038

decimal Decimal Число с плавающей точкой учетверенной точности

От ±1·10–28 до ±7,9·1028

Чаще всего используется тип double (число двойной точности), т. к. именно

этот тип имеют многие свойства у классов компонентов Silverlight. Для вы-

числений, где не требуется высокая точность, зато пригодится высокое быст-

родействие, применяется тип float (число обычной точности). Тип decimal

(число учетверенной точности) применяется только в научных и финансовых

вычислениях, где требуется очень высокая точность.

Логический

Логический тип представляет особую величину, которая может принимать

только два значения — "истина" и "ложь", — обозначаемые соответственно

ключевыми словами true и false.

Логический тип обозначается ключевым словом bool. Ему соответствует

структура Silverlight Boolean.

Символьный

Символьный тип представляет один-единственный символ в кодировке

Unicode (о кодировках см. раздел, посвященный строковому типу данных).

Он может быть задан как обычный символ, как специальный символ или

Page 156: Дронов В. Самоучитель Silverlight 3 (2010)

144 Часть III. Язык C#

в виде его кода. Значение символьного типа заключается в одинарные кавыч-

ки. Например: 't', '\\', '\u0058'.

Символьный тип обозначается ключевым словом char. Ему соответствует

структура Silverlight Char.

Значимые типы

Все типы данных в Silverlight делятся на две принципиально разные группы.

Настолько разные, что о них нужно поговорить особо.

Все перечисленные ранее типы данных относятся к значимым. Это значит, что в переменной любого значимого типа хранится само значение. Так, если

мы присвоим переменной x число 4, то получим в данной переменной именно

число 4.

Если мы впоследствии присвоим значение переменной x переменной y, то

переменная y получит копию значения переменной x. Мы сможем делать со значениями этих переменных что угодно, не опасаясь, что изменение значе-

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

"клон".

Рассмотрение второй группы типов данных Silverlight — ссылочных — мы отложим до главы 10.

Преобразование типов

А пока что рассмотрим ситуацию, когда мы присваиваем значение одного

типа переменной другого типа или используем в арифметическом выражении операнды разных типов. Что произойдет в этом случае? Ответ — преобразо-

вание типов, явное или неявное.

Неявное преобразование типов

Неявное преобразование типов выполняется самой средой исполнения

Silverlight. Она берет значение какого-то типа, оценивает, получается ли пре-образовать его в другой тип без потерь на округление, и, если получается,

преобразует.

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

� int — преобразуется в long, float, double и decimal.

� uint — в long, ulong, float, double и decimal.

� short — в int, long, float, double и decimal.

Page 157: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 145

� ushort — в int, uint, long, ulong, float, double и decimal.

� long — в float, double и decimal.

� ulong — в float, double и decimal.

� float — в double.

� char — в ushort, int, uint, long, ulong, float, double и decimal.

Если выполнить конкатенацию числа и строки, то число будет неявно преоб-

разовано в строку.

Вообще, лучший способ определить, произойдет ли в данном случае неявное преобразование типов, — это положиться на Visual Web Developer 2008. Если

он считает, что неявное преобразование в данном месте невозможно, то под-черкивает его волнистой красной линией, как при ошибке. Наведя курсор

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

Явное преобразование типов

Если неявное преобразование типов выполнить не удается, нам остается

только прибегнуть к явному.

Явное преобразование типов выполняется самим программистом с помощью особого оператора преобразования типов. Этот оператор представляет собой

название типа (ключевое слово C# или имя класса или структуры Silverlight), взятое в скобки и поставленное перед значением, которое нужно преобразо-

вать.

double d = 1234.567;

int c;

с = (int)d;

Это небольшой пример явного преобразования типа double в тип int. Неявно это преобразование выполнить не получится, т. к. при нем произойдет округ-ление числа с плавающей точкой до целого, иначе говоря, потери.

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

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

� int — преобразуется в short, ushort, uint, ulong и char.

� uint — в short, ushort, ulong и char.

� short — в ushort, uint, ulong и char.

Page 158: Дронов В. Самоучитель Silverlight 3 (2010)

146 Часть III. Язык C#

� ushort — в short и char.

� long — в short, ushort, int, uint, ulong и char.

� ulong — в short, ushort, int, uint, long и char.

� double — в short, ushort, int, uint, long, ulong, char, float и decimal.

� float — в short, ushort, int, uint, long, ulong, char и decimal.

� decimal — в short, ushort, int, uint, long, ulong, char, float и double.

� char — в short.

Для преобразования числа в строку следует использовать метод ToString(),

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

типы данных. Для преобразования строки в число используются статические

методы TryParse() соответствующих классов и структур. Мы рассмотрим их

в главе 10.

Переменные

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

грубо говоря, фрагмент оперативной памяти компьютера, предназначенный

для хранения какого-либо значения и имеющий уникальное имя.

Именование переменных

Имя переменной может содержать только латинские буквы, цифры и симво-

лы подчеркивания (_), причем первый символ имени должен быть либо бук-

вой, либо символом подчеркивания. Например, sgInches и _decodedValue —

правильные имена переменных, а 2number и Введенное значение — непра-

вильные.

Язык C# чувствителен к регистру, в котором набраны имена переменных. Это

значит, что переменные decodedvalue и DecodedValue — разные.

При выборе имен переменных необходимо следовать трем простым прави-

лам. Во-первых, имя должно быть "говорящим", указывать, для чего предна-

значена эта переменная. Например, переменную, хранящую перекодирован-

ное значение, лучше назвать decodedValue — так будет сразу понятно ее на-

значение. Конечно, не стоит впадать в крайности, называя переменную

decodedFromStringToDoubleValue — такие имена просто-напросто тяжело на-

бирать.

Во-вторых, имя переменной должно начинаться с прописной (маленькой)

буквы, например, decodedValue, dbMillimetres. Дело в том, что в Silverlight

имена классов всегда начинаются со строчной (большой) буквы, и именова-

Page 159: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 147

ние переменных с прописной (маленькой) буквы позволит сразу отличить их

от классов.

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

ный префикс, обозначающий тип этой переменной. Например, iCount (пере-

менная типа int), sInches (переменная типа string), dcPrice (переменная типа

decimal). Такие префиксы пригодятся, если мы используем в методе множе-

ство разных переменных, в которых храним аргументы или промежуточные

результаты вычислений.

Служебным переменным, наподобие счетчиков циклов, практически всегда

дают одно- или двухбуквенные имена. Например, i, s, t2 и пр.

Объявление переменных. Строгая типизация

Перед использованием переменной в коде метода следует выполнить ее объ-

явление, тем самым фактически создав ее. Для этого используется особое вы-

ражение, в котором сначала указывается ключевое слово C#, обозначающее

тип данных, или имя класса или структуры Silverlight, а за ним, через про-

бел, — имя объявляемой переменной. И не забываем о символе ;, которым

должно завершаться любое выражение.

string sInches;

double sgInches;

int i;

Здесь мы объявили переменные sInches строкового типа, sgInches типа числа

с плавающей точкой двойной точности и целочисленную i. После этого мы

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

Сразу при объявлении переменной мы можем присвоить ей какое-либо зна-

чение, воспользовавшись знакомым нам оператором присваивания =:

int i = 2;

А теперь запомним две очень важные вещи.

Во-первых, выражение объявления переменной обязательно должно предше-ствовать первому обращению к ней. И это понятно — как среда исполнения Silverlight может обратиться к переменной, если она даже о ней не "знает"?

Во-вторых, язык C# и платформа Silverlight используют строгую типизацию

при объявлении и использовании переменных. Это значит, что тип переменной указывается при ее объявлении и впоследствии не может быть изменен. Пом-ните об этом!

Page 160: Дронов В. Самоучитель Silverlight 3 (2010)

148 Часть III. Язык C#

Обычно все выражения объявления переменных располагают в самом начале кода метода. Это считается хорошим стилем программирования.

Доступность переменных

И еще одна чрезвычайно важная вещь.

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

Если нам понадобится сделать какие-то данные доступными в нескольких

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

либо хранить их в полях или свойствах. (О полях и свойствах класса будет

рассказано в главе 10.)

Переменные, хранящие значения

параметров метода

Как мы знаем, методы могут принимать параметры. Так, обработчики собы-

тий принимают два параметра, описанных в главе 6.

private void btnConvert_Click(object sender, RoutedEventArgs e)

{

. . .

}

Для каждого из параметров, переданных методу, сама среда исполнения

Silverlight неявно создает переменную, где хранится его значение. Данная

переменная имеет то имя, что указано в выражении, определяющем метод.

Так, в приведенном ранее примере будут созданы переменные sender типа

object и e типа RoutedEventArgs.

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

внутри данного метода. При завершении выполнения кода метода они унич-

тожаются.

Операторы

Ну что ж, с типами данных и переменными все ясно. Пора приступать к рас-

смотрению операторов.

Page 161: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 149

Арифметические операторы

Арифметические операторы служат для выполнения арифметических дейст-вий над значениями констант и переменных. Все арифметические операторы, поддерживаемые C#, перечислены в табл. 9.4.

Таблица 9.4. Арифметические операторы

Оператор Описание

- Смена знака числа

+ Сложение

- Вычитание

* Умножение

/ Деление

% Остаток от деления

++ Инкремент (увеличение на единицу)

-- Декремент (уменьшение на единицу)

Арифметические операторы делятся на две группы: унарные и бинарные. Унарные операторы выполняются над одним операндом; к ним относятся операторы смены знака, инкремента и декремента. Унарный оператор берет значение из переменной, изменяет его и снова помещает в ту же переменную. Приведем пример выражения с унарным оператором:

++r;

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

s = ++r;

— то же значение будет присвоено и переменной s.

Операторы инкремента и декремента могут ставиться как перед операндом, так и после него. Если оператор инкремента стоит перед операндом, то зна-чение операнда сначала инкрементируется, а уже потом используется в даль-нейших вычислениях. Например:

r = 3;

s = ++r — 1;

При выполнении этих выражений оператор инкремента сначала увеличит

значение переменной r на единицу, потом поместит результат (4) обратно

в переменную r, а уже после этого он (результат) будет использован в выра-

жении. В итоге переменная s получит значение 3.

Page 162: Дронов В. Самоучитель Silverlight 3 (2010)

150 Часть III. Язык C#

Если же оператор инкремента стоит после операнда, то его значение сначала используется в других вычислениях, а уже потом инкрементируется. На- пример:

r = 3;

s = r++ — 1;

В этом случае значение переменной r сначала будет использовано в выраже-нии, и переменная s получит значение 2. А уже потом оператор инкремента увеличит значение переменной r на единицу и поместит результат (4) обрат-но в нее.

Точно так же ведет себя оператор декремента.

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

x = x + t;

l = r * 3.14;

f = -e / 2;

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

Оператор конкатенации

Оператор конкатенации, или объединения строк, + позволяет соединить две строки в одну. Например, сценарий:

s1 = "Microsoft";

s2 = "Silverlight";

s3 = "3";

s = s1 + " " + s2 + " " + s3;

поместит в переменную s строку "Microsoft Silverlight 3".

Операторы присваивания

Оператор простого присваивания = нам уже знаком. С его помощью перемен-ной присваивается новое значение:

a = 2;

Кроме оператора простого присваивания, C# поддерживает операторы сложного присваивания. Такие операторы позволяют выполнять операцию присваивания одновременно с другой операцией:

Page 163: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 151

a = a + b;

a += b;

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

пользован оператор сложного присваивания +=.

Все операторы сложного присваивания, поддерживаемые C#, и их эквивален-ты приведены в табл. 9.5.

Таблица 9.5. Операторы сложного присваивания

Оператор Эквивалентное выражение

a += b; a = a + b;

a -= b; a = a — b;

a *= b; a = a * b;

a /= b; a = a / b;

a %= b; a = a % b;

Оператор сложного присваивания выполняется быстрее пары "оператор про-стого присваивания+арифметический оператор".

Операторы сравнения

Операторы сравнения сравнивают два операнда и возвращают логическое

значение. Если условие сравнения выполняется, возвращается значение true,

если не выполняется — false. Вот примеры выражений с операторами при-сваивания:

a1 = 2 < 3;

a2 = -4 > 0;

a3 = r < t;

Переменная a1 получит значение true (2 меньше 3), переменная a2 — значе-

ние false (-4 не может быть больше нуля), а значение переменной a3 будет

зависеть от значений переменных r и t.

Все поддерживаемые C# операторы сравнения приведены в табл. 9.6.

Таблица 9.6. Операторы сравнения

Оператор Описание

< Меньше

> Больше

== Равно

Page 164: Дронов В. Самоучитель Silverlight 3 (2010)

152 Часть III. Язык C#

Таблица 9.6 (окончание)

Оператор Описание

<= Меньше или равно

>= Больше или равно

!= Не равно

Логические операторы

Логические операторы выполняют действия над логическими значениями.

Все они приведены в табл. 9.7. А в табл. 9.8 и 9.9 показаны результаты вы-

полнения этих операторов.

Таблица 9.7. Логические операторы

Оператор Описание

! НЕ (логическая инверсия)

&& И (логическое умножение)

|| ИЛИ (логическое сложение)

Таблица 9.8. Результаты выполнения операторов И и ИЛИ

Операнд 1 Операнд 2 && (И) || (ИЛИ)

true true true true

true false false true

false true false true

false false false false

Таблица 9.9. Результаты выполнения оператора НЕ

Операнд ! (НЕ)

true false

false true

Вот примеры выражений с логическими операторами:

a = (b > 0) && (c + 1 != d);

flag = !(status = 0);

Page 165: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 153

Условный оператор

Условный оператор ? возвращает один из двух операндов в зависимости от

значения третьего операнда. Вот его формат:

<условие> ? <значение true> : <значение false>

Если условие равно true, оператор вернет значение true. В противном случае

будет возвращено значение false.

s = (i > 2) ? "меньше или равно двум" : "больше двух";

Обратим внимание, что условие всегда заключается в скобки.

Приоритет операторов

Выражения языка C# могут содержать несколько операторов. Например:

a = b + c — 10;

В этом случае сначала к значению переменной b будет прибавлено значение

c, а потом из суммы будет вычтено 10. Как видим, операторы в этом выраже-

нии выполняются в порядке слева направо.

Теперь рассмотрим такое выражение:

a = b + c * 10;

Здесь сначала будет выполнено умножение значения c на 10, а уже потом

к полученному произведению будет прибавлено значение b. Порядок "строго

слева направо" нарушен.

Почему? Дело в том, что каждый оператор имеет свой приоритет, и опера-

торы с бо́льшим приоритетом выполняются раньше, чем операторы с мень-

шим приоритетом.

В только что рассмотренном нами выражении оператор умножения выпол-

нился раньше, чем оператор сложения, поскольку имел больший приоритет.

А самый низкий приоритет имел оператор присваивания, поэтому он выпол-

нился самым последним.

В табл. 9.10 перечислены все изученные нами операторы в порядке убывания

их приоритетов.

Таблица 9.10. Приоритет операторов (в порядке убывания)

Операторы Описание

<значение>++

<значение>-- Последующие инкремент и декремент

Page 166: Дронов В. Самоучитель Silverlight 3 (2010)

154 Часть III. Язык C#

Таблица 9.10 (окончание)

Операторы Описание

++<значение>

--<значение>

- !

(<тип>)

Предварительные инкремент и декремент, смена знака, логиче-ское НЕ, явное преобразование типов

* / % Умножение, деление, взятие остатка

+ - Сложение и объединение строк, вычитание

< > <= >= = != Сравнение

= <оператор>= Присваивание, простое и сложное

&& Логическое И

|| Логическое ИЛИ

? Логический оператор

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

В общем, обычный порядок выполнения всех операторов таков: сначала вы-

полняются операторы с более высоким приоритетом, а уже потом — опера-

торы с более низким. Операторы с одинаковым приоритетом выполняются

в порядке их следования (слева направо).

Но что делать, если нам нужно нарушить этот порядок? Воспользуемся скоб-

ками. При такой записи заключенные в скобки операторы выполняются пер-

выми.

a = (b + c) * 10;

В этом случае сначала будет выполнено сложение значений переменных b

и c, а потом получившаяся сумма будет умножена на 10.

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

часто используются многократно вложенные скобки:

a = ((b + c) * 10 — d) / 2 + 9;

Здесь операторы будут выполнены в такой последовательности:

1. Сложение b и c.

2. Умножение полученной суммы на 10.

3. Вычитание d из произведения.

Page 167: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 155

4. Деление разности на 2.

5. Прибавление 9 к частному.

Если удалить скобки:

a = b + c * 10 — d / 2 + 9;

то порядок выполнения операторов будет таким:

1. Умножение c и 10.

2. Деление d на 2.

3. Сложение b и произведения c и 10.

4. Вычитание из полученной суммы частного от деления d на 2.

5. Прибавление 9 к полученной разности.

Совсем другой результат, не так ли?

Сложные выражения

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

Блоки

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

{

b = "12";

c = a — b;

}

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

Блоки используются в C#-коде сплошь и рядом в составе других сложных выражений. Да мы и сами уже с ними сталкивались еще в главе 3.

Условные выражения

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

Page 168: Дронов В. Самоучитель Silverlight 3 (2010)

156 Часть III. Язык C#

Условное выражение имеет следующий формат:

if (<условие>)

<блок true>

else

<блок false>

Для написания условных выражений используются ключевые слова if и else.

Поэтому часто условные выражения называются "выражениями if-else".

Условие — это и есть логическое выражение, в соответствии с которым среда

исполнения Silverlight принимает решение, какой блок выполнить. Оно

всегда записывается в скобках. Если условие имеет значение true, то вы-

полняется блок true. Если же условие имеет значение false, то выполняется

блок false.

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

if (x == 1)

{

a = "Единица";

b = 1;

}

else

{

a = "Не единица";

b = 22222;

}

Здесь мы сравниваем значение переменной x с единицей и в зависимости от

результатов сравнения присваиваем переменным f и h разные значения.

if ((x == 1) && (y > 10))

{

f = 3;

}

else

{

f = 33;

}

А здесь мы использовали сложное условие, возвращающее значение true

в случае, если значение переменной x равно 1 и значение переменной y боль-

ше 10.

Существует также другая, "вырожденная" разновидность условного выраже-

ния, содержащая только один блок true, который выполняется при выполне-

нии условия и пропускается, если условие не выполнено.

Page 169: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 157

if (<условие>)

<блок true>

Например:

if ((x == 1) && (y > 10))

{

f = 3;

}

Выражения выбора

Выражение выбора — это фактически несколько условных выражений, объ-единенных в одном. Его формат таков:

switch (<исходное выражение>) {

case <значение 1> :

<блок 1>

break;

[case <значение 2> :

<блок 2>

break;]

<... другие секции case>

[default :

<блок, исполняемый для остальных значений>]

}

В выражениях выбора используются ключевые слова switch, case и default и оператор прерывания break, о котором мы поговорим потом. Часто такие вы-ражения называются "выражениями switch-case".

Результат вычисления исходного выражения последовательно сравнивается со значением 1, значением 2 и т. д. и, если такое сравнение увенчалось успехом, выполняется соответствующий блок кода (блок 1, блок 2 и т. д.). Если же ни одно сравнение не увенчалось успехом, выполняется блок кода, находящийся в секции default (если, конечно, она присутствует).

Вот пример выражения выбора:

switch (a) {

case 1 :

out = "Единица";

break;

case 2 :

out = "Двойка";

break;

case 3 :

out = "Тройка";

break;

Page 170: Дронов В. Самоучитель Silverlight 3 (2010)

158 Часть III. Язык C#

default :

out = "Другое число";

}

Здесь, если переменная a содержит значение 1, переменная out получит зна-

чение "Единица", если 2 — значение "Двойка", а если 3 — значение "Тройка".

Если же переменная a содержит какое-то другое значение, переменная out

получит значение "Другое число".

Встретив оператор прерывания break, среда исполнения Silverlight прерывает

выполнение блока, в котором он присутствует, и начинает выполнение кода,

следующего за выражением выбора. Если же данный оператор отсутствует,

то после выполнения текущего блока будет выполнен следующий. Так, если

значение условия совпало со значением 1 и был выполнен блок 1, не содер-

жащий оператора break, будет также выполнен блок 2.

Давайте уберем все действия break в нашем примере:

switch (a) {

case 1 :

out = "Единица";

case 2 :

out = "Двойка";

case 3 :

out = "Тройка";

default :

out = "Другое число";

}

В этом случае все блоки будут выполняться последовательно, один за дру-

гим. И переменной out всегда будет присваиваться строка "Другое число".

Циклы

Циклы — это особые выражения, позволяющие выполнить один и тот же

блок кода несколько раз. Выполнение кода прерывается по наступлению не-

коего условия.

C# предлагает программистам несколько разновидностей циклов. Рассмот-

рим их.

Цикл со счетчиком

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

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

цикла.

Page 171: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 159

Цикл со счетчиком записывается так:

for (<выражение инициализации>; <условие>; <приращение>)

<тело цикла>

Здесь используется ключевое слово for. Поэтому такие циклы часто называ-ют "циклами for".

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

Следующий шаг — проверка условия. Оно определяет момент, когда выпол-нение цикла прервется и начнет выполняться следующий за ним код. Как

правило, условие сравнивает значение счетчика цикла с его граничным значе-

нием. Если условие возвращает true, выполняется тело цикла, в противном случае цикл завершается и начинается выполнение кода, следующего за цик-лом.

После прохода тела цикла выполняется выражение приращения, изменяющее значение счетчика. Это выражение обычно инкрементирует счетчик (увели-

чивает его значение на единицу). Далее снова проверяется условие, выполня-

ется тело цикла, приращение и т. д., пока условие не вернет false.

Пример цикла со счетчиком:

for (int i = 1; i < 11; i++)

{

a += 3;

b = i * 2 + 1;

}

Этот цикл будет выполнен 10 раз. Мы присваиваем счетчику i начальное

значение 1 и после каждого выполнения тела цикла увеличиваем его на еди-ницу. Цикл перестанет выполняться, когда значение счетчика увеличится до 11, и условие цикла станет ложным.

Счетчик цикла можно использовать в одном из выражений тела цикла — как

это сделали мы. Счетчик i будет содержать последовательно возрастающие значения от 1 до 10, которые можно использовать в вычислениях.

Приведем еще два примера цикла со счетчиком:

for (int i = 10; i > 0; i--)

{

a += 3;

b = i * 2 + 1;

}

Page 172: Дронов В. Самоучитель Silverlight 3 (2010)

160 Часть III. Язык C#

Здесь значение счетчика декрементируется. Начальное его значение равно 10.

Цикл выполнится 10 раз и завершится, когда счетчик i будет содержать 0;

при этом значения последнего будут последовательно уменьшаться от 10

до 1.

for (int i = 2; i < 21; i += 2)

{

b = i * 2 + 1;

}

А в этом примере начальное значение счетчика равно 2, а конечное — 21, но

цикл выполнится, опять же, 10 раз. А все потому, что значение счетчика уве-

личивается на 2 и последовательно принимает значения 2, 4, 6... 20.

В особом "вырожденном" случае цикл for может даже не содержать тела.

В этом случае "полезную нагрузку" цикла несет на себе выражение прираще-ния.

Цикл с постусловием

Цикл с постусловием во многом похож на цикл со счетчиком, а именно

в том, что он выполняется до тех пор, пока остается истинным условие цикла.

Причем условие проверяется не до, а после выполнения тела цикла, отчего

цикл с постусловием и получил свое название. Такой цикл выполнится хотя

бы один раз, даже если его условие с самого начала ложно.

Формат цикла с постусловием:

do

<тело цикла>

while (<условие>);

Для задания цикла с постусловием используются ключевые слова do и while.

Поэтому такие циклы часто называют "циклами do-while".

Вот пример цикла с постусловием:

do {

a = a * i + 2;

i = ++i;

} while (a < 100);

А вот еще один пример:

var a = 0, i = 1;

do {

a = a * i + 2;

Page 173: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 161

i = ++i;

} while (i < 20);

Здесь мы используем счетчик, чье конечное значение ограничено. Хотя, ко-нечно, в данном примере удобнее было бы использовать уже знакомый нам и специально предназначенный для таких случаев цикл со счетчиком.

Цикл с предусловием

Цикл с предусловием отличается от цикла с постусловием тем, что условие проверяется перед выполнением тела цикла. Так что, если оно (условие) из-начально ложно, цикл не выполнится ни разу.

while (<условие>)

<тело цикла>

Для создания цикла с постусловием используется ключевое слово while. Поэтому такие циклы называют еще "циклами while" (не путать с "циклами do-while"!).

Пример цикла с предусловием:

while (a < 100) {

a = a * i + 2;

i = ++i;

}

Прерывание и перезапуск цикла

Иногда бывает нужно прервать выполнение цикла. Для этого C# предостав-

ляет программистам операторы break и continue.

Уже знакомый нам по выражениям выбора оператор прерывания break по-зволяет прервать выполнение цикла и перейти к следующему за ним выра-жению.

while (a < 100) {

a = a * i + 2;

if (a > 50)

{

break;

}

++i;

}

В этом примере мы прерываем выполнение цикла, если значение перемен-

ной a превысит 50.

Оператор перезапуска continue позволяет перезапустить цикл, т. е. оставить

невыполненными все последующие выражения, входящие в тело цикла, и

Page 174: Дронов В. Самоучитель Silverlight 3 (2010)

162 Часть III. Язык C#

запустить выполнение цикла с самого его начала: проверка условия, выпол-

нение приращения и тела и т. д.

while (a < 100) {

i = ++i;

if (i > 9 && i < 11)

{

continue;

}

a = a * i + 2;

}

Здесь мы пропускаем выражение вычисления a для всех значений i от 10

до 20.

Безусловный переход

Иногда бывает нужно прервать выполнение кода и продолжить его с другого

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

оператор безусловного перехода goto.

Метка помечает место в исходном коде, на который планируется в дальней-

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

<имя метки>:

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

но в пределах метода и удовлетворять тем же правилам, что и имя перемен-

ной.

Оператор безусловного перехода goto имеет такой вид:

goto <имя метки>;

Встретив такой оператор, среда исполнения Silverlight прервет выполнение

кода и продолжит его с того места, где присутствует метка с заданным име-

нем.

if (x > 100)

{

goto TooBig;

}

. . .

TooBig:

. . .

Здесь, если значение переменной x больше 100, будет выполнен переход на

метку TooBig.

Page 175: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 163

Оператор goto можно использовать и в выражениях выбора (см. ранее). В этом случае он должен иметь такой вид:

goto <секция case, на которую должен быть выполнен переход>;

Например:

switch (a) {

case 1 :

out = "Единица или двойка";

break;

case 2 :

goto case 1;

case 3 :

out = "Тройка";

break;

}

В этом случае, если значение переменной a будет равно 2, произойдет пере-

ход на секцию case 1.

Массивы

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

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

Не забываем, что нумерация элементов массива начинается с нуля, а не еди-ницы.

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

Выражение для создания массива может иметь один из двух следующих форматов:

<тип элементов массива>[] <имя массива> =

�new <тип элементов массива>[<размер массива>];

Page 176: Дронов В. Самоучитель Silverlight 3 (2010)

164 Часть III. Язык C#

или

<тип элементов массива>[] <имя массива> =

�{<список значений элементов массива, разделенных запятыми>};

Выражение первого формата создает пустой массив с заданными именем,

типом элементов и размером. Выражение второго формата создает массив

с заданными именем и типом элементов и сразу же присваивает его элементам

указанные значения; размер массива при этом задается равным количеству

значений элементов, указанных в фигурных скобках.

Массив фактически представляет собой объект класса Array. Поэтому для его

создания используется знакомый нам по главе 7 оператор создания объекта new.

string[] sPlatforms = new string[4];

Это выражение создает пустой массив sPlatforms из четырех строковых эле-

ментов.

string[] sPlatforms = {"HTML+CSS+JavaScript", "Flash", "Java",

�"Silverlight"};

А это выражение создает точно такой же массив, но вдобавок присваивает

его элементам указанные в фигурных скобках строки.

Чтобы получить доступ к нужному элементу массива, мы укажем его индекс

в квадратных скобках после имени массива.

s = sPlatforms[2];

После выполнения этого выражения мы получим в переменной s значение

элемента массива sPlatforms с индексом 2 (третьего по счету) — "Java".

sPlatforms[3] = "Silverlight 3";

А это выражение присвоит элементу массива sPlatforms с индексом 3 (чет-

вертому по счету) новое значение.

C# позволяет создавать массивы с несколькими размерностями (многомерные

массивы). Для этого достаточно при создании такого массива перечислить

все его размеры через запятую.

int[,] iMatrix = new int[4, 4];

Это выражение создает двумерный массив iMatrix — матрицу размерами

4×4. Обратим внимание, что мы указали оба его размера во вторых по счету

квадратных скобках и поставили запятую в первых, объявив тем самым, что

наш массив будет двумерным.

Page 177: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 165

double[, ,] dCube = new double[3, 3, 3];

А здесь мы создали трехмерный массив ("куб"). Обратим внимание, что в

первых квадратных скобках стоят уже две запятые. То есть количество запя-тых в первых квадратных скобках должно быть на единицу меньше количе-

ства размерностей массива.

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

iMatrix[1, 2] = 4;

dValue = dCube[2, 0, 1];

А еще мы можем создать массив, каждый элемент которого будет представ-

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

<тип элементов массива>[][] <имя массива> =

�new <тип элементов массива>[<размер массива>][];

Например:

int[][] iMatrix2 = new int[2][];

iMatrix2[0] = new int[2];

iMatrix2[1] = new int[4];

iMatrix2[2] = new int[5];

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

Чтобы обратиться к элементу "внутреннего" массива, мы укажем сначала ин-

декс элемента "внешнего" массива, а потом — индекс "внутреннего" массива, каждый — в своих квадратных скобках.

iMatrix2[1][2] = 2;

iMatrix2[2][4] = 7;

Цикл просмотра

Мы уже знакомы с тремя разновидностями циклов, предлагаемыми нам C#.

Сейчас мы изучим еще один, четвертый по счету, цикл — цикл просмотра.

Он позволит нам "пройти" по всем элементам массива и что-либо с ними сде-

лать.

Цикл просмотра создается с помощью выражения следующего вида:

foreach (<тип элементов массива> <имя переменной элемента> in <массив>)

<тело цикла>

Как видим, здесь используются ключевые слова foreach и in. Поэтому цикл

просмотра часто называют "цикл foreach-in".

Page 178: Дронов В. Самоучитель Silverlight 3 (2010)

166 Часть III. Язык C#

Прежде всего, создается переменная элемента с указанным именем. В эту пе-

ременную помещается значение первого элемента массива. Выполняется тело

цикла. После этого в переменную элемента помещается значение второго

элемента массива, снова выполняется тело цикла и т. д. для всех элементов массива.

Переменная элемента может использоваться в теле цикла просмотра — соб-

ственно, для этого она и создается. При этом она доступна только в теле цикла; по завершению цикла она уничтожается.

string output = "";

foreach (string str in sPlatforms)

{

output += str + "\r\n";

}

Данный цикл перебирает все элементы массива sPlatforms и добавляет их

к значению строковой переменной output, разделяя символами возврата ка-ретки и перевода строки.

int s = 0;

foreach (int i in iMatrix)

{

s += i;

}

А после выполнения этого цикла мы получим в переменной s сумму значе-

ний всех элементов массива iMatrix. Вспомним: этот массив у нас двумер-ный — значит, цикл просмотра работает и с многомерными массивами!

Комментарии

Очень часто бывает, что нужно поместить прямо в C#-код какие-либо приме-чания для себя или коллег по работе. Для этого используются особые выра-

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

Для вставки комментариев в код ActionScript предусмотрены два оператора

комментария: // и /*...*/. Первый из них позволяет вставить в код одно-строчный комментарий:

// Строка комментария

a = b + c; // Это однострочный комментарий

Заметим, что во втором случае комментарий ставится после точки с запятой,

обозначающей конец выражения.

Page 179: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 9. Основные конструкции языка C# 167

А оператор /*...*/ позволяет вставить в код комментарий любого размера:

/*

В этом выражении мы складываем содержимое двух переменных

и помещаем результат в третью

*/

a = b + c;

Разумеется, в комментариях нужно писать не то, что делает данное выраже-

ние (это и так видно), а его назначение. Ведь именно для этого они и преду-

смотрены.

Что дальше?

Вот мы и познакомились с началами языка программирования C#. Весьма

мощный язык, вы не находите?

Однако вся мощь C# проявится только при работе со сложными данными:

классами и объектами. В следующей главе мы займемся ими вплотную. Так-

же мы изучим еще несколько типов данных C#: структуры, интерфейсы и пе-

речисления. Ну и попутно узнаем много чего по мелочи.

Глава будет большой!

Page 180: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 10

Сложные типы данных C#

В предыдущей главе мы изучили основы языка программирования C#. Те-

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

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

ков событий. В нашей программистской практике может потребоваться, ска-

жем, написать свой класс со свойствами и методами. А мы так и не научились

это делать!

В этой главе мы как раз и займемся классами и структурами — сложными

типами данных C#. Заодно мы изучим другие сложные типы, не относящиеся

к классам: интерфейсы и перечисления. Кто знает, может, они нам тоже при-

годятся...

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

О них все было рассказано в главе 4. Напомним только, что все изучаемые

здесь типы данных — классы, структуры, интерфейсы и перечисления —

должны находиться в том или ином пространстве имен. Это требование са-

мой платформы Silverlight.

Классы и объекты

Об объектах и классах мы узнали еще в главе 2. Повторим пройденное, чтобы

все вспомнить.

Объект — это сложная сущность, хранящая сами данные, предоставляющая

инструменты для манипуляции этими данными и самим объектом и реагиро-

вания на события, происходящие с объектом.

Класс — это образец, на основе которого создаются объекты с реальными

данными. Или, говоря совсем уж по-программистски, тип объекта.

Page 181: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 169

Элементы класса

Класс может содержать элементы шести различных типов. Давайте их кратко

рассмотрим.

Поля

Поля — это своего рода переменные, являющиеся собственностью объекта.

Они практически ничем не отличаются от уже известных нам по главе 9 пе-

ременных, используемых в методах.

Поля применяются для хранения каких-либо значений, составляющих данные

объекта этого класса. Как правило, в них хранятся внутренние данные объек-

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

Для предоставления доступа к этим данным обычно применяются свойства,

речь о которых пойдет далее.

Методы

Метод, как мы уже знаем, — это фрагмент исполняемого кода, выполняющий

определенные действия либо над данными, которые могут храниться в дан-

ном объекте или быть сторонними по отношению к нему, либо с самим объ-

ектом.

Метод может принимать параметры указанного при определении этого мето-

да типа и также может возвращать результат. Это мы также знаем.

Любой класс должен содержать, по крайней мере, один метод, который вы-

полняется при создании объекта на основе этого класса, — конструктор. Кон-

структор должен иметь то же имя, что и сам класс, и обычно задает какие-то

изначальные значения для свойств объекта.

Свойства

Свойство также можно представить в виде переменной, являющейся собст-

венностью объекта и предназначенной для хранения какой-либо единицы

данных. Однако это более сложная сущность, нежели поле.

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

стей, работающих совместно.

� Поле, используемое для хранения значения этого свойства. Это поле все-

гда делается недоступным извне объекта данного класса.

� Get-метод, выполняющийся при получении значения данного свойства.

� Set-метод, выполняющийся при присваивании этому свойству нового зна-

чения.

Page 182: Дронов В. Самоучитель Silverlight 3 (2010)

170 Часть III. Язык C#

С полем, в принципе, все понятно (переменная — штука незатейливая). А вот

get- и set-методы заслуживают более подробного рассказа.

Как мы только что узнали, get-метод выполняется при получении значения

данного свойства. Этот метод не принимает параметров и возвращает резуль-

тат, который и получит программист, если обратится к данному свойству,

в качестве его (свойства) значения.

Обычно get-метод возвращает значение, хранящееся в соответствующем

свойству поле. Но во многих случаях он выполняет некоторые дополнитель-

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

основе хранящегося в поле значения другое, которое и возвращает в качестве

результата.

Set-метод не возвращает никакого результата и принимает единственный па-

раметр — новое значение, присвоенное данному свойству программистом.

Обычно он просто присваивает его соответствующему полю, но зачастую

выполняет и некоторые дополнительные действия: производит проверку до-

пустимости нового значения свойства, изменяет попутно значения других

полей или выполняет какие-либо манипуляции над самим объектом.

Отсюда вытекает радикальное отличие свойства от поля и его же огромное

преимущество: мы можем выполнять какие-либо действия над объектом дан-

ного класса при получении значения этого свойства и при присваивании ему

нового значения!

Так, если мы присвоим новое значение свойству IsEnabled объекта класса

TextBox (поле ввода), среда исполнения Silverlight не только выполнит собст-

венно присваивание этого значения, но и сделает данное поле ввода, соответ-

ственно, доступным или недоступным для пользователя.

Далеко не всякое свойство содержит в своем составе и поле, и get-, и set-

методы. Например, оно может не содержать ни поля, ни set-метода. Такое

свойство будет доступно только для чтения; при обращении к нему get-метод

вычислит значение этого свойства и вернет его в качестве результата. Напри-

мер, класс String (строковый тип данных) поддерживает свойство Length,

возвращающее длину строки; это свойство доступно только для чтения и

представляет собой один только get-метод.

Также свойство может не содержать get-метода. Тогда оно будет доступно

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

События

Событие — это некое условие, возникающее в объекте данного класса в про-

цессе действий пользователя, работы Web-обозревателя, операционной сис-

темы или самого приложения. Событие может быть обработано с помощью

Page 183: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 171

особых методов — обработчиков события, которые создаются в классе стра-

ницы. Собственно, мы это давно знаем, т. к. уже написали несколько обра-

ботчиков событий в процессе изучения платформы Silverlight.

Мы еще не говорили об этом, но к одному событию может быть привязано сразу несколько обработчиков, и все они будут выполняться при возникнове-нии данного события. Хотя обычно вполне хватает одного обработчика.

События — весьма сложные сущности, еще более сложные, чем свойства, поэтому разговор о них, пожалуй, растянется на целую главу. Кроме того, нужда в собственных событиях возникает нечасто, тем более у начинающих программистов. Поэтому создание собственных событий мы рассматривать не будем. Желающие выяснить, как это делается, могут обратиться к Microsoft SDK for Visual Studio 2008.

Именованные константы

В главе 9 мы познакомились с константами — величинами, значение которых не изменяется никогда. Это всевозможные строки, числа и значения true и

false, которые мы используем в коде приложения.

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

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

Вложенные типы

Кроме того, класс может содержать другие типы: классы, структуры, интер-фейсы и перечисления. Это так называемые вложенные типы. Используются они нечасто, но иногда бывают полезны.

Статические элементы класса

Обычно все элементы класса — поля, свойства, методы и события — отно-сятся к объекту, созданному на основе этого класса. Поля и свойства хранят его данные, методы манипулируют им, а события сигнализируют, что в объ-екте что-то произошло.

Однако есть возможность создать поле, свойство, метод или событие, кото-рое будет относиться не к объекту, а к классу. Такое поле, свойство, метод или событие называется статическим.

Page 184: Дронов В. Самоучитель Silverlight 3 (2010)

172 Часть III. Язык C#

Статические поля и свойства хранят какие-либо данные о самом классе или

величины, которые должны быть одинаковыми для всех объектов этого клас-

са, например, константы, значения которых вычисляются в процессе работы

приложения. (Именованные константы в этом случае использовать не полу-

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

ложения.)

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

выполнения общих для всех объектов данного класса задач и пр.

Статические события... автор даже представить себе не может, зачем они мо-

гут понадобиться. Но возможность создавать их есть.

Именованные константы сами по себе являются статическими элементами

класса. Специально делать их статическими смысла нет.

Наследование

Классы имеют одно замечательное преимущество перед другими типами

данных Silverlight. Они могут быть созданы на основе других классов. Об

этом уже говорилось в главе 2, так что не будем повторяться.

Класс-потомок, созданный на основе класса-родителя, наследует все его эле-

менты: поля, свойства, методы (в том числе конструкторы), события, имено-

ванные константы и вложенные типы. Такие элементы класса называются

унаследованными, а сам процесс — наследованием.

Класс-потомок может либо оставить унаследованные свойства и методы как

есть, либо переопределить их, дополнив или полностью заменив их логику.

Это используется в Silverlight сплошь и рядом.

Работа с объектами и классами

Теперь более подробно рассмотрим работу с объектами: их создание и доступ

к их элементам; также выясним, как работать со статическими элементами

класса. И попутно изучим вторую разновидность типов данных C# — ссы-

лочные типы.

Создание объектов

Объект создается, как мы выяснили в главе 7, с помощью оператора созда-

ния объекта new. Выражение создания объекта имеет такой вид:

new <имя класса>([<список значений параметров конструктора,

�разделенных запятыми>]);

Page 185: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 173

Фактически за оператором new следует вызов конструктора данного класса.

В скобках указываются параметры этого конструктора; если конструктор не

принимает параметров, скобки все равно следует указать.

Оператор new возвращает созданный объект (точнее, указатель на него, но об

этом позже), который можно присвоить переменной. Эта переменная должна

иметь тип данного класса либо класса, от которого порожден данный класс

(т. е. должна иметь, как говорят программисты, объектный тип).

SomeClass sc;

OtherClass oc;

sc = new SomeClass();

oc = new OtherClass(10, 4.5);

В некоторых случаях требуется указать полное имя класса со списком всех

пространств имен, в которые он последовательно вложен. (Это требуется,

если пространство имен не отображено. О пространствах имен и их отобра-

жении см. главу 4.)

sc = new OuterNameSpace.InnerNameSpace.SomeClass();

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

нем", классе, имя "внешнего" класса также включается в полное имя класса,

на основе которого мы создаем объект.

sc = new OuterNameSpace.InnerNameSpace.OuterClass.InnerClass();

Ссылочные типы

В главе 9 мы выяснили, что встроенные типы Silverlight — строки, числа,

логические величины и символы — являются значимыми, т. е. переменная

такого типа хранит само значение. В случае с объектами это не так.

Объекты в Silverlight — это ссылочные типы. Переменная такого типа хранит

указатель на объект — своего рода его адрес в оперативной памяти компью-

тера. Сам же объект существует отдельно от переменной и может продолжать

существование, даже если переменная уже уничтожена.

Если мы присвоим значение переменной, хранящей указатель на объект, дру-

гой переменной, произойдет копирование указателя в данную переменную.

В итоге мы получим две переменные, указывающие на один и тот же объект,

и сможем обращаться к этому объекту, используя любую из них.

Кстати, в той же главе 9 мы узнали о массивах. Так вот, массивы — это

тоже ссылочные типы, т. к. фактически представляют собой объекты класса

Array.

Page 186: Дронов В. Самоучитель Silverlight 3 (2010)

174 Часть III. Язык C#

Работа с элементами объекта и статическими элементами класса

Обратиться к полю или свойству объекта проще простого. Пишем имя объек-та (т. е. имя переменной, хранящей указатель на него), ставим точку и после нее указываем имя поля или свойства.

sc.SomeProperty = 20;

txtMillimetres.Text = "100";

sInches = txtInches.Text;

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

if (txtInches.Text == String.Empty)

Здесь мы обратились к статическому свойству Empty класса String, храняще-му пустую строку, чтобы сравнить с ней значение свойства Text объекта

txtInches.

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

словом this; эту переменную создает сама среда исполнения Silverlight, и доступна она только в теле метода. Хотя часто это ключевое слово опус- кают.

this.SomeProperty = 40;

SomeProperty = 40;

Эти два выражения абсолютно равноценны и обращаются к свойству

SomeProperty того же объекта, в котором выполняется данный код.

Вызов метода, как обычного, так и статического, выполняется так же просто. Мы, соответственно, указываем имя объекта или класса (для статического метода; возможно, придется указать полное имя класса), ставим точку, пи-шем имя метода и перечисляем в скобках через запятую значения его пара-метров. Если метод не принимает параметров, мы поставим после его имени пустые скобки.

sc.SomeMethod(2, 56, "абвгд");

txtInches.Focus();

Метод может возвращать результат. Мы можем сохранить его в переменной, использовать в сложном выражении, а можем и проигнорировать.

m = sc.OtherMethod(22);

n = (sc.OtherMethod(44) + 3) / 0.5;

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

Page 187: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 175

В таком случае, во-первых, в качестве такого параметра должна использо-ваться переменная (что понятно), а во-вторых, перед ней следует поставить

ключевое слово out.

result1 = sc.ThirdMethod(65.2, out result2);

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

в переменную result1, еще и другое значение, которое попадет в переменную

result2.

Теперь о событиях. Проще всего привязать к нужному событию обработчик в XAML-коде — там нам на помощь придет Visual Web Developer 2008; он и покажет нам список всех доступных событий, и создаст заготовку для обра-ботчика. Если же мы хотим привязать обработчик к событию в C#-коде, нам сначала придется его написать.

private void btnConvert_Click(object sender, RoutedEventArgs e)

{

}

Хотя, конечно, мы можем назвать его по-другому — просто так называет об-работчики событий Visual Web Developer 2008.

Далее нам нужно привязать его к событию. Пишем имя объекта (или имя класса, возможно, полное, если хотим привязать обработчик к статическому событию), ставим точку, пишем имя события — все, как в случае свойства или метода. И просто прибавляем к этому событию написанный ранее метод-

обработчик с помощью хорошо нам знакомого оператора сложения +.

btnConvert.Click = btnConvert.Click + btnConvert_Click;

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

Но обычно в таких случаях пользуются оператором сложного присваивания:

btnConvert.Click += btnConvert_Click;

Если нам нужно "отвязать" обработчик от события, мы просто отнимем его, воспользовавшись оператором вычитания — или аналогичным оператором сложного присваивания.

btnConvert.Click = btnConvert.Click — btnConvert_Click;

btnConvert.Click -= btnConvert_Click;

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

a = SomeClass.SOME_NAMED_CONSTANT;

Обращение к вложенным типам мы уже рассмотрели, когда говорили о соз-

дании объектов на основе вложенных классов в соответствующем разделе.

Page 188: Дронов В. Самоучитель Silverlight 3 (2010)

176 Часть III. Язык C#

Операторы проверки типа и преобразования ссылочных типов

В главе 9 мы изучили большинство операторов языка C#, которые пригодятся

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

ми, нам понадобятся еще два оператора, которые сейчас самое время рас-

смотреть.

Оператор проверки типа is возвращает true, если объект, указанный слева

от него, создан на основе класса, стоящего справа, или одного из его классов-

потомков. Если это не так, возвращается false.

if (txtInches is TextBox)

{

. . .

}

В данном случае условие выполнится, т. к. объект txtInches создан на основе

класса TextBox (является полем ввода).

if ("Silveright" is Double)

{

. . .

}

А это условие не выполнится — значение "Silverlight" не является числом

с плавающей точкой.

Оператор преобразования ссылочных типов as выполняет преобразование

типа объекта, указанного слева от него, к типу (классу), стоящему справа.

Если преобразование удалось, возвращается указатель на данный объект,

приведенный к заданному типу; в противном случае возвращается null.

FrameworkElement felControl;

felControl = txtInches as FrameworkElement;

Здесь мы получим в переменной felControl указатель на поле ввода txtInches,

преобразованный к типу FrameworkElement. (Класс FrameworkElement — базо-

вый класс, от которого порождены все классы компонентов.)

double d;

d ="Silverlight" as Double;

А здесь мы получим в переменной d значение null.

Но ведь в главе 9 мы изучили отличный оператор преобразования типов вида

(<целевой тип>)<преобразуемое значение>. Зачем нужен еще один оператор,

выполняющий то же самое?

Page 189: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 177

Дело в том, что оператор (<целевой тип>)<преобразуемое значение> в случае

невозможности преобразования типов выбрасывает исключение. (Об исклю-

чениях и их обработке мы поговорим в главе 12.) А оператор as, как мы уже

знаем, возвращает значение null. Как видим, не такие уж они и одинаковые,

эти два оператора...

Значение null

Мы уже упоминали несколько раз непонятное значение null. Что это такое?

Это особое значение, которое могут принимать переменные объектного типа.

Оно обозначает отсутствие любых данных в переменной, в случае перемен-

ной объектного типа — отсутствие в ней указателя на объект.

FrameworkElement felControl;

felControl = txtInches as FrameworkElement;

if (felControl != null)

. . .

Значение null обычно применяется в условных выражениях (как показано

в данном примере). Но его можно присвоить переменной объектного типа,

что в некоторых случаях может быть полезно.

felControl = null;

Уничтожение объектов

Так, объект мы создали, использовали и теперь хотим его уничтожить. Как

это делается?

Собственно, нам самим это делать не нужно. Среда исполнения Silverlight

сама позаботится о том, чтобы уничтожить не нужный более объект и осво-

бодить память подо что-то полезное.

Когда объект создается, указатель на него присваивается какой-либо пере-

менной, полю или свойству другого объекта. Так вот, пока на объект имеется

хотя бы один указатель, он существует в памяти. Как только последний ука-

затель на него теряется (уничтожается хранящая его переменная или уничто-

жается объект, чье поле или свойство хранит его), объект считается ненуж-

ным и уничтожается. Это правило неукоснительно соблюдается средой ис-

полнения Silverlight.

Полезные встроенные классы Silverlight

Сейчас настала пора рассмотреть некоторые встроенные классы платформы

Silverlight, они могут оказаться для нас полезными в самое ближайшее вре-

Page 190: Дронов В. Самоучитель Silverlight 3 (2010)

178 Часть III. Язык C#

мя. Все встроенные классы перечислены и описаны в документации по

Silverlight.

Класс Object

Класс Object — самый базовый в иерархии классов Silverlight; все остальные классы и структуры — строки, числа, компоненты, вспомогательные — про-исходят от него и наследуют его элементы.

Классу Object Silverlight соответствует ключевое слово object C#.

Класс String

Класс String Silverlight представляет строковое значение. Вероятно, это единственный класс, используемый для представления элементарных данных в Silverlight.

Свойство Chars этого класса возвращает символ строки, чей номер указан по-сле этого свойства в круглых скобках. Символ возвращается как величина

символьного типа (char).

string str = "Silverlight";

char ch;

ch = str.Chars(2);

После выполнения этого кода мы получим в переменной ch символ 'l' —

третий по счету в строке "Silverlight". (Нумерация символов в строке начи-нается с нуля.)

Свойство Length возвращает длину строки в символах в виде целого числа.

Метод IndexOf() возвращает номер первого вхождения указанного символа или подстроки в строке в виде целого числа. Формат его вызова таков:

IndexOf(<искомый символ или подстрока>

�[, <номер символа, с которого следует начать поиск>])

Здесь первым параметром может быть передан как искомый символ в сим-

вольном (char) виде, так и искомая подстрока в строковом виде. Вторым па-раметром передается целочисленный номер символа в строке, начиная с ко-торого следует приступать к поиску; если он не передан, поиск будет вестись с начала строки.

string str = "Silverlight";

int i1, i2;

i1 = str.IndexOf('l', 4);

i2 = str.IndexOf("ver");

Здесь мы получим в переменной i1 число 6 (номер по счету второго символа

'l' в строке "Silverlight"), а в переменной i2 — число 3.

Page 191: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 179

На самом деле класс String определяет несколько методов IndexOf, разли-

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

Метод LastIndexOf() аналогичен методу IndexOf() за тем исключением, что

поиск символа или подстроки ведется не с начала, а с конца строки.

string str = "Silverlight";

int i;

i1 = str.LastIndexOf('l');

Здесь мы получим в переменной i число 6.

Метод Substring() возвращает подстроку заданной длины, начинающуюся

с заданного символа в виде строковой величины.

Substring(<номер первого символа подстроки>[, <длина подстроки>])

Первый параметр задает номер символа возвращаемой подстроки, второй —

ее длину; если второй параметр не указан, возвращенная подстрока будет со-

держать все оставшиеся символы строки. Оба параметра задаются в виде це-

лых чисел.

string str = "Silverlight", s;

s = str.Substring(6, 3);

В переменной s окажется строка "lig".

На самом деле класс String определяет два метода Substring, различающих-

ся числом и типом параметров. Один из этих методов принимает один пара-метр, другой — два параметра.

Не принимающие параметров методы ToLower() и ToUpper() возвращают ко-

пию строки, в которой все символы приведены к нижнему и верхнему реги-

стру соответственно.

Вообще, класс String поддерживает гораздо больше методов. Все они описа-

ны в документации по Silverlight.

Класс Math

Класс Math содержит только именованные константы и статические методы.

Именованные константы E и PI содержат значения математических значений

e и π соответственно.

Page 192: Дронов В. Самоучитель Silverlight 3 (2010)

180 Часть III. Язык C#

Некоторые из статических методов класса Math перечислены в табл. 10.1.

Таблица 10.1. Некоторые методы класса Math

Метод Описание

Abs(<параметр>) Возвращает абсолютное значение параметра

Acos(<параметр>) Возвращает арккосинус параметра в радианах

Asin(<параметр>) Возвращает арксинус параметра в радианах

Atan(<параметр>) Возвращает арктангенс параметра в радианах

Ceiling(<параметр>) Возвращает ближайшее целое число, большее или равное параметру

Cos(<параметр>) Возвращает косинус параметра, заданного

в радианах

Exp(<параметр>) Возвращает значение eпараметр

Floor(<параметр>) Возвращает ближайшее целое число, меньшее параметра или равное ему

Log(<параметр>) Возвращает натуральный логарифм параметра

Log10(<параметр>) Возвращает десятичный логарифм параметра

Max(<параметр 1>, <параметр 2>) Возвращает наибольший из параметров

Min(<параметр 1>, <параметр 2>) Возвращает наименьший из параметров

Pow(<основание>, <порядок>) Возвращает значение основаниепорядок

Round(<параметр>) Возвращает значение параметра, округленное

до ближайшего целого

Sign(<параметр>) Возвращает –1, если значение параметра

меньше нуля, 0, если равно нулю, и 1, если больше нуля

Sin(<параметр>) Возвращает синус параметра, заданного

в радианах

Sqrt(<параметр>) Возвращает квадратный корень из параметра

Tan(<параметр>) Возвращает тангенс параметра, заданного

в радианах

Создание собственных классов

Если мы собираемся серьезно работать с Silverlight, то должны научиться

создавать свои собственные классы. Во многих случаях без этого не обой-

тись.

Page 193: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 181

Создание самих классов

Класс, как мы помним из главы 3, создается с помощью особого выражения

объявления класса, в котором используется ключевое слово class. Это клю-

чевое слово, собственно, и говорит о том, что мы определяем новый класс.

[<список модификаторов, разделенных пробелами>] class <имя класса>

�[: <имена класса-родителя и реализуемых интерфейсов,

�разделенные запятыми>]

{

<объявления элементов класса>

}

Имя класса должно удовлетворять тем же критериям, что и имя переменной

(подробнее об именах переменных см. в главе 9). Не забываем также и о том,

что Silverlight учитывает регистр, в котором набраны имена классов; так,

классы SomeClass и someclass — с точки зрения Silverlight разные.

За именем класса через двоеточие может указываться имя класса-родителя.

Если оно не указано, его родителем станет класс Object. Там же могут указы-

ваться имена интерфейсов, которые этот класс реализует; они отделяются

друг от друга и от имени класса-родителя запятыми. (Об интерфейсах речь

пойдет в конце этой главы.)

У класса может быть только один класс-родитель. Однако класс может реали-зовывать сколько угодно интерфейсов.

Модификаторы — это особые ключевые слова C#, задающие дополнитель-

ные параметры класса. Давайте их рассмотрим.

� public — данный класс может использоваться в текущей сборке и во всех

сборках, ссылающихся на текущую (публичный класс). Так, классы, опре-

деленные в библиотечных сборках и предназначенные для использования

в сторонних приложениях, делают публичными.

� internal — данный класс может использоваться только в текущей сборке

(внутренний класс). Частными обычно делают служебные классы.

� private — данный вложенный класс может использоваться только в том

классе, в котором определен (частный класс). Применим только к вло-

женным классам.

� sealed — на основе данного класса не могут быть созданы классы-

потомки. Такие классы обрабатываются средой исполнения Silverlight не-

сколько быстрее.

Page 194: Дронов В. Самоучитель Silverlight 3 (2010)

182 Часть III. Язык C#

� abstract — на основе данного класса не могут быть созданы объекты, т. е.

он предназначен только для роли класса-родителя других классов (абст-

рактный класс). Обычно абстрактными классами делают базовые, "корне-

вые" классы иерархии.

� static — этот класс содержит только статические элементы (статический

класс).

� partial — часть объявления класса. Остальные части находятся в других

файлах.

Класс не может быть одновременно помечен модификаторами sealed и

abstract (что, впрочем, понятно).

Статический класс может содержать только статические элементы (поля, свой-ства, методы и события).

Яркий пример класса, разные части которого определены в разных файлах, — класс страницы Silverlight-приложения.

Определение элементов класса пишется в фигурных скобках, расположенных

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

abstract class SomeClass

Здесь мы объявили абстрактный внутренний класс SomeClass, доступный

только в том пространстве имен, в котором он объявлен. Поскольку мы не

указали класс-родитель, он станет потомком класса Object.

public class OtherClass: ParentClass, ISomeInterface

А здесь мы объявили публичный класс OtherClass, являющийся потомком

класса ParentClass и реализующий интерфейс ISomeInterface.

Создание полей

Поля создаются с помощью выражения объявления такого вида:

[<список модификаторов, разделенных пробелами>] <тип поля> <имя поля>

�[= <начальное значение поля>];

Типом поля может быть либо ключевое слово C#, либо имя класса, структуры,

интерфейса или перечисления Silverlight. Имя поля должно удовлетворять тем

же критериям, что и имя переменной, за тем лишь исключением, что оно

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

буквой f (от англ. field — поле) или символом подчеркивания.

Page 195: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 183

Модификаторы здесь могут быть использованы следующие.

� public — данное поле доступно во всех объектах классов, определенных в текущей сборке и всех сборках, ссылающихся на текущую (публичное поле).

� protected — данное поле доступно только в объектах классов-потомков (защищенное поле).

� internal — данное поле доступно только в объектах классов, определен-ных в текущей сборке (внутреннее поле).

� protected internal — данное поле доступно только в объектах классов-потомков, определенных в текущей сборке (защищенное внутреннее поле).

� private — данное поле доступно только внутри самого класса, в котором определено (частное поле).

� static — статическое поле.

� readonly — поле доступно только для чтения.

Сразу же при объявлении поля мы можем присвоить ему значение.

Значения статических полей задаются либо при объявлении, либо в стати- ческом конструкторе класса. (О статических конструкторах мы поговорим позже.)

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

Публичное поле (помеченное модификатором public) — очень плохой стиль программирования. Для таких целей рекомендуется использовать свойства.

protected string fName;

Здесь мы объявили защищенное строковое поле fName.

static public int fCounter = 0;

А здесь мы объявили публичное (только для примера!) статическое целочис-ленное поле fCounter, которому сразу же присвоили значение 0.

Создание методов

Метод объявляется с помощью выражения следующего вида:

[<список модификаторов, разделенных пробелами>]

�<тип возвращаемого методом результата> <имя метода>

�([<описания параметров метода, разделенные запятыми>])

{

<тело метода>

}

Page 196: Дронов В. Самоучитель Silverlight 3 (2010)

184 Часть III. Язык C#

Типом возвращаемого результата может быть либо ключевое слово C#, либо

имя класса, структуры, интерфейса или перечисления Silverlight. Если метод

не должен возвращать результата, в качестве типа ставится ключевое слово

void.

Имя метода должно удовлетворять тем же критериям, что и имя поля.

В круглых скобках указываются описания параметров метода. Они представ-

ляют собой пары вида:

<тип значения параметра> <имя переменной, которая будет создана

�в теле метода для хранения значения параметра>

разделенные запятыми. Если метод не принимает параметров, круглые скоб-

ки оставляют пустыми.

Тело метода — собственно его код — пишется в фигурных скобках, сле-

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

Если какой-либо параметр метода используется для передачи вычисленного

значения из метода вызвавшему его коду, перед его описанием следует по-

ставить ключевое слово out.

Модификаторы могут использоваться следующие.

� public — данный метод доступен во всех объектах классов, определенных

в текущей сборке и всех сборках, ссылающихся на текущую (публичный

метод).

� protected — данный метод доступен только в объектах классов-потомков

(защищенный метод).

� internal — данный метод доступен только в объектах классов, определен-

ных в текущей сборке (внутренний метод).

� protected internal — данный метод доступен только в объектах классов-

потомков, определенных в текущей сборке (защищенный внутренний ме-

тод).

� private — данный метод доступен только внутри самого класса, в котором

определен (частный метод).

� static — статический метод.

� overide — данный метод представляет собой переопределение метода,

унаследованного от класса-родителя (перекрытый метод).

� virtual — данный метод может быть перекрыт в классе-потомке (вир-

туальный метод).

� abstract — данный метод не имеет тела и поэтому должен быть пере-

крыт в классе-потомке (абстрактный метод).

Page 197: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 185

� sealed — данный метод не может быть перекрыт в классе-потомке. Так помечаются только унаследованные и перекрытые методы.

Если в классе имеется хоть один абстрактный метод, сам класс также стано-вится абстрактным.

Для возврата результата из тела метода используется оператор возврата return. Формат его использования таков:

return <возвращаемое значение>;

Мы уже знаем, что для обращения к полям, свойствам, методам и событиям текущего объекта применяется ключевое слово this. Но в статических мето-дах это ключевое слово использовать нельзя, поэтому там просто пишется имя нужного поля, свойства, метода или события без указания на класс.

В статических методах можно использовать только статические элементы клас-сов.

Часто бывает нужно в перекрываемом методе вызвать оригинальный метод класса-родителя. Для этого используется такая конструкция:

base.<вызов метода>

Здесь ключевое слово base обозначает класс-родитель.

Платформа Silverlight позволяет создавать у класса несколько методов, имеющих одно и то же имя, но различающихся типом и количеством пара-метров. Такие методы-"тезки" называются перегруженными.

Приведем несколько примеров методов:

public int Increment(int iPar)

{

return iPar + 1;

}

. . .

i = sc.Increment(1);

Здесь мы написали абсолютно бесполезный публичный метод, который при-нимает один целочисленный параметр и возвращает целочисленное же зна-чение, увеличенное на единицу. Потом мы вызвали этот метод у объекта sc, передав ему в качестве параметра 1, и получили в переменной i значение 2.

public void Increment2(out int iPar)

{

iPar += 1;

}

Page 198: Дронов В. Самоучитель Silverlight 3 (2010)

186 Часть III. Язык C#

. . .

i = 1;

sc.Increment2(out i);

Здесь мы написали другой такой же бесполезный публичный метод, который

принимает один целочисленный параметр и увеличивает его значение на

единицу. Потом мы присвоили переменной i значение 1, вызвали этот метод

у объекта sc, передав ему в качестве параметра данную переменную, и после

выполнения метода получили в ней значение 2.

protected virtual string SomeMethod()

Этот защищенный метод может быть перекрыт в классах-потомках.

protected override string SomeMethod()

{

. . .

base.SomeMethod();

. . .

}

Здесь мы в классе-потомке перекрываем этот метод. А в теле перекрытого

метода мы выполняем вызов изначального метода класса-родителя.

Создание конструкторов

В Silverlight существуют две разновидности конструкторов. Рассмотрим их.

Первая разновидность — конструктор объекта. Он выполняется при созда-

нии объекта на основе данного класса. Собственно, эта разновидность нам

уже знакома.

Конструктор объекта должен соответствовать следующим требованиям.

� Он должен иметь то же имя, что и сам класс.

� Он должен быть публичным (иначе мы не сможем создать объект на осно-

ве данного класса).

� Он не должен возвращать результата.

public SomeClass()

{

this.SomeProperty = 10;

}

Здесь мы создали конструктор, который присваивает свойству SomeProperty

объекта класса SomeClass значение 10. Обратим внимание, что для доступа

к данному объекту мы использовали ключевое слово this.

Page 199: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 187

public SomeClass(int iSP)

{

this.SomeProperty = iSP;

}

А этот конструктор принимает один параметр, значение которого присваива-

ет тому же свойству.

Конструкторов может быть несколько (перегруженные конструкторы); при этом они должны различаться количеством и типов принимаемых парамет-

ров. Так, класс SomeClass может содержать оба конструктора, приведенных ранее.

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

Вторая разновидность — статический конструктор. Он выполняется при первой попытке получить доступ к любому из статических элементов класса.

Требования к статическому конструктору примерно такие же.

� Он должен иметь то же имя, что и сам класс.

� Он должен быть статичным.

� Он не должен принимать параметров.

� Он не должен возвращать результата.

static SomeClass()

{

SomeStaticProperty = 228;

}

Этот статический конструктор присваивает статическому свойству

SomeStaticProperty класса SomeClass значение 228. Обратим внимание, что

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

Создание свойств

Свойство объявляется с помощью выражения следующего вида:

[<список модификаторов, разделенных пробелами>] <тип свойства>

�<имя свойства>

{

[get

{

<код get-метода>

}]

Page 200: Дронов В. Самоучитель Silverlight 3 (2010)

188 Часть III. Язык C#

[set

{

<код set-метода>

}]

}

Типом свойства может быть либо ключевое слово C#, либо имя класса, струк-туры, интерфейса или перечисления Silverlight. Имя свойства должно удовле-творять тем же критериям, что и имя поля.

Модификаторы здесь могут быть использованы следующие.

� public — данное свойство доступно во всех объектах классов, определен-ных в текущей сборке и всех сборках, ссылающихся на текущую (публич-ное свойство).

� protected — данное свойство доступно только в объектах классов-потомков (защищенное свойство).

� internal — данное свойство доступно только в объектах классов, опреде-ленных в текущей сборке (внутреннее свойство).

� protected internal — данное свойство доступно только в объектах клас-сов-потомков, определенных в текущей сборке (защищенное внутреннее свойство).

� private — данное свойство доступно только внутри самого класса, в кото-ром определено (частное свойство).

� static — статическое свойство.

Get-метод обычно занимается тем, что извлекает значение из специально предназначенного для этого поля и возвращает его. Set-метод обычно поме-щает переданное ему значение в данное поле.

Новое значение поля доступно в set-методе в переменной value. Эту пере-менную создает сама среда исполнения Silverlight, и доступна она только в теле set-метода.

private int fSomeProperty;

public int SomeProperty

{

get

{

return this.fSomeProperty;

}

set

{

this.fSomeProperty = value;

}

}

Page 201: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 189

Если в объявлении свойства не указать set-метод, свойство будет доступно

только для чтения. Если опустить get-метод, свойство будет доступно только

для записи.

Язык C# предоставляет возможность быстрого объявления свойства. Для это-

го используется выражение такого формата:

[<список модификаторов, разделенных пробелами>] <тип свойства>

�<имя свойства> { get; set; }

Встретив такое выражение при компиляции кода, Visual Web Developer 2008

сам создаст в классе частное поле для хранения значения этого свойства, get-

метод, возвращающий значение из данного поля, и set-метод, помещающий

в поле новое значение свойства.

public int SomeProperty { get; set; }

Создание именованных констант

Именованные константы создаются с помощью выражения объявления

с ключевым словом const:

[<список модификаторов, разделенных пробелами>] const <тип константы>

�<имя константы> = <значение константы>;

Типом константы может быть либо ключевое слово C#, либо имя класса,

структуры, интерфейса или перечисления Silverlight. Имя константы должно

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

целиком пишутся прописными (большими) буквами.

Значение именованной константы должно быть известно на этапе компиляции.

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

участием только констант, в том числе именованных.

Модификаторы могут быть использованы следующие.

� public — данная константа доступна во всех объектах классов, опреде-

ленных в текущей сборке и всех сборках, ссылающихся на текущую (пуб-

личная константа).

� protected — данная константа доступна только в объектах классов-

потомков (защищенная константа).

� internal — данная константа доступна только в объектах классов, опреде-

ленных в текущей сборке (внутренняя константа).

� protected internal — данная константа доступна только в объектах клас-

сов-потомков, определенных в текущей сборке (защищенная внутренняя

константа).

Page 202: Дронов В. Самоучитель Silverlight 3 (2010)

190 Часть III. Язык C#

� private — данная константа доступна только внутри самого класса, в ко-

тором определена (частная константа).

public const string PLATFORM_NAME = "Silverlight";

public const int PLATFORM_VERSION = 3;

Здесь мы объявили две именованные константы: строковую PLATFORM_NAME и

целочисленную PLATFORM_VERSION.

Структуры

Структуры можно рассматривать как своего рода "облегченные" объекты.

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

конструкторы. Однако структуры имеют и много отличий, которые перечис-

лены далее.

� Структуры — значимый, а не ссылочный тип.

� Структуры не могут быть созданы на основе других структур и классов

(однако могут реализовывать интерфейсы).

� Структуры могут содержать только конструкторы, принимающие пара-

метры.

� Значение не может быть присвоено полю структуры сразу при его объяв-

лении.

Структуры отнимают меньше системных ресурсов, чем объекты, быстрее об-

рабатываются и поэтому используются там, где применение "полноразмер-

ных" объектов слишком накладно. Так, в Silverlight логический, символьный

и все численные типы представляют собой структуры.

Работа со структурами

Если мы хотим создать пустую структуру, мы просто объявим переменную

нужного типа и начнем ее использовать.

SomeStructure st;

st.SomeProperty1 = 2;

st.SomeProperty2 = "eee";

st.SomeMethod();

Здесь мы создали структуру st типа SomeStructure, присвоили ее свойствам

SomeProperty1 и SomeProperty2 значения и напоследок вызвали метод

SomeMethod. Как видим, доступ к полям, свойствам и методам структуры запи-

сывается так же, как у объекта.

Page 203: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 191

Если мы хотим создать структуру с помощью конструктора, то должны ис-

пользовать оператор new.

SomeStructure st;

st = new SomeStructure(2, "eee");

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

Полезные встроенные структуры Silverlight

Сейчас мы рассмотрим встроенные структуры платформы Silverlight, которые могут нам пригодиться. Все встроенные структуры перечислены и описаны

в документации по Silverlight.

Int16, Int32, Int64, UInt16, UInt32 и UInt64

Данные структуры соответствуют шести целочисленным типам данных, пе-речисленным в табл. 9.2.

Именованные константы MaxValue и MinValue возвращают значения, соответ-ственно, максимального и минимального числа, которое может хранить зна-

чение данного типа.

Статический метод TryParse() пытается преобразовать целое число из стро-кового представления в числовое.

TryParse(<строка с числом>, out <переменная целочисленного типа>)

Первый параметр задает саму строку, содержащую число, а вторым парамет-ром ставится переменная, в которую попадет преобразованное число. Метод

TryParse() возвращает true, если преобразование удалось, и false в против-ном случае.

Не принимающий параметров метод ToString() возвращает строку, содержа-щую текущее число.

Double и Single

Эти структуры представляют типы чисел с плавающей точкой double и float

соответственно (см. табл. 9.3).

Они поддерживают несколько именованных констант, которые перечислены в табл. 10.2.

Четверка не принимающих параметров статических методов IsInfinity(),

IsNaN(), IsNegativeInfinity() и IsPositiveInfinity() возвращают true, если значение переданного им единственного параметра представляет собой мате-

матическую ∞, значение Not A Number, –∞ и +∞ соответственно, и false в противном случае.

Page 204: Дронов В. Самоучитель Silverlight 3 (2010)

192 Часть III. Язык C#

Таблица 10.2. Именованные константы структур Double и Single

Именованная константа Описание

Epsilon Минимальное положительное ненулевое число данного типа

MaxValue Максимально возможное число данного типа

MinValue Минимально возможное число данного типа

NaN Значение Not A Number — "нечисло"

NegativeInfinity Математическая –∞

PositiveInfinity Математическая +∞

Статический метод TryParse() пытается преобразовать число с плавающей

точкой из строкового представления в числовое.

TryParse(<строка с числом>,

�out <переменная типа числа с плавающей точкой>)

Первый параметр задает саму строку, содержащую число, а вторым парамет-

ром ставится переменная, в которую попадет преобразованное число. Метод

TryParse() возвращает true, если преобразование удалось, и false в против-

ном случае.

Не принимающий параметров метод ToString() возвращает строку, содержа-

щую текущее число.

Decimal

Эта структура представляет тип числа с плавающей точкой decimal

(см. табл. 9.3). Она поддерживает довольно много именованных констант и

методов, описание которых можно найти в документации по Silverlight. Мы

рассмотрим только некоторые.

Именованные константы MaxValue и MinValue возвращают значения, соответ-

ственно, максимального и минимального числа, которое может хранить зна-

чение данного типа.

Статический метод TryParse() пытается преобразовать целое число из стро-

кового представления в числовое.

TryParse(<строка с числом>,

�out <переменная типа числа с плавающей точкой>)

Первый параметр задает саму строку, содержащую число, а вторым парамет-

ром ставится переменная, в которую попадет преобразованное число. Метод

Page 205: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 193

TryParse() возвращает true, если преобразование удалось, и false в против-

ном случае.

Не принимающий параметров метод ToString() возвращает строку, содержа-

щую текущее число.

DateTime

Структура DateTime используется для хранения значений даты и времени.

С помощью вызова конструктора вида

new DateTime(<год>, <номер месяца>, <дата>

�[, <часы>, <минуты>, <секунды>[, <миллисекунды>]])

мы можем создать значение, хранящее нужную нам дату. Все параметры это-

го конструктора задаются в виде целых чисел.

DateTime dt;

dt = new DateTime(2009, 10, 27, 23, 0, 0);

Здесь мы создаем в переменной dt значение даты и времени, хранящее дату

27 октября 2009 года и время 23:00:00.

Структура DateTime поддерживает множество свойств, которые перечислены

в табл. 10.3. Они доступны только для чтения и возвращают части значения

даты и времени, хранящиеся в значении этого типа.

Таблица 10.3. Некоторые свойства структуры DateTime

Свойство Описание

Date Возвращает значение типа DateTime, хранящее только значение даты

Day Возвращает число в виде целого числа

DayOfWeek Возвращает значение, имеющее тип перечисления DayOfWeek и обо-

значающее день недели. В перечислении DayOfWeek доступны элемен-

ты Sunday (воскресенье), Monday (понедельник), Tuesday (вторник),

Wednesday (среда), Thursday (четверг), Friday (пятница) и Saturday

(суббота)

DayOfYear Возвращает номер текущего дня в году в виде целого числа

Hour Возвращает час в виде целого числа

Millisecond Возвращает миллисекунды в виде целого числа

Minute Возвращает минуты в виде целого числа

Month Возвращает номер месяца в виде целого числа

Now Возвращает значение типа DateTime, хранящее значение текущей

даты и времени. Статическое свойство

Page 206: Дронов В. Самоучитель Silverlight 3 (2010)

194 Часть III. Язык C#

Таблица 10.3 (окончание)

Свойство Описание

Second Возвращает секунды в виде целого числа

TimeOfDay Возвращает значение типа TimeSpan, хранящее только значение вре-

мени (о структуре TimeSpan мы поговорим потом)

Today Возвращает значение типа DateTime, хранящее только значение теку-

щей даты. Статическое свойство

Year Возвращает год в виде целого числа

Также структура DateTime поддерживает множество методов, из которых мы

рассмотрим самые для нас полезные (табл. 10.4).

Таблица 10.4. Некоторые методы структуры DateTime

Метод Описание

Add(<параметр>) Добавляет к текущему значению временной

промежуток, заданный в параметре типа TimeSpan

AddDays(<параметр>) Добавляет к текущему значению заданное

в параметре типа числа с плавающей точкой коли-

чество дней

AddHours(<параметр>) Добавляет к текущему значению заданное

в параметре типа числа с плавающей точкой коли-

чество часов

AddMilliseconds(<параметр>) Добавляет к текущему значению заданное

в параметре типа числа с плавающей точкой

количество миллисекунд

AddMinutes(<параметр>) Добавляет к текущему значению заданное

в параметре типа числа с плавающей точкой

количество минут

AddMonths(<параметр>) Добавляет к текущему значению заданное

в параметре типа целого числа количество

месяцев

AddSeconds(<параметр>) Добавляет к текущему значению заданное

в параметре типа числа с плавающей точкой

количество секунд

AddYears(<параметр>) Добавляет к текущему значению заданное

в параметре типа целого числа количество лет

Page 207: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 195

DateTime dt;

dt = DateTime.Now;

dt.AddYears(2);

dt.AddMonth(6);

Здесь мы получаем в переменной dt значение текущей даты и времени и до-бавляем к нему два с половиной года. Так сказать, машина времени на C#.

TimeSpan

Структура TimeSpan используется для хранения значений временны́х проме-жутков.

С помощью вызова конструктора вида

new TimeSpan([<дни>, ]<часы>, <минуты>, <секунды>[, <миллисекунды>])

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

TimeSpan ts;

ts = new TimeSpan(2, 20, 0);

Здесь мы создаем в переменной ts временной промежуток в 2 часа 20 минут.

Структура TimeSpan поддерживает множество свойств, которые перечислены в табл. 10.5. Они доступны только для чтения и возвращают части временно́-го промежутка, хранящиеся в значении этого типа.

Таблице 10.5. Некоторые свойства структуры TimeSpan

Свойство Описание

Days Возвращает количество дней в виде целого числа

Hours Возвращает количество часов в виде целого числа

Milliseconds Возвращает количество миллисекунд в виде целого числа

Minutes Возвращает количество минут в виде целого числа

Seconds Возвращает количество секунд в виде целого числа

TotalDays Возвращает продолжительность временного промежутка в днях в виде числа с плавающей точкой

TotalHours Возвращает продолжительность временного промежутка в часах в виде числа с плавающей точкой

TotalMilliseconds Возвращает продолжительность временного промежутка в миллисекундах в виде числа с плавающей точкой

TotalMinutes Возвращает продолжительность временного промежутка в минутах в виде числа с плавающей точкой

TotalSeconds Возвращает продолжительность временного промежутка в секундах в виде числа с плавающей точкой

Page 208: Дронов В. Самоучитель Silverlight 3 (2010)

196 Часть III. Язык C#

Также структура TimeSpan поддерживает множество методов, описание кото-

рых можно найти в документации по Silverlight.

Создание собственных структур

Структуры создаются примерно так же, как и классы. Только используется

для этого ключевое слово struct.

[<список модификаторов, разделенных пробелами>] struct <имя структуры>

�[: <имена реализуемых интерфейсов, разделенные запятыми>]

{

<объявления элементов структуры>

}

Имя структуры должно удовлетворять тем же правилам, что и имя класса.

Модификаторы используются те же, что и в случае класса.

public struct SomeStructure

{

public short SomeProperty1 { get; set; }

public string SomeProperty2 [ get; set; }

public void SomeMethod()

{

. . .

}

}

Интерфейсы

Интерфейсы — это своего рода заготовки для создания классов и структур.

Они чем-то похожи на абстрактные классы, но именно чем-то.

Прежде всего, интерфейс, в отличие от абстрактного класса, может содержать

только объявления методов, свойств и событий. Объявления полей, имено-

ванных констант и вложенных типов не допускаются.

Интерфейс не может содержать никакого кода, кроме собственно объявлений

методов, свойств и событий. Это значит, что объявленный в интерфейсе ме-

тод не должен содержать тела; то же касается get- и set-методов свойств.

Интерфейсы предназначены для того, чтобы классы и структуры их реализо-

вывали. Реализующий интерфейс класс (структура) должен содержать код,

описывающий тела всех методов, которые включает данный интерфейс. При

этом класс (структура) может реализовать сразу несколько интерфейсов.

Page 209: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 197

Интерфейсы создаются примерно так же, как классы и структуры. За тем ис-

ключением, что в их объявлениях используется ключевое слово struct.

[<список модификаторов, разделенных пробелами>]

�interface <имя интерфейса>

�[: <имена интерфейсов-родителей, разделенные запятыми>]

{

<объявления элементов интерфейса>

}

Имя интерфейса должно удовлетворять тем же правилам, что имя класса и

структуры. Обычно имя интерфейса предваряется большой буквой I (от англ.

interface). Модификаторы используются те же.

При объявлении элементов интерфейса модификаторы использовать нельзя.

public interface ISomeInterface

{

double SomeInterfaceMethod(double db);

int SomeProperty { get; set; }

}

Здесь мы создали интерфейс ISomeInterface с методом SomeInterfaceMethod, принимающим единственный параметр типа числа с плавающей точкой и

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

class SomeClass: ISomeInterface

{

public double SomeInterfaceMethod(double db)

{

. . .

}

public int SomeProperty

{

get

{

. . .

}

set

{

. . .

}

}

Page 210: Дронов В. Самоучитель Silverlight 3 (2010)

198 Часть III. Язык C#

. . .

}

class OtherClass: ISomeInterface

{

public double SomeInterfaceMethod(double db)

{

. . .

}

public int SomeProperty

{

get

{

. . .

}

set

{

. . .

}

}

. . .

}

А здесь мы создали классы SomeClass и OtherClass, реализующие данный ин-терфейс. После чего мы можем использовать эти классы.

SomeClass sc = new SomeClass();

OtherClass oc = new OtherClass();

double d11 = sc.SomeInterfaceMethod(4.2);

double d12 = oc.SomeInterfaceMethod(0.74);

Но это не очень наглядно... Давайте сделаем немного по-другому.

ISomeInterface isi1 = new SomeClass();

ISomeInterface isi2 = new OtherClass();

double dl1 = isi1.SomeInterfaceMethod(4.2);

double d12 = isi2.SomeInterfaceMethod(0.74);

Интерфейс — полноценный тип данных. А это значит, что мы можем объ- явить переменную типа данного интерфейса и присвоить ей указатель на объект класса, реализующего этот интерфейс. Фактически мы можем рабо-тать с объектами классов, реализующих данный интерфейс, единообразным порядком, используя одну и ту же переменную. Зачастую это бывает полезно.

Интерфейс — ссылочный тип данных. В этом интерфейсы схожи с объек- тами.

Платформа Silverlight содержит множество встроенных интерфейсов, кото-рые реализуют ее же встроенные классы. Мы также можем реализовать их

Page 211: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 10. Сложные типы данных C# 199

в своих классах, если возникнет такая нужда. Все эти интерфейсы описаны в документации по Silverlight.

Перечисления

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

Перечисление создается с помощью ключевого слова enum. Объявляющее его выражение имеет такой формат:

[<список модификаторов, разделенных пробелами>]

�enum <имя перечисления>

�{<список имен его элементов, разделенных запятыми>};

Имя перечисления должно удовлетворять тем же правилам, что и имя класса.

Модификаторы используются те же, что и в случае класса.

Например:

public enum Platforms {HTMLCSSJavaScript, Flash, Java, Silveright};

Для доступа к элементам перечисления используется тот же синтаксис, что и для доступа к статическим элементам класса: имя перечисления, точка и имя элемента.

Platforms pl;

pl = Platforms.Flash;

. . .

if (plPlatformName == Platforms.Silverlight)

. . .

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

Осталось только сказать, что любое перечисление — значимый тип.

Что дальше?

Уф! Ох и много типов данных у Silverlight!.. И классы, и структуры, и интер-фейсы, и перечисления, и делегаты, которым, правда, здесь не нашлось мес-та... И как же нам управиться с этим богатством?..

Управимся. Дайте только срок. А сейчас сделаем вдох-выдох и продолжим изучение сложных типов данных. Да-да, разговор о них еще не закончен! В следующей главе мы займемся весьма специфическими и зачастую незаме-нимыми классами, которые называются...

Page 212: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 11

Коллекции

В предыдущей главе мы изучили некоторые из сложных типов данных: клас-

сы, структуры, интерфейсы и перечисления. Мы научились использовать

встроенные сложные типы Silverlight и создавать собственные. И преиспол-

нились благоговейным трепетом перед великой и могучей Silverlight, содер-

жащей столько типов данных, что для их полного описания потребуется

толстенная книга. (Достаточно открыть документацию по Silverlight, чтобы

убедиться в этом.)

Но разговор о сложных типах данных еще не закончен. Мы еще не рассмот-

рели коллекции.

Понятие коллекции

С коллекциями мы познакомились еще в главе 3, когда создавали свое первое

Silverlight-приложение. Коллекция — это объект, представляющий собой

упорядоченный набор объектов одного класса (элементов коллекции); этими

объектами могут быть строки, числа (хоть они и не объекты, а структуры),

пункты списка, строки и столбцы контейнера "таблица" или вообще объекты

созданного нами класса.

Коллекции очень похожи на уже знакомые нам по главе 9 массивы. Однако

между ними есть существенные отличия.

� Коллекция имеет переменный размер, а массив — постоянный, заданный

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

лять элементы коллекции; с массивом же такой номер не пройдет.

� Массив может иметь несколько размерностей, а коллекция — только одну.

Page 213: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 11. Коллекции 201

На самом деле массив — это тоже коллекция, но специализированная.

Коллекции используются в Silverlight-приложениях очень часто, пожалуй,

даже чаще, чем массивы. Так, великолепная возможность привязки компо-

нентов к данным (о ней речь пойдет в главе 13) как раз основана, в том числе, и на коллекциях.

Обобщенные типы

А теперь нам нужно прояснить один исключительно важный вопрос, которо-го мы еще не касались.

Мы уже знаем, что платформа Silverlight использует строгую типизацию. Это

значит, что, если мы объявим переменную какого-либо типа, то сможем при-своить ей только значение данного типа, типа, порожденного от него (класса-

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

То же самое касается и коллекций. При создании коллекции мы должны явно

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

Но как нам указать тип элементов коллекции? С помощью особого парамет-ра, который передается конструктору класса данной коллекции прямо при ее

создании. Этот параметр как раз и указывает тип ее элементов.

В терминологии Silverlight классы, конструкторам которых передается пара-метр, указывающий тип, называются обобщенными. Таких классов довольно

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

Коллекция List

Одна из самых часто используемых коллекций — List. Это обычная коллек-

ция, без всяких дополнительных "наворотов" — просто упорядоченный спи-

сок объектов заданного класса. Сейчас мы будем изучать именно ее.

Создание объекта коллекции List

Прежде всего, мы должны объявить переменную типа List, которой будет

присвоен указатель на создаваемую коллекцию. Для этого используется вы-

ражение объявления такого вида:

List<<тип элементов коллекции>> <имя переменной>;

Page 214: Дронов В. Самоучитель Silverlight 3 (2010)

202 Часть III. Язык C#

Заметим, как указывается тип элементов данной коллекции — сразу после

имени класса — и заключается в символы < и >. Это справедливо не только

для класса List, но и для всех обобщенных типов данных.

Сама же коллекция создается знакомым нам способом — с помощью опера-

тора new. Здесь мы тоже должны указать тип ее элементов:

new List<<тип элементов коллекции>>()

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

List<string> lstCol;

lstCol = new List<string>();

Здесь мы создали коллекцию lstCol, которая будет хранить элементы стро-

кового типа (класс String).

Получение сведений о коллекции

Класс List поддерживает свойство Count, возвращающее количество элемен-

тов в коллекции в виде целого числа. Это свойство доступно только для чте-

ния.

Добавление и удаление

элементов коллекции

Любая коллекция создается изначально пустой, не содержащей ни одного

элемента. Поэтому мы должны сначала ее наполнить (если, конечно, нам не

нужна именно пустая коллекция).

Метод Add добавляет в коллекцию новый элемент. Он принимает единствен-

ный параметр — сам добавляемый элемент — и не возвращает результата.

lstCol.Add("HTML + CSS + JavaScript");

lstCol.Add("Flash");

lstCol.Add("Silverlight");

Теперь наша коллекция содержит три элемента — названия трех платформ

для создания интернет-приложений.

Метод Insert вставляет новый элемент в указанную позицию коллекции. Вот

формат его вызова:

Insert(<индекс вставляемого элемента>, <вставляемый элемент>)

Первым параметром передается номер (индекс), под которым в коллекции будет числиться вставляемый элемент. Не забываем, что нумерация элемен-

Page 215: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 11. Коллекции 203

тов в коллекции начинается с нуля. Вторым параметром задается сам встав-ляемый элемент. Метод Insert не возвращает результата.

lstCol.Insert(2, "Java");

Чуть не забыли Java!

Метод Remove удаляет элемент коллекции, переданный ему в качестве единст-венного параметра. Он возвращает true, если удаление успешно выполнено, и false в противном случае (например, если в коллекции данный элемент не найден).

lstCol.Remove("Flash");

Устраняем ближайшего конкурента Silverlight.

Метод RemoveAt удаляет элемент коллекции, целочисленный индекс которого передан ему в качестве единственного параметра. Он не возвращает резуль-тата.

lstCol.RemoveAt(0);

Еще одним конкурентом меньше...

Метод Clear очищает коллекцию, удаляя все ее элементы. Он не принимает параметров и не возвращает результата.

lstCol.Clear();

Получение элемента коллекции

Чтобы получить нужный нам элемент коллекции по его индексу, мы восполь-зуемся свойством Item. Индекс элемента указывается после имени этого свойства в квадратных скобках — как и индекс элемента массива.

string st;

st = lstCol.Item[3];

После выполнения этого кода мы получим в переменной st строку

"Silverlight".

Свойство Item в классе List помечено как свойство по умолчанию. Это зна-чит, что мы можем при обращении к нему опустить его имя.

string st;

st = lstCol[3];

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

lstCol[3] = "Silverlight 3";

Это выражение заменит четвертый элемент коллекции lstCol — строку "Silverlight" — строкой "Silverlight 3".

Page 216: Дронов В. Самоучитель Silverlight 3 (2010)

204 Часть III. Язык C#

Поиск нужного элемента коллекции

Но как нам определить индекс нужного элемента коллекции? Для этого класс

List и другие классы коллекций предоставляют целый набор особых ме- тодов.

Метод Contains позволяет определить, присутствует ли в коллекции элемент,

переданный ему в качестве единственного параметра. Он возвращает true,

если такой элемент есть, и false в противном случае.

bool flag1, flag2;

flag1 = lstCol.Contains("Flash");

flag2 = lstCol.Contains("Delphi");

После выполнения этого кода мы получим в переменной flag1 значение true

(в коллекции lstCol есть элемент "Flash"), а в переменной flag2 — значение

false (элемент "Delphi" там отсутствует).

Метод IndexOf возвращает целочисленный индекс переданного ему элемента. Формат его вызова таков:

IndexOf(<искомый элемент>[, <начальный индекс>

�[, <количество элементов>]])

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

Вторым параметром передается индекс элемента, с которого начнется поиск. Если он не указан, поиск начнется с первого элемента коллекции.

Третьим параметром задается количество элементов коллекции, начиная с того, что задан вторым параметром, среди которых будет выполняться поиск. Если он отсутствует, поиск будет вестись до конца коллекции.

int indx;

indx = lstCol.IndexOf("Java");

В переменной indx мы получим число 2.

Метод LastIndexOf отличается от метода IndexOf тем, что при его вызове поиск ведется от конца коллекции к ее началу. В остальном он вызывается так же.

Кроме того, для перебора всех элементов коллекции мы можем использовать знакомый нам по главе 9 цикл просмотра.

string output = "";

foreach (string str in lstCol)

{

output += str + "\r\n";

}

Page 217: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 11. Коллекции 205

Коллекция наших собственных объектов

Давайте зададим платформе Silverlight более сложную задачу. А именно —

создадим свой собственный класс и используем коллекцию List для хранения

списка объектов этого класса.

Предположим, нам нужно хранить и обрабатывать список специалистов по

интернет-приложениям. Сведения о каждом таком специалисте будут вклю-

чать его фамилию, имя, отчество и список платформ для создания интернет-

приложений, которыми он владеет.

Для хранения сведений об одном специалисте мы напишем простенький

класс Person. Он будет содержать строковые свойства F (фамилия), N1 (имя) и

N2 (отчество) и свойство Platforms типа List<string> (список платформ; как

видим, это уже знакомая нам коллекция List).

class Person

{

public string F { get; set; }

public string N1 { get; set; }

public string N2 { get; set; }

public List<string> Platforms { get; set; }

public Person()

{

Platforms = new List<string>();

}

}

Конструктор этого класса создает пустую коллекцию типа List<string> и

присваивает ее свойству Platforms каждого создаваемого объекта.

Теперь создадим саму коллекцию.

List<Person> lstPersons;

lstPersons = new List<Person>();

И наполним ее элементами.

Person prs;

prs = new Person();

prs.F = "Петров";

prs.N1 = "Петр";

prs.N2 = "Петрович";

prs.Platforms.Add("Flash");

lstPersons.Add(prs);

prs = new Person();

prs.F = "Иванов";

Page 218: Дронов В. Самоучитель Silverlight 3 (2010)

206 Часть III. Язык C#

prs.N1 = "Иван";

prs.N2 = "Иванович";

prs.Platforms.Add("HTML + CSS + JavaScript");

prs.Platforms.Add("Flash");

prs.Platforms.Add("Java");

lstPersons.Add(prs);

prs = new Person();

prs.F = "Дронов";

prs.N1 = "Владимир";

prs.N2 = "Александрович";

prs.Platforms.Add("Silverlight");

lstPersons.Add(prs);

Здесь все просто: мы создаем очередной объект класса Person, присваиваем

его переменной prs, наполняем данные и добавляем в коллекцию lstPersons.

Обратим внимание на синтаксис, применяемый для наполнения элементами

"внутренней" коллекции Platforms объектов класса Person, — для доступа к

ней мы указываем имя объекта, точку, имя свойства, хранящего эту коллек-

цию, снова точку и нужный нам метод данной коллекции. Также обратим

внимание, что для хранения всех трех созданных объектов класса Person мы

применяем всего одну переменную prs — так мы сэкономим память компью-

тера.

Все, теперь мы можем работать с данной коллекцией и ее элементами.

prs = lstPersons[2];

bool flag = prs.Platforms.Contains("Java");

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

Java.

prs = lstPersons[0];

prs.F = "Петрухин";

Первый специалист сменил фамилию.

prs = lstPersons[1];

prs.Platforms.Add("Silverlight");

А второй овладел платформой Silverlight.

Как мы видим, коллекция List весьма универсальна. Она может хранить объ-

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

класса элементов этой коллекции свойство типа коллекции, мы сможем хра-

нить в ней иерархически организованные данные. Зачастую это бывает очень

полезно!

Page 219: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 11. Коллекции 207

Словарь Dictionary

Еще один полезный класс коллекции — Dictionary. В нем каждому элементу

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

элемент можно однозначно идентифицировать. Этот ключ, в отличие от це-

лочисленного индекса коллекции List, мы задаем сами при добавлении эле-

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

Dictionary позволяет применять и ключи других типов (например, целочис-

ленные или вообще объекты).

Такие коллекции, использующие ключ вместо индекса, в терминологии

Silverlight называются словарями.

Создание объекта словаря Dictionary

При объявлении переменной типа Dictionary, которой будет присвоен указа-

тель на создаваемый словарь, мы должны указать два типа: тип значений

ключей и тип самих элементов. Для этого используется выражение объявле-

ния такого вида:

Dictionary<<тип значений ключей>, <тип элементов коллекции>>

�<имя переменной>;

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

При создании самого словаря с помощью оператора new мы также должны

указать типы значений ключей и самих его элементов:

new Dictionary<<тип значений ключей>, <тип элементов коллекции>>()

Dictionary<string, string> dicDict; dicDict = new Dictionary<string, string>();

Здесь мы создали словарь dicDict, который будет хранить элементы строко-

вого типа со строковыми же ключами.

Получение сведений о словаре

Класс Dictionary также поддерживает свойство Count, возвращающее количе-

ство элементов в словаре в виде целого числа. Это свойство доступно только

для чтения.

Добавление и удаление элементов словаря

Метод Add в случае словаря имеет такой формат вызова:

Add(<ключ>, <элемент>)

Page 220: Дронов В. Самоучитель Silverlight 3 (2010)

208 Часть III. Язык C#

Первым параметром передается значение ключа, а вторым — добавляемый

элемент. Результата этот метод не возвращает.

dctDict.Add("hcjs", "HTML + CSS + JavaScript");

dctDict.Add("fl", "Flash");

dctDict.Add("j", "Java");

dctDict.Add("sl", "Silverlight");

Метод Remove удаляет элемент коллекции, ключ которого передан ему в каче-

стве единственного параметра. Он возвращает true, если удаление успешно

выполнено, и false в противном случае (например, если в коллекции не най-

ден элемент с таким ключом).

dctDict.Remove("fl");

Метод Clear очищает словарь, удаляя все его элементы. Он не принимает па-

раметров и не возвращает результата.

Получение элемента словаря

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

емся свойством Item. Ключ элемента указывается после имени этого свойства

в квадратных скобках.

string st;

st = dctDict.Item["sl"];

Свойство Item в классе Dictionary также помечено как свойство по умолча-

нию. И мы можем при обращении к нему опустить его имя.

string st;

st = dctDict["sl"];

Это же свойство мы можем использовать для того, чтобы заменить элемент

словаря с заданным ключом другим.

dctDict["sl"] = "Silverlight 3";

Метод TryGetValue также позволяет получить элемент по его ключу.

TryGetValue(<значение ключа>, out <переменная, куда попадет элемент>)

Первым параметром передается значение ключа, а вторым указывается пере-

менная, которой будет присвоен соответствующий ключу элемент, если, ко-

нечно, он присутствует в словаре. Метод TryGetValue возвращает true, если

соответствующий ключу элемент будет найден, и false в противном случае.

string st;

if (dctDict.TryGetValue("fl", out st))

. . .

Page 221: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 11. Коллекции 209

Поиск нужного элемента словаря

Метод ContainsKey позволяет определить, присутствует ли в словаре элемент

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

щает true, если такой элемент есть, и false в противном случае.

bool flag1, flag2;

flag1 = dctDict.ContainsKey("fl");

flag2 = dctDict.ContainsKey("d");

Метод ContainsValue позволяет определить, присутствует ли в словаре эле-

мент, переданный ему в качестве единственного параметра. Он возвращает

true, если такой элемент есть, и false в противном случае.

bool flag1, flag2;

flag1 = dctDict.ContainsValue("Flash");

flag2 = dctDict.ContainsValue("Delphi");

Для перебора всех элементов словаря мы также можем использовать цикл

просмотра. Однако в этом случае в переменную массива будет помещаться

сразу пара "ключ-элемент", так что обычной переменной элементарного типа

нам не обойтись. Нам придется использовать в качестве типа переменной

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

ключа и самого элемента.

foreach (KeyValuePair<string, string> kvp in dctDict)

Как видим, типы значений у этой структуры указываются так же, как типы у

самого словаря.

Структура KeyValuePair поддерживает два свойства:

� Key — содержит значение ключа;

� Value — содержит сам элемент.

string outputKeys, outputValues;

foreach (KeyValuePair<string, string> kvp in dctDict)

{

outputKeys += kvp.Keys + "\r\n";

outputValues += kvp.Values + "\r\n";

}

Специализированные коллекции

Здесь мы кратко рассмотрим несколько классов коллекций, имеющие специ-

альное применение.

Page 222: Дронов В. Самоучитель Silverlight 3 (2010)

210 Часть III. Язык C#

Очередь Queue

Класс Queue представляет особую коллекцию, называемую очередью. Она ра-

ботает по принципу "первым выходит тот элемент, который был первым до-

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

ни ключи, она просто упорядочивает элементы в том порядке, в котором они

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

Создается очередь так же, как обычная коллекция List.

Queue<string> queQ;

queQ = new Queue<string>();

Метод Enqueue добавляет в очередь элемент, переданный ему в качестве

единственного параметра. Результата этот метод не возвращает.

queQ.Enqueue("HTML + CSS + JavaScript");

queQ.Enqueue("Flash");

queQ.Enqueue("Java");

queQ.Enqueue("Silverlight");

Метод Dequeue возвращает элемент, находящийся в начале очереди (тот, что

был добавлен первым), и удаляет его; при этом в начале очереди оказывается

следующий элемент. Он не принимает параметров.

string st1;

st = queQ.Dequeue();

После выполнения этого кода в переменной st1 окажется строка "HTML + CSS

+ JavaScript" — первый элемент очереди queQ. Данный элемент будет удален

из очереди, и первым тогда станет следующий за ним элемент — "Flash".

Метод Peek возвращает первый элемент очереди, не удаляя его. Параметров

он не принимает.

string st2;

st = queQ.Peek();

В переменной st2 окажется строка "Flash" — первый элемент очереди queQ.

При этом из очереди он удален не будет.

Также класс Queue поддерживает свойство Count и методы Clear и Contains.

Стек Stack

Класс Stack представляет особую коллекцию, чем-то похожую на очередь и

называемую стеком. В отличие от очереди, стек работает по принципу "пер-

вым выходит тот элемент, который был добавлен последним".

Page 223: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 11. Коллекции 211

Создается стек уже знакомым нам образом.

Stack<string> stkStack;

stkStack = new Stack<string>();

Метод Push добавляет ("заталкивает") в стек элемент, переданный ему в каче-

стве единственного параметра. Результата этот метод не возвращает.

stkStack.Push("HTML + CSS + JavaScript");

stkStack.Push("Flash");

stkStack.Push("Java");

stkStack.Push("Silverlight");

Метод Pop извлекает ("выталкивает") и возвращает элемент, находящийся

в начале стека, удаляя его при этом; в начале стека оказывается следующий

за ним элемент. Параметров этот метод не принимает.

string st1 = stkStack.Pop();

string st2 = stkStack.Pop();

В переменных st1 и st2 окажутся строки "Silverlight" и "Java" соответст-

венно. При этом соответствующие им элементы стека stkStack будут удале-

ны из него.

Метод Peek возвращает первый элемент стека, не удаляя его. Параметров он

не принимает.

string st;

st = stkStack.Peek();

В переменной st окажется строка "Flash" — первый элемент стека stkStack

из оставшихся. При этом из стека он удален не будет.

Также класс Stack поддерживает свойство Count и методы Clear и Contains.

Свойства компонентов, являющиеся коллекциями

Мы уже достаточно много говорили о компонентах Silverlight, в том числе их

свойствах. Эти свойства бывают самыми разными и имеют самый разный

тип, в том числе и тип той или иной коллекции.

Все эти коллекции суть "порождение" уже знакомого нам класса List. А это

значит, что они поддерживают все его свойства и методы.

Рассмотрим, например, класс ListBox (список) и его свойство Items. Из

главы 6 мы знаем, что оно имеет тип коллекции ItemCollection, которая со-

держит объекты класса FrameworkElement. Но в случае списка ее наполняют

объектами класса ListBoxItem, который представляет пункт списка.

Page 224: Дронов В. Самоучитель Silverlight 3 (2010)

212 Часть III. Язык C#

Давайте возьмем список lstList и наполним его элементами.

lstList.Item.Clear();

lstList.Item.Add("Дюймы в миллиметры");

lstList.Item.Add("Футы в метры");

Здесь мы сначала на всякий случай очистили список, после чего добавили

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

которые станут пунктами этого списка.

А теперь — самое интересное! Дело в том, что компоненты, которые мы

вставляем в контейнер, становятся элементами его коллекции Children. Эта

коллекция имеет тип UIElementCollection и хранит объекты класса UIElement,

от которого порожден класс FrameworkElement — базовый для всех классов

компонентов.

Из этого следует, что мы можем программно создавать компоненты и про-

граммно же добавлять их в контейнер, в том числе и главный. То есть созда-

вать приложения с меняющимся во время работы интерфейсом.

Свойство Children всех классов компонентов помечено как свойство по

умолчанию. Это значит, что мы можем не указывать его в XAML-коде (мы,

собственно, его нигде и не указывали).

Множество примеров работы со свойствами компонентов, являющимися

коллекциями, приведено в документации по Silverlight. Интересующиеся

этим вопросом могут их поискать.

Собственно, разговор о коллекциях на этом можно заканчивать. Хотя мы еще

вернемся к ним в главе 13, когда будем рассматривать привязку компонентов

к данным.

Что дальше?

Разговор о языке C# подходит к концу. Мы уже изучили его в той мере, в ка-

кой он пригодится нам как начинающим Silverlight-программистам. Осталось

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

Мы уже упоминали об исключениях, которые среда исполнения Silverlight

выбрасывает при возникновении исключительной ситуации (например,

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

мы завершим рассмотрение языка C#.

Page 225: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 12

Исключения

В предыдущей главе мы закончили изучение сложных типов данных C#

и Silverlight, рассмотрев коллекции. Мы научились создавать их и использо-вать для хранения множества однотипных объектов. А также узнали кое-

какие забавные факты из "жизни" компонентов, например, что все компонен-

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

А еще в предыдущих главах мы упоминали об исключениях. Упоминали, но толком не рассматривали, что же это такое. Настала пора выяснить о них все.

Понятие исключения

При работе Silverlight-приложения время от времени возникают различные

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

несуществующему элементу массива или коллекции. Программист-растяпа забудет создать объект перед тем, как его использовать. В конце концов, ка-

кое-то особо "прожорливое" приложение может израсходовать всю доступ-ную память компьютера (случай по нынешним временам маловероятный, но

вполне возможный).

В общем, в приложении возникнет ошибка.

Что произойдет в таком случае?

Прежде всего, среда исполнения Silverlight остановит работу приложения,

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

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

Page 226: Дронов В. Самоучитель Silverlight 3 (2010)

214 Часть III. Язык C#

Этот объект, содержащий сведения об ошибке, в частности текстовое сооб-щение о возникшей ошибке, и называется исключением.

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

Обработка исключений

Что делать с исключением? Обрабатывать, т. е. как-то реагировать на его возникновение. (В жаргоне программистов есть термин "ловить исключение", означающий то же самое.) Так, если исключение возникнет при делении на ноль, надо предупредить пользователя о введенных им неверных данных, ко-торые привели к этому.

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

Но если исключение вызвано не ошибкой в приложении, а действиями поль-зователя (как рассмотренный нами ранее случай с делением на ноль), такой вариант не пройдет. И тогда нам придется самим обрабатывать исключения. Так, в случае деления на ноль мы можем вывести для пользователя "челове-ческое" сообщение о допущенной им ошибке и, возможно, сделать какие-то другие действия.

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

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

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

Page 227: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 12. Исключения 215

После обработки исключения, независимо от того, обработали его мы или среда исполнения Silverlight, объект исключения будет удален из памяти. Среда исполнения Silverlight "забудет", что в приложении возникла ошибка.

Встроенные классы исключений

Silverlight представляет довольно много встроенных классов исключений, которые мы можем использовать. (В основном, конечно, их использует сама среда исполнения Silverlight.) Самые полезные для нас перечислены в табл. 12.1.

Таблица 12.1. Некоторые встроенные классы исключений Silverlight

Класс Класс-родитель Описание

Exception Класс-родитель для всех классов исключений

SystemException Exception Класс-родитель для классов исключений, возникающих при работе приложения

ArrayTypeMismatchException SystemException Возникает при помещении в массив элемента неподхо-дящего типа

IndexOutOfRangeException SystemException Возникает при обращении к элементу массива или кол-лекции по несуществующему индексу

InvalidCastException SystemException Возникает при недопустимом преобразовании типов, явном или неявном

InvalidOperationException SystemException Возникает при недопустимом обращении к методу

NullReferenceException SystemException Возникает при обращении к еще не созданному объекту

OutOfMemoryException SystemException Возникает при недостатке памяти

StackOverflowException SystemException Возникает при слишком большом количестве после-довательно вызывающих друг друга методов

ArgumentException SystemException Класс-родитель для классов исключений, возникающих при задании недопустимых значений параметров у мето-дов

Page 228: Дронов В. Самоучитель Silverlight 3 (2010)

216 Часть III. Язык C#

Таблица 12.1 (окончание)

Класс Класс-родитель Описание

ArgumentNullException ArgumentException Возникает при задании зна-чения параметра у метода

равным null

ArgumentOutOfRangeException ArgumentException Возникает при задании недо-пустимого значения парамет-ра у метода

ArithmeticException SystemException Класс-родитель для классов исключений, возникающих при арифметических ошибках

DivideByZeroException ArithmeticException Возникает при делении на ноль

NotFiniteNumberException ArithmeticException Возникает, если значение числа с плавающей точкой не является собственно числом (–∞, +∞ или NaN)

OverflowException ArithmeticException Возникает при переполнении во время преобразования типов, когда часть данных теряется (например, при-своении целочисленного зна-

чения int переменной типа

short)

Все встроенные классы исключений поддерживают свойство Message, содер-

жащее сообщение о возникшей в приложении ситуации в виде строки. Веро-

ятно, это самое полезное для нас свойство этих классов.

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

Обработка исключений

Теперь выясним, как обрабатывать исключения. Рассмотрим оба случая: и

реагирование на само исключение, и выполнение завершающих операций.

Реагирование на само исключение

Для реагирования на исключение используется особое выражение, формат

которого представлен далее.

Page 229: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 12. Исключения 217

try

<блок кода, в котором может возникнуть исключение>

catch (<класс исключения> <переменная исключения>)

<блок кода, реагирующего на исключение>

Как видим, здесь применяются ключевые слова try и catch. Поэтому подоб-

ное выражение также называется "блоком try-catch".

Если в блоке кода, в котором может возникнуть исключение (блоке try), ис-

ключение возникает, исполнение этого блока прерывается, и начинает вы-

полняться блок catch. Сначала выясняется, относится ли возникшее исключе-

ние к указанному там классу исключения или порожденному от него, и, если

относится, указатель на него присваивается переменной исключения. Эта пере-

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

для получения информации об исключении.

Если же класс исключения не соответствует указанному в блоке catch, этот

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

try

{

int x = 1, y = 0;

int z = x / y;

}

catch (DivideByZeroException e)

{

. . .

}

Здесь мы реагируем на исключение DivideByZeroException — деление на

ноль.

Блоков catch может быть несколько — по одному на каждое возможное ис-

ключение.

try

{

. . .

}

catch (DivideByZeroException e)

{

. . .

}

catch (ArithmeticException e)

{

. . .

}

Page 230: Дронов В. Самоучитель Silverlight 3 (2010)

218 Часть III. Язык C#

catch (SystemException e)

{

. . .

}

Здесь первым размещается блок catch, реагирующий на исключение DivideByZeroException. Если возникнет деление на ноль, он выполнится пер-вым и обработает данное исключение. Поскольку исключение после обработ-ки будет удалено, остальные блоки catch будут пропущены.

Далее находится блок catch, реагирующий на все арифметические исключе-ния (класс ArithmeticException). Он будет выполнен, если возникшее исклю-чение не относится к классу DivideByZeroException, и первый блок catch про-пущен.

Последним мы поставили блок catch, реагирующий на все исключения, которые могут возникнуть в процессе работы приложения (класс SystemException). Он выполнится, если исключение не относится ни к классу DivideByZeroException, ни даже к классу ArithmeticException, и первые два блока catch пропущены.

Как видим, в этом случае первым размещается блок catch, реагирующий на более специфичные исключения (в нашем случае — DivideByZeroException), а за ним — реагирующие на все менее и менее специфичные (ArithmeticException и SystemException). Получается этакое многоуровневое "сито", сквозь которое "просеиваются" "куски" все меньших и меньших раз-меров.

Выполнение завершающих операций

Если нам нужно выполнить некие завершающие операции, независимо от того, возникло исключение или нет, мы добавим к рассмотренному ранее блоку try-catch еще один блок.

<блок try-catch>

finally

<блок кода, выполняющий завершающие операции>

Из-за того, что здесь используется ключевое слово finally, данный блок но-сит название "блока finally".

Блок кода, выполняющий завершающие операции, будет выполнен в любом слу-чае, независимо от того, возникло в блоке try-catch исключение или нет.

try

{

btnDoCompute.IsEnabled = false;

. . .

}

Page 231: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 12. Исключения 219

catch (ArithmeticException e)

{

. . .

}

finally

{

btnDoCompute.IsEnabled = true;

}

Здесь мы используем блок finally для того, чтобы напоследок сделать доступ-

ной кнопку, запускающую вычисления.

Генерирование исключений

Если в приложении возникнет нештатная ситуация, среда исполнения

Silverlight не преминет сообщить нам об этом, сгенерировав соответствующее

случаю исключение. Однако может случиться так, что нам самим придется

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

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

ArgumentOutOfRangeException e = new ArgumentOutOfRangeException

�("Данный параметр не может быть равен нулю.");

Здесь мы создаем объект исключения типа ArgumentOutOfRangeException. От-

метим, что его конструктору передается строковый параметр, содержащий

описание возникшей нештатной ситуации.

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

Теперь мы можем выполнить генерирование исключения. Для этого исполь-

зуется оператор генерирования исключения throw, за которым указывается

сам объект исключения.

throw e;

Здесь мы генерируем исключение на основе созданного ранее объекта.

Также мы можем создавать свои классы исключений. Как это делается, описано в документации по Silverlight.

Вот и все об исключениях.

Page 232: Дронов В. Самоучитель Silverlight 3 (2010)

220 Часть III. Язык C#

Что дальше?

Мы рассмотрели исключения — особый механизм Silverlight, позволяющий

информировать пользователя о том, что в приложении произошло нечто не-

предвиденное. И на этом в основном закончили рассмотрение языка C#.

Следующая глава будет посвящена механизму привязки компонентов к дан-

ным — одной из самых замечательных возможностей Silverlight. Правильно

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

жизнь, конечно...

Page 233: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ IV

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

к данным.

LINQ

Глава 13. Привязка компонентов к данным

Глава 14. LINQ

Page 234: Дронов В. Самоучитель Silverlight 3 (2010)
Page 235: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 13

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

к данным

В предыдущих главах мы изучили язык C# в том объеме, в котором он нам пригодится в ближайшее время. Теперь мы можем писать достаточно слож-ный C#-код. Чем в самое ближайшее время и займемся.

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

Понятие привязки

Что нужно для того, чтобы присвоить свойству какого-либо компонента но-вое значение? Правильно, написать соответствующее выражение на язы-ке C#.

txtMillimetres.Text = cnvConvertor.Millimetres;

Здесь мы присваиваем свойству Text поля ввода txtMillimetres значение

свойства Millimetres объекта cnvConvertor. (Подразумевается, что это свой-ство содержит уже преобразованное значение и уже в строковом виде.)

Да, язык C# позволяет писать очень простой и ясный для понимания код. Но что делать, если мы не хотим его писать? Что делать, если мы хотим, чтобы

новое значение появилось в поле ввода txtMillimetres сразу после того, как

оно будет присвоено свойству Millimetres объекта cnvConvertor, без написа-ния любого C#-кода?

Для этого нам следует просто указать прямо в XAML-коде, что свойство

Text компонента txtMillimetres должно извлекать значение из свойства

Millimetres объекта cnvConvertor, причем делать это каждый раз при измене-

нии значения свойства Millimetres. И среда исполнения Silverlight сама за всем проследит.

Page 236: Дронов В. Самоучитель Silverlight 3 (2010)

224 Часть IV. Привязка компонентов к данным. LINQ

Другими словами, мы должны выполнить привязку свойства Text компонента

txtMillimetres к свойству Millimetres объекта cnvConvertor. То есть сделать

так, чтобы компонент сам, без дополнительных указаний брал указанные на-

ми данные для отображения их на странице.

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

мы можем привязать к каким-либо данным свойства HorizontalAlignment,

IsChecked, SelectedIndex и любые другие. Silverlight это также позволяет.

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

формировать свои пункты на основе элементов этой коллекции. Но это уже

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

Привязка к свойству объекта

Начнем мы с самого простого случая привязки — одного свойства компонен-

та к одному свойству объекта. И рассмотрим его на примере объекта класса

Convertor, который занимается тем, что преобразует величины из дюймов в

миллиметры. То есть мы во второй уже раз перепишем наше первое

Silverlight-приложение.

Создадим в Visual Web Developer 2008 новый проект и назовем его

Convertor3. На единственной странице нашего нового приложения поместим

две надписи, два поля ввода txtInches и txtMillimetres и кнопку btnConvert.

Зададим для них такие же свойства, что у одноименных компонентов прило-

жения Convertor. Единственное — не будем создавать обработчик события

Click кнопки btnConvert, т. к. он нам в данный момент не понадобится.

Сохраним файл с XAML-кодом главной страницы и откроем файл с ее

C#-кодом. Создадим прямо в пространстве имен Convertor3 класс Convertor.

Не будем делать его вложенным в класс главной страницы MainPage.

public class Convertor

{

private double fInches;

public string Inches

{

get

{

return fInches.ToString();

}

set

{

double dInches = 0;

double.TryParse(value, out dInches);

Page 237: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 225

if (dInches != fInches)

{

fInches = dInches;

}

}

}

public string Millimetres

{

get

{

double dMillimetres = fInches * 25.4;

return dMillimetres.ToString();

}

}

}

В этом классе мы объявили частное поле fInches типа числа с плавающей

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

ние величины в дюймах.

Далее мы объявили публичное строковое свойство Inches. Это свойство мы

сделали доступным и для чтения, и для записи, указав для него get- и set-

методы. Get-метод просто возвращает значение поля, преобразовав его в

строковый вид. Set-метод преобразует новое значение свойства из строкового

вида в число с плавающей точкой и заносит его в поле fInches. Однако перед

занесением он выполняет проверку, не равно ли новое значение свойства уже

имеющемуся в поле, и присваивает его полю только в случае неравенства;

зачем это нужно, мы узнаем потом.

Еще в классе Converter присутствует публичное строковое свойство

Millimetres. Оно уже доступно только для чтения, т. к. мы написали для него

только get-метод. Он извлекает значение из поля fInches, пересчитывает его

в миллиметры, преобразует в строковый вид и возвращает.

Конструктор для класса Converter мы не создали; таким образом, этот класс

унаследует конструктор у класса Object, на основе которого он создан (мы

ведь не указали класс-родитель). Класс Converter настолько прост, что не ну-

ждается в специальном конструкторе.

Отметим, что мы сделали класс Converter публичным. Зачем это нужно, мы

узнаем чуть позже.

Сохраним файл с C#-кодом. И снова откроем файл с XAML-кодом. Сейчас

мы будем работать именно с ним.

Page 238: Дронов В. Самоучитель Silverlight 3 (2010)

226 Часть IV. Привязка компонентов к данным. LINQ

Помещение на Silverlight-страницу произвольных объектов. Ресурсы страницы и ресурсы приложения

Теперь нам нужно написать код, создающий объект на основе класса

Convertor. Мы можем поместить этот код либо в обработчик события Load

страницы, либо в конструктор класса страницы, после вызова метода

InitializeComponent. Для хранения указателя на созданный объект мы можем

использовать частное поле, которое создадим в классе страницы. Сам же объ-

ект создается уже знакомым нам способом — с помощью оператора new.

Но мы не будем это делать. Мы используем еще одну интересную возмож-

ность Silverlight, которая позволяет помещать на страницы, помимо компо-

нентов, произвольные объекты указанных нами классов.

Поместим на страницу объект класса Convertor, объявленного в пространстве

имен Convertor3. (Как мы помним из главы 4, Visual Web Developer 2008 для

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

совпадает с именем проекта.) Но пространство имен Convertor3 не подключе-

но к XAML-коду страницы, так что нам придется самим его подключить,

вставив в тег <UserControl>, определяющий саму страницу, соответствующий

атрибут xmlns.

Сделаем его. Наберем в теге <UserControl> буквы xmlns, поставим знак двое-

точия и укажем префикс, которым будем помечать классы, объявленные в

подключаемом пространстве имен. Обычно в этом случае используют пре-

фикс local (программисты, пишущие на языке Visual Basic, также использу-

ют префикс my). Поставим знак равенства. Visual Web Developer 2008 выведет

список автоподстановки, перечисляющий все доступные пространства имен;

выберем в нем пункт Convertor3 in assembly Convertor3 — пространство

имен Convertor3 в одноименной сборке, т. е. нашем приложении. У нас дол-

жен получиться такой атрибут тега:

xmlns:local="clr-namespace:Convertor3"

Вполне возможно, Visual Web Developer 2008 подчеркнет значение атрибута те-га xmlns синей волнистой чертой, предупреждая о том, что не может найти

данную сборку. В этом случае откомпилируем проект, не запуская само прило-жение, для чего выберем пункт Build <имя проекта> меню Build.

Все готово. Сохраним исходный код.

В терминологии Silverlight любые объекты, помещенные на страницу и не

являющиеся компонентами или относящимися к ним вспомогательными объ-

Page 239: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 227

ектами (строки таблицы, пункты списка и др.), называются ресурсами. А точ-

нее, ресурсами страницы, поскольку относятся к данной странице.

Не путайте ресурсы страницы и ресурсы приложения, о которых мы узнаем позже, с описанными в главе 8 ресурсами сборки. Это совершенно разные вещи!

Класс UserControl, на основе которого создаются классы страниц, поддержи-

вает свойство Resources, имеющее тип словаря ResourceDictionary (о слова-

рях было рассказано в главе 11). И ключи, и элементы этого словаря имеют

тип Object — это значит, что мы можем использовать в качестве его ключей

и элементов любые объекты. На практике же ключи применяются исключи-

тельно строковые, т. к. с ними проще работать.

Чтобы описать все ресурсы, которые мы хотим создать на данной странице,

мы используем объектную запись. Сначала поместим прямо в тег

<UserControl> другой тег, указывающий свойство Resources. В него мы вло-

жим теги, описывающие сами объекты, которые станут ресурсами. Имя каж-

дого такого тега укажет класс, на основе которого создается объект, возмож-

но, с префиксом, указывающим на содержащее этот класс пространство имен.

А в теге мы создадим атрибут x:Key, значение которого укажет ключ, под ко-

торым этот объект будет добавлен в словарь.

Класс, на основе которого мы хотим создать объект-ресурс, должен быть пуб-личным. Иначе среда исполнения Silverlight не сможет до него добраться.

Меньше слов — больше дела! Вот какой XAML-код нам нужно написать и

поместить в тег <UserControl>, чтобы создать ресурс на основе нашего класса

Convertor:

<UserControl.Resources>

<local:Convertor x:Key="cnvConvertor"></local:Convertor>

</UserControl.Resources>

Здесь мы указали в качестве имени тега, создающего объект, имя класса

Convertor с префиксом local. А значение cnvConvertor атрибута этого тега

x:Key задает его ключ.

Вот и все. Снова сохраним код.

Кроме ресурсов страницы, Silverlight позволяет создавать ресурсы приложе-

ния. Они описываются точно таким же способом, что и ресурсы страницы, но

в файле App.xaml, где хранится часть исходного кода класса приложения,

в теге <Application>. В отличие от ресурсов страницы, ресурсы приложения

Page 240: Дронов В. Самоучитель Silverlight 3 (2010)

228 Часть IV. Привязка компонентов к данным. LINQ

доступны на всех страницах, составляющих приложение, и могут быть по-

лезны, если мы используем одни и те же данные на нескольких страницах.

Создание самой привязки

Теперь все готово для того, чтобы создать собственно привязку.

� Во-первых, свойство Text поля ввода txtInches будет привязано к свойст-

ву Inches объекта класса Convertor, который мы поместили на страницу

в качестве ресурса. Причем значение свойства Text будет помещаться

в свойство Inches.

� Во-вторых, свойство Text поля ввода txtMillimetres будет привязано к

свойству Millimetres все того же объекта класса Convertor. Здесь уже зна-

чение свойства Millimetres будет присваиваться свойству Text.

Начнем с поля ввода txtMillimetres, т. к. выполнить его привязку проще.

Найдем описывающий его тег и впишем в него атрибут Text с пустым значе-

нием.

Привязкой компонентов к данным управляет объект класса Binding. Мы соз-

дадим этот объект (создав тем самым привязку), использовав расширенную

запись XAML.

<привязываемое свойство компонента>="{Binding <параметры привязки>}"

Как мы видим, описание привязки (фактически — реализующего ее объекта

класса Binding) указывается в качестве значения свойства компонента, кото-

рое нужно привязать к данным, и помещается в фигурные скобки. В этих

скобках сначала стоит имя класса, на основе которого создается объект при-

вязки, — Binding, а за ним идут параметры привязки (свойства объекта при-

вязки и их значения). Параметры привязки указываются как пары вида <имя

свойства объекта привязки>=<значение без кавычек> и разделяются запятыми.

Отметим, что в этом случае значения свойств указываются без кавычек.

Что касается свойств класса Binding, задающего параметры привязки, то сей-

час нам будут полезны два из них.

� Path — указывает свойство объекта, к которому привязывается данное

свойство компонента. Это свойство объявлено как свойство по умолча-

нию, так что его имя можно не указывать.

� Source — указывает сам объект, к которому привязывается компонент.

Свойство Source требует более подробного рассмотрения. В самом деле,

бо́льшая часть объектов, к которым привязываются компоненты, представ-

ляют собой ресурсы страницы или приложения. Как нам указать нужный ре-

сурс?

Page 241: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 229

С помощью все той же расширенной записи. Для этого в качестве значения

свойства Source указывается вот что:

{StaticResource <ключ нужного элемента словаря Resources>}

Значит, чтобы привязать свойство Text поля ввода txtMillimetres к свойству

Millimetres объекта-ресурса с ключом cnvConvertor, мы впишем в форми-

рующий данное поле ввода тег <TextBox> такой атрибут:

Text="{Binding Path=Millimetres, Source={StaticResource cnvConvertor}}"

После этого поле ввода txtMillimetres будет извлекать значение свойства

Millimetres объекта-ресурса с ключом cnvConvertor при каждом изменении

значения этого свойства и присваивать его своему свойству Text.

Перейдем к полю ввода txtInches. Здесь ситуация обратная: нам нужно, что-

бы оно присваивало значение своего свойства Text при каждом его измене-

нии свойству Inches объекта-ресурса с ключом cnvConvertor. Поэтому нам

нужно будет специально сообщить об этом объекту привязки, воспользовав-

шись свойством Mode класса Binding. Это свойство имеет тип перечисления

BindingMode из трех элементов:

� OneWay — компонент будет получать значение указанного свойства объек-

та при его изменении (односторонний обмен данными; значение по умол-

чанию);

� TwoWay — компонент будет получать значение указанного свойства объек-

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

при его изменении указанному объекту (двусторонний обмен данными);

� OneTime — компонент получит значение указанного свойства объекта

только один раз и впоследствии не будет обновлять его (однократное по-

лучение данных).

Исходя из всего этого, пишем такой атрибут тега <TextBox>, создающего поле

ввода txtInches:

Text="{Binding Path=Inches, Source={StaticResource cnvConvertor},

�Mode=TwoWay}

К сожалению, указать, что компонент должен отправлять объекту изменив-

шееся значение свойства, мы не можем... Придется действовать так — разре-

шать "двусторонний" обмен данными. Кстати, именно поэтому мы сделали

свойство Inches класса Convertor доступным и для чтения, и для записи —

чтобы поле ввода txtInches могло получить его значение.

А теперь — внимание! Компонент отправляет объекту изменившееся значе-

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

свойство Text поля ввода TextBox, значение которого отправляется объекту

Page 242: Дронов В. Самоучитель Silverlight 3 (2010)

230 Часть IV. Привязка компонентов к данным. LINQ

только после того, как поле ввода потеряет фокус. Именно поэтому мы ос-

тавили на странице кнопку — когда пользователь щелкнет на ней, поле ввода

потеряет фокус и, следовательно, отправит данные объекту.

Запустим готовое приложение на исполнение. Введем какое-либо число в по-ле ввода Дюймы и нажмем кнопку Преобразовать. В поле ввода Милли-

метры появится... появится... ничего не появится. В чем дело?

Уведомление компонента об изменении данных

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

А делается это довольно просто. Мы должны всего лишь реализовать в своем

классе Convertor интерфейс INotifyPropertyChanged, содержащий событие

PropertyChanged. Возникновение этого события нам следует инициировать

при каждом изменении значения свойства Millimetres; этим мы сигнализиру-ем привязанному компоненту о том, что ему следует получить новые данные.

Когда компонент, привязанный к данным, создается, он берет объект, к кото-

рому привязан, и пытается привести его тип к типу INotifyPropertyChanged. Если преобразование удалось, значит, данный объект реализует этот интер-

фейс. Тогда компонент привязывает к событию PropertyChanged в качестве обработчика один из своих методов, который как раз и занимается извлече-нием новых данных из объекта.

Интерфейс INotifyPropertyChanged и все связанные с событием PropertyChanged

типы определены в пространстве имен System.ComponentModel. Оно изначаль-но не отображено, поэтому мы либо должны отобразить его, либо использо-вать полные имена типов. (Об отображении пространств имен см. главу 4.)

Реализовать в классе Convertor интерфейс INotifyPropertyChanged несложно. Далее приведен фрагмент кода этого класса; добавленные фрагменты выде-лены полужирным шрифтом.

public class Convertor : System.ComponentModel.INotifyPropertyChanged

{

private double fInches;

public event System.ComponentModel.PropertyChangedEventHandler

�PropertyChanged;

public string Inches

{

get

{

return fInches.ToString();

}

Page 243: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 231

set

{

double dInches = 0;

double.TryParse(value, out dInches);

if (dInches != fInches)

{

fInches = dInches;

NotifyPropertyChanged("Millimetres");

}

}

}

. . .

public void NotifyPropertyChanged(string propertyName)

{

if (PropertyChanged != null)

{

PropertyChanged(this,

�new System.ComponentModel.PropertyChangedEventArgs(propertyName));

}

}

}

Здесь мы сначала объявляем событие PropertyChanged типа

PropertyChangedEventHandler и делаем его публичным. (Как видим, делается

это с помощью ключевого слова event.) Тем самым мы сообщаем среде исполнения Silverlight, что реализуем данное событие интерфейса

INotifyPropertyChanged.

Далее мы добавляем в класс метод NotifyPropertyChanged, который иниции-

рует возникновение события PropertyChanged. Он принимает единственный параметр — строку с именем свойства, значение которого изменилось, — и не возвращает результата.

В теле этого метода мы сначала проверяем, привязан ли к событию

PropertyChanged хоть один обработчик. (Ведь если к событию не привязан ни один обработчик, его незачем инициировать.) Делается это с помощью обыч-

ного оператора сравнения. Если значение события PropertyChanged не равно

null, значит, к событию были привязаны обработчики, и мы можем иниции-ровать его.

Инициируется событие простым его вызовом, как и в случае метода. При этом мы передаем событию два параметра: объект, в котором возникло это

событие (ключевое слово this), и объект, хранящий сведения о событии. При создании этого объекта мы передаем его конструктору в качестве единствен-ного параметра строку с именем свойства, значение которого изменилось; это

имя метод NotifyPropertyChanged получает в качестве параметра.

Page 244: Дронов В. Самоучитель Silverlight 3 (2010)

232 Часть IV. Привязка компонентов к данным. LINQ

Осталось только добавить вызов метода NotifyPropertyChanged в get-метод

свойства Inches. Передадим ему в качестве параметра имя свойства

Millimetres, т. к. значение именно этого свойства должен получать привязан-

ный к объекту компонент — поле ввода txtMillimetres.

Теперь понятно, почему перед присваиванием нового значения полю fInches

и инициированием события PropertyChanged мы проверили, действительно ли

значение, присвоенное свойству Inches, новое? Чтобы лишний раз не "дер-гать" компонент, заставляя его получать из объекта неизменившиеся данные.

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

Проверка вводимых данных

Для компонентов, которые могут отсылать измененные данные объекту (зна-

чение свойства Mode объекта привязки установлено в TwoWay), Silverlight под-держивает возможность проверки отправляемых данных на правильность. Проверка эта заключается в том, что в случае некорректности данных объект генерирует исключение, а среда исполнения Silverlight прямо на странице выделяет данный компонент и выводит предупреждение.

Класс Binding поддерживает свойство ValidatesOnExceptions. Если его значе-

ние равно true, среда исполнения Silverlight будет визуально выделять привя-занный компонент, когда объект при попытке занести в него данные сгенери-

рует исключение. По умолчанию его значение равно false.

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

вводить отрицательные числа. Для этого сначала зададим для свойства Text

поля ввода txtInches значение свойства ValidatesOnExceptions объекта при-

вязки равным true.

Text="{Binding Path=Inches, Source={StaticResource cnvConvertor},

�Mode=TwoWay, ValidatesOnExceptions=True}"

После чего внесем в код set-метода свойства Inches класса Convertor сле-дующие изменения (добавленный код выделен полужирным шрифтом):

set

{

double dInches = 0;

double.TryParse(value, out dInches);

if (dInches < 0)

{

throw new Exception("Значение должно быть неотрицательным");

}

Page 245: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 233

if (dInches != fInches)

{

fInches = dInches;

NotifyPropertyChanged("Millimetres");

}

}

То есть в случае, если введенное пользователем число меньше нуля, мы сге-

нерируем исключение с соответствующим сообщением.

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

отрицательное число и нажмем кнопку Преобразовать. Рамка поля ввода

станет красной, а в ее верхнем правом уголке появится небольшой красный

треугольник; наведя на него курсор мыши, мы получим на экране всплываю-

щую подсказку с текстом "Значение должно быть неотрицательным".

Привязка компонента к компоненту

Зря мы городили огород с классом Convertor и интерфейсом

INotifyPropertyChanged... Silverlight позволяет привязать компонент к друго-

му компоненту. Делается это точно так же, как и в случае с объектом, но

свойства объекта привязки используются другие.

Создадим в Visual Web Developer 2008 еще один новый проект с именем

Convertor4. На единственной странице приложения поместим, опять же, две

надписи и два поля ввода txtInches и txtMillimetres (кнопка нам в данном

случае не понадобится). Зададим для них такие же свойства, что у одноимен-

ных компонентов приложения Convertor3, за исключением привязок.

Нам нужно привязать свойство Text поля ввода txtMillimetres к одноимен-

ному свойству поля ввода txtInches. Создадим соответствующий объект

привязки, использовав описанную ранее расширенную запись. Для указания

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

мы используем знакомое нам свойство Path класса Binding. А для указания

имени компонента применим свойство ElementName.

Таким образом, мы должны поместить в формирующий поле ввода txtInches

тег <TextBox> такой атрибут:

Text="{Binding Path=Text, ElementName=txtInches}"

Сохраним код, запустим приложение и попробуем ввести что-нибудь в поле

ввода Дюймы. Введенное нами значение сразу же появится в поле ввода

Миллиметры. То есть компонент, привязанный к другому компоненту, по-

лучает от него новые данные сразу же после их изменения.

Page 246: Дронов В. Самоучитель Silverlight 3 (2010)

234 Часть IV. Привязка компонентов к данным. LINQ

Использование конвертеров

Теперь нам нужно реализовать преобразование величин из дюймов в милли-

метры. Старый номер здесь не пройдет, поэтому придется искать что-то

новое.

Silverlight идет нам на помощь. Она позволяет в параметрах объекта привязки

указать так называемый конвертер — объект, реализующий интерфейс

IValueConverter. Этот интерфейс содержит два метода:

� Convert — выполняет преобразование данных, которые привязанный ком-

понент получает от объекта (или другого компонента);

� ConvertBack — выполняет преобразование данных, которые привязанный

компонент отправляет объекту (или другому компоненту).

Интерфейс IValueConvertеr определен в пространстве имен

System.Windows.Data. Оно изначально не отображено, поэтому мы либо долж-

ны отобразить его, либо использовать полные имена типов.

Оба метода должны принимать следующие параметры:

� исходное значение типа Object;

� тип, в который оно должно быть преобразовано. Этот параметр имеет тип

Type — особого класса, описывающего тип данных;

� дополнительный параметр типа Object;

� описание пользовательских настроек операционной системы, касающихся

языка и форматов отображения чисел. Он имеет тип CultureInfo — особо-

го класса, описывающего эти настройки. Отметим, что класс CultureInfo

объявлен в пространстве имен System.Globalization, которое изначально

не отображено.

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

методов. Остальные параметры игнорируют.

Возвращать оба этих метода должны преобразованное значение типа Object.

Исходя из этого, нам следует создать класс и реализовать в нем интерфейс

IValueConverter. Реализация будет включать только метод Convert; метод

ConvertBack мы оставим пустым.

Назовем наш класс так же — Convertor. Сделаем его публичным и поместим

прямо в пространство имен Convertor4.

public class Convertor : System.Windows.Data.IValueConverter

{

public object Convert(object value, Type targetType, object parameter,

System.Globalization.CultureInfo culture)

Page 247: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 235

{

string sInches;

double dInches, dMillimetres;

sInches = value as string;

if (double.TryParse(sInches, out dInches))

{

dMillimetres = dInches * 25.4;

}

else

{

dMillimetres = 0;

}

return dMillimetres.ToString();

}

public object ConvertBack(object value, Type targetType,

object parameter, System.Globalization.CultureInfo culture)

{

throw new NotImplementedException();

}

}

Здесь комментировать особо нечего. Единственное — в теле метода

ConvertBack мы генерируем исключение NotEmplementedException, сообщаю-

щее о том, что данный метод не реализован (поскольку он нам не нужен).

Нам осталось только указать, что объект привязки должен использовать соз-

данный нами конвертер. Для этого мы сначала подключим к XAML-коду

пространство имен Convertor4 (как это сделать, было описано ранее), а потом

поместим на страницу в качестве ресурса страницы объект класса Convertor,

указав для него ключ cnvConvertor (это также было описано ранее). Напосле-

док укажем данный ресурс в качестве конвертера в объекте привязки с по-

мощью свойства Converter класса Binding.

Text="{Binding Path=Text, ElementName=txtInches,

�Converter={StaticResource cnvConvertor}}"

Обратим внимание, что, поскольку конвертер является ресурсом страницы,

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

для указания объекта, к которому привязывается компонент.

Вот и все! Сохраним код, запустим приложение и проверим его в дейст-

вии.

Page 248: Дронов В. Самоучитель Silverlight 3 (2010)

236 Часть IV. Привязка компонентов к данным. LINQ

Привязка к коллекции

Несомненно, привязать компонент к объекту может быть полезно хотя бы для того, чтобы сэкономить пару строк C#-кода. Но гораздо полезнее другая воз-можность Silverlight — привязка списка к коллекции. Такой список будет формировать свои пункты на основе коллекции, к которой привязан.

Привязка к коллекции элементарных типов

Создадим в Visual Web Developer 2008 очередной проект с именем ListDemo.

На единственной странице поместим список lstDemo. И зададим для него ка-кие-либо параметры — они в данном случае не важны.

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

списка. И привяжем эту коллекцию к списку lstDemo.

Но какую же коллекцию выбрать? В главе 11 мы изучили несколько. Однако ни одна из них нам в данном случае не подойдет, поскольку не "умеет" ин-формировать привязанный к ней список о том, что в элементе коллекции или самой коллекции произошли какие-то изменения. А если говорить строго по-программистски, ни одна из этих коллекций не реализует интерфейс

INotifyCollectionChanged.

Специально для хранения данных, которые будут привязываться к спискам,

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

Класс ObservableCollection определен в пространстве имен System.

Collections.ObjectModel. Оно изначально не отображено, поэтому мы либо должны отобразить его, либо использовать полные имена типов.

Мы можем создать коллекцию класса в обработчике события Load страницы или конструкторе ее класса, а потом заполнить ее элементами-строками, ко-торые станут пунктами списка. Но мы поступим элегантнее: создадим на ос-

нове класса ObservableCollection другой класс — ListDemoItemCollection, конструктор которого сразу при создании коллекции добавит в нее нужные

элементы. Поместим этот класс в пространство имен ListDemo и сделаем пуб-личным.

public class ListDemoItemCollection :

�System.Collections.ObjectModel.ObservableCollection<string>

{

public ListDemoItemCollection()

Page 249: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 237

{

Add("Дюймы в миллиметры");

Add("Футы в метры");

Add("Миллиметры в дюймы");

Add("Метры в футы");

}

}

Поскольку класс коллекции ObservableCollection обобщенный, не забываем указать тип его элементов.

Закончив с классом ListDemoItemCollection, переключимся на XAML-код.

Подключим к XAML-коду пространство имен ListDemo и поместим на стра-ницу в качестве ресурса страницы объект данного класса, задав для него

ключ colItems.

Для привязки списка к коллекции мы используем свойство ItemsSource, кото-

рое поддерживается всеми классами списков. В качестве значения этого

свойства мы укажем объект — ресурс страницы с ключом colItems, исполь-зовав расширенную запись.

<ListBox . . . ItemsSource="{StaticResource colItems}" . . .>

Испытаем приложение в действии. Да, список успешно заполняется пункта-

ми, созданными на основе элементов нашей коллекции...

Привязка к коллекции объектов

...только все это мало впечатляет. Давайте попробуем привязать список

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

В главе 11 мы создали класс Person, хранящий сведения о специалисте по ин-

тернет-технологиям. Давайте позаимствуем его и немного расширим.

public class Person

{

public string F { get; set; }

public string N1 { get; set; }

public string N2 { get; set; }

public int Age { get; set; }

public List<string> Platforms { get; set; }

public Person()

{

Platforms = new List<string>();

}

}

Page 250: Дронов В. Самоучитель Silverlight 3 (2010)

238 Часть IV. Привязка компонентов к данным. LINQ

Как видим, мы добавили в него целочисленное свойство Age, хранящее воз-

раст специалиста. Поместим этот класс в пространство имен ListDemo.

Перепишем класс ListDemoItemCollection так, чтобы он хранил объекты

класса Person.

public class ListDemoItemCollection :

�System.Collections.ObjectModel.ObservableCollection<Person>

{

public ListDemoItemCollection()

{

Person prs;

prs = new Person();

prs.F = "Петров";

prs.N1 = "Петр";

prs.N2 = "Петрович";

prs.Age = 30;

prs.Platforms.Add("Flash");

Add(prs);

prs = new Person();

prs.F = "Иванов";

prs.N1 = "Иван";

prs.N2 = "Иванович";

prs.Age = 25;

prs.Platforms.Add("HTML + CSS + JavaScript");

prs.Platforms.Add("Flash");

prs.Platforms.Add("Java");

Add(prs);

prs = new Person();

prs.F = "Дронов";

prs.N1 = "Владимир";

prs.N2 = "Александрович";

prs.Age = 38;

prs.Platforms.Add("Silverlight");

Add(prs);

}

}

Следующим шагом укажем списку lstDemo, из какого свойства класса Person ему следует брать значения для создания пунктов. Это выполняется с по-

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

<ListBox . . . ItemsSource="{StaticResource colItems}"

�DisplayMemberPath="F" . . .>

Page 251: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 239

Здесь мы указали списку lstDemo, чтобы он брал значения для пунктов из

свойства F класса Person.

Сохраним код и запустим приложение на выполнение. Список покажет нам

все фамилии специалистов.

Вывод в пункте списка сразу нескольких значений. Шаблоны

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

свойств класса Person? Есть. Это шаблоны, о которых мы сейчас поговорим.

Шаблон — это либо компонент, либо контейнер с компонентами, который

список будет использовать для создания своих пунктов. Фактически в каче-

стве пункта список будет выводить шаблон, заполняя его компоненты дан-

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

свойствам класса, объекты которого хранятся в коллекции, после чего шаб-

лон подключается к списку, который привязан к этой коллекции. Остальное,

как говорится, дело техники.

Для подключения шаблона к списку используется свойство ItemTemplate,

поддерживаемое всеми классами списков. Его значение должно иметь тип

DataTemplate — базовый класс, на основе которого создаются шаблоны.

Чтобы задать шаблон для списка в XAML-коде, нам придется использовать

объектную запись. Внутрь тега, создающего список, мы поместим тег, опре-

деляющий свойство ItemTemplate, в него — тег, создающий объект класса

DataTemplate, а внутрь него — теги, определяющие компонент или контейнер

с несколькими компонентами.

<ListBox x:Name="lstDemo" ItemsSource="{StaticResource colItems}">

<ListBox.ItemTemplate>

<DataTemplate>

<StackPanel Orientation="Horizontal">

<StackPanel>

<TextBlock Text="{Binding Path=F}"></TextBlock>

<TextBlock Text="{Binding Path=N1}"></TextBlock>

<TextBlock Text="{Binding Path=N2}"></TextBlock>

</StackPanel>

<TextBlock Text="{Binding Path=Age}" FontSize="26"></TextBlock>

</StackPanel>

</DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

Page 252: Дронов В. Самоучитель Silverlight 3 (2010)

240 Часть IV. Привязка компонентов к данным. LINQ

Здесь мы поместили в шаблон контейнер "стопка" с горизонтальным распо-

ложением компонентов. В нем находится, во-первых, еще один контейнер

"стопка", уже с вертикальным расположением компонентов, содержащий три

надписи, привязанные к свойствам F, N1 и N2. Во-вторых, там присутствует

надпись, выводящая содержимое свойства Age увеличенным шрифтом.

Отметим две вещи. Первая: в определениях объектов привязки в компонентах

шаблона мы не указываем объект, к которому они привязывают (т. е. нашу

коллекцию). Мы уже указали ее в параметрах самого списка. Вторая: мы

убрали из тега, создающего список, свойство DisplayMemberPath, поскольку

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

Сохраняем, запускаем, смотрим. Ну и дела — каждый пункт списка содержит

значения сразу из четырех свойств! Наш шаблон работает!

Кстати, все классы списков поддерживают очень полезное свойство

SelectedItem. Оно задает или возвращает элемент коллекции, выбранный

в данный момент в списке, в виде значения типа Object. Совсем скоро мы

используем это свойство.

Отображение связанных данных

И совсем было бы неплохо как-то вывести на страницу элементы "внутрен-

ней" коллекции Platforms объектов класса Person. Понятно, что удобнее всего

для этого использовать другой список, но как это реализовать?

Давайте пока что создадим на странице еще один список и назовем его

lstDetail. И подумаем. Чтобы вывести в этот список содержимое "внутрен-

ней" коллекции, нам нужно привязать его к этой коллекции — к свойству

Platforms того элемента коллекции, который в данный момент выбран в спи-

ске lstDemo. А чтобы получить выбранный элемент списка, достаточно полу-

чить значение свойства SelectedItem этого списка (вот оно нам и пригоди-

лось!) и преобразовать его в тип Person.

Но как выполнить привязку списка к коллекции в C#-коде? С помощью свой-

ства DataContext, поддерживаемого всеми компонентами. Значение этого

свойства имеет тип Object и задает объект, к которому привязан данный ком-

понент.

Вооружившись новыми знаниями, начнем. Создадим обработчик события

SelectionChanged списка lstDemo; это событие, как мы помним из главы 6,

возникает, когда пользователь выбирает в списке другой пункт. Код этого

обработчика приведен далее.

private void lstDemo_SelectionChanged(object sender,

�SelectionChangedEventArgs e)

Page 253: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 241

{

lstDetail.DataContext = (lstDemo.SelectedItem as Person).Platforms;

}

Здесь мы получаем выбранный в настоящее время объект, преобразуем его в

тип Person и его "внутреннюю" коллекцию Platforms присваиваем свойству

DataContext списка lstDetail. Обратим внимание, как это можно сделать в

одно выражение.

А теперь в XAML-коде добавим к формирующему список lstDetail тегу

<ListBox> вот такой атрибут:

ItemsSource="{Binding}"

Фактически мы создали пустой объект привязки, говоря списку о том, что он

привязан к объекту. Но к какому именно объекту — мы укажем потом, при-

своив нужное значение его свойству DataContext.

Вот и все! Можно сохранить код и проверить приложение в действии.

Использование таблицы DataGrid для вывода данных из коллекции

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

многих случаях лучше использовать специально предназначенный для этого

компонент DataGrid — таблицу (не путать с контейнером "таблица"!).

Компонент DataGrid (и соответствующий ему класс) создает на странице спи-

сок, организованный в виде таблицы со строками и столбцами, — таблицу.

Этот класс определен в той части пространства имен System.Windows.Controls,

что находится в библиотечной сборке System.Windows.Controls.Data.dll.

<data:DataGrid></data:DataGrid>

Обычно Visual Web Developer 2008 для указания на это пространство имен оп-ределяет при его подключении префикс data. Мы тоже всегда будем его ис-

пользовать.

Когда мы привяжем таблицу к коллекции, элементы этой коллекции образу-

ют ее строки, а свойства этих элементов — столбцы.

Компонент DataGrid поддерживает чрезвычайно много свойств, методов

и событий. Мы рассмотрим только некоторые из них.

Прежде всего, класс DataGrid поддерживает свойства ActualHeight,

ActualWidth, DataContext, FontFamily, FontSize, FontStretch, FontStyle,

FontWeight, Height, HorizontalAlignment, IsEnabled, IsTabStop, ItemsSource,

Page 254: Дронов В. Самоучитель Silverlight 3 (2010)

242 Часть IV. Привязка компонентов к данным. LINQ

Margin, MaxHeight, MaxWidth, MinHeight, MinWidth, Name, Padding, SelectedIndex,

SelectedItem, TabIndex, VerticalAlignment, Visibility и Width, метод Focus и

событие SelectionChanged.

Свойство IsReadOnly позволяет сделать таблицу доступной только для чте-

ния, для чего ему достаточно присвоить логическое значение true. Логиче-

ское значение false, наоборот, делает ее доступной и для чтения, и для прав-

ки данных (значение по умолчанию).

Свойства HorizontalScrollBarVisibility и VerticalScrollBarVisibility ука-

зывают, будет ли в таблице присутствовать, соответственно, горизонтальная

или вертикальная полоса прокрутки. Эти свойства принимают значения типа

перечисления ScrollBarVisibility с четырьмя элементами:

� Auto — полоса прокрутки появляется только при необходимости;

� Hidden — полоса прокрутки не появляется, но пользователь может про-

кручивать содержимое таблицы с помощью клавиш-стрелок;

� Visible — полоса прокрутки присутствует всегда;

� Disabled — полоса прокрутки не появляется, и пользователь даже не мо-

жет прокручивать содержимое таблицы с помощью клавиш-стрелок.

Значение по умолчанию обоих этих свойств — Auto.

Свойство HeadersVisibility указывает, будет ли в таблице присутствовать

"шапка" с заголовками столбцов. Это свойство принимает значение типа пе-

речисления DataGridHeadersVisibility; нам будут полезны его элементы

Columns ("шапка" присутствует; значение по умолчанию) и None ("шапка" от-

сутствует).

Свойство GridLinesVisibility указывает, будут ли в таблице присутствовать

линии, разграничивающие строки и столбцы. Оно принимает значение типа

перечисления DataGridGridLinesVisibility с четырьмя элементами:

� None — никаких линий в таблице не отображается;

� Horizontal — отображаются линии, разграничивающие строки;

� Vertical — отображаются линии, разграничивающие столбцы;

� All — отображаются все линии (значение по умолчанию).

Еще три полезных свойства, чьи значения имеют логический тип.

� CanUserReorderColumns — позволяет пользователю перетаскивать столбцы

с места на место мышью.

� CanUserResizeColumns — позволяет пользователю менять размеры столб-

цов, перетаскивая мышью границы между ними.

Page 255: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 243

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

Значение true активирует данную возможность, а значение false — отключа-

ет. Значение по умолчанию — true.

Свойство AutoGenerateColumns принимает значение логического типа. Значе-

ние true указывает таблице, чтобы она сама создала столбцы для отображе-ния значений всех свойств объектов, являющихся элементы коллекции, к ко-

торой она привязана. Значение false предписывает таблице не создавать столбцы самой, а использовать те, что мы описали с помощью свойства

Columns.

Свойство Columns задает столбцы, которые должны присутствовать в таблице.

Оно имеет тип коллекции ObservableCollection<DataGridColumn>, которая со-

держит элементы типа DataGridColumn — класса, на основе которого созданы все классы столбцов таблицы.

Silverlight предоставляет нам три класса столбцов таблицы.

� DataGridTextColumn — отображает обычный текст. Используется в боль-шинстве случаев.

� DataGridCheckBoxColumn — отображает флажок. Используется для полей логического типа.

� DataGridTemplateColumn — отображает произвольное содержимое, исполь-зуя заданный шаблон (о шаблонах было рассказано ранее). Используется для вывода содержимого полей сложных типов или в случаях, если требу-ется каким-то образом форматировать данные при выводе.

Рассмотрим свойства, поддерживаемые всеми этими классами. (Полезных для нас методов и событий они не поддерживают.)

Прежде всего, все эти классы поддерживают свойства ActualWidth,

IsReadOnly, MaxWidth, MinWidth, Visibility и Width.

Свойство Header строкового типа задает текст в заголовке столбца.

Свойство DisplayIndex целочисленного типа задает или возвращает номер столбца в таблице.

Три следующих свойства имеют логический тип.

� CanUserReorder — позволяет пользователю перетаскивать данный столбец с места на место мышью.

� CanUserResize — позволяет пользователю менять размеры данного столб-ца, перетаскивая мышью его правую границу.

� CanUserSort — позволяет пользователю сортировать таблицу по содержи-

мому данного столбца, щелкнув мышью на его заголовке.

Page 256: Дронов В. Самоучитель Silverlight 3 (2010)

244 Часть IV. Привязка компонентов к данным. LINQ

Значение true активирует данную возможность, а значение false — отключа-

ет. Значением по умолчанию является значение соответствующего свойства таблицы.

Классы DataGridTextColumn и DataGridCheckBoxColumn поддерживают свойство

Binding, задающее привязку столбца к полю объекта. Для этого используется знакомая нам расширенная запись.

Класс DataGridTextColumn поддерживает свойства FontFamily, FontSize,

FontStyle и FontWeight.

Класс DataGridCheckBoxColumn поддерживает свойство IsThreeState. Логиче-

ское значение true указывает, что флажок, выводимый в столбце, может при-

нимать третье, неопределенное, состояние, а значение false — не может (это значение по умолчанию).

Класс DataGridTemplateColumn поддерживает свойства CellTemplate и

CellEditingTemplate. Оба имеют тип DataTemplate. Первое свойство задает шаблон, используемый столбцом для вывода данных, а второй — для их

правки (шаблон вывода; подробнее использование этого свойства мы рас-смотрим в конце главы).

Настало время применить полученные знания на практике. Создадим в Visual

Web Developer 2008 новый проект с именем GridDemo. В C#-код единствен-

ной страницы приложения поместим объявление классов Person и

ListDemoItemCollection, позаимствовав их из проекта ListDemo. В XAML-коде

выполним подключение пространства имен GridDemo и поместим на страницу

в качестве ресурса страницы объект класса ListDemoItemCollection, задав для

него ключ colItems.

После этого поместим на страницу таблицу grdDemo. Воспользовавшись свой-

ством ItemsSource, привяжем ее к только что созданному ресурсу страницы

с ключом colItems. Зададим для таблицы еще какие-нибудь свойства, так ска-зать, по вкусу. И проверим приложение в действии.

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

им свойств класса Person. И столбец Platforms, в котором выводится полное

имя класса ObservableCollection, тоже не добавляет лоска. (Компонент

DataGrid не умеет выводить в столбцах содержимое коллекций, а свойство

Platforms — именно коллекция. Поэтому он выводит там полное имя ее клас-

са.) Давайте это исправим.

Нам придется самим указать, какие столбцы будут присутствовать в таблице. Их будет пять: фамилия, имя, отчество, возраст и количество платформ, ко-

торыми владеет данный специалист. Это будут обычные текстовые столбцы,

которые описывает класс DataGridTextColumn.

Page 257: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 245

Прежде всего, зададим для свойства AutoGenerateColumns значение false, что-

бы таблица не создавала столбцы сама. Потом вставим в тег <data:DataGrid>,

который создает таблицу, следующий код, определяющий ее столбцы:

<data:DataGrid.Columns>

<data:DataGridTextColumn Binding="{Binding Path=F}"

�Header="Фамилия"></data:DataGridTextColumn>

<data:DataGridTextColumn Binding="{Binding Path=N1}"

�Header="Имя"></data:DataGridTextColumn>

<data:DataGridTextColumn Binding="{Binding Path=N2}"

�Header="Отчество"></data:DataGridTextColumn>

<data:DataGridTextColumn Binding="{Binding Path=Age}"

�Header="Возраст"></data:DataGridTextColumn>

<data:DataGridTextColumn Binding="{Binding Path=Platforms.Count}"

�Header="Кол-во платформ"></data:DataGridTextColumn>

</data:DataGrid.Columns>

Никаких особых пояснений здесь не требуется. За исключением последнего

столбца, который мы привязали к свойству Count "внутренней" коллекции

Platforms класса Person. Да-да, здесь также используется знакомый нам син-

таксис "с точкой"!

Запустим исправленное приложение. Вот теперь все нормально!

Напоследок давайте поэкспериментируем с шаблонами. Удалим из описы-

вающего таблицу тега <data:DataGrid> код, создающий столбцы Фамилия,

Имя и Отчество. И вставим на их место тег, который создаст столбец класса

DataGridTemplateColumn, — тот самый, что использует для вывода данных

шаблон.

<data:DataGridTemplateColumn Header="ФИО">

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<StackPanel>

<TextBlock Text="{Binding Path=F}"></TextBlock>

<TextBlock Text="{Binding Path=N1}"></TextBlock>

<TextBlock Text="{Binding Path=N2}"></TextBlock>

</StackPanel>

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

Созданный нами шаблон содержит контейнер "стопка" с вертикальным рас-

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

привязанные к свойствам F, N1 и N2.

Page 258: Дронов В. Самоучитель Silverlight 3 (2010)

246 Часть IV. Привязка компонентов к данным. LINQ

Проверим, что мы написали, в работе. Теперь фамилии, имена и отчества

специалистов выводятся в первом столбце таблицы "в столбик".

Реализация правки данных в таблице DataGrid

Но что делать, если мы собираемся дать пользователю возможность править

данные, отображаемые в таблице DataGrid? Да ничего особенно!

Таблица DataGrid по умолчанию дает возможность пользователю править

данные в ячейках всех ее столбцов, за исключением:

� привязанных к свойствам, недоступным для записи;

� принадлежащих к классу DataGridTemplateColumn;

� явно помеченных как недоступные для правки установкой значения свой-

ства IsReadOnly равным true.

И, кроме того, нужно также проверить, не установлено ли значение свойства

IsReadOnly самой таблицы равным true. Но это очевидно.

Если мы будем использовать для правки данных коллекции, к которой привяза-на таблица, какие-то другие компоненты или править их прямо в C#-коде, нам придется реализовать в классе, к которому принадлежат элементы коллекции, интерфейс INotifyPropertyChanged. Как это сделать, описано в начале этой

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

А теперь — внимание! Когда пользователь исправит что-либо в ячейке таб-

лицы, эти изменения не сохранятся в коллекции, пока он не нажмет клавишу

<Enter> или не перейдет на другую строку таблицы. Если пользователь захо-

чет отказаться от сделанных в ячейке изменений, он должен будет нажать

клавишу <Esc>. Повторное нажатие клавиши <Esc> отменит изменения, сде-

ланные во всех ячейках данной строки таблицы.

К сожалению, таблица не позволяет нам добавлять новые элементы в коллек-

цию и удалять их оттуда. Для этого нам придется предусмотреть другие эле-

менты управления. Так, для добавления нового элемента в коллекцию нам

понадобится набор компонентов для ввода значений свойств добавляемого

элемента и кнопка, нажатие которой добавит этот элемент в коллекцию. Для

удаления же выбранного в таблице элемента будет достаточно одной только

кнопки, которая выполнит операцию удаления.

Вы можете дополнить данное приложение этими возможностями самостоя-

тельно. Пусть это будет вашим домашним заданием.

Page 259: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 13. Привязка компонентов к данным 247

Использование шаблонов ввода в таблице DataGrid

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

вывода данных шаблоны. Заданный нами шаблон будет использоваться спи-

ском для формирования пункта, а таблицей — содержимого ячейки.

Однако таблица, точнее, столбец класса DataGridTemplateColumn, позволяет

использовать так называемые шаблоны ввода. Они применяются не для вы-вода, а для ввода данных.

Первый столбец нашей таблицы выводит сразу три значения: фамилию, имя

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

DataGridTemplateColumn). Чтобы он смог это делать, мы должны создать и подключить к этому столбцу шаблон ввода.

Для подключения шаблона ввода используется свойство CellEditingTemplate

класса DataGridTemplateColumn. Оно аналогично свойству CellTemplate за двумя исключениями.

1. Мы должны создать в шаблоне ввода компоненты, которые позволяют

вводить данные (поля ввода, списки, переключатели, календари и пр.).

2. При привязке этих компонентов к свойствам объектов — элементов кол-

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

Mode их объектов привязки значения TwoWay.

Поэтому мы вставим в тег <data:DataGridTemplateColumn>, создающий первый столбец таблицы, вот такой код:

<data:DataGridTemplateColumn.CellEditingTemplate>

<DataTemplate>

<StackPanel>

<TextBox Text="{Binding Path=F, Mode=TwoWay}"></TextBox>

<TextBox Text="{Binding Path=N1, Mode=TwoWay}"></TextBox>

<TextBox Text="{Binding Path=N2, Mode=TwoWay}"></TextBox>

</StackPanel>

</DataTemplate>

</data:DataGridTemplateColumn.CellEditingTemplate>

Три поля ввода. Просто и незатейливо.

Проверим исправленное приложение в деле. Должно работать.

Собственно, на этом описание привязки компонентов к данным можно за-кончить. Желающие знать больше могут обратиться к документации по Silverlight.

Page 260: Дронов В. Самоучитель Silverlight 3 (2010)

248 Часть IV. Привязка компонентов к данным. LINQ

Что дальше?

В этой главе мы изучили одну из самых интересных возможностей

Silverlight — привязку компонентов к данным. Мы научились привязывать

компоненты к свойствам одного объекта и к целым коллекциям, узнали, как

можно заставить компонент отображать те данные, которые нам нужны,

с помощью шаблонов, и рассмотрели мощнейший компонент таблицы.

В следующей главе мы продолжим разговор о данных, а именно о коллек-

циях. Мы научимся получать из них те данные, которые нам нужны. И ис-

пользуем для этого запросы, написанные на специально предназначенном для

этого языке LINQ, исключительно простом и мощном.

Page 261: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 14

LINQ

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

В этой главе мы продолжим работу с данными. А именно — займемся сред-ствами Silverlight по поиску и выборке данных в соответствии с определен-ными критериями. К таким средствам относится язык LINQ, на котором пи-шутся запросы по выборке данных из коллекций. Кстати, это будет уже тре-тий по счету компьютерный язык, который мы изучаем...

Введение в запросы и язык LINQ

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

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

теле будем проверять, присутствует ли во "внутренней" коллекции Platforms

элемент "Flash". Элементы изначальной коллекции, во "внутренней" коллек-ции которых есть этот элемент, будем добавлять в новую коллекцию. И, ко-гда цикл просмотра закончит свою работу, мы будем иметь в новой коллек-ции всех специалистов — знатоков Flash.

Чтобы реализовать все это, нам придется написать довольно много C#-кода, как говорится, вручную. Конечно, для опытного программиста это не соста-вит труда. Но мы-то с вами неопытные! Да и гуру от программирования тоже не всегда хочется "работать руками"...

Page 262: Дронов В. Самоучитель Silverlight 3 (2010)

250 Часть IV. Привязка компонентов к данным. LINQ

Среда исполнения Silverlight и здесь придет нам на помощь! Она поддержи-

вает написание особых выражений — запросов к данным. Такие запросы

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

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

вид.

Пишутся эти запросы на специальном языке, называемом LINQ (Language-

INtegrated Query — запрос, встроенный в язык), помещаются прямо в C#-код

и компилируются в MSIL. Представляют собой они обычные выражения C# и

мало чем отличаются от выражений, выполняющих присваивание перемен-

ной значения.

Вот только сам язык LINQ, мягко говоря, необычен. Как и XAML, он описы-

вает не то, что должна делать программа, а то, что мы должны получить в

результате его выполнения. То есть мы говорим среде исполнения Silverlight:

"Дай мне вот эти данные в таком порядке и вот в таком виде" — а уж она са-

ма позаботится о том, чтобы все это сделать.

Язык LINQ основан на популярном языке запросов к базам данных SQL (Structured Query Language — структурированный язык запросов). Так что про-граммисты, знающие язык SQL, без труда поймут LINQ.

Выборка одного значения

Что ж, давайте, не откладывая дела в долгий ящик, познакомимся с языком

LINQ. И начнем мы знакомство с самых простых запросов — выборки дан-

ных.

Создадим в Visual Web Developer 2008 новый проект и назовем его LINQDemo.

На единственной странице нашего нового приложения поместим список

lstDemo — его мы будем использовать для просмотра результатов наших

LINQ-запросов. Зададим для него привязку к данным, создав пустой объект

привязки.

<ListBox x:Name="lstDemo" ItemsSource="{Binding}"></ListBox>

Этим мы сообщим списку, что собираемся привязать его к коллекции, кото-

рую укажем с помощью свойства DataContext. (Подробнее о привязке спи-

сков к коллекциям см. в главе 13.)

В C#-код страницы поместим объявления классов Person и

ListDemoItemCollection, позаимствованных из приложения ListDemo. Класс

ListDemoItemCollection переименуем в LINQCollection, чтобы он соответство-

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

Page 263: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 251

этого класса, чтобы он помещал в коллекцию побольше элементов, и нам бы-

ло из чего выбирать.

Вообще, для создания коллекций, данные из которых будут выбираться с помощью LINQ-запросов, можно использовать не только класс ObservableCollection или его классы-потомки. Также прекрасно подойдет

класс List.

Код, выполняющий запросы, мы будем писать в конструкторе класса страни-

цы, сразу после вызова метода InitializeComponent. Не будем городить ого-

род с обработчиком события Load страницы и ресурсами страницы — у нас и так есть чем заняться.

Итак, сначала нам нужно написать запрос на языке LINQ. Что для этого нуж-но знать?

� Запрос пишется в виде выражения присваивания, выступая в качестве зна-

чения, которое присваивается переменной. Эта переменная называется пе-ременной запроса.

� Результат запроса представляет собой коллекцию.

� Переменная запроса должна иметь тип, соответствующий коллекции —

результату запроса.

� Запрос не выполняется до тех пор, пока мы не обратимся к его результа-

там (привязав к ним список, обработав в цикле просмотра или просто не получив значение какого-либо его элемента).

Чтобы проиллюстрировать сказанное, давайте сначала создадим объект клас-

са LINQCollection и присвоим его переменной colPersons:

LINQCollection colPersons = new LINQCollection();

И напишем вот такой запрос, извлекающий из коллекции colPersons фамилии

специалистов:

IEnumerable<string> qryPersons = from person in colPersons

select person.F;

Сначала обратим внимание на переменную запроса qryPersons. Как мы уже знаем, она должна иметь тип коллекции — результата выполнения запроса.

Но какого типа коллекцию вернет запрос? Нам нужно это знать, иначе, если

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

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

обобщенный интерфейс IEnumerable, который реализуют все классы коллек-

Page 264: Дронов В. Самоучитель Silverlight 3 (2010)

252 Часть IV. Привязка компонентов к данным. LINQ

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

Поскольку интерфейс IEnumerable обобщенный, мы должны указать для него

тип элементов коллекции. Мы указали строковый тип (string), поскольку

извлекаем из коллекции colPersons фамилии специалистов, которые как раз строкового типа.

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

� Берет очередной элемент коллекции colPersons и помещает его в пере-

менную элемента person (from person in colPersons). Переменная элемен-та служит для временного хранения очередного элемента коллекции и доступна только внутри запроса.

� Извлекает значение свойства F элемента, хранящегося в переменной

person (select person.F).

Так, запрос мы создали. Осталось привязать список lstDemo к результату кол-лекции.

lstDemo.DataContext = qryPersons;

Сохраним код и запустим приложение на выполнение. В списке отобразятся фамилии специалистов. Наш запрос работает!

Точно так же мы можем извлечь из коллекции любые другие данные.

IEnumerable<string> qryPersons = from person in colPersons

select person.F + " " + person.N1 + " " + person.N2;

Здесь мы получаем строки, содержащие фамилии, имена и отчества специа-листов, разделенные пробелами.

Мы можем получить и сами элементы изначальной коллекции.

IEnumerable<Person> qryPersons = from person in colPersons select person;

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

Обобщая полученные знания, скажем, что запрос на выборку данных из кол-лекции имеет такой формат:

from <переменная элемента> in <коллекция>

select <свойство элемента или выражение,

�использующее значения из свойств элемента>

Page 265: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 253

Здесь используются ключевые слова from, in и select. А соответствующие им

части запроса называются секциями from и select.

Выборка нескольких значений. Анонимные типы

Но что делать, если нам нужно извлечь сразу несколько значений из элемен-

тов нашей коллекции colPersons? Можно ли это сделать вообще?

Разумеется можно. Мы можем создать в секции select объекты и присвоить

извлекаемые из элементов коллекции значения их свойствам. Результат за-

проса будет представлять собой коллекцию этих объектов.

var qryPersons = from person in colPersons

�select new { F = person.F, N1 = person.N1, N2 = person.N2 };

Здесь мы для каждого элемента коллекции colPersons прямо в секции select

создаем объект, в который добавляем три свойства, хранящие фамилии, име-

на и отчества специалистов соответственно. Обратим внимание на то, как

создается этот объект: сначала идет хорошо знакомый нам оператор new, а за

ним в фигурных скобках через запятую перечисляются выражения, присваи-

вающие свойствам этого объекта их значения и создающие тем самым эти

свойства (F, N1 и N2). Подобные выражения, создающие объекты в фигурных

скобках, называются инициализаторами.

Все это, конечно, замечательно... Но какой тип имеет полученный данным

способом объект? Ведь мы нигде не указали его тип!

Здесь мы столкнулись с так называемыми анонимными типами Silverlight.

Анонимные типы генерируются самим Visual Web Developer 2008 при ком-

пиляции проекта для всех объектов, которые мы создали с помощью инициа-

лизатора. Они имеют существенные отличия от обычных типов.

� В анонимных типах допускаются только свойства.

� Свойства объектов анонимных типов доступны только для чтения.

� Анонимные типы существуют только в том методе, в котором присутст-

вуют их объекты. Передать объект анонимного типа в качестве параметра

другому методу или вернуть его в качестве результата можно, но другой

метод не сможет получить доступ к свойствам этого объекта.

� Объект анонимного типа нельзя преобразовать к любому другому типу, за

исключением Object.

Теперь обратим внимание на переменную запроса. Поскольку мы не можем

указать тип элементов коллекции, которая будет присвоена этой переменной,

Page 266: Дронов В. Самоучитель Silverlight 3 (2010)

254 Часть IV. Привязка компонентов к данным. LINQ

мы используем для ее объявления ключевое слово var. Этим мы указываем,

что Visual Web Developer 2008 должен сам задать для переменной подходя-

щий тип при компиляции кода (неявное указание типа).

Анонимные типы — отличная вещь, если нам нужно обработать результаты

запроса в том же методе, где этот запрос определен. Но когда нам понадобит-

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

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

объекта анонимного типа мы потерпим неудачу.

Так, если мы укажем в списке lstDemo с помощью свойства DisplayMemberPath

имя свойства, из которого следует брать значения для формирования пунк-

тов, то получим список с пустыми пунктами. Компонент ListBox не может

получить значения свойств объектов анонимного типа!

Выход из этого положения только один: мы должны создать другую коллек-

цию типа, скажем, ObservableCollection, задать для него какой-либо тип эле-

ментов, объявленный в коде, и перенести в эту коллекцию содержимое ре-

зультата запроса. А уж к этой новой коллекции мы со спокойной душой мо-

жем привязать список.

System.Collections.ObjectModel.ObservableCollection<string> colTemp =

�new System.Collections.ObjectModel.ObservableCollection<string>();

foreach (var prs in qryPersons)

{

colTemp.Add(prs.F + " " + prs.N1 + " " + prs.N2);

}

lstDemo.DataContext = colTemp;

Здесь показано, как это может быть реализовано. Обратим внимание, что пе-

ременную элемента в цикле просмотра мы также объявляем с помощью клю-

чевого слова var, т. к. в этой переменной будут храниться объекты анонимно-

го типа.

Вообще, по мнению автора, анонимными типами желательно не пользоваться. Слишком много с ними проблем...

Фильтрация данных

Итак, выбирать данные из коллекции мы научились. Только смысла в том,

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

полезнее научиться фильтровать данные, извлекая только те, что удовлетво-

ряют определенным критериям, которые мы зададим в запросе.

Page 267: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 255

Для указания фильтра — критериев отбора данных — мы используем сек-

цию where, которая создается с помощью одноименного ключевого слова.

where <условие фильтрации>

Условие фильтрации задает критерии отбора данных и представляет собой

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

главе 3. Если вычисленное им значение равно true, данный элемент помеща-

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

логические операторы и вызовы методов.

Помещается секция where между секциями from и select.

IEnumerable<string> qryPersons = from person in colPersons

where person.Age > 30

select person.F + " " + person.N1 + " " + person.N2;

Здесь мы получаем фамилии, имена и отчества только тех специалистов, чей

возраст превышает 30 лет.

IEnumerable<string> qryPersons = from person in colPersons

where person.Age < 30 && person.Platforms.Contains("Flash")

select person.F + " " + person.N1 + " " + person.N2;

А здесь мы получаем фамилии, имена и отчества тех специалистов, возраст

которых меньше 30 лет и которые владеют технологией Flash. Здесь мы ис-

пользуем логический оператор И && и вызов метода Contains. (Подробнее об

этом методе и других методах классов коллекций см. главу 11.)

Сортировка данных

А еще мы можем сортировать извлекаемые из коллекций данные. Для этого

используется секция orderby запроса, создаваемая с помощью одноименного

ключевого слова.

orderby <список свойств и выражений,

�по значениям которых выполняется сортировка,

�разделенных запятыми>

Если в качестве критериев сортировки мы укажем несколько свойств или вы-

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

вого свойства (выражения). Если в коллекции встретятся элементы с одина-

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

ваны по значению второго по порядку свойства и т. д.

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

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

Page 268: Дронов В. Самоучитель Silverlight 3 (2010)

256 Часть IV. Привязка компонентов к данным. LINQ

поставим после имени свойства (выражения) ключевое слово descending, от-

делив его пробелом.

Секция orderby ставится между секциями from и select. Если в запросе при-сутствует секция where, то секция orderby ставится между ней и секцией select.

IEnumerable<string> qryPersons = from person in colPersons

where person.Age <= 30

orderby person.F

select person.F + " " + person.N1 + " " + person.N2;

Здесь мы извлекаем фамилии, имена и отчества специалистов, чей возраст не превышает 30 лет, и сортируем их по фамилии.

IEnumerable<string> qryPersons = from person in colPersons

orderby person.Platforms.Count() descending, person.F

select person.F + " " + person.N1 + " " + person.N2;

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

ства платформ мы используем метод Count.

Связывание данных

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

Для описания подразделения объявим новый класс Dept. В нем создадим два

свойства: ID (уникальный цифровой идентификатор подразделения) и Name (название подразделения).

public class Dept

{

public short ID { get; set; }

public string Name { get; set; }

}

После этого объявим класс DeptCollection, хранящий список подразделений.

public class DeptCollection :

�System.Collections.ObjectModel.ObservableCollection<Dept>

{

public DeptCollection()

{

Dept dpt;

Page 269: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 257

dpt = new Dept();

dpt.ID = 1;

dpt.Name = "Главный отдел";

Add(dpt);

dpt = new Dept();

dpt.ID = 2;

dpt.Name = "Шаляй-валяй-контора";

Add(dpt);

}

}

Двух подразделений пока что будет достаточно.

После этого добавим в класс Person объявление свойства DeptID, которое бу-

дет хранить идентификатор подразделения.

public short DeptID { get; set; }

И дополним код конструктора класса LINQCollection, чтобы он присваивал

свойствам DeptID создаваемых элементов коллекции какие-либо значения.

prs.F = "Петров";

prs.N1 = "Петр";

prs.N2 = "Петрович";

prs.DeptID = 1;

. . .

prs.F = "Иванов";

prs.N1 = "Иван";

prs.N2 = "Иванович";

prs.DeptID = 1;

. . .

prs.F = "Дронов";

prs.N1 = "Владимир";

prs.N2 = "Александрович";

prs.DeptID = 2;

И создадим в конструкторе страницы объект класса.

DeptCollection colDepts = new DeptCollection();

И вот теперь к нам приходит начальник и срочно требует, чтобы мы предос-

тавили ему список специалистов с указанием подразделений, где они рабо-

тают. Что делать?

Нам нужно как-то привязать в запросе каждый элемент коллекции

LINQCollection к соответствующему ему элементу коллекции DeptCollection,

используя для связки свойства DeptID и ID. Если значения этих свойств рав-

ны, значит, этот специалист работает в данном подразделении.

Page 270: Дронов В. Самоучитель Silverlight 3 (2010)

258 Часть IV. Привязка компонентов к данным. LINQ

Назовем коллекцию LINQCollection первичной, или главной. А коллекцию

DeptCollection назовем вторичной, или подчиненной. А сам процесс привязки

элемента первичной коллекции к элементу вторичной пусть называется свя-

зыванием.

Для связывания элементов разных коллекций используется секция join, фор-

мируемая с помощью ключевых слов join, in, on и equals.

join <переменная элемента вторичной коллекции> in <вторичная коллекция>

�on <свойство элемента первичной коллекции>

�equals <свойство элемента вторичной коллекции>

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

дет помещаться очередной элемент вторичной коллекции, а после ключевого

слова in — сама вторичная коллекция. После ключевого слова on следует

указать свойство элемента первичной коллекции, а после ключевого слова

equals — свойство элемента коллекции вторичной — по значениям этих

свойств будет выполняться связывание элементов обеих коллекций.

Секция join ставится после секции from и перед остальными секциями за-

проса.

var qryPersons = from person in colPersons

join dept in colDepts on person.DeptID equals dept.ID

select new {Name = person.F + " " + person.N1 + " " + person.N2,

�Dept = dept.Name};

Вот так мы связали коллекции LINQCollection и DeptCollection и получили

в качестве результата запроса коллекцию объектов анонимного типа, содер-

жащих фамилии, имена, отчества специалистов и подразделения, в которых

они работают. Начальник будет доволен.

Группировка данных

А чтобы начальство было довольно еще больше, мы можем сгруппировать

результат запроса. Группировка — это объединение элементов изначальной

коллекции в коллекции поменьше (группы) по какому-либо признаку (ключу

группировки). Таким признаком в нашем случае может быть название подраз-

деления, в котором работают специалисты.

Для группировки данных в запросе используется секция group. Она создается

с помощью ключевых слов group и by.

group <значение, которое попадет в группу> by <ключ группировки>

Секция group ставится после секции from или join.

Page 271: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 259

Результат группировки и в этом случае представляется в виде коллекции типа

IEnumerable. Элементы же этой коллекции имеют тип обобщенного интер-

фейса IGrouping, который содержит два свойства.

� Key — содержит ключ группировки, по которому была создана данная

группа.

� Value — содержит указатель на "внутреннюю" коллекцию, содержащую

группу элементов, которая была создана на основе указанного ключа

группировки.

Поскольку интерфейс IGrouping обобщенный, мы должны указать для него

типы и ключей, и элементов "внутренних" коллекций.

IEnumerable<IGrouping<string, string>> qryDepts = from dept in colDepts

join person in colPersons on dept.ID equals person.DeptID

group person.F + " " + person.N1 + " " + person.N2 by dept.Name;

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

циалистов, по названию подразделения, где работают эти специалисты. В ре-

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

качестве групп — коллекции строк с фамилиями, именами и отчествами.

Обратим внимание, как мы указали тип переменной запроса при ее объявле-

нии. Сам результат запроса будет представлять собой коллекцию и иметь тип

IEnumerable. Тип элементов этой коллекции будет IGrouping, для которого мы

указали строковый тип ключей и строковый же тип сгруппированных зна-

чений.

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

указание типа с помощью ключевого слова var (см. раздел об анонимных типах

этой главы).

Чтобы вывести результаты такого запроса на экран, нам понадобятся два

списка. Один — lstDemo — у нас уже есть, так что создадим второй, назовем

его lstDetail и зададим для него такие же параметры, как у первого списка.

Привязать список к коллекции, которую возвратил запрос с группировкой,

конечно, можно, но бессмысленно — список не сможет получить из нее дан-

ные для формирования пунктов. Поэтому мы используем другой подход —

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

знакомого типа ObservableCollection. А уж списки к этим коллекциям мы

привяжем запросто.

Создадим в классе страницы частные поля colKeys и colValues для хранения

ключей и групп соответственно.

Page 272: Дронов В. Самоучитель Silverlight 3 (2010)

260 Часть IV. Привязка компонентов к данным. LINQ

private System.Collections.ObjectModel.ObservableCollection<string>

�colKeys;

private System.Collections.ObjectModel.ObservableCollection

�<System.Collections.ObjectModel.ObservableCollection<string>> colValues;

Для поля colKeys мы задали строковый тип элементов коллекции. А для поля

colValues мы задали тип элементов как коллекцию со строковыми элемента-

ми. Обратим внимание, как это делается.

И добавим в конструктор класса страницы такой код:

colKeys = new

�System.Collections.ObjectModel.ObservableCollection<string>();

colValues = new System.Collections.ObjectModel.ObservableCollection

�<System.Collections.ObjectModel.ObservableCollection<string>>();

foreach (IGrouping<string, string> dpt in qryDepts)

{

colKeys.Add(dpt.Key);

System.Collections.ObjectModel.ObservableCollection<string>

�lst = new

�System.Collections.ObjectModel.ObservableCollection<string>();

foreach (string prs in dpt)

{

lst.Add(prs);

}

colValues.Add(lst);

}

lstDemo.DataContext = colKeys;

Здесь мы создаем две коллекции и присваиваем указатели на них полям

colKeys и colValues. В коллекцию colKeys мы переносим из результата запро-

са ключи, а в коллекцию colValues — коллекции сгруппированных строк.

Напоследок мы привязываем список lstDemo к коллекции colKeys.

Теперь привяжем к списку lstDemo обработчик события SelectionChanged. Он

будет таким:

lstDetail.DataContext = colValues[lstDemo.SelectedIndex];

Сначала мы получаем номер выбранного в списке lstDemo пункта. Этот номер

является индексом соответствующих данному пункту списка элементов обе-

их коллекций — и colKeys, и colValues. Поэтому мы можем со спокойной

душой использовать его, чтобы получить соответствующий элемент коллек-

ции colValues и привязать к нему список lstDetail.

Теперь можно проверить приложение в деле и полюбоваться на связанные

списки, отображающие результат группировки.

Page 273: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 261

Получение агрегатных данных

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

Самые полезные из этих методов перечислены в табл. 14.1. Все они не при-нимают параметров.

Таблица 14.1. Некоторые методы интерфейса IEnumerable, возвращающие агрегатные данные о коллекции

Метод Описание

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

Count Возвращает общее количество элементов коллекции в виде целого числа

LongCount Возвращает общее количество элементов коллекции в виде длинного целого числа. Применяется в очень больших коллекциях

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

Min Возвращает минимальный из числовых элементов коллекции. Тип воз-вращаемого значения такой же, как и у элементов коллекции

Sum Возвращает сумму числовых элементов коллекции. Тип возвращаемо-го значения такой же, как и у элементов коллекции

IEnumerable<string> qryAggregate = from person in colPersons

where person.Platforms.Contains("Java")

select person.F;

lblAggregate.Text = qryAggregate.Count().ToString();

Здесь мы создаем запрос, выбирающий фамилии тех специалистов, что вла-деют платформой Java. Далее с помощью метода Count получаем количество элементов результирующей коллекции, т. е. количество специалистов — зна-токов Java, преобразуем его в строковый вид вызовом метода ToString и вы-

водим в надписи lblAggregate.

Этот код можно записать вообще в одно выражение:

lblAggregate.Text = (from person in colPersons

where person.Platforms.Contains("Java")

select person.F).Count().ToString();

Page 274: Дронов В. Самоучитель Silverlight 3 (2010)

262 Часть IV. Привязка компонентов к данным. LINQ

Ясен принцип? Мы вызываем метод Count прямо у запроса, заключив его

предварительно в скобки, чтобы он был выполнен в первую очередь, перед

вызовом метода. Так, кстати, часто и делают.

lblAggregate.Text = (from person in colPersons

select person.Platforms.Count()).Max().ToString();

А здесь мы выводим в надписи lblAggregate максимальное количество плат-

форм, которыми владеют наши разработчики.

Использование подзапросов и вложенных запросов. Временные переменные запроса

Язык LINQ невероятно гибок. В числе прочего, он позволяет создавать под-

запросы и вложенные запросы, которые представляют собой дополнительные

запросы, либо добавленные к основному запросу, либо помещенные прямо

в какую-либо из его секций.

Так, запросы, которые добавляются к основному запросу, называются подза-

просами. Мы можем их добавить после секций from, select или group.

Например, рассмотрим такой запрос:

IEnumerable<string> qryPlatforms = from person in colPersons

from platform in person.Platforms

orderby platform

select platform;

Сначала мы выполняем запрос в коллекции colPersons, написав секцию from

person in colPersons, как проделывали много раз до этого. Потом мы выпол-

няем подзапрос — уже во "внутренней" коллекции Platforms каждого эле-

мента коллекции colPersons, написав from platform in person.Platforms.

Таким образом, мы получим все элементы "внутренних" коллекций всех

элементов коллекции colPersons, которые напоследок отсортируем.

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

и той же платформой, мы получим в результате запроса повторяющиеся

строки. Чтобы избежать этого, немного дополним наш запрос.

IEnumerable<string> qryPlatforms = from person in colPersons

from platform in person.Platforms

orderby platform

group platform by platform into plGroup

select plGroup.Key;

Page 275: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 263

Здесь мы группируем названия платформ по ним же самим, получая тем са-

мым элементы с ключами, содержащими названия платформ, и "внутренни-

ми" коллекциями, содержащими строки с названиями платформ. Поскольку

нам нужны только ключи, то мы включаем в результат запроса только их.

Здесь мы впервые столкнулись с временными переменными запроса. Эти

переменные хранят какую-либо единицу данных — элементарное значение

или целый объект, — используемую в запросе, и доступны только внутри за-

проса.

Временная переменная запроса создается с помощью секции into, которая

может присутствовать в конце секции group или select. После ключевого сло-

ва into ставится имя временной переменной. Данная переменная будет хра-

нить:

� в случае секции group — элемент типа IGrouping, содержащий одну группу;

� в случае секции select — элемент, содержащий значение или значения,

указанные в данной секции. Тип этого элемента зависит от типа значений,

указанных в секции select, и может быть как обычным, так и анонимным.

В секции group приведенного ранее запроса мы создали временную перемен-

ную plGroup. Эта переменная будет содержать текущую группу. Впоследст-

вии, в секции select, мы обратились к этой переменной, чтобы получить ключ

группы.

На "закуску" — еще один запрос с подзапросом:

var qryPersons = from person in colPersons

select new { FNN = person.F + " " + person.N1 + " " + person.N2,

�Age = person.Age } into pr

group pr by pr.Age;

Здесь мы выбираем из коллекции colPersons все элементы, формируем на

основе каждого элемента объект анонимного типа со свойствами FNN (фами-

лия, имя и отчество) и Age (возраст) и группируем эти объекты по возрасту.

Мы можем также включить в одну из секций основного запроса другой за-

прос, создав так называемый вложенный запрос.

var qryDepts = from dept in colDepts

select new { deptname = dept.Name,

cnt = (from person in colPersons

where person.DeptID == dept.ID &&

�person.Platforms.Contains("Silverlight")

select person).Count() };

Здесь мы выбираем из коллекции colDepts все элементы, формируем на осно-

ве каждого элемента объект анонимного типа со свойствами deptname (назва-

Page 276: Дронов В. Самоучитель Silverlight 3 (2010)

264 Часть IV. Привязка компонентов к данным. LINQ

ние подразделения) и cnt (количество специалистов, владеющих платформой

Silverlight и работающих в этом подразделении). Значение для свойства

deptname получить нетрудно, а для вычисления значения свойства cnt мы соз-

дали вложенный запрос.

Во вложенном запросе мы выбираем из коллекции colPersons те элементы,

значение свойства DeptID которых равно значению свойства ID текущего эле-

мента коллекции colDepts, а во "внутренней" коллекции Platforms содержит-

ся элемент "Silverlight". И вычисляем количество выбранных таким обра-

зом элементов вызовом метода Count.

Использование временных переменных запроса для хранения произвольных данных

В предыдущем разделе мы узнали о временных переменных запроса, в кото-

рых мы можем сохранить какие-либо единицы данных для дальнейшего ис-

пользования. Но область применения временных переменных на этом не за-

канчивается. Мы можем использовать такую переменную для хранения про-

извольного, заданного нами самими значения.

Временная переменная запроса, хранящая произвольное значение, создается

с помощью секции let и одноименного ключевого слова.

let <имя временной переменной> = <значение>

Как видим, секция let, создающая временную переменную, очень похожа на

аналогичное выражение C#, объявляющее переменную. За одним только ис-

ключением — при создании временной переменной запроса не указывается

ее тип; фактически такие переменные имеют анонимный тип.

Временные переменные запроса хороши в том случае, если нам нужно ис-

пользовать в запросе одно и то же значение, вычисляемое длинным выраже-

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

В этом случае выражение вычисляется всего однажды — при присвоении

временной переменной, — а потом уже просто берется из этой переменной и

используется.

IEnumerable<string> qryPersons = from person in colPersons

let FNN = person.F + " " + person.N1 + " " + person.N2

orderby FNN descending

select FNN;

Здесь мы создаем временную переменную запроса FNN, хранящую строку с фамилией, именем и отчеством текущего специалиста, сортируем элементы

Page 277: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 265

коллекции colPersons по убыванию значений этой переменной и формируем

из них же результирующую коллекцию.

IEnumerable<string> qryDepts = from dept in colDepts

let persons = (from person in colPersons where person.DeptID == dept.ID

�select person)

where persons.Count() > 1

select dept.Name;

Этот запрос выбирает подразделения из коллекции colDepts и для каждого из

них создает временную переменную persons, хранящую целый запрос, точ-

нее, его результат. (Вот, кстати, еще один пример вложенного запроса.) Он выбирает тех специалистов, которые работают в данном подразделении.

Далее мы отбираем только те подразделения, в которых работает больше од-

ного человека, используя при этом значение временной переменной persons.

Открытое связывание данных

Мы уже рассмотрели связывание данных, хранящихся в разных коллекциях.

И вовсю им пользуемся, и вполне довольны.

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

ного человека — никого туда еще не успели зачислить.

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

структор класса DeptCollection, включив в него код, создающий новое под-разделение.

dpt = new Dept();

dpt.ID = 3;

dpt.Name = "Валяй-шаляй-контора";

Add(dpt);

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

Дело в том, что привычное нам связывание с помощью секции join имеет одну специфическую особенность. Оно выбирает только те элементы пер-

вичной коллекции, для которых существует хоть один элемент вторичной коллекции. Иначе говоря, секция join по умолчанию создает закрытое связы-

вание.

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

листов!

Page 278: Дронов В. Самоучитель Silverlight 3 (2010)

266 Часть IV. Привязка компонентов к данным. LINQ

Но начальство ничего не хочет знать о тонкостях языка LINQ, а знай себе

требует списки. Так что нам придется, хочешь не хочешь, а что-то делать...

Нам нужно создать связывание, в котором возвращаются все элементы пер-

вичной коллекции, независимо от того, существуют ли для них элементы

коллекции вторичной. Иначе говоря, открытое связывание.

Вспомним, какой мы использовали запрос для получения сгруппированного

списка подразделений и работающих в них специалистов:

IEnumerable<IGrouping<string, string>> qryDepts = from dept in colDepts

join person in colPersons on dept.ID equals person.DeptID

group person.F + " " + person.N1 + " " + person.N2 by dept.Name;

Нам придется его дополнить следующим образом:

IEnumerable<IGrouping<string, string>> qryDepts = from dept in colDepts

join person in colPersons on dept.ID equals person.DeptID into prGroup

from pr in prGroup.DefaultIfEmpty(new Person())

group pr.F + " " + pr.N1 + " " + pr.N2 by dept.Name;

Прежде всего, мы добавляем в секцию join секцию into, создавая тем самым

группирующее связывание. Это, кстати, еще один способ группировки дан-

ных, но далеко не столь гибкий, как использование секции group.

Как мы видим, группирующее связывание создает для каждого элемента пер-

вичной коллекции группу (фактически — коллекцию) prGroup, содержащую

соответствующие ему элементы вторичной коллекции. Если таковых элемен-

тов нет, создается пустая группа, не содержащая элементов.

Теперь нам нужно просмотреть только что созданную группу и выбрать из

нее элементы. Это выполняется подзапросом, который мы поместили после

секции group, создающей группирующее связывание. Он вызывает метод

DefaultIfEmpty данной группы и помещает его результат в переменную за-

проса pr.

Что делает метод DefaultIfEmpty? Он возвращает очередной элемент группы,

если группа не пуста. А вот если группа пуста, он возвращает значение, пере-

данное ему единственным параметром.

Давайте посмотрим, что получается. Если в текущем подразделении есть

специалисты, они попадут в группу prGroup, и метод DefaultIfEmpty этой

группы вернет их одного за другим в подзапросе. Если же подразделение

"пусто", группа prGroup также окажется пустой, и метод DefaultIfEmpty вер-

нет значение, которое мы указали в качестве его параметра, — пустой объект

класса Person, "пустой" специалист. То есть фактически мы для каждого под-

разделения, в котором не работает никто, создаем вот такого "пустого" спе-

циалиста.

Page 279: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 14. LINQ 267

Напоследок мы выполняем группировку всех специалистов, работающих

в данном подразделении, по названию этого подразделения. Отметим, что мы

извлекаем фамилии, имена и отчества из переменной запроса pr, т. е. из груп-

пы, созданной группирующим связыванием.

Надо сказать, в Silverlight открытое связывание реализовано весьма коряво и неочевидно. В других платформах, в частности языке SQL, с этим делом обсто-ит гораздо лучше.

Запустим готовое приложение. В списке подразделений мы увидим все три

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

список специалистов. Ну да ладно, главное — начальство будет довольно...

Что дальше?

На этом мы закончим рассмотрение языка LINQ и запросов к данным

Silverlight. Желающие знать больше могут обратиться к документации по

Visual Web Developer 2008 — там приведено довольно много примеров

LINQ-запросов, зачастую весьма мудреных.

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

возможностях Silverlight. И начнем мы с того, что изучим возможности рисо-

вания.

Page 280: Дронов В. Самоучитель Silverlight 3 (2010)

268 Часть IV. Привязка компонентов к данным. LINQ

Page 281: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ V

Графические возможности Silverlight.

Многостраничные приложения

Глава 15. Графика

Глава 16. Эффекты и преобразования

Глава 17. Анимация

Глава 18. Многостраничные приложения

Глава 19. Вторичные окна

Page 282: Дронов В. Самоучитель Silverlight 3 (2010)
Page 283: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 15

Графика

В предыдущих главах мы изучали возможности Silverlight по работе с дан-

ными: привязку к ним компонентов и создание запросов на языке LINQ для

их выборки. И написали много C#-кода, так сказать, практикуясь в теории.

Настала пора отдохнуть и посозерцать что-нибудь приятное глазу. А что мо-

жет быть приятнее красивой картинки! Поэтому в данной и последующих

главах мы займемся графическими возможностями Silverlight. А если коро-

че — научимся рисовать на страницах Silverlight-приложений.

И заодно вспомним язык XAML. Давненько мы на нем не писали...

Рисование элементарных геометрических фигур

Нарисовать элементарную геометрическую фигуру — линию, круг или пря-

моугольник — проще простого. Для этого Silverlight предоставляет целый

набор компонентов (и соответствующих им XAML-тегов).

Компонент Line создает на странице прямую линию. Он поддерживает сле-

дующие свойства:

� X1 — горизонтальная координата начальной точки;

� Y1 — вертикальная координата начальной точки;

� X2 — горизонтальная координата конечной точки;

� Y2 — вертикальная координата конечной точки;

� Stroke — цвет линии;

� StrokeThickness — толщина линии в пикселах.

Page 284: Дронов В. Самоучитель Silverlight 3 (2010)

272 Часть V. Графические возможности Silverlight. Многостраничные приложения

Не забываем, что горизонтальная координата (X) отсчитывается от левой границы контейнера, а вертикальная (Y) — от верхней.

Свойства X1, Y1, X2, Y2 и StrokeThickness имеют тип числа с плавающей точ-

кой double. Что касается свойства Stroke, то оно имеет тип класса Brush, за-дающего цвет; в реальности используют один из классов-потомков, задаю-щих одну из разновидностей цвета.

Цвет в XAML-коде обычно задается в виде строки, являющейся названием

цвета в Silverlight. Такими строками могут быть, например, Green (зеленый),

Red (красный), Blue (синий), Black (черный), White (белый) или Transparent (прозрачный, "невидимый" цвет). Подробнее работа с цветом в Silverlight бу-дет рассматриваться в конце этой главы.

<Line X1="100" Y1="100" X2="300" Y2="200" Stroke="Aqua"></Line>

Здесь мы задали для линии ярко-синий цвет (значение Aqua свойства Stroke).

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

На самом деле класс Line поддерживает гораздо больше свойств, позволяю-

щих указать различные параметры линии: ее стиль (будет ли она сплошной, пунктирной или точечной), вид ее концов (будут ли они плоскими или закруг-ленными), вид ее пересечения с другими линиями и др. Все эти свойства опи-саны в документации по Silverlight.

Компонент Rectangle создает на странице прямоугольник. Его свойства пере-числены далее; некоторые из них нам уже знакомы.

� Width — задает ширину прямоугольника.

� Height — задает высоту прямоугольника.

� Stroke — задает цвет линий прямоугольника.

� StrokeThickness — задает толщину линий прямоугольника.

� Fill — задает цвет заливки прямоугольника. Значение по умолчанию —

Transparent, т. е. прозрачный цвет.

� Opacity — задает уровень прозрачности прямоугольника в виде числа от 0 (полная прозрачность) до 1 (полная непрозрачность). Значение по умолча-нию — 1.

� RadiusX — задает радиус закругления углов прямоугольника по горизонта-ли в пикселах. Значение по умолчанию — 0.

Page 285: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 273

� RadiusY — задает радиус закругления углов прямоугольника по вертикали

в пикселах. Значение по умолчанию — 0.

Значения свойств Width, Height, StrokeThickness, Opacity, RadiusX и RadiusY

задаются в виде чисел с плавающей точкой, а значения свойств Stroke и

Fill — в виде объекта одного из потомков класса Brush.

Обычно компонент Rectangle используется в контейнерах "холст" (Canvas). В этом случае мы можем задать координаты его верхнего левого угла с по-

мощью знакомых нам по главе 5 свойств Canvas.Left и Canvas.Top. Первое свойство задает горизонтальную координату, второе — вертикальную; оба

задаются в пикселах в виде чисел с плавающей точкой.

<Canvas>

<Rectangle Canvas.Left="10" Canvas.Top="10" Width="100" Height="200"

�Stroke="Green" StrokeThickness="10" Fill="Transparent"></Rectangle>

</Canvas>

Здесь мы указали для линий прямоугольника зеленый цвет, а для его залив-ки — прозрачный, получив тем самым прямоугольник без заливки. Тот же

самый результат мы можем получить, вообще не указав свойство Fill.

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

поэтому он будет помещен в верхний левый угол контейнера.

<Grid>

<Rectangle Stroke="Blue" Fill="Blue"></Rectangle>

<TextBox Margin="5,5,5,5"></TextBox>

</Grid>

Здесь мы поместили в контейнер "таблица" прямоугольник и поле ввода. По-

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

оно будет наложено на этот прямоугольник. Для усиления эффекта мы задали

для поля ввода отступы от границ контейнера — так поле ввода получит сво-его рода рамку синего цвета.

Компонент Ellipse в очень многом похож на компонент Rectangle, но рисует

на странице эллипс. Он поддерживает свойства Width, Height, Stroke,

StrokeThickness, Fill и Opacity.

<Ellipse Canvas.Left="200" Canvas.Top="150" Stroke="Yellow" Fill="Yellow"

�Width="100" Height="75"></Ellipse>

Здесь мы рисуем желтый (Yellow) эллипс в контейнере "холст".

<Ellipse Canvas.Left="100" Canvas.Top="100" Stroke="Black"

�Width="200" Height="200"></Ellipse>

Page 286: Дронов В. Самоучитель Silverlight 3 (2010)

274 Часть V. Графические возможности Silverlight. Многостраничные приложения

А здесь мы получим на странице круг без заливки, т. е. окружность. Заметим, что для того, чтобы получить круг, мы задали для эллипса одинаковые шири-ну и высоту.

Рисование полигонов

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

Компонент Polygon рисует на странице полигон, состоящий из отрезков пря-мых линий. Если контур такого полигона не замкнут, компонент сам его замкнет, проведя еще один отрезок из конечной точки последнего отрезка в начальную точку первого. Замкнутые участки полигона закрашиваются цве-том, заданным для него в качестве цвета заливки.

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

отрезки, используется свойство Points. Оно имеет тип коллекции

PointCollection и хранит коллекцию структур типа Point, задающих коорди-наты точек, между которыми будут проведены отрезки. В XAML-коде

значение этого свойства задается в виде пар <горизонтальная координата>,

<вертикальная координата>, разделенных пробелами; значения координат ука-зываются в пикселах в виде чисел с плавающей точкой.

Класс Polygon также поддерживает знакомые нам свойства Stroke,

StrokeThickness, Fill и Opacity.

Как и его "коллега" Line, компонент Polygon может быть использован в лю-бом контейнере. И "таблица", и "стопка", и "холст" отобразят его правильно.

<Polygon Points="0,100 100,0 100,200" Stroke="Black"></Polygon>

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

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

<Polygon Points="10,10 10,60 30,60 10,10 10,100 100,100 10,10"

�Stroke="Black" Fill="Blue"></Polygon>

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

Page 287: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 275

Рис. 15.1. Полигон с совпадающими участками, которые не были закрашены

(для свойства FillRule установлено значение EvenOdd)

Мы можем сами задавать правила, согласно которым Silverlight выясняет,

находится ли данная точка внутри замкнутой части контура или вне его

(и, соответственно, должна ли она закрашиваться или нет). Для этого служит

свойство FillRule, имеющее тип перечисления FillRule с элементами EvenOdd

и NonZero.

Если для свойства FillRule установлено значение EvenOdd (это, кстати, значе-

ние по умолчанию), Silverlight "мысленно" проводит из точки луч в любую

сторону и подсчитывает количество отрезков, которые этот луч пересек. Если

оно нечетное, точка находится внутри замкнутого участка, а если четное —

нет.

При установке значения NonZero для свойства FillRule Silverlight использует

более сложный алгоритм. Она также считает количество отрезков, которые

пересек проведенный из точки луч, но при этом также учитывает, в каком

направлении проведен отрезок. Если он проведен слева направо, она добав-

ляет к количеству отрезков единицу, если же отрезок проведен справа нале-

во — отнимает единицу. В случае если получившееся количество не равно

нулю, считается, что точка находится внутри замкнутого участка, в против-

ном случае — вне его.

Мы можем задать для свойства FillRule полигона, формируемого приведен-

ным выше XAML-кодом, значение NonZero и посмотреть, что получится.

А получится то, что показано на рис. 15.2.

Рис. 15.2. Полигон с совпадающими участками, которые были закрашены

(для свойства FillRule установлено значение NonZero)

Page 288: Дронов В. Самоучитель Silverlight 3 (2010)

276 Часть V. Графические возможности Silverlight. Многостраничные приложения

Компонент Polyline также создает на странице полигон, но при этом не дела-

ет контур замкнутым, если мы не замкнули его явно. Он поддерживает те же

свойства, что и компонент Polygon.

<Polyline Points="0,100 100,0 100,200" Stroke="Black"></Polyline>

А этот код нарисует не замкнутый треугольник, а только его неполный кон-

тур из двух линий.

Рисование сложных фигур. Пути

И, наконец, высший пилотаж Silverlight-графики — сложные фигуры, в тер-

минологии этой платформы — пути. Такие пути могут представлять собой

как элементарные (линии, прямоугольники и эллипсы), так и сложные фигу-

ры с контурами, образованными отрезками прямых и кривых линий.

Для рисования сложных контуров используется компонент Path. Его-то мы и

будем рассматривать.

Компонент Path поддерживает свойство Data типа Geometry. Класс Geometry

является родителем всех классов, создающих различные фрагменты пути; мы

будем использовать именно их.

Также класс Path поддерживает свойства Stroke, StrokeThickness, Fill и

Opacity. И может быть использован в любом контейнере.

Рисование путей в виде элементарных фигур

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

используется класс LineGeometry. Он поддерживает свойства StartPoint и

EndPoint, которые задают, соответственно, начальную и конечную точки

прямой. Оба этих свойства имеют тип Point; в XAML-коде их значения

указываются в виде <горизонтальная координата>,<вертикальная координата>;

значения координат указываются в пикселах в виде чисел с плавающей

точкой.

<Path Stroke="Black">

<Path.Data>

<LineGeometry StartPoint="0,150" EndPoint="400,150"></LineGeometry>

</Path.Data>

</Path>

Этот код создает сложный путь из черной горизонтальной линии. (Хотя что

в нем такого сложного...)

Page 289: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 277

Немногим сложнее нарисовать прямоугольный путь. Для этого используется

класс RectangleGeometry.

Он поддерживает следующие свойства:

� Rect — задает координаты и размеры прямоугольника в пикселах;

� RadiusX — задает радиус закругления углов прямоугольника по горизонта-ли в пикселах;

� RadiusY — задает радиус закругления углов прямоугольника по вертикали в пикселах.

Свойства RadiusX и RadiusY имеют тип числа с плавающей точкой. А свойство

Rect принимает значение типа структуры Rect. В XAML-коде его значение задается в следующем формате:

<горизонтальная координата верхнего левого угла>,

�<вертикальная координата верхнего левого угла>,<ширина>,<высота>

<RectangleGeometry Rect="20,20,200,200"

�RadiusX="5" RadiusY="5"></RectangleGeometry>

Этот код создает прямоугольный путь с закругленными углами.

Примерно так же рисуется эллиптический путь. Только используется для это-

го класс EllipseGeometry, поддерживающий такие свойства:

� Center — задает координаты центра эллипса в пикселах;

� RadiusX — задает горизонтальный радиус эллипса в пикселах;

� RadiusY — задает вертикальный радиус эллипса в пикселах.

Свойства RadiusX и RadiusY имеют тип числа с плавающей точкой. Свойство

Center принимает значение типа структуры Point.

<EllipseGeometry Center="200,150"

�RadiusX="100" RadiusY="75"></EllipseGeometry>

Комбинирование элементарных путей. Группы путей

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

нарисовать, используя изученные в начале главы компоненты Line, Rectangle

и Ellipse. Классы простейших путей пригодятся нам, когда мы начнем объединять их в группы, создавая более сложные пути.

Группа путей создается с помощью класса GeometryGroup. Этот класс является

потомком класса Geometry, так что мы можем присвоить объект этого класса

свойству Data компонента Path.

Page 290: Дронов В. Самоучитель Silverlight 3 (2010)

278 Часть V. Графические возможности Silverlight. Многостраничные приложения

Класс GeometryGroup поддерживает свойство Children. Оно имеет тип коллек-

ции GeometryCollection, которая содержит объекты класса Geometry. Это зна-

чит, что мы можем поместить в коллекцию, являющуюся значением свойства

Children, объекты любого из описанных ранее классов. И создать тем самым

сложный путь, комбинирующий линии, прямоугольники и эллипсы.

Еще класс GeometryGroup поддерживает свойство FillRule. Это позволит нам

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

точка внутри контура фигуры или вне его, и, следовательно, нужно ли ее за-

крашивать (заливать).

<Path Stroke="Black" Fill="Blue">

<Path.Data>

<GeometryGroup>

<GeometryGroup.Children>

<GeometryCollection>

<RectangleGeometry Rect="150,0,100,300"></RectangleGeometry>

<RectangleGeometry Rect="0,100,400,100"></RectangleGeometry>

</GeometryCollection>

</GeometryGroup.Children>

</GeometryGroup>

</Path.Data>

</Path>

Этот код рисует два перекрещивающихся прямоугольника. При этом цен-

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

залита.

<Path Stroke="Black" Fill="Blue">

<Path.Data>

<GeometryGroup>

<GeometryGroup.Children>

<GeometryCollection>

<EllipseGeometry Center="200,150" RadiusX="100"

�RadiusY="100"></EllipseGeometry>

<RectangleGeometry Rect="150,100,100,100"></RectangleGeometry>

</GeometryCollection>

</GeometryGroup.Children>

</GeometryGroup>

</Path.Data>

</Path>

А этот код рисует круг с квадратной "дыркой" посередине.

Page 291: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 279

Рисование сложных путей

Настала пора заняться изучением класса PathGeometry, который создает на

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

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

криволинейных частей (сегментов). Это самый сложный и мощный "графи-

ческий" класс Silverlight.

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

Figures этого класса. Оно имеет тип коллекции PathFigureCollection, которая

хранит набор объектов класса PathFigure, описывающих сами фигуры. Также

класс PathGeometry поддерживает свойство FillRule.

Класс PathFigure, в свою очередь, поддерживает свойство Segments, имеющее

тип коллекции PathSegmentCollection; эта коллекция хранит объекты класса

PathSegment, которые описывают сегменты фигуры. На деле применяют клас-

сы-потомки, создающие сегменты различной формы.

Кроме того, класс PathFigure поддерживает свойства, перечисленные далее.

� StartPoint — задает координаты начальной точки фигуры и имеет тип

структуры Point.

� IsClosed — задает или возвращает признак, будет ли контур фигуры авто-

матически замкнут самим компонентом. Если значение этого свойства

равно false (значение по умолчанию), контур фигуры не будет замкнут,

если true — будет замкнут.

� IsFilled — задает или возвращает признак, будут ли замкнутые фрагмен-

ты фигуры залиты; цвет заливки указывается в компоненте Path. Если

равно true (значение по умолчанию), замкнутые фрагменты фигуры будут

залиты, если false — не будут.

Так, с самим сложным путем и составляющими его фигурами все ясно. А что

же сегменты фигур? За их рисование "отвечает" целый набор классов, кото-

рые мы сейчас рассмотрим.

Но сначала давайте усвоим три правила, согласно которым рисуются фигуры

сложного пути.

� Начальная точка фигуры задается в ней самой — в свойстве StartPoint

объекта класса PathFigure.

� Следующий сегмент начинается в конечной точке предыдущего сегмента.

� Фигура может иметь замкнутый или незамкнутый контур. Если контур

замкнут или если в нем существуют замкнутые участки, то они будут за-

литы. Если в объекте класса PathFigure для свойства IsClosed задано зна-

чение true, контур будет замкнут автоматически. Если там же для свойст-

Page 292: Дронов В. Самоучитель Silverlight 3 (2010)

280 Часть V. Графические возможности Silverlight. Многостраничные приложения

ва IsFilled задано значение false, замкнутый контур и замкнутые участки

контура залиты не будут.

Прямолинейный сегмент рисуется с помощью объекта класса LineSegment.

Свойство Point этого класса задает конечную точку прямой в виде значения

типа структуры Point.

<Path Stroke="Black" Fill="Blue">

<Path.Data>

<PathGeometry>

<PathGeometry.Figures>

<PathFigure StartPoint="0,150" IsClosed="True" IsFilled="False">

<PathFigure.Segments>

<LineSegment Point="150,0"></LineSegment>

<LineSegment Point="150,300"></LineSegment>

</PathFigure.Segments>

</PathFigure>

<PathFigure StartPoint="200,150">

<PathFigure.Segments>

<LineSegment Point="350,0"></LineSegment>

<LineSegment Point="350,300"></LineSegment>

<LineSegment Point="200,150"></LineSegment>

</PathFigure.Segments>

</PathFigure>

</PathGeometry.Figures>

</PathGeometry>

</Path.Data>

</Path>

Этот код рисует на странице путь, состоящий из двух треугольников. Для

первого треугольника мы задали автоматическое закрытие контура и отсутст-

вие заливки. Второй треугольник мы замкнули явно, и он будет залит.

Класс PolyLineSegment позволяет нарисовать сегмент в виде полигона, со-

стоящего из прямолинейных отрезков. Он поддерживает свойство Points, за-

дающее точки, между которыми будут проведены отрезки; это свойство ана-

логично одноименному свойству уже известных нам компонентов Polygon и

Polyline.

<Path Stroke="Black" Fill="Blue">

<Path.Data>

<PathGeometry>

<PathGeometry.Figures>

<PathFigure StartPoint="0,150" IsClosed="True" IsFilled="False">

<PathFigure.Segments>

Page 293: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 281

<PolyLineSegment Points="150,0 150,300"></PolyLineSegment>

</PathFigure.Segments>

</PathFigure>

<PathFigure StartPoint="200,150">

<PathFigure.Segments>

<PolyLineSegment Points="350,0 350,300

�200,150"></PolyLineSegment>

</PathFigure.Segments>

</PathFigure>

</PathGeometry.Figures>

</PathGeometry>

</Path.Data>

</Path>

Этот код рисует те же два треугольника, незалитый и залитый.

Класс ArcSegment занимается рисованием эллиптических дуг. Он поддержива-

ет довольно много свойств, которые мы сейчас рассмотрим.

Свойство Point задает конечную точку дуги в виде значения типа структуры

Point.

Свойство Size задает размеры дуги по горизонтали и вертикали. Оно прини-

мает значение типа структуры Size, которое в XAML-коде задается в виде

<размер по горизонтали>,<размер по вертикали>; оба этих значения задаются

в пикселах в виде чисел с плавающей точкой.

Свойство RotationAngle указывает угол, на который будет повернут эллипс,

формирующий рисуемую дугу, в виде числа с плавающей точкой. Значение

по умолчанию — 0.

Свойство SweepDirection указывает направление, по которому должна рисо-

ваться дуга, и имеет тип перечисления SweepDirection с двумя элементами:

� Counterclockwise — дуга рисуется против часовой стрелки (значение по

умолчанию);

� Clockwise — дуга рисуется по часовой стрелке.

Свойство IsLargeArc указывает, должна ли дуга быть больше 180º (значение

true) или меньше (значение false; это значение по умолчанию).

<PathFigure StartPoint="0,0">

<PathFigure.Segments>

<ArcSegment Point="100,100" Size="100,50"></ArcSegment>

<ArcSegment Point="0,0" Size="50,100"></ArcSegment>

</PathFigure.Segments>

</PathFigure>

Page 294: Дронов В. Самоучитель Silverlight 3 (2010)

282 Часть V. Графические возможности Silverlight. Многостраничные приложения

Этот код рисует на странице симпатичное сердечко. Не хватает только прон-

зившей его стрелы...

<PathFigure StartPoint="0,0">

<PathFigure.Segments>

<ArcSegment Point="100,100" Size="100,50"></ArcSegment>

<ArcSegment Point="0,0" Size="100,50"

�RotationAngle="90"></ArcSegment>

</PathFigure.Segments>

</PathFigure>

А этот код тоже рисует сердечко, но немного другими средствами.

Если нам нужно нарисовать кривую Безье (весьма распространенная разно-

видность кривых, часто используемая в графических программах), мы вос-

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

задают формирующие кривую точки и имеют тип структуры Point.

� Point1 — задает координаты первой из контрольных точек кривой. Кон-

трольная точка кривой Безье — это точка, в которой сходятся касатель-

ные, проведенные к ее кривому участку. Или, как написано в документа-

ции по Silverlight, точка, "притягивающая" линию и заставляющая ее ис-

кривляться.

� Point2 — задает координаты второй из контрольной точек кривой.

� Point3 — задает конечную точку кривой.

<PathFigure StartPoint="0,0">

<PathFigure.Segments>

<BezierSegment Point1="50,100" Point2="200,0"

�Point3="100,300"></BezierSegment>

</PathFigure.Segments>

</PathFigure>

Этот код рисует кривую Безье.

Если нам понадобится нарисовать несколько последовательных кривых

Безье, мы воспользуемся классом PolyBezierSegment. Свойство Points этого

класса имеет тип коллекции PointCollection и содержит набор структур типа

Point, задающих координаты точек. В XAML-коде оно задается в виде набора

значений вида <координаты первой контрольной точки>,<координаты второй

контрольной точки>,<координаты конечной точки>, разделенных запятыми; все

эти значения задаются в пикселах в виде чисел с плавающей точкой.

<PathFigure StartPoint="0,0">

<PathFigure.Segments>

Page 295: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 283

<PolyBezierSegment

�Points="50,100,200,0,100,150,50,100,0,200,0,0"></PolyBezierSegment>

</PathFigure.Segments>

</PathFigure>

При взгляде на фигуру, которую рисует этот код, у автора возникает ассо-

циация с древесным листом.

Для рисования квадратичных кривых Безье используется класс

QuadraticBezierSegment. У квадратичных кривых Безье всего одна контроль-

ная точка, координаты которой задаются свойством Point1. А свойство Point2

задает координаты конечной точки кривой. Оба этих свойства имеют тип

структуры Point.

<PathFigure StartPoint="0,0">

<PathFigure.Segments>

<QuadraticBezierSegment Point1="50,100"

�Point2="200,0"></QuadraticBezierSegment>

</PathFigure.Segments>

</PathFigure>

Квадратичная кривая Безье не столь заковыриста, как обычная.

Если же возникнет нужда нарисовать несколько последовательных квадра-

тичных кривых Безье, на помощь придет класс PolyQuadraticBezierSegment.

Свойство Points этого класса имеет тип коллекции PointCollection и содер-

жит набор структур типа Point, задающих координаты точек. В XAML-коде

оно задается в виде набора значений вида <координаты контрольной точ-

ки>,<координаты конечной точки>, разделенных запятыми; эти значения зада-

ются в пикселах в виде чисел с плавающей точкой.

<PathFigure StartPoint="0,0">

<PathFigure.Segments>

<PolyQuadraticBezierSegment

�Points="50,100,50,50,0,200,0,0"></PolyQuadraticBezierSegment>

</PathFigure.Segments>

</PathFigure>

Этот код рисует... в общем, то, что рисует.

<PathFigure StartPoint="100,100">

<PathFigure.Segments>

<LineSegment Point="50,100"></LineSegment>

<ArcSegment Point="50,0" Size="100,50"></ArcSegment>

<BezierSegment Point1="0,0" Point2="100,50"

�Point3="200,300"></BezierSegment>

Page 296: Дронов В. Самоучитель Silverlight 3 (2010)

284 Часть V. Графические возможности Silverlight. Многостраничные приложения

<QuadraticBezierSegment Point1="400,0"

�Point2="0,0"></QuadraticBezierSegment>

</PathFigure.Segments>

</PathFigure>

А этот код объединяет в одной фигуре сегменты всех знакомых нам видов и

рисует что-то совсем уж несусветное.

Мы можем использовать сложные пути в составе групп путей (см. ранее). Хотя,

скорее всего, это не понадобится — возможности класса PathGeometry позво-

ляют построить любую сложную фигуру без привлечения сторонних средств.

Компонент Border

Все рассмотренное нами графическое богатство Silverlight мы будем исполь-

зовать нечасто. Гораздо чаще нам может пригодиться компонент Border, ри-

сующий прямоугольную рамку вокруг какого-либо компонента или контей-

нера с компонентами. Давайте поговорим о нем.

Ключевое свойство класса Border — Child. Оно имеет тип UIElement; это зна-

чит, что мы можем поместить в рамку любой компонент Silverlight.

Другие свойства класса Border перечислены далее.

� BorderBrush — задает цвет рамки компонента в виде значения типа Brush.

� Background — задает цвет фона компонента в виде значения типа Brush.

� BorderThickness — задает толщину рамки в виде значения структуры

Thickness. В XAML-коде оно задается либо в виде <толщина левой стороны рамки>,<толщина верхней стороны рамки>,<толщина правой стороны рамки>,

<толщина нижней стороны рамки>, где все значения должны быть числами

с плавающей точкой, либо в виде одного числа с плавающей точкой —

тогда данное значение будет применено ко всем сторонам рамки. Все эти

значения задаются в пикселах.

� CornerRadius — задает радиус скругления углов прямоугольной рамки

в виде значения структуры CornerRadius. В XAML-коде оно задается либо в

виде <радиус скругления верхнего левого угла рамки>,<радиус скругления верхнего правого угла рамки>,<радиус скругления нижнего правого угла

рамки>,<радиус скругления нижнего левого угла рамки>, где все значения

должны быть числами с плавающей точкой, либо в виде одного числа

с плавающей точкой — тогда данное значение будет применено ко всем

углам рамки. Все эти значения задаются в пикселах.

Page 297: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 285

Класс Border также поддерживает свойства ActualHeight, ActualWidth, Height,

HorizontalAlignment, Margin, MaxHeight, MaxLength, MaxWidth, MinHeight,

MinWidth, Name, Padding, VerticalAlignment, Visibility и Width.

<Border BorderBrush="Black" BorderThickness="3" CornerRadius="5"

�Padding="10,10,10,10">

<data:DataGrid>

. . .

</data:DataGrid>

</Border>

Здесь мы помещаем компонент DataGrid в рамку со скругленными углами.

Работа с цветом

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

другие компоненты имеют множество свойств, "отвечающих" за цвет. Так,

многие компоненты поддерживают свойства Foreground, задающее цвет для

"внутренних" линий компонента (например, текста), Background, задающее

цвет фона, и BorderBrush, указывающее цвет рамки вокруг компонента.

Но как задать цвет?

Здесь Silverlight предлагает нам множество вариантов. Мы можем использо-вать для раскрашивания наших приложений сплошной цвет, можем создать

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

фильм. И сейчас самое время поговорить обо всем этом.

Silverlight для задания цвета предоставляет набор классов; каждый класс "от-

вечает" за свою разновидность цвета. Все эти классы являются потомками

класса Brush.

Сплошные цвета

Проще всего задать для цвета линий или заливки сплошной цвет. Мы уже

знаем, как это делается. В качестве значения соответствующего свойства ука-зываем имя нужного цвета в терминологии Silverlight — и дело сделано!

<Line X1="100" Y1="100" X2="300" Y2="200" Stroke="Aqua"></Line>

Здесь мы имеем дело с так называемыми предопределенными цветами

Silverlight — цветами, которые можно задать просто указанием их имен. Все предопределенные цвета перечислены на странице .NET Framework Class

Library for Silverlight / System.Windows.Media Namespace / SolidColor-Brush Class документации по Silverlight.

Page 298: Дронов В. Самоучитель Silverlight 3 (2010)

286 Часть V. Графические возможности Silverlight. Многостраничные приложения

При разборе откомпилированного XAML-кода среда исполнения Silverlight,

встретив имя предопределенного цвета, создает объект класса

SolidColorBrush, хранящий сведения о заданном сплошном цвете, и присваи-

вает его соответствующему свойству.

Если нам нужно задать произвольный цвет, мы можем создать нужный объ-

ект класса SolidColorBrush "вручную", определив его в XAML-коде.

<Line X1="100" Y1="100" X2="300" Y2="200">

<Line.Stroke>

<SolidColorBrush Color="#984EA5"></SolidColorBrush>

</Line.Stroke>

</Line>

Как видим, для указания самого цвета используется свойство Color класса

SolidColorBrush. Его значение имеет тип структуры Color и в XAML-коде

задается в формате #[<уровень непрозрачности>]<уровень красного><уровень

зеленого><уровень синего>, где все значения задаются в шестнадцатеричном

виде от 00 до FF. Если уровень непрозрачности не указан, цвет будет пол-

ностью непрозрачным. Так, ранее мы указали для линии непрозрачный сире-

невый цвет.

Кроме того, класс SolidColorBrush поддерживает свойство Opacity, с по-

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

ства должно иметь тип числа с плавающей точкой от 0 (цвет полностью про-

зрачен) до 1 (цвет полностью непрозрачен).

<SolidColorBrush Color="#984EA5" Opacity="0.5"></SolidColorBrush>

Градиентные цвета

Градиентные цвета, или просто градиенты, будучи применены к месту, вы-

глядят эффектнее сплошных. Они представляют собой совокупности цветов,

плавно перетекающих друг в друга. В качестве примера градиентного цвета

можно привести цвет заголовков окон в Windows 2000 и в стандартной теме

Windows XP и Windows Vista.

Градиентные цвета бывают двух видов. Линейные градиентные цвета "рас-

пространяются" по прямой линии, горизонтальной, вертикальной или диаго-

нальной. Пример линейного градиентного цвета можно увидеть на рис. 15.3.

Линейный градиентный цвет создается с помощью объекта класса

LinearGradientBrush. Этот класс поддерживает несколько свойств, самые по-

лезные из которых мы сейчас рассмотрим.

Прежде всего, нам нужно задать размеры градиентного цвета относительно

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

Page 299: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 287

StartPoint (начальная точка градиента) и EndPoint (конечная точка градиен-

та), значения которых имеют тип структуры Point. Как мы уже знаем, значе-

ния такого типа в XAML-коде задаются в виде <горизонтальная координата>,

<вертикальная координата>, где значения координат указываются в виде чисел

с плавающей точкой.

Рис. 15.3. Линейный градиентный цвет

А теперь — внимание! Для задания размеров линейного градиента использу-

ется относительная система координат, действующая внутри закрашиваемой

данным градиентом фигуры. Начало координат этой системы — точка

[0,0] — находится в верхнем левом углу фигуры, а конец — точка [1,1] —

в ее нижнем правом углу. Если же нам нужно указать координаты точки, рас-

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

ные числа от 0 до 1. Так, точка [0,5,0,5] обозначает центр фигуры.

<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">

</LinearGradientBrush>

Здесь мы задаем начало градиента в точке с координатами [0,0] и конец —

в точке [1,0]. Это значит, что градиент будет "распространяться" по горизон-

тальной линии (как на рис. 15.3) от начала до конца фигуры.

<LinearGradientBrush StartPoint="0,0" EndPoint="0,0.5">

</LinearGradientBrush>

Page 300: Дронов В. Самоучитель Silverlight 3 (2010)

288 Часть V. Графические возможности Silverlight. Многостраничные приложения

А этот градиент будет "распространяться" по вертикальной линии до середи-

ны фигуры. (Каким цветом будет залита остальная часть фигуры, не "охва-

ченная" градиентом, мы узнаем потом.)

<LinearGradientBrush StartPoint="0,1" EndPoint="1,0">

</LinearGradientBrush>

Что касается этого градиента, то он "распространится" по диагональной

линии, идущей через всю фигуру от ее нижнего левого угла до верхнего пра-

вого.

Теперь следует задать цвета, составляющие градиент. Для этого мы исполь-

зуем свойство GradientStops, значение которого имеет тип коллекции

GradientStopCollection, хранящей объекты класса GradientStop. Вот как раз

объекты класса GradientStop и задают ключевые точки градиента — точки,

в которых один цвет перетекает в другой, — и соответствующие им цвета.

Класс GradientStop поддерживает два ключевых свойства, перечисленные

далее.

� Color — задает цвет в данной ключевой точке и имеет тип Color.

� Offset — задает местоположение ключевой точки на воображаемой ли-

нии, по которой "распространяется" градиент, в виде числа с плавающей

точкой от 0 (начало линии) до 1 (конец линии).

<Rectangle Width="400" Height="300">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">

<LinearGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="Black" Offset="0"></GradientStop>

<GradientStop Color="White" Offset="0.5"></GradientStop>

<GradientStop Color="Blue" Offset="1"></GradientStop>

</GradientStopCollection>

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

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

занный на рис. 15.3. Для градиента мы задаем "распространение" по горизон-

тальной линии от начала до конца фигуры и три ключевые точки. Первая

ключевая точка располагается в начале градиента (координата 0) и имеет

черный цвет, вторая — посередине градиента (координата 0,5) и имеет белый

цвет, третья — в конце (координата 1) и имеет синий цвет.

Page 301: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 289

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

Также класс LinearGradientBrush поддерживает свойство Opacity, позволяю-щее задать прозрачность всего градиента.

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

указать Silverlight три варианта закраски с помощью свойства SpreadMethod

класса LinearGradientBrush. Это свойство имеет тип перечисления

GradientSpreadMethod с тремя элементами:

� Pad — оставшаяся часть фигуры будет закрашена цветом, указанным в по-следней ключевой точке градиента (значение по умолчанию);

� Reflect — оставшаяся часть фигуры будет закрашена градиентом, полу-ченным зеркальным отображением заданного;

� Repeat — оставшаяся часть фигуры будет закрашена тем же градиентом без изменений.

<LinearGradientBrush StartPoint="0,0" EndPoint="0,0.5"

�SpreadMethod="Reflect">

. . .

</LinearGradientBrush>

В отличие от линейного, радиальный градиент "распространяется" по окружности от заданного центра до границ фигуры (рис. 15.4). Чем-то он по-хож на круги, которые разбегаются по воде от брошенного в нее камня.

Радиальный градиентный цвет создается с помощью объекта класса

RadialGradientBrush.

Для задания размеров радиального градиента используется все та же относи-тельная система координат, в которой точка [0,0] находится в верхнем левом углу закрашиваемой им фигуры, а точка [1,1] — в ее нижнем правом углу. Только размеров здесь уже четыре, и задают их четыре свойства класса

RadialGradientBrush, перечисленные далее.

� GradientOrigin — задает координаты точки, в которой начинается гради-

ент, в виде значения типа Point;

� Center — задает координаты центра воображаемого круга, на котором за-

канчивается градиент, в виде значения типа Point;

� RadiusX — задает радиус воображаемого круга, на котором заканчивается градиент, по горизонтали в виде числа с плавающей точкой;

� RadiusY — задает радиус воображаемого круга, на котором заканчивается градиент, по вертикали в виде числа с плавающей точкой.

Page 302: Дронов В. Самоучитель Silverlight 3 (2010)

290 Часть V. Графические возможности Silverlight. Многостраничные приложения

Рис. 15.4. Радиальный градиентный цвет

Для задания ключевых точек радиального градиента и соответствую-

щих им цветов также используется свойство GradientStops. Еще класс

RadialGradientBrush поддерживает свойства Opacity и SpreadMethod.

<Rectangle Width="400" Height="300">

<Rectangle.Fill>

<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5"

�RadiusX="0.5" RadiusY="0.5">

<RadialGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="Black" Offset="0"></GradientStop>

<GradientStop Color="White" Offset="0.5"></GradientStop>

<GradientStop Color="Blue" Offset="1"></GradientStop>

</GradientStopCollection>

</RadialGradientBrush.GradientStops>

</RadialGradientBrush>

</Rectangle.Fill>

</Rectangle>

Этот код создает прямоугольник с радиальной градиентной заливкой, пока-

занный на рис. 15.4.

Page 303: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 291

<Rectangle Width="400" Height="300">

<Rectangle.Fill>

<RadialGradientBrush GradientOrigin="0.75,0.25" Center="0.5,0.5"

�RadiusX="0.5" RadiusY="0.5">

<RadialGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="White" Offset="0"></GradientStop>

<GradientStop Color="Black" Offset="0.5"></GradientStop>

</GradientStopCollection>

</RadialGradientBrush.GradientStops>

</RadialGradientBrush>

</Rectangle.Fill>

</Rectangle>

А этот код создает прямоугольник, залитый радиальным градиентом со сме-щенным к верхнему правому углу центром (рис. 15.5). Такое впечатление, что наш прямоугольник освещен сбоку фонариком.

Рис. 15.5. Радиальный градиентный цвет со смещенным центром

Графические цвета

Графический цвет использует для закраски фигуры изображение, являющее-ся ресурсом сборки. (О ресурсах сборки шла речь в главе 8.)

Page 304: Дронов В. Самоучитель Silverlight 3 (2010)

292 Часть V. Графические возможности Silverlight. Многостраничные приложения

Для создания графического цвета применяется класс ImageBrush. Свойство

ImageSource задает изображение, которое станет графическим цветом; в каче-

стве значения этого свойства указывается имя графического файла — ресурса

сборки.

<Rectangle Width="400" Height="300">

<Rectangle.Fill>

<ImageBrush ImageSource="image.jpg"></ImageBrush>

</Rectangle.Fill>

</Rectangle>

Свойство AlignmentX позволяет указать, по какому краю закрашиваемой гра-

фическим цветом фигуры должно горизонтально выравниваться изображе-

ние, которое, собственно, и станет графическим цветом. Его значение имеет

тип перечисления AlignmentX с тремя элементами:

� Left — выравнивается по левому краю;

� Right — выравнивается по правому краю;

� Center — выравнивается по центру (значение по умолчанию).

Свойство AlignmentY задает выравнивание изображения по вертикали. Его

значение имеет тип перечисления AlignmentY с тремя элементами:

� Top — выравнивается по верхнему краю;

� Bottom — выравнивается по нижнему краю;

� Center — выравнивается по центру (значение по умолчанию).

Свойство Stretch указывает, как изображение должно заполнять фигуру. Его

значение имеет тип перечисления Stretch с четырьмя элементами:

� None — изображение не изменяется в размерах. При этом фигура может

оказаться не закрашенной полностью;

� Fill — изображение изменяется в размерах так, чтобы заполнить всю фи-

гуру. При этом оригинальные пропорции изображения могут быть нару-

шены (значение по умолчанию);

� Uniform — изображение изменяется в размерах так, чтобы заполнить фи-

гуру, без нарушения ее пропорций. Однако фигура может оказаться не за-

крашенной полностью;

� UniformToFill — изображение изменяется в размерах так, чтобы заполнить

всю фигуру, без нарушения ее пропорций. Однако изображение может

быть обрезано.

Page 305: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 15. Графика 293

Также класс ImageBrush поддерживает свойство Opacity.

<Rectangle Width="600" Height="800" Stroke="Black">

<Rectangle.Fill>

<ImageBrush ImageSource="image.jpg" AlignmentX="Right"

�Stretch="None"></ImageBrush>

</Rectangle.Fill>

</Rectangle>

Видеоцвет

И, пожалуй, одна из самых эффектных графических возможностей Silver-

light — использование фильма в качестве цвета (видеоцвет). Этот фильм

также должен быть ресурсом сборки.

Не забываем, что для того чтобы видеоролик был успешно выведен на страни-цу, нам нужно будет либо сделать его включенным ресурсом, либо указать Visual Web Developer 2008, чтобы он при каждой компиляции проекта копировал его в папку, где хранится откомпилированное приложение. Как это сделать, бы-ло описано в главах 7 и 8.

Прежде чем использовать фильм в качестве видеоцвета, нам придется создать

на странице компонент MediaElement, в который загрузить этот фильм.

(О компоненте MediaElement было подробно рассказано в главе 7.) Также мы

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

ный 0, чтобы он не был виден на странице.

<MediaElement x:Name="melFill" Source="video.wmv"

�Opacity="0"></MediaElement>

Видеоцвет создается с помощью объекта класса VideoBrush. Этот класс под-

держивает свойство SourceName, с помощью которого задается имя компонен-

та MediaElement, откуда будет взят фильм для создания видеоцвета.

<Rectangle Width="400" Height="300" Stroke="Black">

<Rectangle.Fill>

<VideoBrush SourceName="melFill"></VideoBrush>

</Rectangle.Fill>

</Rectangle>

Также класс VideoBrush поддерживает свойства AlignmentX, AlignmentY,

Opacity и Stretch.

Page 306: Дронов В. Самоучитель Silverlight 3 (2010)

294 Часть V. Графические возможности Silverlight. Многостраничные приложения

Цвета как ресурсы страницы и приложения

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

использовать его на странице сразу в нескольких компонентах. Но писать

один и тот же XAML-код, создающий этот цвет, для каждого свойства каж-

дого компонента нам не хочется. Как быть?

Мы можем поместить данный цвет в ресурсы страницы или приложения, а

потом просто сослаться на него, применив расширенную запись. (Подробнее

о ресурсах страниц и приложения и их использовании было рассказано в гла-

ве 13.) Причем ссылаться на этот ресурс мы можем сколько угодно раз.

<UserControl.Resources>

<LinearGradientBrush x:Key="lgbFill" StartPoint="0,0" EndPoint="1,0">

<LinearGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="Black" Offset="0"></GradientStop>

<GradientStop Color="White" Offset="0.5"></GradientStop>

<GradientStop Color="Blue" Offset="1"></GradientStop>

</GradientStopCollection>

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</UserControl.Resources>

На забудем задать для объекта, который станет ресурсом, ключ. Впоследст-

вии по этому ключу мы сможем его найти.

<Rectangle Width="400" Height="300" Stroke="{StaticResource lgbFill}"

�Fill="{StaticResource lgbFill}"></Rectangle>

Здесь мы использовали созданный ранее цвет-ресурс для закраски и контура,

и заливки прямоугольника.

Вот, пожалуй, и все о цветах. Да и о графике тоже.

Что дальше?

В этой главе мы изучили графические возможности Silverlight. Конечно, соз-

давать трехмерные игры на этой платформе вряд ли получится, но оформить

приложение вполне можно. Главное — соблюсти меру.

Но разговор о графических средствах Silverlight еще не закончен. Мы можем

создавать графические эффекты и преобразования, с помощью которых мож-

но заметно оживить интерфейс приложений. Вот о них-то и пойдет речь

в следующей главе.

Page 307: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 16

Эффекты и преобразования

В предыдущей главе мы изучали возможности Silverlight по рисованию на

страницах. Возможности эти не очень богаты, но все равно впечатляют.

В этой главе мы продолжим разговор о Silverlight-графике. И изучим воз-

можности по созданию эффектов и преобразований, которые при должном

применении могут заметно оживить интерфейс наших приложений. Причем

эти эффекты и преобразования мы можем применить к любому компоненту

Silverlight.

Эффекты

Эффектом в терминологии Silverlight называется действие над компонентом,

изменяющее его внешний вид, но не затрагивающее его местоположение.

Компонент остается на своем месте, но выглядит уже несколько по-другому.

Эффектов в Silverlight предусмотрено немного — всего два. Плюс к эффек-

там можно отнести возможность обрезки компонента по заданной форме и

задание для него маски прозрачности. Вот с обрезки-то мы и начнем.

Обрезка компонента

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

этого используется свойство Clip, поддерживаемое абсолютно всеми класса-

ми компонентов.

Свойство Clip имеет тип Geometry. Ему присваивается объект любого класса,

являющегося потомком класса Geometry, т. е. любой путь, за исключением

прямой линии (класс LineGeometry), или группа путей. Все эти классы и их

использование мы изучили в главе 15.

Page 308: Дронов В. Самоучитель Silverlight 3 (2010)

296 Часть V. Графические возможности Silverlight. Многостраничные приложения

Участки компонента, которые находятся внутри заданного в качестве формы

обрезки пути, будут показаны на странице. Участки компонента, не попа-

дающие внутрь пути, показаны не будут.

<Image Source="image.jpg">

<Image.Clip>

<EllipseGeometry Center="150,200" RadiusX="150"

�RadiusY="200"></EllipseGeometry>

</Image.Clip>

</Image>

Здесь мы задаем для изображения эллиптическую форму обрезки. На страни-

це будет выведена только та часть изображения, что попадет в этот эллипс

(рис. 16.1).

Рис. 16.1. Результат обрезки изображения по эллиптической форме

Маска полупрозрачности

Но значительно эффектнее выглядит компонент с заданной для него маской

прозрачности. Маска прозрачности определяет уровень прозрачности раз-

личных частей компонентов, позволяя сделать некоторые из частей полупро-

зрачными, а некоторые — полностью прозрачными, т. е. невидимыми.

Page 309: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 16. Эффекты и преобразования 297

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

Для задания маски полупрозрачности используется свойство OpacityMask,

поддерживаемое всеми классами компонентов. Оно имеет тип Brush.

<Image Source="image.jpg">

<Image.OpacityMask>

<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5"

�RadiusX="0.5" RadiusY="0.5">

<RadialGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="#FF000000" Offset="0"></GradientStop>

<GradientStop Color="#99000000" Offset="0.75"></GradientStop>

<GradientStop Color="#00000000" Offset="1"></GradientStop>

</GradientStopCollection>

</RadialGradientBrush.GradientStops>

</RadialGradientBrush>

</Image.OpacityMask>

</Image>

Этот код создает на странице симпатичную виньетку (рис. 16.2).

Рис. 16.2. Результат применения к изображению маски прозрачности

в виде радиального градиента

Page 310: Дронов В. Самоучитель Silverlight 3 (2010)

298 Часть V. Графические возможности Silverlight. Многостраничные приложения

Настоящие эффекты — размытие и тень

Теперь рассмотрим создание настоящих эффектов. Как уже говорилось, их

предусмотрено всего два: размытие и тень.

Для задания эффекта используется свойство Effect, поддерживаемое всеми

классами компонентов. Оно имеет тип класса Effect; в реальности же ис-

пользуются его классы-потомки, задающие различные эффекты.

Эффект размытия создается с помощью объекта класса BlurEffect. Этот

класс поддерживает свойство Radius, задающее радиус размытия в пикселах

в виде числа с плавающей точкой; значение по умолчанию этого свойст-

ва — 5.

<TextBlock Text="Эффект!" FontSize="36">

<TextBlock.Effect>

<BlurEffect></BlurEffect>

</TextBlock.Effect>

</TextBlock>

Этот код создает надпись с эффектом размытия (рис. 16.3).

Рис. 16.3. Надпись с эффектом размытия

Для создания тени используется объект класса DropShadowEffect. Данный

класс поддерживает свойства, перечисленные ниже.

� BlurRadius — задает радиус размытости тени в пикселах в виде числа

с плавающей точкой. Значение по умолчанию — 5.

� Color — задает цвет тени в виде значения типа структуры Color. Значение

по умолчанию — #FF000000 (тень имеет черный цвет и непрозрачна).

� Direction — задает угол падения тени в градусах в виде числа с плаваю-

щей точкой. Значение по умолчанию — 315º.

� Opacity — задает уровень прозрачности тени в виде числа с плавающей

точкой от 0 (тень полностью прозрачна) до 1 (тень полностью непрозрач-

на; это значение по умолчанию).

� ShadowDepth — задает расстояние от компонента до тени в пикселах в виде

числа с плавающей точкой. Значение по умолчанию — 5.

Page 311: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 16. Эффекты и преобразования 299

<TextBlock Text="Эффект!" FontSize="36">

<TextBlock.Effect>

<DropShadowEffect Color="#FFCCCCCC"

�ShadowDepth="10"></DropShadowEffect>

</TextBlock.Effect>

</TextBlock>

Этот код создает надпись с тенью (рис. 16.4).

Рис. 16.4. Надпись с тенью

Преобразования

Преобразование же, в отличие от эффекта, затрагивает местоположение ком-

понента, а также, возможно, его внешний вид. То есть компонент в результа-

те преобразования обязательно сместится.

Преобразований в Silverlight предусмотрено значительно больше, чем эффек-

тов. Они делятся на две группы: двумерные и трехмерные.

Двумерные преобразования

Двумерные преобразования выполняют смещение, поворот, сдвиг или мас-

штабирование компонента. Это самые простые преобразования.

Двумерные преобразования могут быть привязаны к компоненту, пути (объ-

екту класса Geometry) или цвету (объекту класса Brush).

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

RenderTransform.

� Для привязки преобразования к пути используется свойство Transform.

� Для привязки преобразования к цвету используются свойства Transform

или RelativeTransform. В первом случае преобразование выполняется в

обычной системе координат, когда для указания параметров преобразова-

ния используются пикселы, а во втором — в относительной системе коор-

динат, той самой, что применяется для указания местоположения ключе-

вых точек градиентов (подробнее — в главе 15).

Page 312: Дронов В. Самоучитель Silverlight 3 (2010)

300 Часть V. Графические возможности Silverlight. Многостраничные приложения

Все эти свойства имеют тип класса Transform. На деле же применяются его

классы-потомки, создающие конкретные преобразования.

Самое простое преобразование — смещение компонента (пути, цвета) отно-

сительно точки, в которой он должен находиться изначально. Для этого ис-

пользуется объект класса TranslateTransform. Этот класс поддерживает свой-

ства X и Y, задающие величину сдвига компонента по горизонтальной и вер-

тикальной оси соответственно в виде чисел с плавающей точкой.

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

и вниз, отрицательные — влево и вверх соответственно.

<TextBlock Text="Преобразование!" FontSize="36">

<TextBlock.RenderTransform>

<TranslateTransform X="2" Y="2"></TranslateTransform>

</TextBlock.RenderTransform>

</TextBlock>

<TextBlock Text="Преобразование!" FontSize="36"

�Foreground="Aqua"></TextBlock>

Этот код создает ярко-голубую надпись с черной тенью. Сначала создается

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

ния смещается вниз и вправо на 2 пиксела. Далее создается ярко-голубая

надпись; поскольку она создана позже, то будет наложена на черную

надпись.

<Rectangle Width="400" Height="300">

<Rectangle.Fill>

<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5"

�RadiusX="0.5" RadiusY="0.5">

<RadialGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="Black" Offset="0"></GradientStop>

<GradientStop Color="White" Offset="0.5"></GradientStop>

<GradientStop Color="Blue" Offset="1"></GradientStop>

</GradientStopCollection>

</RadialGradientBrush.GradientStops>

<RadialGradientBrush.RelativeTransform>

<TranslateTransform X="0.25" Y="-0.25"></TranslateTransform>

</RadialGradientBrush.RelativeTransform>

</RadialGradientBrush>

</Rectangle.Fill>

</Rectangle>

А этот код создает прямоугольник, закрашенный радиальным градиентом,

который мы сместили вправо и вверх с помощью преобразования смещения.

Page 313: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 16. Эффекты и преобразования 301

Обратим внимание, что, т. к. мы задали это преобразование с помощью свой-

ства RelativeTransform, то должны для задания его параметров использовать

относительные координаты.

Объект класса ScaleTransform позволяет масштабировать компонент. Он под-

держивает свойства, перечисленные далее.

� ScaleX и ScaleY — задают масштаб, соответственно, по горизонтальной и

вертикальной оси в виде чисел с плавающей точкой. Значения по умолча-

нию — 1 (т. е. отсутствие масштабирования).

� CenterX и CenterY — задают горизонтальную и вертикальную координату

базовой точки масштабирования — той самой, из которой будет "расши-

ряться" или в которую будет "сжиматься" компонент в результате масшта-

бирования. Эти координаты задаются в координатной системе самого

компонента в виде чисел с плавающей точкой. Значения по умолча-

нию — 0; это значит, что базовой будет точка [0,0] — верхний левый угол

компонента.

<TextBlock Text="Преобразование!" FontSize="36">

<TextBlock.RenderTransform>

<ScaleTransform CenterX="200" ScaleX="0.5"

�ScaleY="3"></ScaleTransform>

</TextBlock.RenderTransform>

</TextBlock>

Этот код растягивает нашу многострадальную надпись по вертикали втрое и

одновременно сжимает по горизонтали наполовину. Отметим, что в качестве

базовой мы задали точку с координатами [200,0] — это примерно середина

верхней границы надписи; именно в эту точку надпись будет "сжиматься" по

горизонтали и из нее будет "расширяться" по вертикали.

Если нам понадобится повернуть компонент на определенный угол, мы вос-

пользуемся классом RotateTransform. Он поддерживает три полезных для нас

свойства, перечисленные далее.

� Angle — задает угол поворота в градусах в виде числа с плавающей точ-

кой. Положительные значения задают поворот по часовой стрелке, отри-

цательные — против часовой стрелки. Значение по умолчанию — 0.

� CenterX и CenterY — задают горизонтальную и вертикальную координату

точки, вокруг которой будет поворачиваться компонент. Эти координаты

задаются в координатной системе самого компонента в виде чисел с пла-

вающей точкой. Значения по умолчанию — 0; это значит, что центром по-

ворота будет точка [0,0] — верхний левый угол компонента.

Page 314: Дронов В. Самоучитель Silverlight 3 (2010)

302 Часть V. Графические возможности Silverlight. Многостраничные приложения

<TextBlock Text="Преобразование!" FontSize="36">

<TextBlock.RenderTransform>

<RotateTransform Angle="45"></RotateTransform>

</TextBlock.RenderTransform>

</TextBlock>

Крутится-вертится наш компонент...

Класс SkewTransform используется, если нужно выполнить сдвиг компонента.

Полезных свойств у него четыре.

� AngleX и AngleY — задают углы сдвига в горизонтальной и вертикальной

плоскости в градусах в виде чисел с плавающей точкой. Положительные

значения задают сдвиг против часовой стрелки, отрицательные — по ча-

совой стрелке. Значения по умолчанию — 0.

� CenterX и CenterY — задают горизонтальную и вертикальную координату

точки, относительно которой будет сдвигаться компонент. Эти координа-

ты задаются в координатной системе самого компонента в виде чисел

с плавающей точкой. Значения по умолчанию — 0; это значит, что цент-

ром сдвига будет точка [0,0] — верхний левый угол компонента.

<TextBlock Text="Преобразование!" FontSize="36">

<TextBlock.RenderTransform>

<SkewTransform AngleX="-45"></SkewTransform>

</TextBlock.RenderTransform>

</TextBlock>

Этот код превращает надпись в подобие курсива.

<Rectangle Width="400" Height="300">

<Rectangle.Fill>

<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5"

�RadiusX="0.5" RadiusY="0.5">

<RadialGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="Black" Offset="0"></GradientStop>

<GradientStop Color="White" Offset="0.5"></GradientStop>

<GradientStop Color="Blue" Offset="1"></GradientStop>

</GradientStopCollection>

</RadialGradientBrush.GradientStops>

<RadialGradientBrush.RelativeTransform>

<SkewTransform CenterX="0.5" CenterY="0.5"

�AngleX="45"></SkewTransform>

</RadialGradientBrush.RelativeTransform>

</RadialGradientBrush>

Page 315: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 16. Эффекты и преобразования 303

</Rectangle.Fill>

</Rectangle>

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

что-то прямо космическое...

Комбинирование двумерных преобразований. Группы преобразований

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

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

Группа преобразований создается с помощью класса TransformGroup. Этот

класс является потомком класса Transform, так что мы можем применить группу преобразований к любому компоненту, пути или цвету так же, как и

обычное преобразование.

Класс TransformGroup поддерживает свойство Children. Оно имеет тип кол-

лекции TransformCollection, которая содержит объекты класса Transform. Это значит, что мы можем поместить в коллекцию, являющуюся значением свой-

ства Children, объекты любого из описанных ранее классов преобразований. И создать тем самым сложное преобразование, включающее смещение, мас-штабирование, сдвиг и поворот.

Преобразования, объединенные в группу, выполняются в том порядке, в ка-

ком они определены в XAML-коде.

<TextBlock Text="Преобразование!" FontSize="36">

<TextBlock.RenderTransform>

<TransformGroup>

<TransformGroup.Children>

<TransformCollection>

<RotateTransform Angle="-90"></RotateTransform>

<TranslateTransform Y="400"></TranslateTransform>

</TransformCollection>

</TransformGroup.Children>

</TransformGroup>

</TextBlock.RenderTransform>

</TextBlock>

Здесь мы сначала поворачиваем надпись на 90º против часовой стрелки, а по-

том смещаем ее вниз на 400 пикселов (это примерно равно ширине надписи).

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

вой границы контейнера.

Page 316: Дронов В. Самоучитель Silverlight 3 (2010)

304 Часть V. Графические возможности Silverlight. Многостраничные приложения

Трехмерные преобразования

А еще Silverlight позволяет выполнять преобразования в трехмерном про-

странстве. Это, кстати, нововведение Silverlight 3; в предыдущих версиях

данной платформы такого не было.

Трехмерные преобразования можно применять только к компонентам. Для

этого используется свойство Projection, имеющее тип класса Projection. На

деле же используют его классы-потомки, создающие конкретные преобразо-

вания.

Мы рассмотрим всего один класс трехмерного преобразования —

PlaneProjection. Он позволяет повернуть и сместить компонент по одной из

координатных осей.

Класс PlaneProjection поддерживает довольно много свойств, которые мы

сейчас рассмотрим.

� RotationX, RotationY и RotationZ — задают угол поворота компонента

вдоль осей X, Y и Z соответственно в градусах в виде чисел с плавающей

точкой. Положительные значения задают поворот по часовой стрелке, от-

рицательные — против часовой стрелки. Значения по умолчанию — 0.

� CenterOfRotationX, CenterOfRotationY и CenterOfRotationZ — задают коор-

динаты точки, вокруг которой будет поворачиваться компонент, по осям

X, Y и Z. Эти координаты задаются в относительной координатной систе-

ме компонента в виде чисел с плавающей точкой от 0 (начало координат)

до 1 (размер компонента вдоль соответствующей оси). Значения по умол-

чанию — 0,5, 0,5 и 0 соответственно; это значит, что центром поворота

будет точка [0,5,0,5,0] — центр компонента на двумерной плоскости.

� LocalOffsetX, LocalOffsetY и LocalOffsetZ — указывают смещения компо-

нента по осям X, Y и Z. Эти смещения задаются в системе координат ком-

понента, которая поворачивается вместе с компонентом при его вращении,

в пикселах в виде чисел с плавающей точкой. Значения по умолча-

нию — 0.

� GlobalOffsetX, GlobalOffsetY и GlobalOffsetZ — указывают смещения

компонента по осям X, Y и Z. Эти смещения задаются в системе коорди-

нат экрана, которая не поворачивается вместе с компонентом при его вра-

щении, в пикселах в виде чисел с плавающей точкой. Значения по умолча-

нию — 0.

<Image Source="image.jpg">

<Image.Projection>

<PlaneProjection RotationY="-30" RotationZ="20"

�GlobalOffsetZ="-30"></PlaneProjection>

Page 317: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 16. Эффекты и преобразования 305

</Image.Projection>

</Image>

Этот код поворачивает изображение на 30º против часовой стрелки по оси Y, на 20º по часовой стрелке по оси Z и смещает по оси Z системы координат

экрана "вниз" на 30 пикселов. Результат всего этого можно увидеть на рис. 16.5. Похоже на фотографию, которую несет ветер...

Рис. 16.5. Результат трехмерных преобразований изображения

В принципе, на этом можно закончить и об эффектах, и о преобразованиях.

Но разговор о графике еще будет продолжен.

Что дальше?

В этой главе мы изучили эффекты и преобразования Silverlight. Научились

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

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

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

Об этом разговор пойдет в следующей главе.

Page 318: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 17

Анимация

В прошлой главе мы продолжили рассмотрение графических возможностей платформы Silverlight, занимаясь эффектами и преобразованиями. Эффекты позволяют нам изменить внешний вид компонентов, а преобразование — и внешний вид, и местоположение. Зачастую получается весьма симпатично, не находите?

Осталось сделать последний шаг в Silverlight-графику, рассмотрев возмож- ности по созданию анимации. Да-да, Silverlight позволит нам "вдохнуть" в компоненты "жизнь"! И сделать это очень просто — прямо в XAML-коде!

Основные понятия Silverlight-анимации

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

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

Анимация бывает двух видов, принципиально отличающихся способами ее создания. Сейчас мы их рассмотрим.

Проще всего создается трансформационная анимация. Мы просто задаем начальное и конечное значения нужного нам свойства компонента и время, в течение которого будет выполняться анимация (продолжительность ани-мации), а среда исполнения Silverlight делает все остальное.

Сначала она вычисляет, на какую величину (приращение) следует изменить

значение указанного нами свойства, чтобы оно достигло конечного значения

Page 319: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 307

за указанное нами время. Потом, когда наступает очередной момент изме-

нить значение свойства (его легкомысленно называют тиком), она прибавля-

ет полученное приращение к этому значению и, если время, отпущенное на-

ми на анимацию, еще не истекло, ждет следующего тика. И так до тех пор,

пока время не истечет — тогда анимация закончится.

Трансформационная анимация подходит для реализации самых простых дей-

ствий над компонентом: перемещения его по прямой линии, плавного исчез-

новения и проявления и пр. Если же нам нужно сделать с компонентом что-

либо более сложное, мы создадим покадровую анимацию.

Для создания покадровой анимации мы укажем начальное значение свойства,

продолжительность анимации и набор ключевых кадров. Ключевой кадр оп-

ределяет момент времени анимации и значение, которое должно принять

свойство компонента в данный момент. То есть, определяя ключевой кадр,

мы говорим среде исполнения Silverlight: "Сделай мне в этот момент времени

вот такое значение этого свойства".

Среда исполнения Silverlight находит первый ключевой кадр, определяет для

него момент времени и значение свойства и обрабатывает их так же, как

трансформационную анимацию (см. ранее). Когда первый ключевой кадр

достигнут, она ищет второй и т. д. Так что покадровую анимацию можно рас-

сматривать как набор трансформационных анимаций, выполняющихся одна

за другой.

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

значение свойства для достижения им указанного значения за указанное вре-

мя. Оно может изменяться по линейному закону, по закону, описываемому

кривой Безье, или скачкообразно (т. е. свойство просто примет новое значе-

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

анимацию.

Анимации в Silverlight всегда объединяются в группы анимаций. Это обяза-

тельно; даже если нам нужно создать всего одну анимацию, мы должны по-

местить ее в группу.

Все анимации, объединенные в группу, выполняются одновременно. Если

какие-то анимации должны быть выполнены позже других, мы можем задать

для них начальную задержку.

Группы могут содержать сколько угодно самых разных анимаций. Также они

могут содержать другие группы анимаций (вложенные группы анимаций).

Анимации никогда не запускаются сами. Их нужно явно запустить, для чего

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

ция должна быть запущена сразу после вывода страницы, запускающий ее

код помещается в обработчик события Loaded страницы.

Page 320: Дронов В. Самоучитель Silverlight 3 (2010)

308 Часть V. Графические возможности Silverlight. Многостраничные приложения

Ни следует помещать запускающий анимацию код в конструктор страницы.

В этом случае анимация не будет запущена.

Теоретически подготовившись, можно начать практиковаться. И начнем мы с самых простых анимаций — трансформационных.

Трансформационная анимация

Чтобы создать трансформационную анимацию, не нужно мудрить с ключе-выми кадрами. Среда исполнения Silverlight сделает все за нас.

Для примера поместим на страницу графическое изображение (компонент

Image) и заставим его, как писал Льюис Кэрролл, "внезапно и плавно исчезать

с глаз". Для этого нам потребуется изменить значение свойства Opacity с 1 до 0. Что касается продолжительности анимации, то пусть она длится 10 секунд.

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

Группа анимаций представляет собой объект класса Storyboard. Этот класс

поддерживает свойство Children типа коллекции TimelineCollection, которая

содержит объекты класса Timeline — родителя всех классов анимаций. Свой-

ство Children помечено как свойство по умолчанию, поэтому его никогда не указывают.

<Storyboard x:Name="sbdDemo">

</Storyboard>

Только вот незадача — мы не можем поместить группу анимации ни в один из контейнеров, доступных в Silverlight. Нам придется превратить группу анимаций в ресурс страницы. (О ресурсах страниц и приложений было рас-сказано в главе 13.)

<UserControl.Resources>

<Storyboard x:Name="sbdDemo">

</Storyboard>

</UserControl.Resources>

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

имя (атрибут x:Name). В таком случае среда исполнения Silverlight, во-первых, задаст для ресурса ключ, совпадающий с указанным нами именем, а во-вторых, сделает данный ресурс доступным в C#-коде. Так сказать, одним вы-стрелом сразу двух зайцев...

Page 321: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 309

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

� ColorAnimation — позволяет анимировать значение типа структуры Color, т. е. сплошной цвет.

� DoubleAnimation — позволяет анимировать значение типа числа с плаваю-щей точкой.

� PointAnimation — позволяет анимировать значение типа структуры Point, т. е. координату точки.

Поскольку свойство Opacity имеет тип числа с плавающей точкой, мы ис-

пользуем класс DoubleAnimation.

Теперь нам нужно указать параметры анимации: начальное и конечное зна-чения анимируемого свойства и продолжительность. Для этого используются три свойства, поддерживаемые всеми тремя классами анимаций; сейчас мы их рассмотрим.

Свойство From задает начальное значение анимируемого свойства. Оно имеет

тип, соответствующий классу анимации. Так, у класса DoubleAnimation оно

имеет тип числа с плавающей точкой, у класса ColorAnimation — тип струк-

туры Color, а у класса PointAnimation — тип структуры Point.

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

Свойство To задает конечное значение свойства. Его тип также соответствует классу анимации. Если данное свойство не указано, в качестве конечного берется текущее значение анимируемого свойства.

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

Свойство Duration указывает продолжительность анимации. Его значение имеет тип структуры Duration. В XAML-коде значение такого типа задается

в виде [<дни>:]<часы>:<минуты>:<секунды>[.<доли секунды>].

<DoubleAnimation . . . Duration="0:0:10" . . .>

Здесь мы задаем продолжительность анимации, равной 10 секундам.

Также в качестве значения свойства Duration мы можем указать строку

Automatic. Она задаст продолжительность анимации в одну секунду.

<DoubleAnimation . . . Duration="Automatic" . . .>

Page 322: Дронов В. Самоучитель Silverlight 3 (2010)

310 Часть V. Графические возможности Silverlight. Многостраничные приложения

Но как задать анимируемый компонент и его свойство? С помощью вот этих

двух свойств:

� Storyboard.TargetName — задает имя компонента;

� Storyboard.TargetProperty — задает имя свойства.

Исходя их всего этого, пишем окончательный XAML-код для создания ани-

мации:

<UserControl.Resources>

<Storyboard x:Name="sbdDemo">

<DoubleAnimation From="1" To="0" Duration="0:0:10"

�Storyboard.TargetName="imgDemo"

�Storyboard.TargetProperty="Opacity"></DoubleAnimation>

</Storyboard>

</UserControl.Resources>

Здесь imgDemo — имя компонента изображения.

Осталось только создать обработчик события Loaded страницы и поместить

в него единственное выражение, запускающее анимацию:

sbdDemo.Begin();

Метод Begin как раз запускает анимацию.

Теперь можно проверить приложение в действии. Если мы все сделали пра-

вильно, изображение должно "плавно исчезнуть с глаз" в течение 10 секунд.

Рассмотрим еще пару примеров анимации.

<Storyboard x:Name="sbdDemo">

<PointAnimation To="150,0" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="StartPoint"></PointAnimation>

</Storyboard>

. . .

<Path Stroke="Black">

<Path.Data>

<LineGeometry x:Name="lngDemo" StartPoint="0,150"

�EndPoint="400,150"></LineGeometry>

</Path.Data>

</Path>

Этот код создает путь из прямой линии и анимирует координаты ее началь-

ной точки. Мы используем класс PointAnimation для создания объекта анима-

ции, который анимирует свойство StartPoint объекта линии. Обратим вни-

мание, что в объекте анимации мы задаем только конечное значение аними-

Page 323: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 311

руемого свойства (150,0); в качестве начального будет использовано текущее

значение — то, что задано в формирующем ее XAML-коде (0,150).

<Storyboard x:Name="sbdDemo">

<ColorAnimation To="Green" Duration="0:0:10"

�Storyboard.TargetName="gdsDemo"

�Storyboard.TargetProperty="Color"></ColorAnimation>

</Storyboard>

. . .

<Rectangle Stroke="Black">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">

<LinearGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop x:Name="gdsDemo" Color="Blue"

�Offset="0"></GradientStop>

<GradientStop Color="Red" Offset="1"></GradientStop>

</GradientStopCollection>

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

А здесь мы анимируем один из цветов градиентной заливки прямоугольника,

для чего используем класс ColorAnimation.

Классы анимации поддерживают еще несколько свойств, которые нам стоит

рассмотреть. Они позволяют задать дополнительные параметры анимации.

Свойство FillBehavior позволяет указать, что случится со значением аними-

рованного свойства после того, как анимация завершится. Это свойство имеет

тип перечисления FillBehavior с двумя элементами:

� HoldEnd — анимируемое свойство будет содержать конечное значение (по-

ведение по умолчанию);

� Stop — анимируемое свойство получит начальное значение.

Свойство AutoReverse интереснее. Если его значение равно false (это, кстати,

значение по умолчанию), анимация просто дойдет до конца. Но если мы ука-

жем для этого свойства значение true, то анимация по завершению выпол-

нится еще раз, но в обратном порядке; при этом анимируемое свойство в ре-

зультате снова получит свое начальное значение. Второй, обратный, проход

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

Обратим внимание: если для свойства AutoReverse было задано значение true,

значение свойства FillBehavior игнорируется. В этом случае анимация будет

Page 324: Дронов В. Самоучитель Silverlight 3 (2010)

312 Часть V. Графические возможности Silverlight. Многостраничные приложения

вести себя так, будто для ее свойства FillBehavior было задано значение

HoldEnd.

По умолчанию анимация воспроизводится всего один раз, после чего оста-

навливается. Однако мы можем заставить ее воспроизводиться раз за разом,

в течение указанного промежутка времени, указанное количество раз или

постоянно, пока на экране присутствует данная страница. Для этого пред-

назначено свойство RepeatBehavior. Его значение имеет тип структуры

RepeatBehavior и может быть задано в XAML-коде одним из трех способов:

� <количество воспроизведений>x — анимация будет воспроизведена задан-

ное количество раз;

� [<дни>:]<часы>:<минуты>:<секунды>[.<доли секунды>] — анимация будет

воспроизводиться в течение указанного промежутка времени;

� Forever — анимация будет воспроизводиться постоянно.

Свойство BeginTime задает задержку перед началом анимации. Его значение

имеет тип структуры TimeSpan, описанной в главе 10. В XAML-коде его мож-

но задать в виде [<дни>:]<часы>:<минуты>:<секунды>[.<доли секунды>]. Значе-

ние по умолчанию — 0, так что анимация будет воспроизводиться сразу же

после запуска.

Сейчас свойство BeginTime нам мало пригодится. Более полезным оно ока-

жется потом, когда мы начнем создавать составные анимации.

По умолчанию анимация изменяет значение анимируемого свойства по ли-

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

свойством EasingFunction типа интерфейса IEasingFunction. Доступных зна-

чений для этого свойства очень много, и их перечисление, вместе с дополни-

тельными параметрами, займет слишком много места. Интересующиеся мо-

гут посмотреть их список вместе с примерами использования на странице

Graphics, Animation, and Media / Animation / Animation Overview докумен-

тации по Silverlight.

<Storyboard x:Name="sbdDemo">

<PointAnimation To="150,0" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="StartPoint">

<PointAnimation.EasingFunction>

<BounceEase EasingMode="EaseInOut"></BounceEase>

</PointAnimation.EasingFunction>

</PointAnimation>

</Storyboard>

. . .

Page 325: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 313

<Path Stroke="Black">

<Path.Data>

<LineGeometry x:Name="lngDemo" StartPoint="0,150"

�EndPoint="400,150"></LineGeometry>

</Path.Data>

</Path>

Здесь мы взяли за основу анимированную ранее линию и задали для ее ани-

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

Покадровая анимация

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

Как и трансформационная анимация, покадровая создается с помощью раз-личных классов, анимирующих значения различных типов. Таких классов четыре.

� ColorAnimationUsingKeyFrames — позволяет анимировать значение типа

структуры Color.

� DoubleAnimationUsingKeyFrames — позволяет анимировать значение типа числа с плавающей точкой.

� PointAnimationUsingKeyFrames — позволяет анимировать значение типа

структуры Point.

Рассмотрение четвертого класса мы отложим на потом.

Все эти классы поддерживают свойства AutoReverse, BeginTime, Duration,

FillBehavior и RepeatBehavior. Свойства From, To и By ими не поддерживают-ся, и понятно, почему.

А еще все эти классы поддерживают свойство KeyFrames, с помощью которо-го задаются ключевые кадры. Это свойство имеет тип коллекции:

� ColorKeyFrameCollection, содержащей объекты класса ColorKeyFrame, —

у класса ColorAnimationUsingKeyFrames;

� DoubleKeyFrameCollection, содержащей объекты класса DoubleKeyFrame, —

у класса DoubleAnimationUsingKeyFrames;

� PointKeyFrameCollection, содержащей объекты класса PointKeyFrame, —

у класса PointAnimationUsingKeyFrames.

Классы ColorKeyFrame, DoubleKeyFrame и PointKeyFrame являются родителями

для классов, создающих конкретные ключевые кадры. Они задают изменение

Page 326: Дронов В. Самоучитель Silverlight 3 (2010)

314 Часть V. Графические возможности Silverlight. Многостраничные приложения

значения анимируемого свойства по определенному закону, линейному или

нелинейному. Все эти классы перечислены далее.

� LinearColorKeyFrame, LinearDoubleKeyFrame и LinearPointKeyFrame — вы-

полняют изменение значения анимируемого свойства по линейному за-

кону.

� DiscreteColorKeyFrame, DiscreteDoubleKeyFrame и DiscretePointKeyFrame —

выполняют мгновенное изменение значения анимируемого свойства в ука-

занное время.

� SplineColorKeyFrame, SplineDoubleKeyFrame и SplinePointKeyFrame — вы-

полняют изменение значения анимируемого свойства по закону, описы-

ваемому кривой Безье.

То есть мы задаем в ключевом кадре значение, которое должно принять ани-

мируемое свойство, и момент времени, в который это свойство должно при-

нять указанное значение. А описанные ранее классы плавно изменяют значе-

ние анимируемого свойства по указанному закону либо изменяют его мгно-

венно.

Для задания момента времени все эти классы поддерживают свойство

KeyTime. Его значение имеет тип структуры KeyTime и в XAML-коде задается

в виде [<дни>:]<часы>:<минуты>:<секунды>[.<доли секунды>].

Значение анимируемого свойства указывается с помощью свойства Value. Его

тип зависит от конкретного класса.

Дальнейший материал лучше рассматривать на примере. Давайте возьмем

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

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

а закончит свое движение по закону, описываемому кривой Безье. Понятно,

что для этого мы используем классы PointAnimationUsingKeyFrames,

LinearPointKeyFrame, DiscretePointKeyFrame и SplinePointKeyFrame.

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

трех классов. Во всех остальных классах ключевых кадров эти параметры

задаются точно так же.

Первый ключевой кадр, который мы создадим, опишет движение по линей-

ному закону в указанную точку за указанное время. Для этого мы используем

класс LinearPointKeyFrame.

<LinearPointKeyFrame KeyTime="0:0:4"

�Value="10,10"></LinearPointKeyFrame>

Здесь все просто. Свойство KeyTime задает время, а свойство Value — коорди-

наты точки в виде значения типа структуры Point.

Page 327: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 315

Второй ключевой кадр заставит начальную точку прямой "прыгнуть" на но-

вое место и будет создан с помощью класса DiscretePointKeyFrame.

<DiscretePointKeyFrame KeyTime="0:0:5"

�Value="10,100"></DiscretePointKeyFrame>

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

Третий ключевой кадр сдвинет начальную точку прямой в указанную точку за указанное время по закону, описываемому кривой Безье. Его мы создадим

с помощью класса SplinePointKeyFrame.

Для указания контрольных точек кривой Безье мы используем свойство

KeySpline типа класса KeySpline. В XAML-коде значение этого свойства

задается в виде <горизонтальная координата первой контрольной точки>, <вертикальная координата первой контрольной точки> <горизонтальная

координата второй контрольной точки>,<вертикальная координата второй

контрольной точки>. Все эти координаты задаются в виде чисел с плавающей точкой от 0 до 1.

<SplinePointKeyFrame KeyTime="0:0:10" Value="150,0"

�KeySpline="0,0.75 0.75,1"></SplinePointKeyFrame>

Теперь начальная точка прямой будет то ускорять, то замедлять свое движе-ние, повинуясь "закону кривой Безье".

Вот полный XAML-код нашего примера:

<Storyboard x:Name="sbdDemo">

<PointAnimationUsingKeyFrames Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="StartPoint">

<PointAnimationUsingKeyFrames.KeyFrames>

<LinearPointKeyFrame KeyTime="0:0:4"

�Value="10,10"></LinearPointKeyFrame>

<DiscretePointKeyFrame KeyTime="0:0:5"

�Value="10,100"></DiscretePointKeyFrame>

<SplinePointKeyFrame KeyTime="0:0:10"

�Value="150,0" KeySpline="0,0.75 0.75,1"></SplinePointKeyFrame>

</PointAnimationUsingKeyFrames.KeyFrames>

</PointAnimationUsingKeyFrames>

</Storyboard>

. . .

<Path Stroke="Black">

<Path.Data>

<LineGeometry x:Name="lngDemo" StartPoint="0,150"

�EndPoint="400,150"></LineGeometry>

</Path.Data>

</Path>

Page 328: Дронов В. Самоучитель Silverlight 3 (2010)

316 Часть V. Графические возможности Silverlight. Многостраничные приложения

А сейчас настала пора рассмотреть последний, четвертый по счету, класс по-

кадровой анимации — ObjectAnimationUsingKeyFrames. Он позволяет аними-

ровать значение типа Object, т. е. фактически любого типа.

Свойство KeyFrames класса ObjectAnimationUsingKeyFrames имеет тип коллек-

ции ObjectKeyFrameCollection, хранящей объекты класса ObjectKeyFrame,

которые описывают ключевые кадры. В реальности для этого применяют

его класс-потомок DiscreteObjectKeyFrame. Это значит, что класс

ObjectAnimationUsingKeyFrames может реализовать только "скачкообразное"

изменение анимируемого свойства с течением времени.

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

дующий XAML-код:

<Storyboard x:Name="sbdDemo">

<ObjectAnimationUsingKeyFrames Duration="0:0:5"

�Storyboard.TargetName="recDemo" Storyboard.TargetProperty="Fill">

<ObjectAnimationUsingKeyFrames.KeyFrames>

<DiscreteObjectKeyFrame KeyTime="0:0:1">

<DiscreteObjectKeyFrame.Value>

<SolidColorBrush Color="Yellow"></SolidColorBrush>

</DiscreteObjectKeyFrame.Value>

</DiscreteObjectKeyFrame>

<DiscreteObjectKeyFrame KeyTime="0:0:2">

<DiscreteObjectKeyFrame.Value>

<SolidColorBrush Color="Red"></SolidColorBrush>

</DiscreteObjectKeyFrame.Value>

</DiscreteObjectKeyFrame>

<DiscreteObjectKeyFrame KeyTime="0:0:3">

<DiscreteObjectKeyFrame.Value>

<SolidColorBrush Color="Green"></SolidColorBrush>

</DiscreteObjectKeyFrame.Value>

</DiscreteObjectKeyFrame>

<DiscreteObjectKeyFrame KeyTime="0:0:4">

<DiscreteObjectKeyFrame.Value>

<SolidColorBrush Color="Blue"></SolidColorBrush>

</DiscreteObjectKeyFrame.Value>

</DiscreteObjectKeyFrame>

<DiscreteObjectKeyFrame KeyTime="0:0:5">

<DiscreteObjectKeyFrame.Value>

<SolidColorBrush Color="Black"></SolidColorBrush>

</DiscreteObjectKeyFrame.Value>

</DiscreteObjectKeyFrame>

</ObjectAnimationUsingKeyFrames.KeyFrames>

Page 329: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 317

</ObjectAnimationUsingKeyFrames>

</Storyboard>

. . .

<Rectangle x:Name="recDemo" Stroke="Black" Fill="White"></Rectangle>

Здесь мы, используя класс ObjectAnimationUsingKeyFrames, анимировали зна-чение свойства Fill, которое имеет тип Brush. Ни один из всех прочих рас-смотренных нами классов анимации на это не способен!

Составная анимация

Но что если нам нужно анимировать значения сразу двух свойств одного компонента? Или значения свойств разных компонентов? И чтобы все это работало одновременно. Можно ли так сделать?

Конечно можно! Еще в начале этой главы мы узнали, что анимации можно объединять в группу, причем все входящие в эту группу анимации будут ра-ботать одновременно. Вот сейчас мы и проверим это на практике.

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

<Storyboard x:Name="sbdDemo">

<PointAnimation To="150,0" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="StartPoint"></PointAnimation>

<PointAnimation To="150,400" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="EndPoint"></PointAnimation>

</Storyboard>

. . .

<Path Stroke="Black">

<Path.Data>

<LineGeometry x:Name="lngDemo" StartPoint="0,150"

�EndPoint="400,150"></LineGeometry>

</Path.Data>

</Path>

Здесь даже нечего объяснять — код говорит сам за себя.

Также мы можем вкладывать в группу анимации другие группы.

<Storyboard x:Name="sbdDemo">

<Storyboard>

<PointAnimation To="150,0" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="StartPoint"></PointAnimation>

Page 330: Дронов В. Самоучитель Silverlight 3 (2010)

318 Часть V. Графические возможности Silverlight. Многостраничные приложения

<PointAnimation To="150,400" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

�Storyboard.TargetProperty="EndPoint"></PointAnimation>

</Storyboard>

<ColorAnimation To="White" Duration="0:0:10"

�Storyboard.TargetName="scbDemo"

�Storyboard.TargetProperty="Color"></ColorAnimation>

</Storyboard>

. . .

<Path>

<Path.Stroke>

<SolidColorBrush x:Name="scbDemo" Color="Black"></SolidColorBrush>

</Path.Stroke>

<Path.Data>

<LineGeometry x:Name="lngDemo" StartPoint="0,150"

�EndPoint="400,150"></LineGeometry>

</Path.Data>

</Path>

Здесь мы создаем группу, включающую анимацию и еще одну вложенную

группу из двух анимаций. Анимация, помещенная в группу, анимирует цвет

линии. А анимации во вложенной группе анимируют координаты начальной

и конечной точек этой же линии. И линия в процессе движения будет все бо-

лее и более "выцветать".

При создании составных анимаций нам очень пригодятся два свойства, кото-

рые поддерживаются и классом группы анимации, и классами анимаций. Да-

вайте познакомимся с этими свойствами.

Первое свойство нам уже известно — BeginTime. Оно имеет тип структуры

TimeSpan (см. главу 10) и задает задержку перед началом воспроизведения

анимации. Эта задержка отсчитывается относительно момента начала вос-

произведения группы анимации, в которой находится данная анимация. На-

пример, если для всей группы анимации была задана задержка в 5 секунд, а

для находящейся в ней анимации — в 3 секунды, то данная анимация начнет

воспроизводиться через 8 (5+3) секунд.

Второе свойство — SpeedRatio. Оно задает относительную скорость воспро-

изведения анимации по сравнению со скоростью, заданной для группы, в ко-

торой эта анимация находится. Значение этого свойства задается в виде числа

с плавающей точкой; значения меньше 1 задают уменьшение скорости,

больше 1— увеличение. Значение по умолчанию — 1 (нормальная скорость).

<Storyboard x:Name="sbdDemo">

<Storyboard BeginTime="0:0:2" SpeedRatio="0.5">

Page 331: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 17. Анимация 319

<PointAnimation To="150,0" Duration="0:0:10"

�Storyboard.TargetName="lngDemo"

. . .

Здесь анимации, находящиеся во вложенной группе, начнут воспроизводить-

ся на 2 секунды позже и в 2 раза медленнее (значение свойства SpeedRatio

равно 0.5).

Программное управление анимацией

И напоследок рассмотрим средства, которые позволят нам управлять анима-

цией из C#-кода.

Прежде всего, это набор перечисленных далее методов класса Storyboard.

� Begin — запускает анимацию. Он уже нам знаком.

� Stop — останавливает анимацию.

� Pause — приостанавливает анимацию.

� Resume — запускает снова приостановленную с помощью метода Pause

анимацию.

Все эти методы не принимают параметров и не возвращают результат.

Методы Seek и SeekAlignedToLastTick позволяют быстро "прокрутить" ани-

мацию на заданный промежуток времени вперед или назад. Они принимают

значение типа структуры TimeSpan, задающее время, на которое надо "про-

крутить" анимацию относительно текущего ее положения; положительное

значение задает "прокрутку" вперед, отрицательное — назад. Результат эти

методы не возвращают.

Метод Seek при вызове ждет наступления очередного тика внутренних "ча-

сов", к которым привязана анимация, и при его наступлении выполняет свою

работу. Метод SeekAlignedToLastTick делает свое дело немедленно, не ожидая

наступления очередного тика, но при этом анимация может дергаться.

sbdDemo.Seek(new TimeSpan(0, 0, 1));

Это выражение "прокручивает" анимацию на одну секунду вперед.

Метод SkipToFill выполняет быструю "прокрутку" анимации до самого ее

конца. Он не принимает параметров и не возвращает результата. Если значе-

ние свойства AutoReverse анимации установлено в true, анимация прокручи-

вается до ее начала. Если значение свойства RepeatBehavior анимации уста-

новлено в Forever, "прокрутки" не происходит, и выбрасывается исключение

InvalidOperation.

Page 332: Дронов В. Самоучитель Silverlight 3 (2010)

320 Часть V. Графические возможности Silverlight. Многостраничные приложения

И класс группы Storyboard, и все классы анимаций поддерживают событие

Completed. Оно возникает после завершения анимации.

private void sbdFirst_Completed(object sender, EventArgs e)

{

sbdSecond.Begin();

}

Здесь мы написали обработчик события Completed анимации sbdFirst, кото-

рый при ее завершении запустит воспроизведение анимации sbdSecond.

Что дальше?

Что ж, возможности Silverlight по анимации компонентов весьма богаты. Не

Flash, конечно, но для оживления интерфейса наших приложений вполне по-

дойдет.

На этом мы заканчиваем с графикой и снова переходим к интерфейсу. И рас-

смотрим мы приемы создания Silverlight-приложений, состоящих из несколь-

ких страниц. Об этом упоминалось еще в главе 2, а в следующей главе дан-

ный вопрос будет описан подробно.

Page 333: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 18

Многостраничные приложения

В предыдущих главах мы проходили графические возможности Silverlight:

собственно рисование, эффекты, преобразования и анимацию. Целых три

главы были посвящены этому!..

Чтобы отдохнуть от графики, сменим тему. Снова вернемся к интерфейсу

Silverlight-приложений. И рассмотрим принципы создания приложений, со-

держащих несколько страниц. Ведь сложные приложения имеют сложный

интерфейс, и обойтись всего одной страницей не всегда получается.

Принципы создания многостраничных приложений

Здесь также не обойтись без небольшого теоретического курса. Так что оста-

вим в покое клавиатуры и мыши и почитаем.

Когда автор начинал писать эту книгу, в ходу была бета-версия Silverlight 3.

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

В проект добавлялось нужное количество страниц, точно таких же, как глав-

ная страница. В C#-коде, реализующем переход между страницами, с по-

мощью оператора new создавался объект страницы, которая потом загружа-

лась и выводилась на Web-страницу, заменяя собой главную. В документации

по бета-версии Silverlight все это было подробно описано. И, надо сказать,

работало — проверено автором.

В окончательной версии Silverlight 3 создание истинно многостраничных

приложений стало невозможным. Почему — неизвестно. Вместо этого пред-

лагается этакая имитация многостраничности, основанная на компоненте

фрейма и подстраницах. Вот ими мы сейчас и займемся.

Page 334: Дронов В. Самоучитель Silverlight 3 (2010)

322 Часть V. Графические возможности Silverlight. Многостраничные приложения

Прежде всего уясним, что теперь любое Silverlight-приложение может содер-

жать только одну — главную — страницу. На этой странице помещаются

компоненты, которые должны присутствовать на экране всегда (поскольку

сама главная страница всегда присутствует на экране). Это могут быть какие-

то заголовки, поясняющие надписи, логотипы и др.

Кроме того, на главной странице помещается особый компонент, называемый

фреймом. Фрейм служит "вместилищем" для тех частей интерфейса, которые

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

фрейм — это контейнер, только очень специализированный.

Сами части интерфейса приложения, которые выводятся во фрейме и сменя-

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

во многом схожа с главной страницей: создающий ее код помещается в от-

дельных файлах, она представляет собой класс, который мы как разработчики

пишем сами, она может вмещать только один компонент — главный контей-

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

только в том, что класс подстраницы является потомком не класса

UserControl, а класса Page.

Одна из подстраниц указывается в параметрах фрейма как загружаемая изна-

чально (начальная подстраница). Остальные подстраницы загружаются во

фрейм по мере надобности, обычно — по командам пользователя.

Понятно, что следует предусмотреть специальные элементы управления для

перехода (навигации) между подстраницами. Это могут быть кнопки или ги-

перссылки; специально для этих целей Silverlight содержит особый компо-

нент-гиперссылку, который мы также рассмотрим.

Одна подстраница может передать другой какие-либо данные. Очень полез-

ная возможность.

Простейшее многостраничное приложение

На этом закончим с теорией и займемся практикой. Создадим многостранич-

ное приложение, которое выводит на экран список специалистов по плат-

формам для создания интернет-приложений и позволяет выбрать любого

специалиста и просмотреть более подробные сведения о нем. При этом спи-

сок специалистов и подробные сведения будут выводиться на разных под-

страницах.

Создадим в Visual Web Developer 2008 новый проект и назовем его MultiPage.

Из нашего старого приложения ListDemo позаимствуем классы Person и

ListDemoItemCollection. Выбросим из класса Person свойство Platforms, что-

Page 335: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 18. Многостраничные приложения 323

бы не переусложнять наше первое многостраничное приложение. Поместим

код, объявляющий эти два класса, прямо в пространство имен MultiPage.

Теперь все готово для создания фрейма и подстраниц.

Создание фрейма

За создание на странице фрейма "отвечает" компонент Frame. Соответст-

вующий ему класс объявлен в той части пространства имен

System.Windows.Controls, которая находится в библиотечной сборке

System.Windows.Controls.Navigation.dll.

Поместим этот компонент на страницу. И увидим, что Visual Web De-

veloper 2008 создаст вот такой XAML-код:

<navigation:Frame></navigation:Frame>

Обычно Visual Web Developer 2008 для указания на часть пространства имен System.Windows.Controls из сборки System.Windows.Controls.Navigation.dll оп-

ределяет при его подключении префикс navigation. Мы тоже всегда будем его

использовать.

Свойство Source класса Frame используется для задания имени подстраницы,

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

тип Uri и фактически указывает интернет-адрес XAML-файла этой подстра-

ницы. (Класс Uri был описан в главе 7.) Значение этого свойства в XAML-

коде задается в виде обычной строки.

<navigation:Frame Source="/ListPage.xaml"></navigation:Frame>

Подстраницу со списком специалистов мы назвали ListPage.xaml. Ее мы соз-

дадим позже. Отметим, что мы указали в начале ее интернет-адреса символ

слэша — это значит, что данная подстраница находится в своеобразном "кор-

не" сборки, там же, где и главная страница.

Из главы 8 мы узнали, что для организации ресурсов сборки мы можем соз-

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

точно так же можем организовывать и подстраницы. Давайте так и сделаем.

Создадим в сборке папку с именем pages. (Как это сделать, было описано

в главе 8.) В ней мы сохраним обе подстраницы нашего приложения. И сразу

же исправим значение свойства Source нашего фрейма.

<navigation:Frame Source="/pages/ListPage.xaml"></navigation:Frame>

Теперь во фрейм будет изначально загружена страница ListPage.xaml, храня-

щаяся в папке pages.

Page 336: Дронов В. Самоучитель Silverlight 3 (2010)

324 Часть V. Графические возможности Silverlight. Многостраничные приложения

Собственно, на этом создание фрейма закончено. Пора приступать к созда-нию подстраниц.

Создание подстраниц

Создать новую подстраницу очень просто. Щелкаем правой кнопкой мыши на "корне" иерархического списка в панели Solution Explorer и выбираем пункт New Item подменю Add. На экране появляется диалоговое окно Add New Item (рис. 18.1).

Рис. 18.1. Диалоговое окно Add New Item

В "ветви" Visual C# иерархического списка Categories выбираем пункт Silverlight (мы ведь работаем над Silverlight-приложением). В списке Templates отыскиваем и выбираем пункт Silverlight Page. В поле ввода Name вводим имя файла подстраницы. И нажимаем кнопку Add. (Кнопка Cancel позволит нам закрыть это окно без добавления подстраницы.)

Сразу после этого Visual Web Developer 2008 создаст файлы, в которых хра-нится исходный код новой подстраницы. Как мы помним, это XAML-файл и C#-файл. Мы увидим их в списке панели Solution Explorer.

Первым же делом переместим эти файлы в созданную ранее папку pages. Это можно сделать, перетащив мышью пункт списка ListPage.xaml.

Как видим, Visual Web Developer 2008 уже открыл XAML-файл только что

созданной подстраницы. Рассмотрим его содержимое.

Page 337: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 18. Многостраничные приложения 325

<navigation:Page x:Class="MultiPage.ListPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:navigation="clr-namespace:System.Windows.Controls;

�assembly=System.Windows.Controls.Navigation"

Title="Список специалистов">

<Grid x:Name="LayoutRoot">

</Grid>

</navigation:Page>

Сразу видно, что класс подстраницы создается на основе класса Page. Этот класс является родителем для всех классов подстраниц, обеспечивает их ос-

новную функциональность и объявлен в той части пространства имен

System.Windows.Controls, которая находится в библиотечной сборке

System.Windows.Controls.Navigation.dll. Еще мы видим, что для указания на

данную часть пространства имен System.Windows.Controls используется все

тот же префикс navigation.

Также мы видим, что класс Page (а значит, и все его потомки) поддерживает

свойство Title. Это свойство задает название подстраницы, которое отобра-жается в заголовке окна Web-обозревателя и помещается в его историю. (Ис-

тория Web-обозревателя представляет собой список посещенных ранее Web-страниц; мы можем выбрать любую из этих Web-страниц, чтобы вывести ее

на экран.) Автор задал в качестве названия подстраницы строку Список

специалистов.

В остальном здесь все то же самое, что и у обычных Silverlight-страниц. Мы

видим атрибут x:Class, задающий имя класса данной подстраницы. Мы ви-дим атрибуты, подключающие различные пространства имен XAML. А еще

мы видим XAML-код, создающий главный контейнер, по умолчанию — "таблицу".

Больше ничего интересного в этом коде нет, поэтому начнем работу. Сначала

поместим на подстраницу в качестве ее ресурса объект класса

ListDemoItemCollection.

<navigation:Page.Resources>

<local:ListDemoItemCollection

�x:Key="colItems"></local:ListDemoItemCollection>

</navigation:Page.Resources>

После этого заменим главный контейнер "таблица" на "стопку" с вертикаль-ной ориентацией — так нам будет проще. И создадим в этом контейнере спи-

сок lstSpecs, привязав его к только что созданному ресурсу-коллекции и за-

дав отображение значений из свойства F. Мы уже проделывали это в главе 13, так что справимся без труда.

Page 338: Дронов В. Самоучитель Silverlight 3 (2010)

326 Часть V. Графические возможности Silverlight. Многостраничные приложения

Осталось только создать кнопку Сведения с именем btnToDetail. Нажатие на

эту кнопку вызовет переход на вторую подстраницу, содержащую сведения

о выбранном в списке lstSpecs специалисте.

Проверим приложение в действии. Если мы все сделали правильно, на стра-

нице должен появиться список, перечисляющий фамилии специалистов, и

неработающая кнопка Сведения.

Точно таким же образом создадим еще одну подстраницу, назвав ее

DetailPage.xaml. Зададим для нее название Сведения о специалисте. Создадим

на ней четыре надписи и четыре поля ввода, доступных только для чтения;

в этих полях ввода будут выводиться фамилия, имя, отчество и возраст спе-

циалиста.

Каждое поле ввода, что мы создадим на новой подстранице, привяжем к со-

ответствующему свойству класса Person. Объект же, к которому мы привязы-

ваем поле ввода, в XAML-коде задавать не будем — его мы зададим

в C#-коде через свойство DataContext. Так, для поля ввода, отображающего

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

<TextBox Text="{Binding Path=F}" . . .>

XAML-код объектов привязки для остальных полей ввода создается анало-

гично.

Не забудем также создать на второй подстранице кнопку Вернуться с име-

нем btnToMain, чтобы пользователь смог вернуться на первую подстраницу со

списком специалистов.

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

Навигация

Настала пора реализовать навигацию между подстраницами нашего прило-

жения. Мы уже предусмотрели соответствующие кнопки на обеих подстра-

ницах, осталось только написать C#-код.

Создадим обработчик события Click кнопки Сведения (btnToDetail), распо-

ложенной на первой подстранице. В нем мы создадим код, во-первых,

выполняющий переход на подстраницу сведений о выбранном в списке спе-

циалисте, во-вторых, передающий второй подстранице номер элемента кол-

лекции, соответствующий выбранному в списке специалисту. Начнем с нави-

гации.

Page 339: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 18. Многостраничные приложения 327

Сначала нам нужно обратиться к свойству NavigationService класса Page. Это

свойство содержит указатель на объект класса NavigationService, свойства и

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

Нам понадобится метод Navigate. Он принимает в качестве единственного

параметра объект класса Uri, хранящий сведения о пути к подстранице, на

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

этот метод возвращает true, в противном случае — false.

Обработчик события Click кнопки btnToDetail будет таким:

if (lstSpecs.SelectedIndex > -1)

{

NavigationService.Navigate(new Uri("/pages/DetailPage.xaml",

�UriKind.Relative));

}

Сначала мы проверяем, был ли в списке lstSpecs выбран какой-либо пункт, и

если был выбран, выполняем навигацию. Методу Navigate мы передаем в ка-

честве параметра объект класса Uri, содержащий путь к подстранице

DetailPage.xaml. Поскольку мы задали путь относительно "корня" сборки, то

должны указать в качестве второго параметра конструктора класса Uri эле-

мент Relative перечисления UriKind.

Теперь займемся кнопкой Вернуться (btnToMain) второй подстраницы. Обра-

ботчик ее события Click будет еще проще:

NavigationService.Navigate(new Uri("/pages/ListPage.xaml",

�UriKind.Relative));

А можно использовать интересную возможность Silverlight, о которой самое

время поговорить. Ранее мы упоминали об истории Web-обозревателя, в ко-

торой сохраняются все посещенные Web-страницы. Но не только Web-

страницы — и подстраницы Silverlight-приложений там тоже сохраняются!

Это значит, что пользователь может перемещаться по подстраницам, просто

щелкая кнопки Вперед (Forward) и Назад (Back) Web-обозревателя.

Класс NavigationService поддерживает несколько свойств и методов, которые

помогут нам двигаться по истории. Сейчас мы их рассмотрим.

Методы GoForward и GoBack выполняют перемещение, соответственно, вперед

и назад по истории, загружая следующую или предыдущую страницу. Они не

принимают параметров и не возвращают результата.

Свойства CanGoForward и CanGoBack возвращают true, если есть возможность

переместиться по истории вперед и назад соответственно, и false в против-

ном случае. Эти свойства доступны только для чтения.

Page 340: Дронов В. Самоучитель Silverlight 3 (2010)

328 Часть V. Графические возможности Silverlight. Многостраничные приложения

Исходя из этого, мы можем написать обработчик события Click кнопки

btnToMain второй подстраницы так:

if (NavigationService.CanGoBack)

{

NavigationService.GoBack();

}

Еще класс NavigationService поддерживает метод StopLoading. Он прерывает

запущенный ранее переход на другую подстраницу. Параметров он не при-

нимает и результат не возвращает.

Класс Page поддерживает событие Loading. Оно возникает сразу после загруз-

ки подстраницы и аналогично одноименному событию страницы.

Класс Frame также поддерживает свойства CanGoForward и CanGoBack и методы

GoForward, GoBack, Navigate и StopLoading. А еще он поддерживает набор со-

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

� Navigating — возникает при запуске перехода на новую подстраницу.

� Navigated — возникает после завершения перехода на новую подстраницу.

� NavigationStopped — возникает при прерывании перехода на новую под-

страницу вызовом метода StopLoading.

� NavigationFailed — возникает, если при переходе на новую подстраницу

случится ошибка (например, при задании неверного пути к подстранице).

Одноименные события поддерживает и класс подстраницы Page, но пользо-

ваться ими не очень удобно.

Передача данных между подстраницами

Увлекшись рассмотрением разномастных свойств, методов и событий, мы

совсем забыли об одной вещи. Мы же не передали второй подстранице номер

элемента коллекции, соответствующего выбранному в списке специалисту!

Более того, мы так и не знаем, как это сделать!

Спокойно! Это не повод для паники. Видите — платформа Silverlight уже

спешит нам на помощь. Она позволяет передать данные в составе пути к за-

гружаемой подстранице в виде набора параметров, каждый из которых мож-

но представить в виде своего рода переменной. Как и переменная, такой па-

раметр должен иметь уникальное в пределах пути имя, по которому к нему

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

Page 341: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 18. Многостраничные приложения 329

Набор параметров отделяется от собственно пути вопросительным зна-

ком (?). Отдельные параметры и их значения записываются в виде пар

<имя параметра>=<значение параметра>, которые отделяются друг от друга

символами &.

Так, если нам нужно передать второй подстранице число 1 — номер второго

по счету пункта в списке — с помощью параметра num, путь ко второй под-

странице будет выглядеть так:

/pages/DetailPage.xaml?num=1

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

Так что мы можем сразу же переписать обработчик события Click кнопки

btnToDetail первой подстраницы таким образом:

if (lstSpecs.SelectedIndex > -1)

{

NavigationService.Navigate(new Uri("/pages/DetailPage.xaml?num=" +

�lstSpecs.SelectedIndex, UriKind.Relative));

}

То есть мы просто добавляем к пути имя параметра (num) и передаваемое

с его помощью значение. Явное преобразование типов — из целого числа

в строку — здесь излишне; среда исполнения Silverlight выполнит преобразо-

вание неявно.

Осталось теперь получить переданные данные во второй подстранице. Для

этого следует использовать свойство NavigationContext класса Page. Оно со-

держит указатель на объект класса NavigationContext, который хранит приня-

тые в составе пути данные. Он поддерживает свойство QueryString, хранящее

указатель на словарь (см. главу 11); ключи элементов этого словаря есть име-

на параметров, а сами элементы — значения данных параметров в строковом

виде.

Получение переданного от первой подстраницы номера специалиста и вы-

борку данных о нем мы реализуем в обработчике события Loaded подстрани-

цы. Вот его код:

ListDemoItemCollection colSpecs;

int specNum = 0;

Page 342: Дронов В. Самоучитель Silverlight 3 (2010)

330 Часть V. Графические возможности Silverlight. Многостраничные приложения

if (NavigationContext.QueryString.ContainsKey("num"))

{

if (int.TryParse(NavigationContext.QueryString["num"], out specNum))

{

colSpecs = new ListDemoItemCollection();

LayoutRoot.DataContext = colSpecs[specNum];

}

}

Сначала мы проверяем, присутствует ли в данных, переданных от первой

подстраницы, параметр с именем num. Это делается с помощью метода

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

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

ListDemoItemCollection, по полученному индексу извлечь ее элемент и привя-зать к нему все поля ввода, что мы создали на второй подстранице.

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

(LayoutRoot). Такое допускается и весьма часто используется. Вообще, мы можем привязать к данным любой контейнер, и в результате привязанными к данным окажутся все содержащиеся в нем компоненты.

Что ж, мы неплохо поработали... Проверим приложение в действии.

Компонент-гиперссылка (HyperlinkButton)

Чтобы сделать Silverlight-приложение более похожим на Web-страницу, мы

можем использовать компонент HyperlinkButton. Он создает на странице на-стоящую гиперссылку, ничем не отличающуюся от тех, что делаются на Web-страницах средствами языка HTML.

<HyperlinkButton Content="Вернуться"

�NavigateUri="/pages/ListPage.xaml"></HyperlinkButton>

Для указания текста гиперссылки используется свойство Content класса

HyperlinkButton. Это свойство имеет тип ContentControl; это значит, что мы можем превратить в гиперссылку любой компонент.

Путь подстраницы, на которую следует выполнить переход, указывается

с помощью свойства NavigateUri типа Uri. В XAML-коде значение этого свойства задается в виде обычной строки.

Класс HyperlinkButton поддерживает событие Click, возникающее при щелч-ке на гиперссылке.

Page 343: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 18. Многостраничные приложения 331

<HyperlinkButton x:Name="hlbToDetail" Content="Сведения"

�Click="btnToDetail_Click"></HyperlinkButton>

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

Класс HyperlinkButton также поддерживает свойства ActualHeight,

ActualWidth, FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, IsTabStop, Margin, MaxHeight, MaxWidth,

MinHeight, MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и

Width и метод Focus.

Навигация на другие Web-страницы

Напоследок поговорим об еще одной интересной и зачастую полезной воз-можности Silverlight — навигации на другие Web-страницы.

Для этой цели используется только что рассмотренный нами компонент ги-

перссылки HyperlinkButton. В его свойстве NavigateUri указываем нужный

интернет-адрес. После этого обязательно задаем для свойства TargetName

значение _new — это предпишет Web-обозревателю открыть Web-страницу в новом окне. И дело сделано!

<HyperlinkButton Content="Сайт Silverlight"

�NavigateUri="http://www.microsoft.com/rus/Silverlight/"

�TargetName="_new"></HyperlinkButton>

Эта гиперссылка реализует переход на русскую версию сайта, посвященного платформе Silverlight.

Если для гиперссылки, ведущей на другую Web-страницу, не указать для свой-ства TargetName значение _new, гиперссылка работать не будет.

На этом закончим разговор о многостраничных приложениях и навигации.

Что дальше?

В этой главе мы познакомились с многостраничными приложениями и на- учились их создавать. Также мы узнали о навигации между подстраницами одного приложения и даже на другие Web-страницы.

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

Page 344: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 19

Вторичные окна

В предыдущей главе мы создавали многостраничное приложение, интерфейс которого разбит на две части и помещается на двух подстраницах. Эти под-страницы мы выводили во фрейме, который поместили на главной (единст-венной) странице приложения.

Конечно, фреймы и подстраницы — штука полезная. Но не всегда они могут помочь. Так, мы можем захотеть вывести часть интерфейса приложения не на отдельной подстранице, а вообще в другом окне. В Windows-приложениях для этого обычно используются диалоговые окна. Можно ли что-то подобное сделать в Silverlight? Разумеется, можно.

Также нам может понадобиться вывести пользователю какое-либо предупре-ждение и, возможно, попросить его сделать какой-либо выбор. В Windows-приложениях для такого случая предусмотрены окна-предупреждения, со-держащие текст и несколько кнопок. Есть ли что-то подобное в Silverlight? Есть.

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

Диалоговые окна

Знакомство с вторичными окнами Silverlight мы начнем с диалоговых окон. Мы добавим такое окно в старое приложение GridDemo и с его помощью реа-лизуем возможность добавления в коллекцию нового специалиста.

Введение в диалоговые окна

Диалоговое окно Silverlight служит для вывода части интерфейса приложения, которая не обязана присутствовать на экране постоянно. Особенности таких окон перечислены далее.

Page 345: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 19. Вторичные окна 333

� Диалоговое окно выводится прямо в окне Web-обозревателя, поверх стра-

ницы Silverlight-приложения, и не может быть вынесено за пределы этого

окна.

� Диалоговое окно блокирует доступ к странице приложения. То есть поль-

зователь не сможет работать со страницей, пока не закроет диалоговое

окно.

� Диалоговое окно может содержать любые компоненты и в этом отноше-

нии аналогично странице и подстранице.

� Диалоговое окно может содержать заголовок (обычно текстовый) и кноп-

ку закрытия. В этом смысле оно похоже на обычное Windows-окно.

� Диалоговое окно может быть выведено на экран как страницей или под-

страницей, так и другим диалоговым окном.

� Код диалогового окна выполняется в другом потоке, нежели код вызвав-

шего это окно метода.

В принципе, все ясно. Кроме одного: что это за другой поток...

Когда среда исполнения Silverlight выполняет код метода, она извлекает

команду языка MSIL из памяти, извлекает данные, обрабатываемые этой

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

Далее она извлекает следующую команду, извлекает данные, выполняет

команду, сохраняет результат... третью команду... четвертую... и т. д. Эта

последовательность выполнения кода "команда-за-командой" называется по-

током.

Обычно в данный момент времени выполняется только один метод, и, следо-

вательно, существует только один поток. Но часто среда исполнения

Silverlight одновременно выполняет несколько методов — и для каждого соз-

дается свой поток. Скажем, если на странице приложения используется ани-

мация, и одновременно идут длительные вычисления, то будут созданы два

потока: один — для воспроизведения анимации, а другой — для выполнения

вычислений. (Пример, правда, не очень корректен — анимация выполняется

самой средой исполнения Silverlight, а не кодом метода. Зато нагляден.)

Так вот, код метода, вызвавшего на экран диалоговое окно, и код, находя-

щийся в классе этого окна, выполняются в разных потоках. Это приводит

к тому, что окно как бы "живет" само по себе, отдельно от страницы. Если

взять пример с анимацией, то анимированные компоненты на странице и

в диалоговом окне будут работать независимо друг от друга.

Зачем нам это нужно знать, мы выясним позже, когда начнем обмениваться

данными с диалоговым окном. Для этого нам придется применить один трюк

с обработчиками событий.

Page 346: Дронов В. Самоучитель Silverlight 3 (2010)

334 Часть V. Графические возможности Silverlight. Многостраничные приложения

Код диалогового окна хранится в отдельных файлах, как и код страниц и

подстраниц. Класс диалогового окна создается самим разработчиком и явля-

ется потомком класса ChildWindow.

На этом теоретический курс закончен. Откроем в Visual Web Developer 2008

приложение GridDemo. Удалим из XAML-кода главной страницы ресурс, со-

ответствующий коллекции специалистов, и создадим в таблице grdDemo вот такую "пустую" привязку:

<data:DataGrid . . . ItemsSource="{Binding}" . . .>

В классе главной страницы создадим поле colItems, где будет храниться ука-затель на коллекцию специалистов:

private ListDemoItemCollection colItems;

И добавим в конструктор класса главной страницы после вызова метода

InitializeComponent вот такой код:

colItems = new ListDemoItemCollection();

grdDemo.DataContext = colItems;

Как видим, он создает коллекцию специалистов и привязывает к ней таблицу

grdDemo.

Все это нужно для того, чтобы мы смогли без проблем получить доступ

к коллекции специалистов из C#-кода — через поле colItems. Ведь мы будем

добавлять в коллекцию новых специалистов, а сделать это можно только в C#-коде.

Осталось только поместить на главной странице кнопку Добавить с именем

btnAdd. И создать диалоговое окно.

Создание диалогового окна

Чтобы создать диалоговое окно, следует щелкнуть правой кнопкой мыши на

"корне" иерархического списка в панели Solution Explorer и выбрать пункт New Item подменю Add. На экране появится уже знакомое нам диалоговое

окно Add New Item (см. рис. 18.1).

В иерархическом списке Categories развернем "ветвь" Visual C#, если она не развернута, и выберем пункт Silverlight. В списке Templates выберем пункт

Silverlight Child Window. В поле ввода Name введем имя файла, где будет храниться код диалогового окна; дадим ему имя AddWindow.xaml. И нажмем

кнопку Add. (Кнопка Cancel позволит нам закрыть это окно без добавления диалогового окна.)

Visual Web Developer 2008 в ответ на наши действия создаст файлы, в кото-

рых хранится исходный код только что созданного диалогового окна, и выве-

Page 347: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 19. Вторичные окна 335

дет их в списке панели Solution Explorer. И сразу же откроет файл

AddWindow.xaml, ненавязчиво приглашая нас к работе над содержимым окна.

Погодим пока с содержимым. Рассмотрим XAML-код, создающий само окно.

<controls:ChildWindow x:Class="GridDemo.AddWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:controls="clr-

�namespace:System.Windows.Controls;assembly=System.Windows.Controls"

Width="400" Height="300"

Title="Добавление специалиста">

<Grid x:Name="LayoutRoot" Margin="2">

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<Button x:Name="btnCancel" Content="Отмена"

�Click="CancelButton_Click" Width="75" Height="23"

�HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />

<Button x:Name="btnOK" Content="ОК" Click="OKButton_Click"

�Width="75" Height="23" HorizontalAlignment="Right"

�Margin="0,12,79,0" Grid.Row="1" />

</Grid>

</controls:ChildWindow>

Код этот довольно велик, так что разберем его по частям.

Мы видим, что класс диалогового окна создается на основе класса

ChildWindow. Этот класс является родителем для всех классов диалоговых окон, обеспечивает их основную функциональность и объявлен в той части

пространства имен System.Windows.Controls, которая находится в библиотеч-ной сборке System.Windows.Controls.dll. Еще мы видим, что для указания на

данную часть пространства имен System.Windows.Controls используется пре-

фикс controls.

Также мы видим, что класс ChildWindow (а значит, и все его потомки) поддер-

живает свойство Title. Это свойство задает заголовок диалогового окна и

имеет тип Object — это значит, что мы можем использовать в качестве заго-ловка любой компонент. На деле же обычно используется текстовый заголо-

вок; именно такой — Добавление специалиста — и задал для окна автор.

Также класс ChildWindow поддерживает свойства Width и Height, задающие ширину и высоту окна соответственно. Скорее всего, с размерами окна при-дется поэкспериментировать, чтобы окно вмещало все компоненты, которые мы в него поместим, и не содержало пустого пространства.

Page 348: Дронов В. Самоучитель Silverlight 3 (2010)

336 Часть V. Графические возможности Silverlight. Многостраничные приложения

Еще класс ChildWindow поддерживает свойство HasCloseButton. Значение true

этого свойства предписывает диалоговому окну вывести в правой части заго-

ловка кнопку закрытия, имеющую вид крестика. Значение false убирает эту

кнопку. По умолчанию свойство HasCloseButton имеет значение true.

Остальное нам уже знакомо. Атрибут x:Class задает имя класса данного окна.

Другие атрибуты подключают различные пространства имен XAML. Ну и

главный контейнер, по умолчанию "таблица", — куда же без него...

Visual Web Developer 2008 уже создал в диалоговом окне две кнопки — ОK

и Отмена, выполняющие закрытие окна. (Изначально они имеют другие

надписи и имена; автор изменил их.) Названием этих кнопок понятно: первая

кнопка закрывает окно с подтверждением введенных в него данных, вторая

кнопка — с отменой.

Также Visual Web Developer 2008 сам создал для обеих этих кнопок обработ-

чики события Click. Мы рассмотрим их потом.

А пока что создадим в этом окне четыре поля ввода — для задания фамилии,

имени, отчества и возраста специалиста — и четыре надписи для них. Назо-

вем поля ввода txtF, txtN1, txtN2 и txtAge соответственно. Поместим их или

прямо в главный контейнер, создав в нем необходимое количество строк и

столбцов, или во вложенный контейнер. И заодно поменяем местами кнопки

ОK и Отмена — так привычнее.

Открытие и закрытие диалогового окна

Теперь вернемся в код главной страницы. Создадим обработчик события

Click кнопки btnAdd. В нем мы реализуем открытие диалогового окна.

Прежде всего, мы должны явно создать объект нашего диалогового окна

с помощью оператора new. Среда исполнения Silverlight за нас этого не сде-

лает.

Для открытия диалогового окна мы должны использовать метод Show класса

ChildWindow. Этот метод не принимает параметров и не возвращает резуль-

тата.

Значит, у нашего обработчика события будет вот такой код, из разряда no

comments:

AddWindow wndAdd = new AddWindow();

wndAdd.Show();

Проверим приложение в действии. Запустим его и нажмем кнопку Добавить.

Диалоговое окно красиво "распахнется" на экране. Закроем его, щелкнув лю-

бую из кнопок ОK или Отмена или кнопку закрытия в заголовке окна.

Page 349: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 19. Вторичные окна 337

Так, диалоговое окно успешно закрылось. Ну, с кнопкой закрытия, которая

находится в заголовке окна, все ясно — она должна закрывать окно, так ска-

зать, по долгу службы... А вот кнопки ОK и Отмена закрывать окна изна-

чально не "обучены". Значит, Visual Web Developer 2008 вставил в обработ-

чики события Click этих кнопок какой-то код, закрывающий окно. Какой?

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

Close класса ChildWindow. Этот метод не принимает параметров и не возвра-

щает результата.

Обычно метод Close используется в диалоговых окнах, выводящих какую-

либо информацию и имеющих одну кнопку, которая просто закрывает окно;

в обработчике события Click этой кнопки он и вызывается. Но гораздо чаще

окна используются для ввода каких-либо данных и имеют две кнопки,

имеющие разное назначение, — ОK и Отмена. (Наше окно именно такое.)

Значит, нам нужно каким-то образом дать главному окну знать, какая из этих

кнопок была нажата.

Для этого предназначено свойство DialogResult класса ChildWindow. Оно име-

ет логический тип и при этом может принимать значение null.

� Когда диалоговое окно открыто, это свойство имеет значение null.

� Если диалоговое окно было закрыто нажатием кнопки закрытия, рас-

положенной в заголовке окна, нажатием комбинации клавиш

<Ctrl>+<Shift>+<F4> или вызовом метода Close, это свойство принимает

значение false.

� Для указания, что окно было закрыто нажатием кнопки ОK, этому свойст-

ву присваивают значение true.

� Для указания, что окно было закрыто нажатием кнопки Отмена, этому

свойству присваивают значение false.

� Присваивание свойству DialogResult любого значения автоматически за-

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

Если мы откроем файл AddWindow.xaml.cs и найдем обработчики события

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

Так, обработчик события Click кнопки ОK имеет такой вид:

this.DialogResult = true;

Он присваивает свойству DialogResult объекта диалогового окна значение

true. Этим он, во-первых, закрывает окно, а во-вторых, дает знать методу, его

открывшему, что окно было закрыто нажатием именно кнопки ОK.

А вот код обработчика события Click кнопки Отмена:

this.DialogResult = false;

Page 350: Дронов В. Самоучитель Silverlight 3 (2010)

338 Часть V. Графические возможности Silverlight. Многостраничные приложения

Он таким образом дает знать, что окно было закрыто именно нажатием кноп-

ки Отмена, и одновременно закрывает окно.

Вот такое полезное свойство DialogResult!

Передача данных в диалоговое окно и из него

Теперь нам нужно сделать так, чтобы при нажатии кнопки ОK диалогового

окна выполнялось добавление в коллекцию нового специалиста, созданного

на основе введенных в это окно данных. Как это сделать?

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

бавление специалиста в коллекцию, после вызова метода Show обработчика

события Click кнопки Добавить в главном окне:

AddWindow wndAdd = new AddWindow();

wndAdd.Show();

if (wndAdd.DialogResult == true)

{

Person prs = new Person();

int iAge;

prs.F = wndAdd.txtF.Text;

prs.N1 = wndAdd.txtN1.Text;

prs.N2 = wndAdd.txtN2.Text;

if (int.TryParse(wndAdd.txtAge.Text, out iAge))

{

prs.Age = iAge;

}

colItems.Add(prs);

}

Но этот код работать не будет. И вот почему...

В начале этой главы мы говорили о том, что код диалогового окна выполня-

ется в другом потоке, отличном от того, в котором выполняется код, открыв-ший данное окно. Это значит, что код, открывший окно, после его открытия

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

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

фамилии, имени, отчества и возраста — ведь данные в поля ввода диалогово-го окна еще не введены. А нам это совсем не нужно.

Выход: использовать для отслеживания момента закрытия окна обработчик

события Closed диалогового окна. Это событие возникает после закрытия

диалогового окна и поддерживается самим классом ChildWindow.

Page 351: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 19. Вторичные окна 339

В качестве обработчика события Closed диалогового окна можно использо-

вать метод класса этого самого диалогового окна. Но зачастую удобнее при-

менить метод класса главной страницы. Именно такой случай мы сейчас и

рассмотрим.

Сначала мы напишем в классе главной страницы обработчик события Closed диалогового окна.

private void wndAdd_Closed(object sender, EventArgs e)

{

AddWindow wndAdd = sender as AddWindow;

if (wndAdd.DialogResult == true)

{

Person prs = new Person();

int iAge;

prs.F = wndAdd.txtF.Text;

prs.N1 = wndAdd.txtN1.Text;

prs.N2 = wndAdd.txtN2.Text;

if (int.TryParse(wndAdd.txtAge.Text, out iAge))

{

prs.Age = iAge;

}

colItems.Add(prs);

}

}

Первым параметром в метод — обработчик события всегда передается указа-тель на объект, в котором возникло это событие; этот указатель имеет тип

Object. Значит, преобразовав его к типу AddWindow — классу нашего диалого-вого окна, — мы получим указатель на это диалоговое окно.

Далее мы проверяем, равно ли значение свойства DialogResult диалогового

окна значению true. Обратим внимание, что, поскольку это свойство может

принимать значения true, false и null, здесь требуется явное сравнение зна-

чения этого свойства с true.

Далее мы создаем нового специалиста, заносим в его свойства значения, взя-

тые из полей ввода txtF, txtN1, txtN2 и txtAge диалогового окна, и добавляем в коллекцию.

Осталось исправить код обработчика события Click кнопки Добавить, вста-

вив в него выражение, привязывающее только что созданный обработчик

к событию Closed диалогового окна:

AddWindow wndAdd = new AddWindow();

wndAdd.Closed += wndAdd_Closed;

wndAdd.Show();

Page 352: Дронов В. Самоучитель Silverlight 3 (2010)

340 Часть V. Графические возможности Silverlight. Многостраничные приложения

Теперь можно проверить готовое приложение в действии. Нажмем кнопку

Добавить на главной странице, введем в поля ввода появившегося на экране

диалогового окна данные о новоприбывшем специалисте и нажмем кнопку

ОK. Если мы все сделали правильно, специалист-новичок должен появиться

в таблице.

А если нам нужно заносить какие-то значения в поля ввода диалогового окна

сразу при его открытии? Поступим точно так же, только будем обрабатывать

событие Loaded этого окна.

Предположим, что мы хотим, чтобы в поле ввода txtAge сразу при открытии

окна присутствовало число 20. Тогда мы напишем такой обработчик события

Loaded:

private void wndAdd_Loaded(object sender, EventArgs e)

{

(sender as AddWindow).txtAge.Text = "20";

}

И дополним код обработчика события Click кнопки Добавить выражением,

привязывающим этот обработчик к событию Closed диалогового окна:

AddWindow wndAdd = new AddWindow();

wndAdd.Loaded += wndAdd_Loaded;

wndAdd.Closed += wndAdd_Closed;

wndAdd.Show();

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

будет изначально присутствовать число 20. Значит, мы все сделали пра-

вильно!

Окна-предупреждения

Окно-предупреждение позволяет вывести короткое текстовое сообщение для

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

блемы в работе программы или, наоборот, сигнал, что длительный процесс

был успешно закончен. Особенности окна-предупреждения перечислены

далее.

� Окно-предупреждение выводится в отдельном от Web-обозревателя окне.

(Фактически платформа Silverlight использует окна-предупреждения

Windows.)

� Окно-предупреждение блокирует доступ к окну Web-обозревателя. Поль-

зователь не сможет работать с Web-страницей, пока не закроет окно-

предупреждение.

Page 353: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 19. Вторичные окна 341

� Окно-предупреждение может содержать только текст и кнопки, одну (ОK)

или две (ОK и Отмена).

� Окно-предупреждение может быть выведено на экран страницей, подстра-

ницей или диалоговым окном.

� При выводе окна-предупреждения выполнение вызвавшего его метода ос-

танавливается, пока данное окно не будет закрыто.

Последний пункт указывает на принципиальную разницу между диалоговы-

ми окнами и окнами-предупреждениями. Если код, вызвавший диалоговое

окно, продолжает выполняться после его вызова, то выполнение кода, вы-

звавшего окно-предупреждение, останавливается до того момента, пока окно-

предупреждение не будет закрыто. Это позволяет вернуть в код, вызвавший

окно-предупреждение, сведения о том, какая кнопка была нажата для закры-

тия этого окна — ОK или Отмена.

Для вывода окна-предупреждения используется класс MessageBox. А имен-

но — его статический метод Show. Формат его вызова таков:

Show(<текст предупреждения>[, <текст заголовка>, <набор кнопок>])

Первым параметром передается строка, содержащая текст предупреждения,

которое нужно вывести на экран.

Вторым параметром передается строка с текстом, который должен присутст-

вовать в заголовке окна-предупреждения. Если этот параметр опущен, заго-

ловок будет пуст.

Третьим параметром передается значение типа перечисления MessageBoxButton.

Оно имеет два элемента: OK (в окне-предупреждении должна присутствовать

только кнопка ОK) и OKCancel (кнопки ОK и Отмена). Если этот параметр

опущен, в окне-предупреждении будет присутствовать только кнопка ОK.

Метод Show возвращает значение типа перечисления MessageBoxResult. Оно

имеет два элемента: OK (была нажата кнопка ОK) и Cancel (была нажата

кнопка Отмена).

Давайте для примера в обработчике события Click кнопки ОK диалогового

окна поместим код, проверяющий, внесены ли какие-то значения в поля вво-

да фамилии и имени, и, если не внесены, блокирующий закрытие окна и вы-

водящий соответствующее предупреждение пользователю. Код обработчика

после внесенных нами изменений станет таким:

if (txtF.Text == String.Empty)

{

MessageBox.Show("Введите фамилию");

txtF.Focus();

}

Page 354: Дронов В. Самоучитель Silverlight 3 (2010)

342 Часть V. Графические возможности Silverlight. Многостраничные приложения

else

{

if (txtN1.Text == String.Empty)

{

MessageBox.Show("Введите имя");

txtN1.Focus();

}

else

{

this.DialogResult = true;

}

}

Здесь мы не только выводим предупреждения, но и устанавливаем фокус

ввода в соответствующее поле ввода, экономя пользователю один щелчок

мышью.

На этом разговор о вторичных окнах Silverlight можно считать законченным.

Что дальше?

В этой главе мы познакомились с диалоговыми окнами и окнами-

предупреждениями Silverlight, которые вместе называются вторичными ок-

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

разделить интерфейс приложения на части и выводить их на экран, только

когда в них возникнет нужда.

В следующей части мы в очередной раз закончим с интерфейсом и опять

начнем работать с данными. На этот раз с данными, хранящимися в локаль-

ных и удаленных файлах и Web-службах. Именно локальным файлам будет

посвящена очередная глава.

Page 355: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ VI

Работа с файлами и Web-службами

Глава 20. Работа с локальными файлами

Глава 21. Работа с удаленными файлами

Глава 22. Работа с Web-службами

Page 356: Дронов В. Самоучитель Silverlight 3 (2010)
Page 357: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 20

Работа с локальными файлами

В предыдущей главе мы изучали возможности Silverlight по выводу части

интерфейса приложения в отдельных окнах. И реализовали в приложении

GridDemo возможность добавления в коллекцию нового специалиста, для вво-

да сведений о котором использовали диалоговое окно.

И наблюдали, как закрытие приложения после очередной проверки на рабо-

тоспособность уничтожает всех добавленных в коллекцию специалистов...

Любое приложение, работающее с данными, должно предусматривать воз-

можность их сохранения (если, конечно, оно не предназначено исключитель-

но для их просмотра). Либо оно сохраняет данные на локальном диске ком-

пьютера, в локальных файлах, либо помещает их на какой-либо удаленный

ресурс (скажем, в Web-службу). В противном случае грош ему цена!

Поэтому нам самое время заняться работой с файлами.

Заметим сразу, что Silverlight 3 предлагает нам три варианта.

� Мы можем сохранить какие угодно файлы в каких угодно папках в так

называемом изолированном хранилище — особой папке, выделенной сре-

дой исполнения Silverlight для хранения файлов данного приложения.

� Мы можем сохранить данные в файле, который выберет пользователь и

который может быть сохранен где угодно.

� Мы можем считать данные из файла, который выберет пользователь и ко-

торый может находиться где угодно.

И начнем мы с изучения изолированного хранилища. А в процессе этого так-

же научимся чтению и письму, в смысле, записывать информацию в файл и

впоследствии читать ее оттуда.

Page 358: Дронов В. Самоучитель Silverlight 3 (2010)

346 Часть VI. Работа с файлами и Web-службами

Изолированное хранилище

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

Изолированное хранилище может содержать сколько угодно файлов разного типа. Для организации этих файлов мы можем использовать папки, вклады-вая их друг в друга.

Каждое конкретное приложение, точнее, сборка, в которой реализовано дан-ное приложение, получает свое собственное изолированное хранилище. Ни-какое другое приложение не сможет получить к нему доступ.

Обычно в изолированном хранилище размещают временные данные, не предназначенные для долгого хранения, служебные данные, настройки при-ложения, и вообще вспомогательную информацию. Рабочие данные, как пра-вило, хранятся в других местах или вообще на удаленном ресурсе (в той же Web-службе).

Давайте для примера реализуем хранение коллекции специалистов нашего

приложения GridDemo в изолированном хранилище. Используем для этого файл specs.dat, который поместим в папку data.

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

дого свойства класса Person — в своей отдельной строке. "Внутреннюю" кол-

лекцию Platforms мы сохраним в конце набора строк в таком же "построч-ном" виде; каждый элемент коллекции — в своей строке. А для разделения наборов строк, хранящих данные о разных специалистах, используем пустую строку.

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

Назовем эти кнопки btnSave и btnLoad. И удалим из класса

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

Открытие изолированного хранилища

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

GetUserStoreForApplication класса IsolatedStorageFile. Этот метод не при-нимает параметров.

Page 359: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 347

Что касается результата, возвращаемого методом GetUserStoreForApplication,

то это объект класса IsolatedStorageFile. Данный класс представляет само

изолированное хранилище, выделенное для данного приложения.

В названии этого класса почему-то присутствует слово file. Притом что изоли-рованное хранилище физически представляет собой не файл, а папку...

Класс IsolatedStorageFile объявлен в пространстве имен System.IO.

IsolatedStorage. Оно изначально не отображено, поэтому мы либо должны

отобразить его, либо использовать полные имена типов.

System.IO.IsolatedStorage.IsolatedStorageFile isfData =

�System.IO.IsolatedStorage.IsolatedStorageFile.

�GetUserStoreForApplication();

Создание папок

Открыв изолированное хранилище, можно начинать работу с ним. В частно-

сти, создать в нем папки и файлы.

Для создания папки используется метод CreateDirectory класса

IsolatedStorageFile. Этот метод принимает в качестве параметра строку

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

isfData.CreateDirectory("\\data\\temp");

Здесь мы создаем папку с путем data\temp. При этом сначала в корневой пап-

ке изолированного хранилища будет создана папка data, а в ней — папка

temp. Фактически мы создали сразу две папки вызовом одного метода.

Обратим внимание, что в пути к создаваемой папке мы дублируем символы

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

ный слэш используется для обозначения специальных символов Silverlight

(подробнее см. в главе 9); чтобы поместить в строку сам символ обратного

слэша, нужно использовать специальный символ \\. Что мы и сделали.

Создание и открытие файлов

Что касается создания файлов, то для этого лучше использовать метод

OpenFile класса IsolatedStorageFile. Этот же метод используется для откры-

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

Формат вызова этого метода таков:

OpenFile(<путь файла>, <действие>[, <режим доступа>])

Page 360: Дронов В. Самоучитель Silverlight 3 (2010)

348 Часть VI. Работа с файлами и Web-службами

Первым параметром передается путь создаваемого или открываемого файла

в виде строки.

Вторым параметром передается значение типа перечисления FileMode, указы-

вающее среде исполнения Silverlight, какое действие следует выполнить над

файлом. Данное перечисление включает следующие элементы:

� CreateNew — создать новый файл с данным именем и открыть его;

� Open — открыть существующий файл;

� OpenOrCreate — открыть файл, если он существует, или создать и открыть

файл, если он не существует;

� Append — открыть существующий файл таким образом, что в него можно

будет только дописывать информацию;

� Truncate — открыть существующий файл и удалить его текущее содержи-

мое;

� Create — открыть файл и удалить его текущее содержимое, если данный

файл существует, или создать и открыть файл, если он не существует.

Третьим параметром передается значение типа перечисления FileAccess, ука-

зывающее, какие действия над содержимым файла сможет выполнить при-

ложение. В этом перечислении имеется три элемента:

� Read — приложение сможет только читать из файла;

� Write — приложение сможет только записывать в файл;

� ReadWrite — приложение сможет и читать из файла, и записывать в файл.

Если третий параметр опущен, приложение получит возможность и читать из

файла, и записывать в файл.

Перечисления FileMode и FileAccess объявлены в пространстве имен

System.IO. Оно изначально не отображено, поэтому мы либо должны отобра-

зить его, либо использовать полные имена типов.

Для представления содержимого открытого файла в Silverlight-приложении

используется так называемый поток. Это объект особого класса, который

хранит содержимое файла в оперативной памяти компьютера и реализует

операции чтения и записи. Когда мы читаем что-то из потока, мы фактически

выполняем чтение из хранящегося на диске файла; то же самое и с записью.

Также потоки используются для представления данных, хранящихся в памяти

и загружаемых по сети. В главе 21, рассматривая загрузку файлов по сети, мы

столкнемся с такими потоками.

Классов потоков в Silverlight довольно много. Родителем для всех их являет-

ся класс Stream; его потомки добавляют функциональность, относящуюся

Page 361: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 349

к определенным типам данных: хранящихся в файлах, в памяти или загру-

жаемых по сети. Так, поток файла, находящегося в изолированном хранили-

ще, представляет класс IsolatedStorageFileStream.

Так вот, метод OpenFile как раз и возвращает значение типа

IsolatedStorageFileStream. То есть поток открытого файла.

Класс IsolatedStorageFileStream объявлен в пространстве имен

System.IO.IsolatedStorage. Оно изначально не отображено, поэтому мы либо

должны отобразить его, либо использовать полные имена типов.

System.IO.IsolatedStorage.IsolatedStorageFileStream issData =

�isfData.OpenFile("\\data\\specs.dat", System.IO.FileMode.Create,

�System.IO.FileAccess.Write);

Здесь мы указываем среде исполнения Silverlight открыть файл /data/specs.dat

и уничтожить его содержимое, если такой файл существует, и создать его

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

System.IO.IsolatedStorage.IsolatedStorageFileStream issData =

�isfData.OpenFile("\\data\\specs.dat", System.IO.FileMode.Open,

�System.IO.FileAccess.Read);

А здесь мы открываем тот же файл только для чтения.

Запись в файл

Все классы потоков представляют набор методов для чтения и записи дан-

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

ками обычно прибегают к помощи классов-"помощников" StreamReader и

StreamWriter. Первый из этих классов обеспечивает удобное чтение данных,

второй — их запись.

Поскольку наш файл еще ничего не содержит, нам нужно что-то в него запи-

сать. Значит, возьмем в оборот класс StreamWriter. Как мы уже выяснили,

этот класс позволяет записывать в поток значения различных типов.

Класс StreamWriter объявлен в пространстве имен System.IO. Оно изначально

не отображено, поэтому мы либо должны отобразить его, либо использовать

полные имена типов.

Прежде всего, мы должны создать объект класса StreamWriter, который будет

реализовывать запись в нужный поток. Для этого мы используем оператор

new и передадим конструктору в качестве единственного параметра указатель

на поток, запись в который мы хотим выполнить.

System.IO.StreamWriter stwData = new System.IO.StreamWriter(issData);

Page 362: Дронов В. Самоучитель Silverlight 3 (2010)

350 Часть VI. Работа с файлами и Web-службами

Класс StreamWriter поддерживает метод WriteLine. Он принимает единствен-

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

любым из элементарных типов Silverlight. Результата данный метод не воз-вращает.

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

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

Unicode.

Person prs = colItems[0];

stwData.WriteLine(prs.F);

stwData.WriteLine(prs.N1);

stwData.WriteLine(prs.N2);

stwData.WriteLine(prs.Age);

foreach (string platform in prs.Platforms)

{

stwData.WriteLine(platform);

}

stwData.WriteLine();

Здесь мы записываем в поток значения свойств первого элемента коллекции специалистов: фамилию, имя, отчество, возраст и список платформ. И завер-

шаем все это пустой строкой, которая отделит данные одного специалиста от данных другого.

В результате мы получим поток с таким содержимым:

Петров

Петр

Петрович

30

Flash

<-- Это пустая строка

Как видим, метод WriteLine сам преобразовал целое число, обозначающее возраст специалиста, в строку, которую и записал в поток.

Чтение из файла

Рассмотрим теперь процесс чтения из файла. Для этого служит класс

StreamReader, позволяющий читать из потока одну за другой хранящиеся там строки.

Page 363: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 351

Класс StreamReader объявлен в пространстве имен System.IO. Оно изначально не отображено, поэтому мы либо должны отобразить его, либо использовать полные имена типов.

Для создания объекта класса StreamReader, который будет читать из потока, мы используем оператор new и передадим конструктору в качестве единст-венного параметра указатель на поток, откуда мы собираемся читать.

System.IO.StreamReader strData = new System.IO.StreamReader(issData);

Класс StreamReader поддерживает метод ReadLine. Он читает очередную стро-ку из потока и возвращает ее в виде результата. Если строк в потоке больше нет (т. е. достигнут конец потока), возвращается значение null. Параметров этот метод не принимает.

Последовательно вызывая метод ReadLine, мы можем прочитать все содер-жимое потока. Нужно только каждый раз проверять, не равен ли возвращен-ный им результат null, — если это так, значит, достигнут конец потока, и чи-тать больше нечего.

Другой полезный метод класса StreamReader — ReadToEnd. Он читает из пото-ка все остальное содержимое, начиная с текущей строки и до самого конца потока, и возвращает его в виде строки. Если достигнут конец потока, воз-вращается пустая строка. Параметров данный метод также не принимает.

Еще может оказаться полезным свойство EndOfStream. Оно возвращает значе-ние true, если достигнут конец потока, и false, если еще есть что читать.

Person prs = new Person();

prs.F = strData.ReadLine();

prs.N1 = strData.ReadLine();

prs.N2 = strData.ReadLine();

if (int.TryParse(strData.ReadLine(), out iAge))

{

prs.Age = iAge;

}

while (!(strData.EndOfStream))

{

s = strData.ReadLine();

if (s == String.Empty)

{

break;

}

else

{

prs.Platforms.Add(s);

}

}

colItems.Add(prs);

Page 364: Дронов В. Самоучитель Silverlight 3 (2010)

352 Часть VI. Работа с файлами и Web-службами

Здесь мы выполняем чтение из потока сохраненных ранее сведений о специа-

листе и создание на их основе нового элемента коллекции специалистов.

Обратим внимание, как мы читаем список платформ, которыми владеет спе-

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

пока не достигнут конец потока. В теле этого цикла мы считываем очередную

строку и проверяем, не пуста ли она, т. е. не достигнут ли конец массива дан-

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

бавляем ее во "внутреннюю" коллекцию Platforms. Если же мы считали пус-

тую строку, то прерываем выполнение цикла — считывание списка платформ

закончено.

Закрытие потока и файла

После завершения всех операций чтения и записи данных нам нужно сделать

две обязательные вещи. Во-первых, следует закрыть объект-"помощник"

класса StreamWriter или StreamReader, с помощью которого мы выполняли

эти операции. Во-вторых, следует закрыть сам поток.

Операция закрытия завершает все процессы обмена информацией с файлом,

закрывает сам этот файл на уровне операционной системы и освобождает па-

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

циях с файлом. Повторим — это обязательные операции, которые нам в лю-

бом случае следует выполнить.

Обычно все операции с потоком заключаются в блок try-finally, в секции

finally которого помещаются операции закрытия объекта-"помощника" и по-

тока. Даже если в процессе работы с потоком произойдет ошибка, операции

закрытия все равно будут выполнены. (Подробнее о блоке try-finally и вооб-

ще об обработке исключений говорилось в главе 12.)

Классы StreamWriter и StreamReader поддерживают метод Close. Он не при-

нимает параметров, не возвращает результата и выполняет закрытие объекта-

"помощника". Заодно он закрывает и поток, к которому привязан, так что

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

strData.Close();

Все классы потоков также поддерживают метод Close. Он полностью анало-

гичен только что нами рассмотренному.

После закрытия потока мы не сможем выполнить над ним никаких действий. Для этого нам снова придется его открыть.

Page 365: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 353

Проверка существования файлов и папок

Среда исполнения Silverlight предлагает нам несколько методов класса

IsolatedStorageFile для проверки существования файлов и папок в изолиро-ванном хранилище. Сейчас мы их рассмотрим.

Метод FileExists возвращает true, если файл с переданным ему в качестве

единственного параметра путем существует, и false в противном случае.

if (isfData.FileExists("\\data\\specs.dat"))

{

. . .

Метод DirectoryExists возвращает true, если папка с переданным ему в каче-

стве единственного параметра путем существует, и false в противном случае.

if (!(isfData.DirectoryExists("\\data")))

{

isfData.CreateDirectory("\\data");

}

Здесь мы проверяем, существует ли папка \data, и, если не существует, созда-ем ее.

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

Шаблоны для имен файлов могут содержать символы * и ?. Первый обозна-чает любое количество любых символов, второй — один любой символ. Так,

шаблон *.* соответствует всем файлам, *.dat — всем файлам с расширением

dat, а ?.txt — всем файлам с расширением txt, чье имя содержит один сим-вол.

Метод GetFileNames возвращает массив строк, содержащий имена найденных файлов. Обратим внимание: не пути, а только имена! Если заданному шабло-ну не удовлетворяет ни один файл, возвращается массив нулевой длины. (О массивах шла речь в главе 9.)

string[] files = isfData.GetFileNames("\\data\\*.txt");

if (files.Length > 0)

{

. . .

Здесь мы получаем список всех файлов с расширением txt, хранящихся

в папке \data.

Page 366: Дронов В. Самоучитель Silverlight 3 (2010)

354 Часть VI. Работа с файлами и Web-службами

Аналогичный метод GetDirectoryNames позволяет получить список всех папок

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

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

вой папке изолированного хранилища. Этот список также возвращается в ви-де массива строк.

string[] folders = isfData.GetDirectoryNames("\\?ata");

if (folders.Length > 0)

{

. . .

Здесь мы получаем список всех папок, хранящихся в корневой папке изоли-рованного хранилища, чьи имена состоят их четырех символов, три послед-

ние из которых — "ata".

Удаление файлов и папок

Файлы и папки приходится не только создавать и удалять. Поэтому рассмот-

рим методы класса IsolatedStorageFile, которые позволят нам это сделать.

Метод DeleteFile удаляет файл, путь которого передан ему в качестве един-ственного параметра в виде строки. Результата он не возвращает.

При вызове метода DeleteFile файл удаляется безвозвратно.

isfData.DeleteFile("\\data\\specs.dat");

Метод DeleteDirectory удаляет папку, путь которой передан ему в качестве единственного параметра в виде строки. Результата он также не возвращает.

При удалении папки вызовом метода DeleteDirectory папка не должна со-

держать ни файлов, ни других папок. Папка будет удалена безвозвратно.

isfData.DeleteDirectory("\\data");

Увеличение квоты изолированного хранилища

Изолированное хранилище имеет ограниченный размер, который называется квотой. По умолчанию квота равна 1 048 576 байт, или 1 Мбайт.

Все Silverlight-приложения, загруженные с одного Web-сайта, делят одну

квоту. Это значит, что общий размер изолированных хранилищ, которые они

Page 367: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 355

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

Silverlight-приложения, запущенные с локального диска, также считаются

загруженными с одного Web-сайта и также имеют одну квоту на всех.

Если какому-либо приложению не хватит в изолированном хранилище места

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

размер квоты. Но при этом нужно обязательно иметь в виду, что пользова-

тель может согласиться увеличить квоту, а может и не согласиться.

Для работы с квотой класс IsolatedStorageFile предоставляет два свойства и

один метод. Давайте о них поговорим.

Свойство Quota, как понятно из названия, возвращает размер квоты в байтах

в виде длинного знакового целого числа (long). Это свойство доступно только

для чтения.

Свойство AvailableFreeSpace возвращает размер доступного в изолированном

хранилище места в байтах в виде длинного знакового целого числа. Это

свойство также доступно только для чтения.

Метод IncreaseQuotaTo пытается увеличить размер квоты до размера, кото-

рый передается ему в качестве единственного параметра в байтах в виде

длинного знакового целого числа. При его вызове на экране появляется окно-

предупреждение, спрашивающее пользователя, согласен ли он увеличить

размер квоты, и содержащее кнопки Yes и No. Если пользователь нажмет

кнопку Yes, соглашаясь с увеличением квоты, метод IncreaseQuotaTo вернет

значение true; если же пользователь не хочет увеличивать квоту и нажмет

кнопку No, этот метод вернет значение false.

long availSpace = 524288;

if (isfData.AvailableFreeSpace < availSpace)

{

if (isfData.IncreaseQuotaTo(isfData.Quota + availSpace))

{

. . .

}

else

{

. . .

}

}

Здесь мы сначала проверяем, доступно ли в изолированном хранилище 512 Кбайт (524 288 байт) свободного пространства, и, если недоступно, за-прашиваем у пользователя увеличение квоты на эту величину. Отметим, что

методу IncreaseQuotaTo мы передали новый размер квоты, а не величину, на

Page 368: Дронов В. Самоучитель Silverlight 3 (2010)

356 Часть VI. Работа с файлами и Web-службами

которую нужно увеличить квоту. Если пользователь согласится с увеличени-ем квоты, мы получим изолированное хранилище предельным размером в 1,5 Мбайт.

Удаление изолированного хранилища

Помимо удаления файлов и папок, Silverlight позволяет нам удалить само изолированное хранилище со всеми хранящимися в нем файлами и папками.

Для этого предназначен метод Remove класса IsolatedStorageFile. Этот метод не принимает параметров и не возвращает результата.

isfData.Remove();

Отметим, что изолированное хранилище будет удалено только после завер-шения приложения. Это позволит нам хранить в изолированном хранилище какие-либо временные данные, не заботясь о том, чтобы не забыть потом их удалить — это сделает сама среда исполнения Silverlight, когда мы закроем приложение.

В результате вызова метода Remove содержимое изолированного хранилища удаляется безвозвратно.

Закрытие изолированного хранилища

После завершения всех манипуляций с изолированным хранилищем его

также следует закрыть. Для этого предназначен метод Dispose класса

IsolatedStorageFile. Он не принимает параметров и не возвращает резуль- тата.

isfData.Dispose();

Как и в случае потока, после закрытия изолированного хранилища мы не смо-жем выполнить над ним никаких действий. Для этого нам снова придется его открыть вызовом статического метода GetUserStoreForApplication класса

IsolatedStorageFile.

Полный код примеров работы с изолированным хранилищем

Кажется, мы собирались реализовать в приложении GridDemo возможность сохранения коллекции специалистов в изолированном хранилище и загрузки ее оттуда... Так и есть! Чуть не забыли!..

Page 369: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 357

Вот код обработчика события Click кнопки Сохранить (btnSave).

System.IO.IsolatedStorage.IsolatedStorageFile isfData =

�System.IO.IsolatedStorage.IsolatedStorageFile.

�GetUserStoreForApplication();

if (!(isfData.DirectoryExists("\\data")))

{

isfData.CreateDirectory("\\data");

}

System.IO.IsolatedStorage.IsolatedStorageFileStream issData =

�isfData.OpenFile("\\data\\specs.dat", System.IO.FileMode.Create,

�System.IO.FileAccess.Write);

System.IO.StreamWriter stwData = new System.IO.StreamWriter(issData);

foreach (Person prs in colItems)

{

stwData.WriteLine(prs.F);

stwData.WriteLine(prs.N1);

stwData.WriteLine(prs.N2);

stwData.WriteLine(prs.Age);

foreach (string platform in prs.Platforms)

{

stwData.WriteLine(platform);

}

stwData.WriteLine();

}

stwData.Close();

isfData.Dispose();

А это — код обработчика события Click кнопки Загрузить (btnLoad).

int iAge;

string s;

System.IO.IsolatedStorage.IsolatedStorageFile isfData =

�System.IO.IsolatedStorage.IsolatedStorageFile.

�GetUserStoreForApplication();

if (isfData.FileExists("\\data\\specs.dat"))

{

System.IO.IsolatedStorage.IsolatedStorageFileStream issData =

�isfData.OpenFile("\\data\\specs.dat", System.IO.FileMode.Open,

�System.IO.FileAccess.Read);

System.IO.StreamReader strData = new System.IO.StreamReader(issData);

colItems.Clear();

while (!(strData.EndOfStream))

Page 370: Дронов В. Самоучитель Silverlight 3 (2010)

358 Часть VI. Работа с файлами и Web-службами

{

Person prs = new Person();

prs.F = strData.ReadLine();

prs.N1 = strData.ReadLine();

prs.N2 = strData.ReadLine();

if (int.TryParse(strData.ReadLine(), out iAge))

{

prs.Age = iAge;

}

while (!(strData.EndOfStream))

{

s = strData.ReadLine();

if (s == String.Empty)

{

break;

}

else

{

prs.Platforms.Add(s);

}

}

colItems.Add(prs);

}

strData.Close();

}

isfData.Dispose();

Ничего нового по сравнению с тем, что говорилось в этой главе, тут нет.

Работа со сторонними файлами

Да, изолированное хранилище — штука полезная. В нем мы сможем сохра-

нить любые данные, и храниться они будут в целости и сохранности, пока мы

их не извлечем. Единственная наша забота в этом случае — не превзойти

квоту.

Но бывают случаи, когда приходится открывать и сохранять произвольные

файлы, которые выберет сам пользователь. В этом случае нам помогут два

полезных класса, предусмотренных заботливой Silverlight. Сейчас мы ими

займемся.

Давайте дополнительно реализуем в нашем приложении GridDemo хранение

коллекции специалистов в произвольном файле. Для этого создадим на глав-

ной странице кнопки Сохранить в файл и Загрузить из файла, которым

Page 371: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 359

дадим имена btnSaveToFile и btnLoadFromFile. Формат хранения пусть будет

тот же, что и в случае изолированного хранилища.

Сохранение данных в стороннем файле

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

SaveFileDialog. Он позволяет вывести на экран стандартное диалоговое окно сохранения файла Windows, в котором пользователь сможет указать нужный файл.

Класс SaveFileDialog поддерживает свойство Filter, позволяющее указать типы файлов, доступные для выбора пользователем. Типы файлов выбирают-ся в раскрывающемся списке Тип файла диалогового окна сохранения фай-ла; после выбора какого-либо типа в списке файлов этого окна будут показа-ны только файлы, соответствующие данному типу.

Значение свойства Filter представляет собой строку, которая содержит опи-сания типов в виде пар следующего вида:

<описание типа файлов>|<шаблоны имен, содержащих только расширения и

�отделенные друг от друга символом точки с запятой>

отделенных друг от друга символом вертикальной черты (|). Шаблоны имен

здесь указываются в виде *.<расширение файла>.

Давайте рассмотрим несколько примеров строк, задающих типы файлов.

Файлы данных (*.dat)|*.dat

Эта строка задает тип файлов данных, имеющих расширение dat.

Файлы данных (*.dat, *.txt)|*.dat;*.txt

Эта строка задает тип файлов данных, имеющих расширение dat или txt.

Файлы данных (*.dat)|*.dat|Текстовые файлы (*.txt)|*.txt|

�Все файлы (*.*)|*.*

А эта строка задает сразу три типа файлов: файлы данных с расширением dat, текстовые файлы с расширением txt и все файлы с любым расширением.

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

ем вида *.* (любые файлы), никакого расширения к имени файла добавлено не будет.

Свойство FilterIndex указывает номер выбранного типа файлов в виде цело-

го числа. Отметим, что нумерация форматов в случае свойства FilterIndex

Page 372: Дронов В. Самоучитель Silverlight 3 (2010)

360 Часть VI. Работа с файлами и Web-службами

начинается не с нуля, а с единицы. Единица, кстати, — значение этого свой-

ства по умолчанию.

Мы можем указать номер выбранного типа перед выводом диалога сохране-

ния файла; тогда данный тип файлов будет изначально выбран в его раскры-

вающемся списке Тип файла. Также мы можем после выбора пользователем

файла для сохранения получить значение этого свойства, чтобы выяснить,

какой тип файлов он выбрал, и записать в файл данные в соответствующем

формате.

Если в качестве значения свойства Filter задана пустая строка (а это значе-

ние данного свойства по умолчанию), диалог сохранения файла будет пока-зывать все файлы, а при вводе пользователем имени файла вручную никакого

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

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

Задав все нужные параметры, мы можем вывести диалог сохранения файла на

экран. Для этого служит метод ShowDialog. Он выводит диалог на экран и ждет, пока пользователь его не закроет. Если пользователь закроет диалог

нажатием кнопки сохранения, метод вернет значение true; если же пользова-

тель нажмет кнопку отмены, будет возвращено значение false. Также в неко-

торых случаях метод ShowDialog может возвращать значение null; автору не удалось выяснить, когда это может случиться. Параметров этот метод не

принимает.

Если пользователь нажал кнопку сохранения в диалоге сохранения файла и

метод ShowDialog вернул значение true, мы можем получить имя файла, ука-

занное пользователем в диалоге. Для этого служит свойство SafeFileName,

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

зователем файла мы не сможем.

Но полезнее всего для нас будет метод OpenFile. Он выполняет открытие вы-

бранного файла и возвращает значение типа Stream — поток, соответствую-щий открытому файлу. Приложение сможет как записывать в этот поток, так

и читать из него. Параметров метод OpenFile не принимает.

А уж с потоком мы умеем управляться — научились, когда работали с фай-

лами в изолированном хранилище.

Давайте создадим для кнопки Сохранить в файл такой обработчик события

Click:

SaveFileDialog sfdData = new SaveFileDialog();

sfdData.Filter = "Файлы данных (*.dat)|*.dat|

�Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*";

Page 373: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 361

if (sfdData.ShowDialog() == true)

{

System.IO.Stream strData = sfdData.OpenFile();

System.IO.StreamWriter stwData = new System.IO.StreamWriter(strData);

foreach (Person prs in colItems)

{

stwData.WriteLine(prs.F);

stwData.WriteLine(prs.N1);

stwData.WriteLine(prs.N2);

stwData.WriteLine(prs.Age);

foreach (string platform in prs.Platforms)

{

stwData.WriteLine(platform);

}

stwData.WriteLine();

}

stwData.Close();

}

Здесь мы создаем объект класса SaveFileDialog, который выведет диалог со-хранения файла на экран, задаем для него список типов файлов и выводим на экран. После того как пользователь закроет этот диалог, мы проверяем, был

ли он закрыт нажатием кнопки сохранения, для чего сравниваем возвращен-

ное методом ShowDialog значение с true. Отметим, что, поскольку данный

метод может в каких-то случаях возвращать значение null, нам придется яв-

но сравнить возвращенное им значение с true.

Дальше мы открываем полученный файл вызовом метода OpenFile, создаем

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

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

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

в Блокноте (или другом простейшем текстовом редакторе, поддерживающем кодировку Unicode) и посмотрим, все ли сохранилось правильно.

Загрузка данных из стороннего файла

Если все сохранилось правильно, перейдем к загрузке данных из стороннего

файла.

Сначала нам нужно спросить у пользователя, из какого файла он хочет загру-

зить данные. Для этого мы используем объект класса OpenFileDialog, кото-

Page 374: Дронов В. Самоучитель Silverlight 3 (2010)

362 Часть VI. Работа с файлами и Web-службами

рый позволяет вывести на экран стандартное диалоговое окно открытия фай-

ла Windows, где пользователь сможет указать нужный ему файл.

Класс OpenFileDialog поддерживает уже знакомые нам свойства Filter и

FilterIndex и метод ShowDialog. Используются они точно так же, как и одно-

именные свойства и метод класса SaveFileDialog.

Чтобы получить выбранный пользователем файл, нам придется использовать

свойство File. Оно возвращает значение типа FileInfo. Класс FileInfo пред-назначен для хранения различных сведений о файле и предоставляет методы для манипуляции им.

Чтобы открыть файл, проще всего вызвать метод OpenText класса FileInfo.

Он сразу создает и поток, и связанный с ним объект класса StreamReader, ко-торый и возвращает в качестве результата. Параметров этот метод не прини-мает.

В качестве альтернативы можно рассмотреть метод OpenRead. Он открывает файл и создает соответствующий ему поток, который и возвращает в качестве

результата. Поток этот представляет собой объект класса FileStream, пред-

ставляющего поток именно файла и являющегося потомком класса Stream.

Параметров метод OpenRead также не принимает.

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

� Name — возвращает имя файла без пути в виде строки.

� Length — возвращает размер файла в байтах в виде длинного знакового целого числа.

� Exists — возвращает true, если файл существует, и false в противном случае.

Теперь — за дело. Создадим для кнопки Загрузить из файла такой обработ-

чик события Click:

int iAge;

string s;

OpenFileDialog ofdData = new OpenFileDialog();

ofdData.Filter = "Файлы данных (*.dat)|*.dat|

�Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*";

if (ofdData.ShowDialog() == true)

{

System.IO.StreamReader strData = ofdData.File.OpenText();

colItems.Clear();

while (!(strData.EndOfStream))

{

Person prs = new Person();

Page 375: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 20. Работа с локальными файлами 363

prs.F = strData.ReadLine();

prs.N1 = strData.ReadLine();

prs.N2 = strData.ReadLine();

if (int.TryParse(strData.ReadLine(), out iAge))

{

prs.Age = iAge;

}

while (!(strData.EndOfStream))

{

s = strData.ReadLine();

if (s == String.Empty)

{

break;

}

else

{

prs.Platforms.Add(s);

}

}

colItems.Add(prs);

}

strData.Close();

}

Проверим приложение в действии. Запустим его, нажмем кнопку Загрузить

из файла, выберем в появившемся на экране диалоге открытия файла сохра-

ненный ранее файл и нажмем кнопку открытия. Если мы не допустили оши-

бок, содержимое этого файла должно загрузиться в приложение и отобра-

зиться в таблице.

И закончим на этом с локальными файлами.

Что дальше?

В этой главе мы научились сохранять информацию в локальных файлах и

загружать ее оттуда. Причем файлы эти могут как храниться в изолирован-

ном хранилище, так и находиться где угодно на диске компьютера.

Завершив разговор о локальных файлах, рассмотрим работу с файлами уда-

ленными, загружаемыми по сети с Web-сервера. Этому будет посвящена сле-

дующая глава.

Page 376: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 21

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

В предыдущей главе мы занимались записью информации в локальные фай-

лы и чтением из них. Также мы узнали об изолированном хранилище — сво-

его рода личном диске для Silverlight-приложений, где они могут хранить

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

А еще мы научились работать со сторонними файлами, которые могут распо-

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

Но, поскольку современный компьютерный мир во многом "сетевой", нам

надо научиться работать и с файлами, хранящимися на удаленных компьюте-

рах. Так сказать, выйти в "большой свет".

Как правило, для распространения файлов в сети, неважно — Интернете или

локальной сети организации, используются Web-серверы. Программное

обеспечение Web-серверов зачастую бесплатно, но при этом легко в развер-

тывании, удобно и имеет богатые настройки на все случаи жизни. Web-

сервер — пожалуй, самый простой и дешевый способ сделать доступными

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

Silverlight предоставляет развитые средства для загрузки произвольных фай-

лов с Web-серверов. И эта глава будет посвящена именно им.

Использование невключенных ресурсов

Если нам нужно загружать с Web-сервера строго ограниченный и известный

изначально набор файлов, проще всего включить эти файлы в состав сборки,

содержащей код приложения, и пометить как невключенные ресурсы. В гла-

ве 7 мы уже работали с таким ресурсом — аудиофайлом, а о том, как превра-

тить ресурс сборки в невключенный, было рассказано в главе 8. Так что все

это нам уже знакомо.

Page 377: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 21. Работа с удаленными файлами 365

Данный подход имеет два значительных преимущества.

� Для собственно загрузки файла, являющегося невключенным ресурсом

сборки, не требуется никакого программирования. Среда исполнения

Silverlight сделает все за нас.

� Файлы, являющиеся невключенными ресурсами сборки, могут быть за-

гружены не только с Web-сервера, но и с локального диска компьютера.

А вот недостаток всего один, но тоже значительный. Нам нужно заранее

знать, какие файлы мы будем использовать в своем приложении, и включить

их в состав сборки в качестве ресурсов. Если же наше приложение в процессе

работы должно загружать и использовать произвольные файлы из разных ис-

точников, нам придется искать другой способ сделать это. В конце концов, не

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

приложению!..

Программная загрузка файлов по сети

Зачастую приходится создавать приложения, которые используют не извест-

ный на момент разработки набор файлов. Так, если мы создаем приложение

для просмотра графических файлов, опубликованных на каком-либо Web-

ресурсе, мы не можем узнать, какие именно файлы там опубликованы! Что

делать в таком случае?

Класс WebClient

Использовать объект класса WebClient. Именно этот класс предоставляет нам

методы и события, позволяющие запустить загрузку файла по сети и отсле-

дить момент завершения его загрузки.

С помощью класса WebClient мы можем загрузить любой файл с любого Web-

сервера. Это его единственное, и ключевое, преимущество.

Что касается недостатков, то их два.

� Чтобы реализовать загрузку файла средствами класса WebClient, нам при-

дется написать довольно много C#-кода. Впрочем, это очевидно.

� Средствами класса WebClient мы можем загрузить файл только с Web-

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

чится.

Так что перед тем, как начать эксперименты с загрузкой удаленных файлов,

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

программ Web-серверов, доступных в данный момент, в том числе и бес-

платных. Проще всего применить поставляемый в составе Windows Microsoft

Page 378: Дронов В. Самоучитель Silverlight 3 (2010)

366 Часть VI. Работа с файлами и Web-службами

Internet Information Services. Установка и работа с ним описаны в интерак-

тивной документации Windows.

Кроме того, мы можем завести пользовательский счет на любом Web-

ресурсе, бесплатно предоставляющем место для публикации сайтов клиентов

(хостинг-провайдере); сейчас таких Web-ресурсов довольно много. Для тех,

кто не имеет опыта работы с программами Web-серверов, это может оказать-

ся наилучшим вариантом.

Так или иначе, запасемся каким-либо графическим файлом, назовем его, ска-

жем, image.jpg и сразу опубликуем его на локально установленном Web-

сервере или загрузим на сайт выбранного нами хостинг-провайдера. Чтобы

не ломать голову, поместим его в корневой папке. (О корневой папке и про-

чем, связанном с Web-сайтами, было рассказано в главе 1.)

Создадим в Visual Web Developer 2008 новый проект с именем RemoteImage.

На главной странице поместим "пустой" компонент Image и дадим ему имя

imgImage. (Как мы помним из главы 7, именно компонент Image используется

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

Запуск загрузки файла

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

Поместим выполняющий это код в обработчик события Loaded страницы.

Сначала мы создадим объект класса WebClient. Выполняется это знакомым

нам способом — с помощью оператора new. Конструктору никаких парамет-

ров передавать не нужно.

WebClient wclImage = new WebClient();

Процесс загрузки файла запускает метод OpenReadAsync класса WebClient.

Формат его вызова таков:

OpenReadAsync(<интернет-адрес файла>[, <дополнительные данные>])

Первым параметром передается интернет-адрес загружаемого файла в виде

объекта класса Uri. Данный класс нам уже знаком по главе 7.

Вторым параметром могут быть переданы некие дополнительные данные.

Тип второго параметра — Object, так что эти данные могут быть любого

типа.

wclImage.OpenReadAsync(new Uri("image.jpg", UriKind.Relative));

Здесь мы передаем методу OpenReadAsync первым параметром относительный

интернет-адрес нашего файла. Дополнительные данные мы в этом случае не

передаем — они нам не нужны.

Page 379: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 21. Работа с удаленными файлами 367

Подождем пока тестировать приложение. Давайте поговорим о том, как рабо-

тает метод OpenReadAsync.

Прежде всего, этот метод запускает процесс загрузки файла. Сразу же после

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

То есть метод OpenReadAsync не ждет пока файл будет загружен.

Но как нам узнать, что файл image.jpg уже загружен, и вывести его в компо-

нент imgImage?

Окончание загрузки файла и его обработка

Именно для этого класс WebClient поддерживает событие OpenReadCompleted.

Оно возникает сразу после окончания загрузки файла.

Метод — обработчик события OpenReadCompleted должен принимать два па-

раметра. Первый параметр имеет тип Object и содержит указатель на объект,

в котором возникло это событие. А второй параметр хранит указатель на объ-

ект класса OpenReadCompletedEventArgs, который содержит дополнительные

сведения о событии.

Собственно, это для нас не новость — все обработчики событий в Silverlight

принимают два аналогичных параметра. Только ранее мы никак не пользова-

лись значениями этих параметров. А сейчас воспользуемся.

Класс OpenReadCompletedEventArgs поддерживает свойство Result. Оно хранит

указатель на объект класса Stream — поток, представляющий содержимое

загруженного файла. Пользуясь приемами, описанными в главе 20, мы смо-

жем прочитать содержимое этого потока.

Также мы можем использовать данный поток в качестве источника графиче-

ских данных для компонента Image. Для этого придется выполнить действия, перечисленные далее.

1. Создать объект класса BitmapImage, представляющий сведения о файле, в котором хранится графическое изображение, — том файле, поток кото-

рого мы получили в обработчике события OpenReadCompleted. (О классе

BitmapImage было рассказано в главе 7.)

2. Подключить этот объект к потоку, вызвав метод SetSource класса

BitmapImage. Данный метод принимает единственный параметр — поток

в виде значения типа Stream — и не возвращает результата.

3. Присвоить этот объект свойству Source компонента Image.

Вот и все. Итого три выражения.

Давайте сначала напишем обработчик события OpenReadCompleted, который

потом привяжем к нашему объекту класса WebClient.

Page 380: Дронов В. Самоучитель Silverlight 3 (2010)

368 Часть VI. Работа с файлами и Web-службами

private void wclImage_OpenReadCompleted(object sender,

�OpenReadCompletedEventArgs e)

{

System.Windows.Media.Imaging.BitmapImage bimImage = new

�System.Windows.Media.Imaging.BitmapImage();

bimImage.SetSource(e.Result);

imgImage.Source = bimImage;

}

В главе 7, создавая объект класса BitmapImage, мы передавали конструктору

в качестве параметра объект класса Uri, содержащий интернет-адрес загру-

жаемого файла. Здесь мы не передаем этот параметр, поскольку нужный нам

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

созданный объект к этому потоку.

Теперь дополним код обработчика события Loaded страницы, где запускается

процесс загрузки файла, таким образом (добавленный код выделен полужир-

ным шрифтом):

WebClient wclImage = new WebClient();

wclImage.OpenReadCompleted += wclImage_OpenReadCompleted;

wclImage.OpenReadAsync(new Uri("image.jpg", UriKind.Relative));

Добавленное выражение привязывает к объекту класса WebClient, реализую-

щего загрузку файла, только что написанный нами обработчик события

OpenReadCompleted.

Откомпилируем приложение, не запуская его, для чего выберем пункт Build

<имя проекта> меню Build. Найдем папку, где хранятся файлы исходного

кода данного проекта, откроем находящуюся в ней папку Bin/Debug и найдем

файлы пакета и текстовой Web-страницы (подробнее об этих файлах говори-

лось в главе 4). Опубликуем их на Web-сервере, в той же папке, где ранее

опубликовали файл графического изображения image.jpg (это корневая пап-

ка). Откроем Web-обозреватель и наберем в нем полный интернет-адрес тес-

товой Web-страницы; он будет иметь вид <интернет-адрес Web-

сервера>/TestPage.html. Если мы все сделали правильно, через некоторое

время мы увидим в окне Web-обозревателя наше изображение.

Ранее мы говорили, что метод OpenReadAsync может принять в качестве второ-

го параметра дополнительные данные в виде значения типа Object. Эти дан-

ные можно получить в обработчике события OpenReadCompleted через свойст-

во UserState класса OpenReadCompletedEventArgs. Значение этого свойства

также имеет тип Object.

Page 381: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 21. Работа с удаленными файлами 369

Отслеживание процесса загрузки файла

Класс WebClient поддерживает весьма примечательное событие

DownloadProgressChanged. Оно периодически возникает в процессе загрузки

файла и позволяет выяснить, какая часть файла уже загружена.

Метод — обработчик события DownloadProcessCompleted должен принимать

два параметра. Первый параметр, как обычно, имеет тип Object и содержит

указатель на объект, в котором возникло это событие. Второй же параметр

хранит указатель на объект класса DownloadProcessChangedEventArgs, который

содержит дополнительные сведения о событии.

Класс DownloadProcessChangedEventArgs поддерживает свойство Progress-

Percentage. Оно содержит целое число от 0 до 100, показывающее процент

загрузки файла.

Давайте поместим на страницу индикатор прогресса (компонент ProgressBar;

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

процесса загрузки файла. Дадим ему имя prbImage.

Напишем обработчик события DownloadProcessChanged, который потом при-

вяжем к нашему объекту класса WebClient.

private void wclImage_DownloadProgressChanged(object sender,

�DownloadProgressChangedEventArgs e)

{

prbImage.Value = e.ProgressPercentage;

}

И добавим в код обработчика события Loaded страницы, где запускается про-

цесс загрузки файла, выражение, привязывающее этот обработчик к событию

DownloadProcessCompleted объекта класса WebClient (оно выделено полужир-

ным шрифтом).

WebClient wclImage = new WebClient();

wclImage.OpenReadCompleted += wclImage_OpenReadCompleted;

wclImage.DownloadProgressChanged += wclImage_DownloadProgressChanged;

wclImage.OpenReadAsync(new Uri("image.jpg", UriKind.Relative));

Откомпилируем приложение, не запуская его, и опубликуем на Web-сервере,

как было описано ранее. Откроем в Web-обозревателе тестовую страницу и

посмотрим на результат. (Хотя, если вы пользуетесь быстрым соединением

с Интернетом, картинка будет загружена очень быстро, и вы просто не успее-

те отследить ход ее загрузки...)

Page 382: Дронов В. Самоучитель Silverlight 3 (2010)

370 Часть VI. Работа с файлами и Web-службами

Прерывание загрузки файла

А еще класс WebClient позволяет нам прервать загрузку файла. Для этого

служит поддерживаемый им метод CancelAsync, не принимающий параметров и не возвращающий результата.

Сразу после прерывания загрузки файла возникает уже знакомое нам событие

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

на. Для этого служит свойство Cancelled класса OpenReadCompletedEventArgs. (Как мы помним, объект этого класса передается вторым параметром в ме-тод — обработчик данного события.) Если загрузка файла была прервана, это

свойство возвращает true, если же файл был успешно загружен — false.

private void wclImage_OpenReadCompleted(object sender,

�OpenReadCompletedEventArgs e)

{

if (!(e.Cancelled))

{

System.Windows.Media.Imaging.BitmapImage bimImage = new

�System.Windows.Media.Imaging.BitmapImage();

bimImage.SetSource(e.Result);

imgImage.Source = bimImage;

}

}

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

Обработка ошибок

При загрузке файлов по сети могут возникнуть ошибки. Запрашиваемый файл может отсутствовать, соединение с Интернетом может пропасть, про-грамма Web-сервера может быть перегружена и не ответит на запрос — да мало ли что может случиться!..

После возникновения ошибки в процессе загрузки файла возникнет все то же

событие OpenReadCompleted. Как видим, возникновение этого события — не повод радоваться успешной загрузке файла...

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

рить свойство Error класса OpenReadCompletedEventArgs. Если ошибка возник-

ла, это свойство вернет значение типа Exception — исключение, описываю-щее возникшую ошибку. (Об исключениях рассказывалось в главе 12.) Если

же файл загрузился благополучно, свойство Error будет содержать значение

null.

Page 383: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 21. Работа с удаленными файлами 371

private void wclImage_OpenReadCompleted(object sender,

�OpenReadCompletedEventArgs e)

{

if ((!(e.Cancelled)) && (e.Error == null))

{

System.Windows.Media.Imaging.BitmapImage bimImage = new

�System.Windows.Media.Imaging.BitmapImage();

bimImage.SetSource(e.Result);

imgImage.Source = bimImage;

}

}

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

Пример простейшего просмотрщика изображений

А теперь давайте рассмотрим пример довольно сложного приложения — просмотрщика изображений, опубликованных на каком-либо Web-сервере. Оно будет выполнять следующие функции:

� загружать с Web-сервера текстовый файл, содержащий список интернет-адресов файлов изображений;

� загружать файлы изображений, чьи интернет-адреса перечислены в этом файле;

� выводить содержимое загруженных файлов в списке.

Пусть текстовый файл, содержащий интернет-адреса файлов изображений, находится в той же папке, что и само приложение, и имеет имя picturelist.txt. Интернет-адреса файлов будут в нем храниться в виде набора строк; каждая строка содержит один интернет-адрес.

Создадим в Visual Web Developer 2008 новый проект с именем ImageViewer. Откроем C#-файл, содержащий исходный код класса главной страницы, и

создадим в пространстве имен ImageViewer класс Picture. Этот класс будет хранить сведения о графическом файле и реализовывать его загрузку. Кроме

того, класс Picture должен реализовывать интерфейс INotifyPropertyChanged (подробнее о нем и вообще о привязке компонентов к данным см. главу 13), чтобы информировать привязанный компонент о том, что значение одного из его свойств изменилось.

public class Picture : System.ComponentModel.INotifyPropertyChanged

{

private System.Windows.Media.Imaging.BitmapImage fSource;

Page 384: Дронов В. Самоучитель Silverlight 3 (2010)

372 Часть VI. Работа с файлами и Web-службами

public event System.ComponentModel.PropertyChangedEventHandler

�PropertyChanged;

public System.Windows.Media.Imaging.BitmapImage Source

{

get

{

return fSource;

}

set

{

fSource = value;

NotifyPropertyChanged("Source");

}

}

public Picture(string url)

{

WebClient wclPicture = new WebClient();

wclPicture.OpenReadCompleted += Picture_OpenReadCompleted;

wclPicture.OpenReadAsync(new Uri(url, UriKind.RelativeOrAbsolute));

}

private void Picture_OpenReadCompleted(object sender,

�OpenReadCompletedEventArgs e)

{

if ((!(e.Cancelled)) && (e.Error == null))

{

System.Windows.Media.Imaging.BitmapImage bimImage = new

�System.Windows.Media.Imaging.BitmapImage();

bimImage.SetSource(e.Result);

Source = bimImage;

}

}

public void NotifyPropertyChanged(string propertyName)

{

if (PropertyChanged != null)

{

PropertyChanged(this, new

�System.ComponentModel.PropertyChangedEventArgs(propertyName));

}

}

}

В классе Picture мы создали свойство Source типа BitmapImage, содержащее

сведения о графическом файле. Изначально это свойство не содержит ника-

кого значения — оно будет получено потом, после успешной загрузки файла.

Page 385: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 21. Работа с удаленными файлами 373

А загружается файл следующим образом. При создании объекта класса

Picture мы передаем его конструктору в качестве единственного параметра строку, содержащую интернет-адрес нужного файла. Конструктор запускает

загрузку файла вызовом метода OpenReadAsync.

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

OpenReadCompleted. В нем мы получаем поток, представляющий содержимое

загруженного файла, и присваиваем его свойству Source данного объекта

класса Picture. При этом возникает событие PropertyChanges, унаследованное

от интерфейса INotifyPropertyChanged и сообщающее привязанному компо-ненту о том, что значение данного свойства изменилось.

Теперь создадим на главной странице список lstPictures. Зададим для него

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

ства Source объектов класса Picture. Для самого списка зададим "пустую" привязку.

<ListBox x:Name="lstPictures" ItemsSource="{Binding}">

<ListBox.ItemTemplate>

<DataTemplate>

<Image Source="{Binding Path=Source}"></Image>

</DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

Теперь нам нужно загрузить текстовый файл со списком интернет-адресов

графических файлов. Это будет выполняться в обработчике события Loaded страницы. Вот его код:

WebClient wclList = new WebClient();

wclList.OpenReadCompleted += wclList_OpenReadCompleted;

wclList.OpenReadAsync(new Uri("picturelist.txt", UriKind.Relative));

Комментировать здесь нечего — все нам уже знакомо.

Как только текстовый файл загрузится, будет выполнен обработчик события

OpenReadCompleted. Его код приведен далее. if ((!(e.Cancelled)) && (e.Error == null))

{

System.IO.StreamReader strPictures = new

�System.IO.StreamReader(e.Result);

System.Collections.ObjectModel.ObservableCollection<Picture>

�colPictures = new

�System.Collections.ObjectModel.ObservableCollection<Picture>();

while (!(strPictures.EndOfStream))

{

Picture pct = new Picture(strPictures.ReadLine());

Page 386: Дронов В. Самоучитель Silverlight 3 (2010)

374 Часть VI. Работа с файлами и Web-службами

colPictures.Add(pct);

}

lstPictures.DataContext = colPictures;

strPictures.Close();

}

Здесь мы читаем содержимое текстового файла picturelist.txt способами, опи-

санными в главе 20, создаем на основе каждой прочитанной строки объект

класса Picture и добавляем его в коллекцию. Напоследок мы привязываем

список lstPictures к этой коллекции.

Вот и все. Простейший просмотрщик изображений готов. Осталось только

проверить его в действии.

Отберем несколько графических файлов, дадим им имена, содержащие толь-

ко буквы латинского алфавита, цифры и знаки подчеркивания, и опубликуем

на Web-сервере. Создадим текстовый файл picturelist.txt, в котором перечис-

лим имена этих графических файлов, каждое имя — в отдельной строке, и

также опубликуем на Web-сервере. Напоследок откомпилируем приложение,

не запуская его, опубликуем на Web-сервере и откроем его тестовую Web-

страницу в Web-обозревателе. Там мы увидим список, который через некото-

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

Что дальше?

Как мы выяснили в этой главе, Silverlight позволяет работать не только с ло-

кальными, но и с удаленными файлами. Причем методы работы в обоих слу-

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

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

В следующей главе мы продолжим работать с удаленными данными. Но те-

перь эти данные будут храниться не в отдельных файлах, а на Web-службах.

А Silverlight вместе с Visual Web Developer 2008 будут только рады нам

помочь.

Page 387: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 22

Работа с Web-службами

В предыдущей главе мы, что называется, вышли в сеть — научились работать

с удаленными файлами. И даже создали приложение-просмотрщик графиче-

ских файлов, хранящихся на Web-сервере.

Но работа с данными, хранящимися в удаленных файлах, — это пройденный

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

рые работают на удаленных компьютерах совместно с Web-серверами, —

серверными приложениями (о серверных приложениях говорилось в главе 1).

Такие приложения принимают данные от пользователя, обрабатывают их,

возможно, сохраняют в файлах и выдают результат.

Существует довольно много разновидностей серверных приложений; они от-

личаются принципами работы, стандартами, согласно которым они ведут об-

мен данными с клиентскими приложениями, и некоторыми другими деталя-

ми. Но самыми современными и динамично развивающимися считаются сей-

час Web-службы, впервые представленные корпорацией Microsoft несколько

лет назад.

Вот именно Web-службами мы сейчас и займемся.

Web-службы

Web-служба — это, как уже говорилось, серверное приложение, работающее

на удаленном компьютере совместно с Web-сервером и обрабатывающее

данные, принятые от пользователей (точнее, от клиентских приложений). От

других разновидностей серверных приложений Web-службы отличаются,

в основном, форматом, в котором пересылаются данные, и принципом рабо-

ты, которые мы здесь не будем рассматривать.

Page 388: Дронов В. Самоучитель Silverlight 3 (2010)

376 Часть VI. Работа с файлами и Web-службами

В реализации Microsoft Web-службы создаются на платформе .NET. Как го-

ворилось в главе 1, эта платформа является "прародителем" Silverlight, а зна-чит, что для создания .NET-приложений применяются те же принципы, что и

при создании Silverlight-приложений. Только .NET, в отличие от "урезанной"

и сильно специализированной Silverlight, позволяет создавать и обычные "на-стольные", и серверные приложения. В том числе и Web-службы.

Web-службы создаются все в том же знакомом нам Visual Web Deve-loper 2008. (Точнее сказать, Visual Web Developer 2008 изначально предна-

значен для создания Web-сайтов и серверных приложений, в том числе и

Web-служб; возможность создания в нем Silverlight-приложений реализуется дополнительными модулями.) Их код пишется на языке C#, который также

нам знаком. Но платформа применяется уже другая — .NET, с другими клас-сами, пространствами имен и библиотеками.

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

Базы данных

А теперь немного отвлечемся от приложений и поговорим о данных, которые они обрабатывают. Эти данные, как мы знаем, могут быть приняты от кли-ентского приложения и могут быть ему отправлены. Но они могут быть и со-хранены. Где?

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

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

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

Баз данных на компьютере может быть много; обычно данные, принадлежа-

щие одному приложению, хранятся в отдельной базе. А процессор данных

всего один, и он "обслуживает" все эти базы данных.

Page 389: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 377

Оставим в покое процессор данных и займемся базами. Каждая база данных

представляет собой весьма сложную структуру, хранящую множество разно-

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

Прежде всего, база данных содержит одну или несколько таблиц. Эти табли-

цы и содержат рабочие данные, организованные в табличном виде, в столбцы

и строки. Если такую таблицу представить в виде коллекции, то ее строки

будут являться элементами коллекции — объектами определенного класса, а

столбцы — полями этого класса. Кстати, столбцы таблицы так и называют —

полями, а строки — записями.

Как и в случае классов C#, каждое поле должно иметь уникальное в пределах

таблицы имя и определенный тип. Типы полей здесь те же, что и в C#: стро-

ковый, целочисленные, числа с плавающей точкой, логический, дата и время

и пр. Причем для строковых полей всегда указывается предельная длина хра-

нящихся в них значений; это позволяет сэкономить место на жестком диске.

Одно из полей практически всегда делается ключевым. Значение ключевого

поля используется для однозначной идентификации какой-либо записи. Ра-

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

уникальные значения.

Иногда в ключевое поле самим процессором баз данных заносятся последо-

вательно увеличивающиеся целочисленные значения. Это делается при соз-

дании каждой новой записи, и впоследствии эти значения не изменяются.

В таком случае ключевое поле называется полем счетчика.

Вообще, базы данных по способу организации в них информации делятся на несколько типов. Здесь мы рассмотрели реляционные базы данных, в которых

информация представляется в виде набора таблиц. Пожалуй, это наиболее

часто используемый тип баз данных; другие типы применяются значительно

реже.

В предыдущих главах мы работали над приложением GridDemo, отображаю-

щим списки специалистов и платформ, которыми владеют эти специалисты.

Окончательная версия этого приложения, которую мы сделали в главе 20,

хранила все свои данные в файлах в изолированном хранилище.

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

Web-службу и Silverlight-приложение.

� База данных persons будет хранить списки специалистов и платформ.

� Silverlight-приложение PersonsClient будет работать с базой данных

persons, позволяя просматривать и править хранящиеся в ней данные.

Page 390: Дронов В. Самоучитель Silverlight 3 (2010)

378 Часть VI. Работа с файлами и Web-службами

� Web-служба PersonsWS будет выступать "посредником" между базой дан-

ных persons и Silverlight-приложением PersonsClient.

И начнем мы с базы данных.

Приведенное далее описание процесса создания базы данных и Web-службы

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

Создание базы данных

В составе Visual Web Developer 2008 поставляется "компактная" версия по-

пулярного процессора баз данных Microsoft SQL Server 2008. А сам Visual

Web Developer 2008 позволяет создавать базы данных SQL Server 2008 прямо

в своей среде.

Создание самой базы данных

Итак, нам нужно создать базу данных persons.

Найдем в окне Visual Web Developer 2008 панель Database Explorer

(рис. 22.1). Если такая панель отсутствует на экране, выберем пункт Database

Explorer меню View или нажмем комбинацию клавиш <Ctrl>+<Alt>+<S>,

чтобы вывести ее.

Рис. 22.1. Панель Database Explorer

Page 391: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 379

Эта панель содержит иерархический список, перечисляющий все базы дан-

ных, которые зарегистрированы в Visual Web Developer 2008. Изначально он

будет содержать только пустую "ветвь" Data Connections — это значит, что

ни одна база данных там еще не зарегистрирована.

Щелкнем правой кнопкой мыши на панели Database Explorer и выберем в

появившемся на экране контекстном меню пункт Add Connection. На экране

появится диалоговое окно Add Connection (рис. 22.2).

Рис. 22.2. Диалоговое окно Add Connection

Проверим, присутствует ли в текстовом поле Data source этого окна строка

Microsoft SQL Server Database File (SqlClient). Если нет, нажмем располо-

женную правее кнопку Change. В списке Data source появившегося на экра-

не диалогового окна Change Data Source (рис. 22.3) выберем пункт Microsoft

SQL Server Database File и нажмем кнопку OK.

В поле ввода Database file name (new or existing) окна Add Connection вве-

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

расположенную правее кнопку Browse и указать имя файла базы данных

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

Windows. Как мы условились ранее, назовем базу данных persons.

Page 392: Дронов В. Самоучитель Silverlight 3 (2010)

380 Часть VI. Работа с файлами и Web-службами

Рис. 22.3. Диалоговое окно Change Data Source

Теперь можно нажать кнопку OK. Visual Web Developer 2008 выведет окно-

предупреждение, спрашивающее, хотим ли мы создать эту базу данных. На-

жмем кнопку Да.

В ответ Visual Web Developer 2008 создаст пустую базу данных и сразу же

зарегистрирует ее в списке панели Database Explorer. Представляющая ее

одноименная "ветвь" появится в "ветви" Data Connections, как показано на

рис. 22.1.

Развернем эту "ветвь". В ней мы увидим другие "ветви", представляющие

различные элементы базы данных, в том числе и таблицы, которые будут пе-

речислены в "ветви" Tables. Изначально она будет пустой, т. к. ни одной таб-

лицы мы еще не создали.

Создание таблиц

Поскольку созданная нами база данных будет хранить два списка — специа-

листов и платформ, — нам понадобятся две таблицы.

Таблица persons будет хранить список самих специалистов. Ее структура,

т. е. список полей с именами, типами и дополнительными параметрами, при-

ведена в табл. 22.1.

Таблица 22.1. Структура таблицы persons базы данных persons

Имя поля

Тип поля в терминологии SQL Server 2008

Длина поля в символах

Описание поля

id int Уникальный идентификатор специалиста. Ключевое поле счетчика

Page 393: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 381

Таблица 22.1 (окончание)

Имя поля

Тип поля в терминологии SQL Server 2008

Длина поля в символах

Описание поля

f nvarchar 20 Фамилия

n1 nvarchar 20 Имя

n2 nvarchar 20 Отчество

age smallint Возраст

Ключевое слово int в терминологии SQL Server 2008 обозначает целое число,

nvarchar — строку, а smallint — короткое целое.

Таблица platforms будет хранить список платформ, которыми владеют спе-

циалисты. Ее структура приведена в табл. 22.2.

Таблица 22.2. Структура таблицы platforms базы данных persons

Имя поля

Тип поля в терминологии SQL Server 2008

Длина поля в символах

Описание поля

id int Уникальный идентификатор платформы. Ключевое поле счетчика

person int Идентификатор специалиста, который владеет данной плат-формой

name nvarchar 25 Название платформы

Но зачем нам понадобилось поле person? А вот зачем...

Когда мы хранили список специалистов в коллекции, мы создали в классе

Person, представляющем специалиста, свойство Platforms типа коллекции со

строковыми элементами. Данная коллекция хранила список названий плат-

форм, которыми владеет данный специалист.

К сожалению, среди многочисленных типов полей, поддерживаемых SQL

Server 2008, нет коллекций или хотя бы массивов. Поэтому здесь применяет-

ся другой подход. Мы создаем отдельную таблицу (в нашем случае —

platforms), в которой будут храниться все платформы, которыми владеют все

специалисты. В этой таблице мы предусматриваем поле для хранения иден-

тификатора специалиста, который владеет данной платформой (у нас — поле

person; в нем будет храниться значение поля id соответствующей записи таб-

лицы persons). И, чтобы получить все платформы, которыми владеет данный

Page 394: Дронов В. Самоучитель Silverlight 3 (2010)

382 Часть VI. Работа с файлами и Web-службами

специалист, мы просматриваем все записи таблицы платформ и отбираем

только те, значение поля person которых совпадает с идентификатором этого

специалиста. Просто и весьма удобно.

Настала пора создать обе этих таблицы. В иерархическом списке панели Database Explorer развернем "ветвь", соответствующую нашей базе данных, и найдем в ней "ветвь" Tables, пока что пустую. Щелкнем на ней правой кнопкой мыши и в появившемся на экране контекстном меню выберем пункт Add New Table. Visual Web Developer 2008 откроет новое окно документа, предназначенное для задания структуры таблицы (рис. 22.4).

Рис. 22.4. Окно документа, предназначенное для задания структуры таблицы

Это окно разделено по горизонтали на две части. Рассмотрим пока что верх-нюю, бо́льшую, часть.

Как видим, там присутствует список, организованный в виде таблицы. В этом списке и вводятся параметры полей. Давайте рассмотрим столбцы этого списка.

� Column Name — имя поля.

� Data Type — тип поля в терминологии SQL Server 2008. Тип строковых

полей задается в виде nvarchar(<длина поля в символах>), например,

nvarchar(20) для строкового поля из 20 символов.

Page 395: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 383

� Allow Nulls. Если флажок, присутствующий в этом поле, установлен, поле

может не содержать никакого значения (фактически будет содержать зна-

чение null). Если же этот флажок сбросить, поле обязательно должно со-

держать значение (обязательное поле).

Итак, сначала создадим таблицу persons. Укажем в описанном ранее списке

все ее поля (см. табл. 22.1). Флажок в столбце Allow Nulls сбросим для всех

полей, кроме n2 (отчество).

Теперь превратим первое поле в поле счетчика. Для этого нам придется обра-

титься к нижней части окна документа, где расположен довольно большой

иерархический список, перечисляющий дополнительные параметры выбран-

ного в верхнем списке поля.

Выберем в верхнем списке поле id и отыщем в нижнем списке "ветвь"

Identity Specification. Развернем ее и выберем пункт (Is Identity). Как видим,

правее его названия появится раскрывающийся список; выберем в нем

пункт Yes.

Осталось превратить поле id в ключевое. Проверим, выбрано ли оно в верх-

нем списке. И либо нажмем кнопку Set Primary Key ( ) на панели инстру-

ментов, либо выберем пункт Set Primary Key меню Table Designer или од-

ноименный пункт контекстного меню соответствующей строки верхнего спи-

ска. В левой части строки, представляющей это поле, появится небольшой

значок в виде желтого ключа.

Сохраним готовую таблицу. Visual Web Developer 2008 выведет небольшое

диалоговое окно Choose Name, где мы должны будем указать имя созданной

таблицы. Введем имя — persons — в поле ввода Enter a name for the table и

нажмем кнопку OK. И увидим, что в "ветви" Tables иерархического списка

панели Database Explorer появится новый пункт persons, соответствующий

только что созданной нами таблице.

Закроем окно документа со структурой таблицы persons. И создадим точно

таким же образом вторую таблицу — platforms.

Создание связи

Данные о специалистах у нас хранятся в двух таблицах. В таблице persons

содержатся основные данные о специалистах: фамилии, имена, отчества и

возраст. А в таблице platforms размещаются дополнительные данные — спи-

ски платформ, которыми владеет каждый специалист. При этом для выборки

платформ из второй таблицы мы используем ее поле person, в котором хра-

нятся уникальные идентификаторы специалистов — значения поля id первой

таблицы.

Page 396: Дронов В. Самоучитель Silverlight 3 (2010)

384 Часть VI. Работа с файлами и Web-службами

Фактически мы создали связанные таблицы. При этом таблица persons, хра-

нящая основные данные, является первичной, или главной, а таблица

platforms, в которой содержатся дополнительные данные, — вторичной, или

подчиненной. (Здесь используется та же терминология, что мы ввели в гла-

ве 14, при выполнении связывания коллекций средствами LINQ.)

Только SQL Server 2008 об этом еще не "знает". Нам нужно явно указать, что

таблицы persons и platforms являются связанными, и тогда он будет выпол-

нять многие действия по выборке данных сам.

Откроем структуру вторичной таблицы — platforms. Для этого щелкнем пра-

вой кнопкой мыши на одноименном пункте "ветви" Tables в иерархическом

списке панели Database Explorer и выберем в появившемся на экране кон-

текстном меню пункт Open Table Definition. Visual Web Developer 2008 от-

кроет знакомое нам окно документа с двумя списками.

Задать связь между таблицами можно нажатием кнопки Relationships ( )

на панели инструментов или выбором пункта Relationships меню Table

Designer. После этого на экране появится диалоговое окно Foreign Key

Relationships (рис. 22.5).

Рис. 22.5. Диалоговое окно Foreign Key Relationships

В списке Selected Relationship перечислены все уже созданные в базе данных

связи; сейчас же, поскольку мы еще не создали ни одной связи, он пуст. Что-

бы создать новую связь, нажмем кнопку Add. Сразу же после этого связь бу-

Page 397: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 385

дет создана, и в списке Selected Relationship мы увидим соответствующий

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

ском списке, представляющем параметры связи, выберем пункт Tables And

Columns Specification и нажмем небольшую кнопку, которая появится в пра-

вой части этого пункта. На экране появится диалоговое окно Tables and

Columns (рис. 22.6).

Рис. 22.6. Диалоговое окно Tables and Columns

В раскрывающемся списке Primary key table этого окна выберем первичную

таблицу — persons. В левой колонке расположенного ниже списка, организо-

ванного в виде таблицы, выберем поле первичной таблицы, по которому вы-

полняется связывание, — id. После чего в той же строке данного списка-

таблицы, но в правой колонке, выберем соответствующее поле вторичной

таблицы — person. И нажмем кнопку OK.

Вновь оказавшись в окне Foreign Key Relationships, развернем "ветвь"

INSERT And UPDATE Specification ее иерархического списка, представ-

ляющего параметры связи. Выберем пункт Delete Rule этой "ветви" и устано-

вим в раскрывающемся списке, который появится в правой части этого пунк-

та, значение Cascade. Это предпишет SQL Server 2008 при удалении записи

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

вторичной таблицы.

Также можно установить значение Cascade и для пункта Update Rule этой

"ветви". Так мы дадим SQL Server 2008 знать, что при обновлении значения

Page 398: Дронов В. Самоучитель Silverlight 3 (2010)

386 Часть VI. Работа с файлами и Web-службами

поля id первичной таблицы нужно соответственно обновлять значения полей

person соответствующих записей вторичной таблицы. Хотя делать это необя-

зательно — все равно значение полей счетчика никогда не меняется.

Закончив со связью, нажмем кнопку Close, чтобы закрыть окно Foreign Key

Relationships. И закроем окно документа со структурой таблицы platforms.

Visual Web Developer 2008 выведет небольшое окно, спрашивающее, нужно

ли сохранить изменения в структуре обеих таблиц — и первичной, и вторич-

ной; нажмем кнопку Yes, чтобы сделать это.

Занесение данных в таблицы

Осталось только внести в обе таблицы несколько записей в качестве отладоч-

ных данных. Так мы сможем проверить, работает ли в создаваемом нами кли-

ентском приложении хотя бы вывод данных.

Начнем с таблицы persons. Щелкнем правой кнопкой мыши на одноименном

пункте "ветви" Tables в иерархическом списке панели Database Explorer и

выберем в появившемся на экране контекстном меню пункт Show Table

Data. Visual Web Developer 2008 откроет окно документа со списком, содер-

жащим присутствующие в таблице данные (рис. 22.7).

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

Как видим, в этом окне присутствует список в виде таблицы. Столбцы этого

списка-таблицы представляют поля таблицы базы данных, а строки — ее

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

Самая нижняя строка списка-таблицы помечена знаком звездочки; значения

всех ее полей — null. Эта строка служит для добавления в таблицу новой

записи. Просто введем в ее поля нужные значения — и новая запись добав-

лена.

Page 399: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 387

Сохранение введенных в поля записи значений происходит при нажатии кла-

виши <Enter> или при переходе на другую строку списка-таблицы. Чтобы

отказаться от внесенных изменений, нажмите клавишу <Esc>.

Введем в таблицу persons несколько записей и закроем окно документа. Точ-

но таким же образом внесем несколько записей в таблицу platforms, обращая

особое внимание на ее поле person — там должны присутствовать значения,

уже имеющиеся в поле id таблицы persons.

На этом работа с базой данных закончена.

Создание Web-службы

Следующий шаг — создание Web-службы, которая выступит "посредником"

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

Создание решения и Web-сайта

Поскольку Web-служба и клиентское приложение выступают у нас в связке,

давайте объединим их в одно решение. Как мы помним из главы 3, решение

объединяет несколько "родственных" проектов; эти проекты компилируются

и работают совместно.

Наше решение объединит следующие проекты:

� клиентское приложение PersonsClient;

� Web-сайт PersonsSite, в составе которого будет работать Web-служба

PersonsWS. Поскольку любая Web-служба есть серверное приложение, она

должна быть составной частью Web-сайта.

Мы создадим простенький Web-сайт, включающий только Web-службу. Для

этого выберем пункт New Project меню File или нажмем комбинацию кла-

виш <Ctrl>+<Shift>+<N>. В иерархическом списке Project types диалогового

окна New Project (см. рис. 3.6) найдем "ветвь" Visual C# и выберем в ней

пункт Web. В списке Templates выберем пункт ASP.NET Web Application.

В поле ввода Name зададим имя Web-сайта — PersonsSite.

Так как мы создаем решение, установим флажок Create directory for

solution. После этого Visual Web Developer 2008 создаст папку для данного

решения, в которую поместит папки с файлами исходного кода всех входя-

щих в него проектов. И зададим для решения имя PersonsSolution — это де-

лается в поле ввода Solution Name.

Осталось нажать кнопку OK. Через некоторое время решение и первый вхо-

дящий в него проект будут созданы.

Page 400: Дронов В. Самоучитель Silverlight 3 (2010)

388 Часть VI. Работа с файлами и Web-службами

Создание модели данных

Модель данных представляет структуру базы данных в виде набора коллек-

ций и классов C#. Можно сказать, что она делает базу данных доступной

в C#-коде.

Для создания модели данных щелкнем правой кнопкой мыши на "корне"

иерархического списка PersonsSite в панели Solution Explorer и выберем

пункт New Item подменю Add. На экране появится диалоговое окно Add

New Item (см. рис. 18.1). В "ветви" Visual C# иерархического списка

Categories выберем пункт Data, а в списке Templates — пункт ADO.NET

Entity Data Model. В поле ввода Name введем имя модели данных —

Persons. И нажмем кнопку Add.

На экране появится диалоговое окно Entity Data Model Wizard, организо-

ванное в виде "мастера" (рис. 22.8). Выберем в списке What should the model

contain? пункт Generate from database, чтобы Visual Web Developer 2008

создал модель данных на основе уже имеющейся базы данных, и нажмем

кнопку Next.

Рис. 22.8. Диалоговое окно Entity Data Model Wizard (первая страница)

Page 401: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 389

На следующей странице "мастера" (рис. 22.9) в раскрывающемся списке

Which data connection should your application use to connect to the

database? выберем нашу базу данных — persons. (Базы данных в этом списке

представлены в виде имен файлов; файлы базы данных SQL Server 2008

имеют расширение mdf.) И опять нажмем кнопку Next.

Рис. 22.9. Диалоговое окно Entity Data Model Wizard (вторая страница)

На очередной странице "мастера" (рис. 22.10) мы увидим большой иерархи-

ческий список Which database objects do you want to include in your model?.

Он содержит все элементы базы данных в виде "ветвей" и пунктов; напротив

каждой "ветви" и каждого пункта стоит флажок, который при установке ука-

зывает включить данный элемент или группу элементов в модель данных.

Установим флажок напротив "ветви" Tables этого списка, тем самым вклю-

чив в модель данных все таблицы нашей базы данных. И нажмем кнопку

Finish.

Через довольно продолжительное время Visual Web Developer 2008 создаст

модель данных и выведет ее в отдельном окне документа (рис. 22.11). Как

видим, здесь все вполне наглядно: есть два класса (persons и platforms),

представляющие записи обеих таблиц, или, другими словами, элементы со-

Page 402: Дронов В. Самоучитель Silverlight 3 (2010)

390 Часть VI. Работа с файлами и Web-службами

Рис. 22.10. Диалоговое окно Entity Data Model Wizard (третья страница)

ответствующих им коллекций; эти классы имеют набор свойств (id, f, n1, n2,

age у класса persons и id и name у класса platforms), которые представляют

поля записей; эти свойства имеют соответствующие типы.

Кроме того, мы видим, что класс persons имеет свойство platforms. Это свой-

ство фактически представляет собой коллекцию, содержащую только те

записи вторичной таблицы, что относятся к данной записи первичной табли-

цы. А класс platforms, в свою очередь, имеет свойство persons, которое ссы-

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

ной таблицы.

Как видим, Visual Web Developer 2008 сделал для нас если не все, то очень

много.

Создание самой Web-службы

Осталось только создать саму Web-службу.

Снова щелкнем правой кнопкой мыши на "корне" иерархического списка

PersonsSite в панели Solution Explorer и выберем пункт New Item подменю

Page 403: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 391

Рис. 22.11. Окно документа, представляющее модель данных

Add. В "ветви" Visual C# иерархического списка Categories окна Add New

Item выберем пункт Web, а в списке Templates — пункт ADO.NET Data

Service. В поле ввода Name введем имя Web-службы — PersonsWS. И нажмем

кнопку Add.

Спустя пару секунд Visual Web Developer 2008 создаст Web-службу и выве-

дет окно документа, содержащее ее C#-код. Найдем в нем вот такую строку:

public class PersonsWS : DataService

�< /* TODO: put your data source class name here */ >

Как видим, оно объявляет класс PersonsWS нашей Web-службы. Он является

потомком обобщенного класса DataService, реализующего основную функ-

циональность Web-служб.

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

Этот класс создается при создании модели данных; его имя имеет вид <имя

Page 404: Дронов В. Самоучитель Silverlight 3 (2010)

392 Часть VI. Работа с файлами и Web-службами

базы данных>Entities. Так, в нашем случае класс соединения будет иметь имя

personsEntities.

На второй странице диалогового окна Entity Data Model Wizard (см. рис. 22.9) мы можем изменить имя класса соединения с базой данных, если хотим.

Исправим приведенную ранее строку кода следующим образом:

public class PersonsWS : DataService<personsEntities>

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

config.SetEntitySetAccessRule("persons", EntitySetRights.All);

config.SetEntitySetAccessRule("platforms", EntitySetRights.All);

Объект config передается методу InitializeService в качестве единственного параметра и имеет тип интерфейса IDataServiceConfiguration. Метод SetEntitySetAccessRule выполняет задание прав доступа. Первым параметром ему передается строка с именем коллекции модели данных, вторым — обо-значение прав доступа к ней в виде элемента перечисления EntitySetRights.

Элемент All как раз и задает полный (и на чтение, и на запись) доступ.

Вот и все. Сохраним код и откомпилируем Web-службу без запуска выбором пункта Build <имя проекта> меню Build.

Если мы после этого посмотрим на панель Solution Explorer, то увидим, что код Web-службы хранится в файле с расширением svc. Данный файл факти-чески представляет собой Web-службу в реализации Microsoft .NET. Кроме того, будет создан соответствующий ему файл с C#-кодом Web-службы — содержимое этого файла мы только что правили.

Создание клиентского приложения

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

Особенности создания Silverlight-приложения, работающего с Web-службой

Создадим в Visual Web Developer 2008 новый проект PersonsClient. При его создании в диалоговом окне New Project мы увидим раскрывающийся список

Page 405: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 393

Solution. Выберем в нем пункт Add to Solution, чтобы создаваемый проект был добавлен в решение, открытое в данный момент (т. е. в решение

PersonsSolution).

После того как мы нажмем кнопку OK окна New Project, на экране появится

знакомое нам диалоговое окно New Silverlight Application. Знакомое, да не

совсем — сейчас оно будет выглядеть иначе — см. рис. 22.12.

Рис. 22.12. Диалоговое окно New Silverlight Application (выполняется добавление Silverlight-приложения в Web-сайт, содержащий Web-службу)

Проверим, установлен ли флажок Host the Silverlight application in a new or

existing Web site in the solution. Он предписывает Visual Web Developer 2008

сделать создаваемое приложение частью указанного Web-сайта. Сам этот

сайт выбирается в раскрывающемся списке, расположенном ниже.

Также проверим, установлены ли флажки Add a test page that references the

application и Make it the start page. Они предписывают создать тестовую

страницу для Silverlight-приложения и сделать ее главной страницей Web-

сайта.

Теперь можно нажать кнопку OK. Вскоре после этого приложение будет соз-

дано.

На главной странице поместим две таблицы: grdPersons для вывода специа-листов и grdPlatforms для вывода списка платформ, которыми владеет вы-

Page 406: Дронов В. Самоучитель Silverlight 3 (2010)

394 Часть VI. Работа с файлами и Web-службами

бранный в первой таблице специалист. Зададим для обеих таблиц пустую привязку. Также поместим на странице четыре кнопки: btnAddPerson для добавления специалиста, btnRemovePerson для удаления специалиста, btnAddPlatform для добавления платформы и btnRemovePlatform для удаления платформы.

Подключение Silverlight-приложения к Web-службе

Теперь подключим клиентское приложение к созданной ранее Web-службе. При этом Visual Web Developer 2008 создаст все классы, необходимые для работы с реализованной в Web-службе моделью данных. Можно сказать, что он перенесет модель данных в наше клиентское приложение, чтобы нам было удобнее с ней работать.

Найдем в иерархическом списке панели Solution Explorer "ветвь" PersonsClient, соответствующую проекту нашего клиентского приложения. Щелкнем на нем правой кнопкой мыши и выберем в появившемся на экране контекстном меню пункт Add Service Reference. На экране появится диало-говое окно Add Service Reference (рис. 22.13).

Рис. 22.13. Диалоговое окно Add Service Reference

Page 407: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 395

В комбинированном списке Address вводится интернет-адрес Web-службы,

после чего следует нажать кнопку Go. Если Web-служба входит в состав Web-сайта, являющегося частью того же решения, что и проект клиентского

Silverlight-приложения, можно сразу нажать кнопку Discover. Так мы и по-

ступим.

Через некоторое время Visual Web Developer 2008 получит от Web-службы ее

описание и выведет ее в иерархическом списке Services. Как видим, там пе-речислены все реализуемые Web-службой модели данных. Выберем "корень"

списка, соответствующий нужной нам Web-службе, или любую из вложен-

ных в него "ветвей".

В поле ввода Namespace указывается имя пространства имен, в котором

Visual Web Developer 2008 создаст все необходимые для работы с Web-

службой классы. Назовем это пространство имен Persons.

Указав все нужные данные, можно нажать кнопку OK. После этого Visual

Web Developer 2008 создаст все необходимые классы.

В нашем случае будет создано пространство имен Persons. В нем будут объ-

явлены классы persons и platforms. Класс persons будет иметь свойства id, f,

n1, n2, age и platforms, а класс platforms — свойства id, persons и name. Как видим, эти классы полностью аналогичны своим "тезкам" из модели данных, реализованной в Web-службе. Все данные, что мы получаем из Web-службы и отправляем ей, будут представлять собой объекты этих классов и их кол-лекции.

Также в пространстве имен Persons будет создан класс personsEntities. Он служит для подключения к Web-службе, получения от нее данных, добавле-ния в коллекции (таблицы базы данных) новых элементов (записей) и неко-

торых других целей. В терминологии Silverlight класс personsEntities назы-вается контекстом Web-службы.

Класс personsEntities является потомком класса DataServiceContext. По-следний предоставляет ряд методов, необходимых для работы со всеми Web-службами. Эти методы мы скоро рассмотрим.

Загрузка данных из Web-службы

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

Кэширование — временное хранение данных в более доступных источни-

ках — позволяет резко сократить объем данных, передаваемых приложением

Page 408: Дронов В. Самоучитель Silverlight 3 (2010)

396 Часть VI. Работа с файлами и Web-службами

по сети. Фактически данные будут загружены всего один раз — при запуске

приложения.

Для кэширования данных мы используем две коллекции типа

ObservableCollection. Первая из них будет хранить специалистов (объекты

класса persons), вторая — платформы (объекты класса platforms). Для хране-

ния этих коллекций мы объявим в классе страницы два частных поля.

private System.Collections.ObjectModel.ObservableCollection

�<Persons.persons> colPersons;

private System.Collections.ObjectModel.ObservableCollection

�<Persons.platforms> colPlatforms;

Кроме того, нам понадобится поле для хранения контекста Web-службы —

объекта класса personsEntities.

private Persons.personsEntities svcPersons;

Не забываем, что классы persons, platforms и personsEntities объявлены

в пространстве имен Persons.

В конструкторе класса страницы создадим обе упомянутые ранее коллекции

и сразу же привяжем к ним таблицы.

colPersons = new System.Collections.ObjectModel.ObservableCollection

�<Persons.persons>();

�colPlatforms = new System.Collections.ObjectModel.ObservableCollection

<Persons.platforms>();

grdPersons.DataContext = colPersons;

grdPlatforms.DataContext = colPlatforms;

И там же создадим контекст Web-службы.

Конструктор любого класса, являющегося потомком DataServiceContext,

принимает всего один параметр — объект класса Uri, хранящий интернет-

адрес Web-службы. Как мы выяснили ранее, Web-службу представляет файл

с расширением svc, в нашем случае — PersonsWS.svc. Значит, выражение,

создающее контекст Web-службы, будет таким:

svcPersons = new Persons.personsEntities(new Uri("PersonsWS.svc",

�UriKind.Relative));

То есть сразу при создании контекста Web-службы мы к ней подключимся.

Загрузку данных мы будем выполнять в обработчике события Loaded страни-

цы. Создадим его.

Чтобы загрузить содержимое нужной коллекции модели данных, реализуе-

мой Web-службой, мы применим обобщенный метод BeginExecute, поддер-

живаемый классом DataServiceContext, а значит, и всеми его потомками. Да-

Page 409: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 397

да, это обобщенный метод — и такое бывает в Silverlight! Он запускает про-

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

данные будут загружены.

Формат вызова метода BeginExecute таков:

BeginExecute<тип получаемых данных>(<коллекция Web-службы>,

�<метод, вызываемый после загрузки данных>, <дополнительные данные>)

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

Это класс объектов, коллекцию которых мы собираемся получить от Web-

службы.

Первым параметром передается объект класса Uri, содержащий имя коллек-

ции Web-службы, откуда мы собираемся загружать данные.

Вторым параметром указывается метод, который будет выполнен по оконча-

нию загрузки данных.

Третьим параметром передаются какие-либо дополнительные данные, кото-

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

иметь тип Object, т. е. могут быть любого типа. Если в дополнительных дан-

ных нет нужды, передается значение null.

Исходя из этого, пишем код обработчика события Loaded страницы. Он будет

таким:

svcPersons.BeginExecute<Persons.persons>(new Uri("persons",

�UriKind.Relative), svcPersons_QueryComplete, null);

Теперь займемся методом svcPersons_QueryComplete, который выполнится

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

этот метод не должен возвращать результата. Во-вторых, он должен прини-

мать единственный параметр — значение типа интерфейса IAsyncResult, хра-

нящее состояние процесса загрузки данных. Мы используем это значение,

чтобы получить саму принятую от Web-службы коллекцию.

Интерфейс IAsyncResult поддерживает свойство AsyncState. Оно хранит те

самые дополнительные данные, что были переданы третьим параметром

в метод BeginExecute, в виде значения типа Object.

В теле метода svcPersons_QueryComplete мы должны сначала получить приня-

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

DataServiceContext и всеми его потомками обобщенный метод EndExecute.

В качестве единственного параметра он принимает значение типа IAsyncResult,

указывающее состояние загрузки данных. Вспомним — это значение переда-

ется методу svcPersons_QueryComplete единственным параметром. В качестве

Page 410: Дронов В. Самоучитель Silverlight 3 (2010)

398 Часть VI. Работа с файлами и Web-службами

результата метод EndExecute возвращает принятую коллекцию в виде значе-

ния типа обобщенного интерфейса IEnumerable.

И для метода EndExecute, и для возвращенного им значения типа интерфейса

IEnumerable мы должны указать тип, поскольку оба они являются обобщен-

ными. Это тип принятых данных — класс объектов, которые хранит принятая

коллекция.

Вот полный код метода svcPersons_QueryComplete:

private void svcPersons_QueryComplete(IAsyncResult result)

{

IEnumerable<Persons.persons> colResult =

�svcPersons.EndExecute<Persons.persons>(result);

colPersons.Clear();

foreach (Persons.persons prs in colResult)

{

colPersons.Add(prs);

}

}

Здесь мы получаем принятую коллекцию, очищаем внутреннюю коллекцию

специалистов и добавляем в нее все элементы принятой коллекции.

Особенности запуска Silverlight-приложения, работающего с Web-службой

Теперь можно проверить клиентское приложение в действии. Но сначала нам

нужно еще кое-что сделать.

Найдем в иерархическом списке панели Solution Explorer "ветвь", соответст-

вующую Web-сайту PersonsSite, — именно в составе этого Web-сайта мы

реализовали Web-службу. Щелкнем на ней правой кнопкой мыши и выберем

в появившемся на экране контекстном меню пункт Set as a StartUp Project.

После этого "ветвь" PersonsSite будет выделена полужирным шрифтом.

В любом решении, содержащем более одного проекта, один из проектов ука-

зывается в качестве стартового. Этот проект будет запущен на выполнение

при запуске решения нажатием кнопки Start Debugging ( ) панели инстру-

ментов, выбором пункта Start Debugging меню Debug или нажатием клави-

ши <F5>. Так вот, в случае, если решение содержит Web-сайт с Web-службой

и клиентское приложение, стартовым проектом должен быть именно Web-

сайт. И вот почему.

Для запуска Web-сайта Visual Web Developer 2008 использует встроенный

в него отладочный Web-сервер. Под управлением этого Web-сервера будут

Page 411: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 399

работать все серверные приложения, в том числе и Web-службы, входящие

в состав сайта.

После запуска Web-сайта Visual Web Developer 2008 откроет его главную страницу. А в качестве главной страницы мы еще на этапе создания в составе данного решения клиентского Silverlight-приложения указали его тестовую Web-страницу. В результате наше клиентское приложение будет успешно запущено и сможет так же успешно получить данные от Web-службы (см. предыдущий абзац).

Давайте сами в этом убедимся. Запустим приложение и подождем, пока на экране не появится окно Web-обозревателя с тестовой Web-страницей. Еще через некоторое время клиентское приложение запустится и выведет на экран полученные от Web-службы данные. Ура!

Если же мы укажем в качестве стартового проекта клиентское приложение, оно успешно запустится, но не сможет получить данные от Web-службы. Web-служба может работать только в составе Web-сайта под управлением Web-сервера, а Web-сервер в данный момент не запущен.

Вполне возможно, что перед запуском Web-службы Visual Web Developer 2008 выведет небольшое диалоговое окно, спрашивающее нас, хотим ли мы задей-ствовать отладку ее кода. По умолчанию в этом окне будет включен верхний переключатель, который как раз и задействует возможность отладки; оставим его включенным и нажмем кнопку OK. Нижний же переключатель этого окна от-ключает возможность отладки кода Web-службы; отключать его не стоит.

Создание LINQ-запросов к Web-службе

Теперь давайте зададим нашему приложению более сложную задачу — ис-пользуем LINQ-запрос для сортировки данных. (Языку LINQ была посвящена глава 14.)

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

в качестве типа этой переменной обобщенный класс DataServiceQuery. Этот класс предоставляет методы для запуска выполнения запроса и получения результата его выполнения в виде коллекции; эти методы аналогичны уже

знакомым нам методам класса DataServiceContext.

Класс DataServiceQuery объявлен в пространстве имен System.Data.Services.

Client. Оно изначально не отображено, поэтому мы либо должны отобразить его, либо использовать полные имена типов.

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

Page 412: Дронов В. Самоучитель Silverlight 3 (2010)

400 Часть VI. Работа с файлами и Web-службами

В качестве переменной запроса мы используем поле класса страницы. Так будет удобнее.

private System.Data.Services.Client.DataServiceQuery<Persons.persons>

�qryPersons;

Теперь начнем писать запрос. Как мы помним из главы 14, он начинается с секции from, где указывается переменная элемента и коллекция, откуда вы-бираются элементы. Но откуда взять эту коллекцию? Ведь у нас ее еще нет — мы только собираемся ее получить с помощью данного запроса!

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

Метод CreateQuery возвращает значение типа DataServiceQuery. Это значение мы и используем в LINQ-запросе в качестве исходной коллекции.

Запрос к Web-службе пишется согласно тем же правилам, что и запросы к обычным коллекциям, созданным в самом приложении. Единственное — перед присвоением его переменной запроса мы должны преобразовать его к типу DataServiceQuery, т. е. к типу переменной запроса.

Чтобы запустить запрос на выполнение, мы используем метод BeginExecute

класса DataServiceQuery. Вот формат его вызова:

BeginExecute(<метод, вызываемый после загрузки данных>,

�<дополнительные данные>)

Как видим, он похож на одноименный метод класса DataServiceContext, за двумя исключениями: он не обобщенный и не принимает в качестве парамет-ра имя коллекции Web-службы. Этот метод мы вызовем прямо у переменной запроса, поскольку она как раз хранит указатель на объект класса DataServiceQuery.

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

qryPersons =

�(System.Data.Services.Client.DataServiceQuery<Persons.persons>)

from person in svcPersons.CreateQuery<Persons.persons>("persons")

orderby person.f

select person;

qryPersons.BeginExecute(svcPersons_QueryComplete, null);

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

Page 413: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 401

Напоследок исправим метод svcPersons_QueryComplete, получающий приня-

тую коллекцию и переносящий все ее элементы во внутреннюю коллекцию.

Как мы помним, собственно получение коллекции выполняет выражение

IEnumerable<Persons.persons> colResult =

�svcPersons.EndExecute<Persons.persons>(result);

Заменим его таким выражением:

IEnumerable<Persons.persons> colResult = qryPersons.EndExecute(result);

Единственное, чем оно отличается от предыдущего выражения, — в нем ис-

пользуется метод EndExecute класса DataServiceQuery, который мы также вы-

зываем прямо у переменной запроса. От одноименного метода класса

DataServiceContext он отличается только тем, что не является обобщенным.

Проверим исправленное приложение в действии. Если мы все сделали пра-

вильно, оно должно вывести специалистов, отсортировав их по фамилиям.

В LINQ-запросах к Web-службам мы также можем использовать фильтра-

цию. Например:

qryPersons =

�(System.Data.Services.Client.DataServiceQuery<Persons.persons>)

from person in svcPersons.CreateQuery<Persons.persons>("persons")

where person.age > 30

orderby person.f

select person;

Здесь мы отбираем только специалистов старше 30 лет.

Загрузка данных из вторичной коллекции

Пора бы заняться выводом списка платформ, которыми владеет выбранный в

таблице специалист... Вы не находите?

Класс persons, который Visual Web Developer 2008 создал при подключении

приложения к Web-службе, содержит свойство platforms. Оно хранит кол-

лекцию объектов класса platforms, т. е. список платформ, которыми владеет

описываемый данным объектом специалист. А раз так, то мы можем восполь-

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

в главе 13.

Можем-то можем, но работать он не будет. Дело в том, что изначально Web-

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

коллекции. Нам нужно специально попросить ее об этом.

Создадим сразу же обработчик события SelectionChanged таблицы

grdPersons. Именно в нем мы и реализуем загрузку коллекции платформ.

Page 414: Дронов В. Самоучитель Silverlight 3 (2010)

402 Часть VI. Работа с файлами и Web-службами

Загрузку значения свойства запускает метод BeginLoadProperty класса

DataServiceContext и всех его классов-потомков. Вот формат его вызова:

BeginLoadProperty(<объект, значение свойства которого требуется

�загрузить>, <имя свойства>, <метод, вызываемый после загрузки данных>,

�<дополнительные данные>)

Первым параметром передается указатель на объект, значение свойства кото-

рого требуется загрузить. Имя самого этого свойства передается вторым па-

раметром в виде строки. Третьим параметром передается метод, который бу-

дет выполнен после загрузки данных, а четвертым — дополнительные дан-

ные, которые будут переданы этому методу.

Исходя из этого, пишем в обработчике события SelectionChanged таблицы

grdPersons следующий код:

svcPersons.BeginLoadProperty(grdPersons.SelectedItem as Persons.persons,

�"platforms", svcPersons_LoadPropertyCompleted, null);

Он начинает загрузку значения свойства platforms объекта, выбранного в

данный момент в таблице grdPersons. По окончанию загрузки будет выпол-

нен метод svcPersons_LoadPropertyCompleted.

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

BeginLoadProperty, должен удовлетворять тем же требованиям, что и в случае

метода BeginExecute. Он выполняет те же задачи: получает принятую коллек-

цию и кэширует ее содержимое во внутренней коллекции.

Чтобы получить принятую коллекцию, мы вызовем метод EndLoadProperty

класса DataServiceContext и всех его классов-потомков. В качестве единст-

венного параметра он принимает значение типа IAsyncResult, указывающее

состояние загрузки данных (как и метод EndExecute). В качестве резуль-

тата он возвращает принятую коллекцию в виде объекта класса

QueryOperationResponse.

Класс QueryOperationResponse представляет "ответ" Web-службы. Он реали-

зует интерфейс IEnumerable; это значит, что мы можем использовать его объ-

ект в цикле просмотра, как обычную коллекцию.

Далее приведен полный код метода svcPersons_LoadPropertyCompleted.

private void svcPersons_LoadPropertyCompleted(IAsyncResult result)

{

System.Data.Services.Client.QueryOperationResponse colResult =

�svcPersons.EndLoadProperty(result);

colPlatforms.Clear();

foreach (Persons.platforms pls in colResult)

Page 415: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 403

{

colPlatforms.Add(pls);

}

}

Почти то же самое, что и в случае метода svcPersons_QueryComplete.

Остается только проверить приложение в работе.

Реализация добавления, правки и удаления данных

Займемся теперь добавлением, правкой и удалением специалистов. Не все же любоваться ими...

Для реализации добавления специалистов мы поступим так же, как в гла-

ве 19, — создадим вторичное окно. Поместим в это окно поля ввода txtF,

txtN1, txtN2 и txtAge, предназначенные для ввода фамилии, имени, отчества

и возраста соответственно. Назовем вторичное окно AddPerson.

Создадим обработчик события Click кнопки btnAddPerson главной страницы. Вот его код:

AddPerson wndAdd = new AddPerson();

wndAdd.Closed += wndAddPerson_Closed;

wndAdd.Show();

Все это нам знакомо по главе 19. Мы создаем окно, привязываем к его собы-

тию Closed метод-обработчик wndAddPerson и выводим окно на экран.

В методе wndAddPerson мы должны сделать следующее:

1. Создать новый объект класса persons.

2. Заполнить его данными, взятыми из вторичного окна.

3. Добавить его во внутреннюю коллекцию, в которой кэшируются специа-листы.

4. Добавить в коллекцию Web-службы.

Первые три пункта мы выполним без труда. А вот четвертый...

Для добавления нового элемента в коллекцию Web-службы служит метод

AddObject, поддерживаемый классом DataServiceContext и всеми его потом-ками. Вот формат его вызова:

AddObject(<имя коллекции>, <добавляемый элемент>)

Первым параметром ему передается строка с именем коллекции Web-

службы, в которую нужно добавить новый элемент, а вторым — сам добав-

ляемый элемент. Результата этот метод не возвращает.

Page 416: Дронов В. Самоучитель Silverlight 3 (2010)

404 Часть VI. Работа с файлами и Web-службами

Но это еще не все. После этого нам нужно запустить процесс сохранения сде-

ланных в коллекции Web-службы изменений. Для чего служит метод

BeginSaveChanges класса DataServiceContext и его потомков. Вот формат его

вызова:

BeginSaveChanges(<метод, вызываемый после сохранения данных>,

�<дополнительные данные>)

Здесь все нам знакомо, так что перейдем к методу, который будет вызван по-

сле сохранения данных. В нем мы должны подтвердить эти сохранения вызо-

вом метода EndSaveChanges, поддерживаемого классом DataServiceContext и

его потомками. В качестве единственного параметра он принимает значение

типа IAsyncResult, указывающее состояние сохранения данных. Это значение

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

единственного параметра.

Вот полный код двух методов, которые выполняют добавление в коллекцию

нового специалиста:

private void wndAddPerson_Closed(object sender, EventArgs e)

{

AddPerson wndAdd = sender as AddPerson;

if (wndAdd.DialogResult == true)

{

Persons.persons prs = new Persons.persons();

short iAge;

prs.f = wndAdd.txtF.Text;

prs.n1 = wndAdd.txtN1.Text;

prs.n2 = wndAdd.txtN2.Text;

if (short.TryParse(wndAdd.txtAge.Text, out iAge))

{

prs.age = iAge;

}

colPersons.Add(prs);

svcPersons.AddObject("persons", prs);

svcPersons.BeginSaveChanges (svcPersons_SaveChangesCompleted, null);

}

}

private void svcPersons_SaveChangesCompleted(IAsyncResult result)

{

svcPersons.EndSaveChanges(result);

}

Правку специалистов реализовать проще. Благо сам компонент DataGrid мак-

симально идет нам навстречу, изначально предоставляя возможность править

Page 417: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 405

данные прямо в нем самом. Нам остается только выполнить сохранение ис-

правленных данных и подтвердить это сохранение.

Класс DataGrid поддерживает событие RowEditEnded, возникающее сразу по-

сле того, как пользователь подтвердит сделанные им в строке таблицы изме-

нения, перейдя на другую строку или нажав клавишу <Enter>. В обработчике

этого события мы и реализуем сохранение исправленного элемента кол-

лекции.

Сохранение исправленного элемента в коллекции Web-службы выполняется

вызовом метода UpdateObject класса DataServiceContext и всех его классов-

потомков. В качестве единственного параметра он принимает сохраняемый

элемент. Результата этот метод не возвращает.

После вызова метода UpdateObject нам следует запустить процесс сохранения

сделанных в коллекции изменений. Делается это уже знакомым нам спосо-

бом — вызовом метода BeginSaveChanges.

Вот код обработчика события RowEditEnded таблицы grdPersons:

svcPersons.UpdateObject(grdPersons.SelectedItem);

svcPersons.BeginSaveChanges(svcPersons_SaveChangesCompleted, null);

Комментировать здесь нечего.

Удаление элемента коллекции сделать также несложно. Нам придется уда-

лить данный элемент из внутренней коллекции и из коллекции Web-службы,

после чего выполнить сохранение исправленных данных и подтвердить это

сохранение.

Удаление элемента из коллекции Web-службы выполняется вызовом метода

DeleteObject класса DataServiceContext и всех его классов-потомков. В каче-

стве единственного параметра он принимает сохраняемый элемент и не воз-

вращает результата.

После вызова метода DeleteObject не забудем запустить процесс сохранения

сделанных в коллекции изменений вызовом метода BeginSaveChanges.

Создадим обработчик события Click кнопки btnRemovePerson и напишем в

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

if (MessageBox.Show("Удалить выбранного специалиста?", "Специалисты",

�MessageBoxButton.OKCancel) == MessageBoxResult.OK)

{

Persons.persons prs = grdPersons.SelectedItem as Persons.persons;

colPersons.Remove(prs);

svcPersons.DeleteObject(prs);

svcPersons.BeginSaveChanges(svcPersons_SaveChangesCompleted, null);

}

Page 418: Дронов В. Самоучитель Silverlight 3 (2010)

406 Часть VI. Работа с файлами и Web-службами

Здесь мы сначала спрашиваем у пользователя, следует ли удалять выбранно-

го им специалиста, выведя на экран соответствующее окно-предупреждение.

(Подробнее об окнах-предупреждениях говорилось в главе 19.) Если пользо-

ватель ответит положительно, нажав кнопку ОK, мы выполняем удаление.

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

тельные изменения в данных, нужно спросить пользователя, действительно ли

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

действие...

Добавление данных

во вторичную коллекцию

Точно таким же образом реализуем правку и удаление платформ. И начнем

реализацию их добавления.

Как и в предыдущем случае, нам нужно создать объект класса platforms,

заполнить его данными и добавить во внутреннюю коллекцию, в кото-

рой кэшируются платформы, соответствующие выбранному специалисту.

А дальше?

Класс persons поддерживает свойство platforms, которое хранит коллекцию

платформ в виде объектов класса platforms. А класс platforms поддерживает

свойство persons, хранящее указатель на специалиста, которому соответству-

ет данная платформа, т. е. на объект класса persons. Исходя из этого, нам

нужно добавить в созданную платформу в коллекции platforms специалиста

и присвоить его же свойству persons платформы. Таким образом мы созда-

дим связь между специалистом и платформой.

Однако так мы можем сделать только на стороне приложения, в его внутрен-

них коллекциях, кэширующих принятые от Web-службы данные. На стороне

самой Web-службы такой номер не пройдет — там все это выполняется

иначе.

Прежде всего, мы должны добавить элемент во вторичную коллекцию. Дела-

ется это вызовом уже знакомого нам метода AddObject контекста Web-

службы.

svcPersons.AddObject("platforms", pls);

Здесь мы добавляем в коллекцию platforms Web-службы новый элемент pls.

Далее мы должны привязать вновь добавленный элемент вторичной коллек-

ции к соответствующему ему элементу первичной коллекции. Для этого ис-

Page 419: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 407

пользуется метод AddLink, поддерживаемый классом DataServiceContext и

всеми его потомками.

Вот формат его вызова:

AddLink(<элемент первичной коллекции>,

�<имя свойства элемента первичной коллекции,

�которое представляет вторичную коллекцию>,

�<привязываемый элемент вторичной коллекции>)

Первым параметром передается элемент первичной коллекции, к которому

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

Имя свойства элемента первичной коллекции, которое представляет вторич-

ную коллекцию, передается в виде строки вторым параметром. Ну а третьим

параметром задается сам привязываемый элемент вторичной коллекции.

svcPersons.AddLink(currentPerson, "platforms", pls);

Здесь мы привязываем элемент вторичной коллекции pls к элементу первич-

ной коллекции currentPerson. Для привязки мы используем свойство элемен-

та первичной коллекции platforms.

Следующий шаг — "обратная" привязка элемента первичной коллекции к

только что добавленному элементу вторичной коллекции. В этом нам помо-

жет метод SetLink класса DataServiceContext и всех его потомков. Вот фор-

мат его вызова:

AddLink(<элемент вторичной коллекции>,

�<имя свойства элемента вторичной коллекции,

�которое представляет элемент первичной коллекции>,

�<привязываемый элемент первичной коллекции>)

Первым параметром передается только что созданный элемент вторичной

коллекции, к которому нужно привязать элемент первичной коллекции. Имя

свойства элемента вторичной коллекции, которое представляет элемент пер-

вичной коллекции, указывается вторым параметром в виде строки. Третьим

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

привязать.

svcPersons.SetLink(pls, "persons", currentPerson);

Это выражение привязывает элемент первичной коллекции currentPerson

к элементу вторичной коллекции pls. Для привязки задействуется свойство

элемента вторичной коллекции persons.

После этого нам остается только инициировать процесс сохранения, чтобы

сделанные нами изменения были перенесены в базу данных Web-службы.

Page 420: Дронов В. Самоучитель Silverlight 3 (2010)

408 Часть VI. Работа с файлами и Web-службами

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

лекцию нового элемента нам нужно:

1. Создать новый объект класса platforms.

2. Заполнить его данными, взятыми из вторичного окна.

3. Добавить его во внутреннюю коллекцию, в которой кэшируются плат-

формы, относящиеся к выбранному специалисту.

4. Выполнить необходимые привязки во внутренних коллекциях.

5. Добавить его в коллекцию Web-службы.

6. Выполнить необходимые привязки на стороне Web-службы.

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

Поместим в это окно поле ввода txtName, предназначенное для ввода названия

платформы. Вторичное окно мы назовем AddPlatform.

Создадим обработчик события Click кнопки btnAddPlatform главной страни-

цы. Вот его код:

AddPlatform wndAdd = new AddPlatform();

wndAdd.Closed += wndAddPlatform_Closed;

wndAdd.Show();

Здесь нам все уже знакомо.

А вот полный код обработчика события Closed вторичного окна AddPlatform:

private void wndAddPlatform_Closed(object sender, EventArgs e)

{

AddPlatform wndAdd = sender as AddPlatform;

if (wndAdd.DialogResult == true)

{

Persons.platforms pls = new Persons.platforms();

pls.name = wndAdd.txtName.Text;

colPlatforms.Add(pls);

Persons.persons currentPerson = grdPersons.SelectedItem as

�Persons.persons;

currentPerson.platforms.Add(pls);

pls.persons = currentPerson;

svcPersons.AddObject("platforms", pls);

svcPersons.AddLink(currentPerson, "platforms", pls);

svcPersons.SetLink(pls, "persons", currentPerson);

svcPersons.BeginSaveChanges(svcPersons_SaveChangesCompleted, null);

}

}

Page 421: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 22. Работа с Web-службами 409

В принципе, здесь тоже нечего комментировать. Все, что делает этот код,

мы разобрали в начале данного раздела. Так что проверим уже полностью

готовое приложение в действии и преисполнимся чувством собственного

достоинства.

Что дальше?

В этой главе мы создали довольно сложное Silverlight-приложение, работаю-

щее с Web-службой. Причем выполнили это без особого труда — благо нема-

лую часть работы сделали за нас сама платформа .NET и Visual Web

Developer 2008.

Дальше будет уже не столь масштабно, но не менее интересно. Мы рассмот-

рим полезные "мелочи", которые помогут нам в создании Silverlight-

приложений. Этому будет посвящена следующая глава.

Page 422: Дронов В. Самоучитель Silverlight 3 (2010)

410 Часть VI. Работа с файлами и Web-службами

Page 423: Дронов В. Самоучитель Silverlight 3 (2010)

ЧАСТЬ VII

Последние штрихи

Глава 23. Полезные мелочи

Глава 24. Распространение Silverlight-приложений

Page 424: Дронов В. Самоучитель Silverlight 3 (2010)
Page 425: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 23

Полезные мелочи

В предыдущей главе мы занимались... да чем только не занимались! Создали

базу данных, Web-сайт, модель данных, Web-службу и, наконец, клиентское

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

работать с данными, хранящимися в базе. Чего только не сделаешь ради

пользователей...

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

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

с пользой.

В этой главе мы узнаем о некоторых полезных возможностях Silverlight, ко-

торые могут нам пригодиться. Это привязка к данным сразу нескольких ком-

понентов, создание всплывающих подсказок для компонентов, реализация

полноэкранного режима и использование изолированного хранилища для

хранения настроек приложения.

Привязка к данным сразу нескольких компонентов

Привязкой компонентов к данным мы занимаемся, начиная с главы 13.

И узнали о ней уже достаточно много. Но не все.

Часто приходится выполнять привязку компонентов к данным в C#-коде. Мы

знаем, как это сделать, — с помощью свойства DataContext, поддерживаемого

всеми компонентами. И неоднократно делали.

Но также часто приходится привязывать к данным сразу несколько компо-

нентов. Вспомним многостраничное приложение MultiPage, которым мы за-

нимались в главе 18, — на второй подстранице этого приложения нам при-

Page 426: Дронов В. Самоучитель Silverlight 3 (2010)

414 Часть VII. Последние штрихи

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

лать это отдельно для каждого компонента, но, как говорится, есть способ

лучше.

Мы можем привязать к данным контейнер, в котором находятся эти ком-

поненты. Что и сделали в том самом многостраничном приложении из гла-

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

контейнер...

Всплывающие подсказки

для компонентов

Во многих Windows-приложениях мы встречали всплывающие подсказки,

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

том, и содержат краткую справочную информацию. В Silverlight-прило-

жениях мы можем создать такие же.

Для задания текста всплывающей подсказки мы используем свойство

ToolTipService.ToolTip, поддерживаемое всеми компонентами. Этому свой-

ству присваивается строка с текстом подсказки.

<Button x:Name="btnAddPerson" Content="Добавить" . . .

ToolTipService.ToolTip="Добавление специалиста" . . .></Button>

Здесь мы задали подсказку для кнопки btnAddPerson приложения

PersonsClient, с которым работали в главе 22.

Вообще, свойство ToolTipService.ToolTip принимает в качестве значения

объект класса ToolTip. Данный класс поддерживает несколько свойств, кото-

рые мы сейчас рассмотрим.

Прежде всего, он поддерживает свойство Content. Это свойство имеет тип

Object — это значит, что мы можем задать в качестве содержимого всплы-

вающей подсказки любой компонент. Обычно ограничиваются простым тек-

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

комбинацию текста и графического изображения, помещенные в контейнер.

<Button x:Name="btnAddPerson" Content="Добавить" . . .>

<ToolTipService.ToolTip>

<ToolTip>

<ToolTip.Content>

<StackPanel>

<Image Source="add.jpg"></Image>

<TextBlock Text="Добавление специалиста"></TextBlock>

</StackPanel>

Page 427: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 23. Полезные мелочи 415

</ToolTip.Content>

</ToolTip>

</ToolTipService.ToolTip>

</Button>

Здесь мы задали для кнопки всплывающую подсказку, представляющую со-

бой комбинацию графического изображения и текста, вложенные в контей-нер "стопка".

Свойства HorizontalOffset и VerticalOffset позволяют задать расстояние

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

свойств по умолчанию — 0.

Свойство IsOpen возвращает значение true, если всплывающая подсказка

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

Свойство IsOpen доступно только в C#-коде!

Также класс ToolTip поддерживает свойства ActualHeight, ActualWidth,

FontFamily, FontSize, FontStretch, FontStyle, FontWeight, Height,

HorizontalAlignment, IsEnabled, Margin, MaxHeight, MaxWidth, MinHeight,

MinWidth, Name, Padding, TabIndex, VerticalAlignment, Visibility и Width, а также все свойства, задающие цвет и прозрачность (см. главы 15 и 16).

<ToolTipService.ToolTip>

<ToolTip Content="Добавление специалиста" FontSize="18"

�FontStyle="Italic" Foreground="Blue"></ToolTip>

</ToolTipService.ToolTip>

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

Еще класс ToolTip поддерживает два события, которые могут быть нам по-

лезны. Событие Opened возникает после появления всплывающей подсказки

на экране, а событие Closed — после ее скрытия.

Реализация полноэкранного режима

Еще в главе 1 мы выяснили, что Silverlight-приложение, как и все клиентские Web-приложения, встраивается в Web-страницу. Оно представляет собой не-

отъемлемую часть этой Web-страницы, как абзац текста, графическое изо-бражение или таблица, и не может быть "вынесено" за ее пределы.

Page 428: Дронов В. Самоучитель Silverlight 3 (2010)

416 Часть VII. Последние штрихи

Но Silverlight предоставляет нам возможность вывести приложение в отдель-

ном окне, которое займет весь экран компьютера. Такой режим работы

Silverlight-приложения называется полноэкранным.

О полноэкранном приложении нужно знать следующие вещи...

� Мы не можем управлять размером окна, в котором открывается Silverlight-

приложение в полноэкранном режиме. Это окно всегда будет занимать

весь экран и выводиться поверх всех остальных окон, включая и окно

Web-обозревателя, в котором открыта содержащая данное приложение

Web-страница.

� Пользователь может переключиться из полноэкранного в обычный режим

(когда Silverlight-приложение выводится в Web-странице), просто нажав

клавишу <Esc> или комбинацию клавиш <Alt>+<F4>. Об этом ему сооб-

щит небольшое предупреждение, которое появится на экране сразу после

переключения в полноэкранный режим и пропадет через несколько се-

кунд.

� Код, активирующий полноэкранный режим, может находиться только в

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

быть, например, обработчик события Click кнопки. Если же данный код

встретится в методе, не являющемся обработчиком такого события, он бу-

дет выполнен, но переключения в полноэкранный режим не произойдет.

� В полноэкранном режиме не работают классы OpenFileDialog и

SaveFileDialog. Это значит, что мы не сможем предоставить пользователю

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

(О работе с локальными файлами рассказывалось в главе 20.)

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

ранный режим и обратно? Разговор об этом придется начать издалека...

1. Мы уже давно знаем, что само Silverlight-приложение представляется

в виде объекта. Класс этого объекта создается самим Visual Web

Developer 2008 при создании приложения и является потомком класса

Application. Класс Application предоставляет базовые свойства, методы и

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

2. В частности, класс Application предоставляет статическое и доступное

только для чтения свойство Current. Оно возвращает указатель на объект

данного приложения, который имеет тип Application.

3. Класс Application также поддерживает доступное только для чтения свой-

ство Host. Оно возвращает указатель на объект класса SilverlightHost,

представляющий среду исполнения Silverlight, под управлением которой

работает данное приложение.

Page 429: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 23. Полезные мелочи 417

4. Класс SilverlightHost поддерживает доступное только для чтения свойст-

во Content. Оно возвращает указатель на объект класса Content, представ-

ляющий пространство на Web-странице, которое отведено под вывод дан-

ного Silverlight-приложения.

5. Класс Content поддерживает свойство IsFullScreen. Значение true этого

свойства вызывает переключение приложения в полноэкранный режим, а

значение false — в обычный.

Значит, для переключения в полноэкранный режим мы должны написать вот

такой код:

Application.Current.Host.Content.IsFullScreen = true;

То есть мы сначала получаем объект текущего приложения через статическое

свойство Current объекта Application, через него — объект среды исполнения

Silverlight (свойство Host), далее — объект выделенного под приложение

пространства на Web-страницы (свойство Content), а уже с помощью этого

объекта выполним переключение в полноэкранный режим (свойство

IsFullScreen).

Давайте поместим на главную страницу приложения PersonsClient, которое

мы создали в главе 22, еще одну кнопку, которую назовем btnFullScreen. Эта

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

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

тию Click обработчик.

Вот код этого обработчика:

if (Application.Current.Host.Content.IsFullScreen)

{

Application.Current.Host.Content.IsFullScreen = false;

btnFullScreen.Content = "В полноэкранный режим";

}

else

{

Application.Current.Host.Content.IsFullScreen = true;

btnFullScreen.Content = "В обычный режим";

}

Здесь мы проверяем, в каком режиме работает приложение, и выполняем

соответствующее переключение, заодно меняя надпись на кнопке

btnFullScreen.

Класс Content также поддерживает событие FullScreenChanged, которое воз-

никает при переключении между режимами.

Page 430: Дронов В. Самоучитель Silverlight 3 (2010)

418 Часть VII. Последние штрихи

Хранение настроек приложения

Зачастую разработчики в своих приложениях предоставляют возможности их

настройки, более или менее гибкие. Чтобы после завершения работы прило-

жения они не потерялись, их сохраняют в особых файлах или в системном

реестре. Когда приложение будет запущено в следующий раз, эти настройки

загружаются и пускаются в дело.

Silverlight предоставляет нам удобные средства для хранения настроек при-

ложения в особом файле в изолированном хранилище. (Подробнее об изоли-

рованном хранилище рассказывалось в главе 20.) Причем собственно сохра-

нение выполняет она сама — при завершении работы приложения, — нам

понадобится только задать необходимые значения настроек приложения.

А при последующем запуске приложения она сама их загрузит, и мы сможем

их прочитать и применить.

За хранение настроек приложения "отвечает" класс IsolatedStorageSettings.

Он предоставляет набор свойств и методов, с помощью которых мы можем

сохранять, извлекать и удалять различные данные, являющиеся значениями

настроек приложения, или просто произвольные данные.

Класс IsolatedStorageSettings объявлен в пространстве имен System.IO.

IsolatedStorage. Оно изначально не отображено, поэтому мы либо должны

отобразить его, либо использовать полные имена типов.

Прежде всего, нам нужно получить объект класса IsolatedStorageSettings,

который будет использоваться для хранения настроек приложения (хранили-

ще настроек). При этом среда исполнения Silverlight также попытается найти

в изолированном хранилище файл, хранящий настройки приложения, и за-

грузить его содержимое в созданное хранилище. Если такой файл найден не

будет, хранилище настроек останется пустым.

Для создания хранилища мы воспользуемся статическим и доступным только

для чтения свойством ApplicationSettings класса IsolatedStorageSettings.

Оно возвращает то, что нам нужно, — объект данного класса.

Обычно под хранилище настроек создается специальное поле или свойство

класса страницы. Так мы сможем получить доступ к хранилищу из любого

метода.

private System.IO.IsolatedStorage.IsolatedStorageSettings iseOptions =

System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings;

Хранилище настроек, т. е. объект класса IsolatedStorageSettings, фактически

представляет собой словарь. (О словарях говорилось в главе 11.) Каждое зна-

чение настройки заносится в него в виде отдельного элемента, и ему сопос-

Page 431: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 23. Полезные мелочи 419

тавляется уникальный ключ. Впоследствии по этому ключу мы сможем найти

нужное нам значение и получить или изменить его. Единственное отличие

класса IsolatedStorageSettings от "настоящих" словарей — ключи должны

быть исключительно строковыми.

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

метод Add.

Add(<ключ>, <значение>)

Первым параметром передается значение ключа в виде строки, а вторым —

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

iseOptions.Add("width", 800);

Здесь мы добавляем в хранилище настроек значение 800 с ключом width.

Получить это значение мы можем тем же способом, что и значение элемента

словаря:

LayoutRoot.Width = iseOptions["width"];

И точно таким же способом изменить:

iseOptions["width"] = 1024;

Метод Contains позволяет определить, присутствует ли в хранилище настро-

ек значение с ключом, переданным ему в качестве единственного параметра.

Он возвращает true, если такое значение есть, и false в противном случае.

if (!(iseOptions.Contains("width")))

{

iseOptions.Add("width", LayoutRoot.Width);

}

Здесь мы проверяем, существует ли настройка с ключом width, и, если не

существует, добавляем ее.

if (iseOptions.Contains("width"))

{

LayoutRoot.Width = iseOptions["width"];

}

А здесь мы проверяем, существует ли настройка с ключом width, и, если

существует, получаем ее значение.

Перед тем как заносить в хранилище настроек новое значение, следует прове-рить, существует ли уже там значение с тем же ключом. А перед тем как читать значение из хранилища настроек, нужно проверить, существует ли оно там.

Page 432: Дронов В. Самоучитель Silverlight 3 (2010)

420 Часть VII. Последние штрихи

Для удаления значения с заданным ключом мы используем метод Remove.

В качестве единственного параметра он принимает ключ удаляемого значе-

ния в виде строки и возвращает true, если удаление успешно выполнено, и

false в противном случае.

iseOptions.Remove("width");

Свойство Count возвращает количество занесенных в хранилище настроек

в виде целого числа.

Метод Clear позволяет удалить все настройки из хранилища. Этот метод не

принимает параметров и не возвращает результата.

Как мы выяснили в начале этого раздела, все занесенные в хранилище на-

стройки переносятся в специально созданный в изолированном хранилище

файл сразу после завершения работы приложения. Однако мы можем выпол-

нить их сохранение в данном файле принудительно. Для этого достаточно

вызвать метод Save класса IsolatedStorageSettings, не принимающий пара-

метра и не возвращающий результата.

Что дальше?

Вероятно, это самая короткая глава во всей книге, посвященная различным

полезным мелочам, которые могут нам пригодиться...

Последняя глава будет посвящена распространению Silverlight-приложений.

Так сказать, заключительному шагу их разработки...

Page 433: Дронов В. Самоучитель Silverlight 3 (2010)

Ã Ë À  À 24

Распространение Silverlight-приложений

В предыдущих двадцати трех главах мы писали Silverlight-приложения. Пи-

сали и одновременно изучали саму платформу Silverlight, языки XAML и C#

и среду разработки Visual Web Developer 2008.

Но вот приложения готовы. Настал волнующий миг — их распространение.

Совсем скоро мы опубликуем наши творения в Сети, сделаем их доступными

для пользователей.

Эта глава будет посвящена исключительно распространению Silverlight-

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

приложения, из каких файлов оно состоит, как задать параметры приложения

и как поместить приложение на Web-страницу. А еще мы превратим наше

приложение в независимое — не требующее для работы Web-обозревателя

и работающее в своем отдельном окне, как обычное Windows-приложение.

Да-да, Silverlight может и это!

Версии Silverlight-приложения. Отладочная и распространяемая версии

Но сначала давайте поговорим о различных версиях Silverlight-приложений

и о том, чем они отличаются.

В общем случае версия — это разновидность данного приложения, имеющая

какие-либо отличия от других его версий. Такими отличиями может быть

"возраст" приложения, набор поддерживаемых возможностей или его назна-

чение.

Начнем с версий, отличающихся "возрастом". Предположим, что мы имеем две версии приложения: одна создана раньше, а другая — позже. Более позд-

Page 434: Дронов В. Самоучитель Silverlight 3 (2010)

422 Часть VII. Последние штрихи

няя (новая) версия может иметь больше возможностей, работать быстрее, и в

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

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

Версии, отличающиеся "возрастом", помечаются разными номерами версий. Нумерация версий идет по возрастанию. Так, версия 2.0 приложения новее,

чем версия 1.0.

Что касается отличий в наборе возможностей, то классический пример — ис-пытательная и полнофункциональная версии условно-бесплатного приложе-

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

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

набором возможностей и имеющую неограниченный "срок годности".

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

� Отладочная версия создается в процессе работы над приложением и за-

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

� Распространяемая версия используется для распространения приложения.

Она создается, когда работа над приложением закончена. Ее исполняемый файл не содержит никакой информации, требуемой для отладки. Также

при компиляции распространяемой версии среда разработки выполняет оптимизацию кода с целью сделать его компактнее и быстрее в работе.

Значит, перед распространением приложения нам нужно создать его распро-

страняемую версию. Как это делается? Сейчас узнаем.

Создание распространяемой версии приложения

Для создания той или иной версии приложения Visual Web Developer 2008

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

ется конфигурацией.

При создании нового проекта Visual Web Developer сам создает для него две конфигурации: отладочную и распространяемую. Первая служит для созда-

ния отладочной версии приложения, вторая — для создания распространяе-мой версии. Изначально, при работе над приложением, используется отла-

дочная конфигурация.

Page 435: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 24. Распространение Silverlight-приложений 423

Выходит, для создания распространяемой версии приложения нам нужно вы-

брать распространяемую конфигурацию. Делается это очень просто.

Выберем пункт Configuration Manager меню Build. На экране появится диа-

логовое окно Configuration Manager (рис. 24.1).

Рис. 24.1. Диалоговое окно Configuration Manager

Все конфигурации, созданные для данного проекта, перечислены в раскры-

вающемся списке Active solution configuration этого окна. Отладочная кон-

фигурация представлена пунктом Debug, а распространяемая — пунктом

Release. Его-то и выберем.

Все! Можно закрыть окно Configuration Manager нажатием кнопки Close.

Теперь Visual Web Developer 2008 будет при компиляции приложения ис-

пользовать выбранную нами конфигурацию.

Сама компиляция приложения запускается выбором пункта Build <имя про-

екта> меню Build. Это нам уже знакомо.

Файлы, составляющие приложение

Приложение готово. Можно его публиковать.

Но что именно мы должны публиковать? Какие файлы составляют приложе-

ние? В главе 4 мы уже это рассмотрели, но довольно поверхностно. Настала

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

Page 436: Дронов В. Самоучитель Silverlight 3 (2010)

424 Часть VII. Последние штрихи

Найдем в папке, где хранятся файлы проекта, папку Bin. В ней мы увидим

две папки: Debug и Release. Они хранят откомпилированные файлы различ-

ных версий приложения — отладочной и распространяемой соответственно.

Поскольку нас интересует распространяемая версия, заглянем в папку

Release.

Там мы увидим, прежде всего, файл пакета (или просто пакет) — архив ZIP,

содержащий все файлы приложения (кроме тестовой Web-страницы, разу-

меется). Этот файл имеет расширение xap и имя, совпадающее с именем про-

екта.

Далее мы найдем там тестовую Web-страницу. Она хранится в файле

TestPage.html.

Остальные файлы, во множестве "сваленные" в эту папку, нам не интересны.

За единственным исключением — динамические библиотеки Windows. Рас-

смотрим им.

Каждая из этих библиотек представляет собой сборку, хранящую код прило-

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

пространства имен, реализованного в ней.

Мы уже давно знаем, что при создании нового проекта Visual Web

Developer 2008 создает под него новое пространство имен, имя которого сов-

падает с именем проекта. Так, для проекта GridDemo он создал пространство

имен GridDemo. А если имя файла сборки совпадает с именем реализованного

в нем пространства имен, то при компиляции проекта GridDemo мы получим

сборку GridDemo.dll. Вывод: сборка, содержащая код приложения, имеет то

же имя, что и проект этого приложения.

Остальные сборки, находящиеся в папке, являются библиотечными — их ис-

пользует созданное нами приложение. Например, приложение GridDemo (по

крайней мере, в реализации автора) использует библиотечные сборки

System.ComponentModel.DataAnnotations.dll, System.Windows.Controls.Data.dll,

System.Windows.Controls.Data.Input.dll, System.Windows.Controls.dll и

System.Windows.Data.dll. Из названий файлов этих библиотек ясно, какие

пространства имен в них реализованы.

А теперь — внимание! Все библиотечные сборки, используемые приложени-

ем, по умолчанию включаются в файл пакета. Нам распространять их вместе

с файлом пакета не нужно.

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

сборки не включались в файл пакета. При этом Visual Web Developer 2008

поместит каждую библиотечную сборку в архив ZIP, чтобы уменьшить ее

размер. И мы должны будем распространять эти архивы вместе с файлом па-

кета.

Page 437: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 24. Распространение Silverlight-приложений 425

Зачем это может понадобиться? Предположим, что вышла новая версия ка-

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

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

библиотечные сборки в пакет, нам будет достаточно опубликовать обновлен-ную версию сборки, заменив ей старую версию, — перекомпилировать все

приложение не понадобится.

Напоследок обратим внимание на набор папок со странными именами: de, es,

fr и пр. Они хранят дополнительные библиотечные сборки, добавляющие

к основным библиотечным сборкам поддержку того или иного языка. Так, сборки для поддержки немецкого языка хранятся в папке de, поддерживаю-

щие испанский язык — в папке es, поддерживающие французский — в папке fr и т. д.

Распространять ли эти папки вместе с остальными файлами приложения? Ес-ли вы собираетесь создавать приложения на данных языках, тогда да. (Хотя и

без них приложение будет работать.) Для англо- и русскоязычных приложе-

ний они не нужны: английский язык библиотечные сборки поддерживают изначально, а поддержкой русского языка в Silverlight 3 корпорация

Microsoft, к сожалению, не озаботилась...

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

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

Итак, подытожим, какие файлы составляют Silverlight-приложение:

� файл пакета;

� файл Web-страницы, на которой будет размещено приложение (это может быть тестовая Web-страница, сгенерированная Visual Web Developer 2008, или Web-страница, созданная нами);

� ZIP-архивы с файлами библиотечных сборок, если мы в параметрах при-ложения указали не включать их в состав файла пакета;

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

� файлы и папки с невключенными ресурсами сборки.

Page 438: Дронов В. Самоучитель Silverlight 3 (2010)

426 Часть VII. Последние штрихи

Все эти файлы и папки на Web-сервере должны быть помещены в одну пап-

ку. Это обязательно!

Параметры приложения

Мы неоднократно упоминали о параметрах Silverlight-приложения. Что это

такое? И как их задать?

Проще простого! Найдем на панели Solution Explorer "ветвь" Properties и

дважды щелкнем на ней. Также можно выбрать пункт <имя проекта>

Properties меню Project. На экране появится новое окно документа, органи-

зованное в виде "блокнота" с вкладками; эти вкладки перечислены в левой

части данного окна.

Полезнее всего для нас будет вкладка Silverlight (рис. 24.2). Как правило, она

выбрана изначально; если это не так, щелкнем на ней, чтобы выбрать.

Рис. 24.2. Вкладка Silverlight окна документа, в котором задаются параметры приложения

Группа элементов управления Application позволяет задать имя проекта, имя

пространства имен, в котором будут объявлены классы проекта, и имя класса

Page 439: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 24. Распространение Silverlight-приложений 427

объекта, который будет создан при запуске приложения (обычно это объект

самого приложения). Эти параметры (как коней на переправе) практически

никогда не меняют.

А еще мы можем задать там параметры сборки, реализующей приложение.

Для этого служит кнопка Assembly Information. Нажмем ее, и на экране по-

явится диалоговое окно Assembly Information (рис. 24.3).

Рис. 24.3. Диалоговое окно Assembly Information

Прежде всего, мы видим там набор полей ввода, в которых задаются сле-

дующие параметры сборки.

� Title — название сборки (изначально совпадает с именем проекта).

� Description — описание сборки.

� Company — название разработчика.

� Product — внутреннее название сборки (обычно совпадает просто с на-

званием).

� Copyright — сведения о правах разработчика.

� Trademark — обозначение разработчика, торговая марка.

Ниже находятся две группы по четыре поля ввода: Assembly Version и File

Version. Они задают, соответственно, номер версии самой сборки и файла

сборки; как правило, их номера версий совпадают.

Page 440: Дронов В. Самоучитель Silverlight 3 (2010)

428 Часть VII. Последние штрихи

Поля ввода в каждой группе служат для ввода (в порядке слева направо):

� старшего номера версии — им различаются версии, имеющие коренные

изменения;

� младшего номера версии — им различаются версии, имеющие менее зна-чительные изменения;

� номера подверсии — им различаются версии, имеющие малозначительные

изменения;

� номера компиляции — им различаются версии, имеющие совсем незначи-

тельные изменения.

В поле ввода GUID отображается уникальный код, идентифицирующий сборку и сгенерированный самим Visual Web Developer 2008 при создании

проекта. Его также практически никогда не меняют.

Раскрывающийся список Neutral Language задает язык, изначально поддер-живаемый данным приложением. Если мы не собираемся создавать много-

языковые версии данного приложения, то выберем в нем пункты (None) (ука-зания на язык отсутствуют), Russian или Russian (Russia) (русский язык).

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

Закончив задание параметров сборки, нажмем кнопку OK, чтобы их сохра-нить. Чтобы отказаться от изменений параметров сборки, следует нажать

кнопку Cancel.

Вернемся на вкладку Silverlight окна документа, где задаются параметры приложения. Что там еще мы не рассмотрели? Ага, группу элементов управ-

ления Silverlight build options!

В поле ввода Xap file name можно изменить имя файла пакета. Может, кому-то это и понадобится...

А вот флажок Reduce XAP size by using application library caching гораздо

полезнее! Будучи установленным, он предписывает Visual Web De-veloper 2008 исключить библиотечные сборки из состава файла пакета и по-

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

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

Закроем окно документа, где они вводятся, и сохраним проект.

После изменения параметров приложения его следует перекомпилировать.

Page 441: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 24. Распространение Silverlight-приложений 429

Вставка Silverlight-приложения в Web-страницу

Для вывода Silverlight-приложения мы можем использовать тестовую Web-

страницу, созданную для нас Visual Web Developer 2008. Эта Web-страница

уже содержит весь необходимый HTML-код, загружающий приложение и

запускающий его под управлением среды исполнения Silverlight.

Но если мы хотим поместить Silverlight-приложение на другую Web-

страницу, нам придется написать этот HTML-код самим. Благо он не очень

сложен.

Вот этот код:

<object width="<ширина приложения>" height="<высота приложения>"

data="data:application/x-silverlight-2,"

type="application/x-silverlight-2" >

<param name="source" value="<интернет-адрес файла пакета>"/>

<a href="http://go.microsoft.com/fwlink/?LinkID=149156"

style="text-decoration: none;">

<img src="http://go.microsoft.com/fwlink/?LinkId=108181"

alt="Get Microsoft Silverlight"

style="border-style: none"/>

</a>

</object>

Здесь мы видим HTML-тег <OBJECT>, который, собственно, и вставляет

Silverlight-приложение в Web-страницу. Ширина и высота приложения зада-

ются в его атрибутах width и height в виде целых чисел в пикселах.

В тег <OBJECT> вложен тег <PARAM>. В его атрибуте value указывается интер-

нет-адрес файла пакета, содержащего файлы приложения. Отметим, что ат-

рибут name данного тега при этом должен содержать значение source.

Файл пакета и все прочие файлы и папки, составляющие Silverlight-приложение, должны находиться в той же папке, что и файл Web-страницы, на которой вы-водится данное приложение.

Еще в тег <OBJECT> вложен тег <A>. Он обрабатывается Web-обозревателем

только в том случае, если на компьютере не установлена среда исполнения

Silverlight, и выводит на Web-страницу графические изображение-гипер-

ссылку. Щелкнув на ней, пользователь перейдет на особую Web-страницу

Page 442: Дронов В. Самоучитель Silverlight 3 (2010)

430 Часть VII. Последние штрихи

сайта Microsoft, где сможет найти дистрибутивный комплект среды исполне-

ния Silverlight и установить ее.

Независимые Silverlight-приложения

И напоследок — самое вкусное! Создание независимых Silverlight-прило-жений, работающих без Web-обозревателя.

� Независимое Silverlight-приложение открывается в своем отдельном окне, как обычное Windows-приложение.

� Независимое Silverlight-приложение устанавливается на компьютер, как обычное Windows-приложение. При этом будут созданы ярлыки для его запуска в меню Пуск (Start) и на рабочем столе. Установить независимое Silverlight-приложение можно прямо с Web-страницы, где оно размещено.

� Независимое Silverlight-приложение, будучи установленным на компью-тер, может быть впоследствии удалено.

� При каждом запуске независимого Silverlight-приложения среда исполне-ния Silverlight проверяет, есть ли соединение с Интернетом, и, если есть, доступна ли на Web-сайте, откуда было загружено приложение, его новая версия. Если новая версия приложения есть, она будет автоматически за-гружена и установлена. Так что пользователь всегда будет иметь актуаль-ную версию Silverlight-приложения.

Чтобы превратить обычное Silverlight-приложение в независимое, нам даже не придется модифицировать его код. Нужно только задать некоторые пара-метры приложения.

Создание независимых Silverlight-приложений

Откроем окно документа, где задаются параметры приложения. (Как это сде-лать, было описано ранее в данной главе.) И переключимся на вкладку Silverlight, если она не отобразилась изначально.

Нам понадобится флажок Enable running application out of the browser, рас-положенный в группе Silverlight build options. Чтобы превратить приложе-ние в независимое, нам нужно установить этот флажок.

Осталось задать дополнительные параметры независимого приложения. Для этого щелкнем кнопку Out-of-Browser Settings, расположенную правее флажка Enable running application out of the browser. На экране появится диалоговое окно Out-of-Browser Settings (рис. 24.4).

В поле ввода Windows Title вводится текст, который появится в заголовке окна независимого Silverlight-приложения. Изначально там присутствует имя проекта.

Page 443: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 24. Распространение Silverlight-приложений 431

Рис. 24.4. Диалоговое окно Out-of-Browser Settings

В полях ввода Width и Height указываются, соответственно, ширина и высо-

та окна приложения в виде целых чисел в пикселах. Если их не указать, окно

будет иметь размеры 800×600 пикселов.

В поле ввода Shortcut name вводится подпись к ярлыкам, которые будут соз-

даны после установки приложения для его запуска. Изначально там присут-

ствует текст вида <имя проекта> Application.

В области редактирования Download description вводится текст для всплы-

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

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

больше похожий на рекламу независимых Silverlight-приложений.

В поля ввода 16 x 16 Icon, 32 x 32 Icon, 48 x 48 Icon и 128 x 128 Icon ука-

зываются пути к файлам иконок размерами 16×16, 32×32, 48×48 и

128×128 пикселов соответственно. Эти иконки будут использованы для соз-

дания ярлыков приложения. Вместо ввода пути к файлу вручную можно на-

жать кнопку, расположенную правее соответствующего поля ввода, и вы-

Page 444: Дронов В. Самоучитель Silverlight 3 (2010)

432 Часть VII. Последние штрихи

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

открытия файла Windows.

Флажок Use GPU Acceleration при включении предписывает среде исполне-

ния Silverlight при выводе интерфейса приложения задействовать мощности

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

заметное при сложном интерфейсе.

Задав параметры, нажмем кнопку OK, чтобы их сохранить. Кнопка Cancel

позволяет отказаться от их сохранения.

Перекомпилируем приложение — и все! Теперь оно стало независимым.

Установка и использование независимых Silverlight-приложений

Установить независимое Silverlight-приложение можно прямо с Web-

страницы, на которой оно размещено. Для этого достаточно щелкнуть где-

либо на приложении правой кнопкой мыши и выбрать в появившемся на эк-

ране контекстном меню пункт Install <подпись к ярлыкам приложения>

onto this computer. (Подпись к ярлыкам приложения вводится в поле ввода

Shortcut name диалогового окна Out-of-Browser Settings (см. рис. 24.4).) На

экране появится небольшое диалоговое окно Install application (рис. 24.5),

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

Рис. 24.5. Диалоговое окно Install application

Флажки Start menu и Desktop при установке указывают среде исполнения

Silverlight создать для запуска приложения ярлыки, соответственно, в меню

Пуск (Start) и на рабочем столе.

Для установки приложения следует нажать кнопку OK. Для отмены установ-

ки служит кнопка Cancel.

Установка приложения занимает несколько секунд. После ее окончания при-

ложение будет запущено, и мы увидим на экране его окно.

Page 445: Дронов В. Самоучитель Silverlight 3 (2010)

Глава 24. Распространение Silverlight-приложений 433

Запустить установленное приложение можно щелчком на ярлыке, созданном

при установке в меню Пуск (Start) или на рабочем столе. Закрыть его можно,

как обычное Windows-приложение, нажав кнопку закрытия окна приложения

(она находится в правой части заголовка окна и имеет вид крестика) или на-

жав комбинацию клавиш <Alt>+<F4>.

Чтобы деинсталлировать установленное Silverlight-приложение, следует сно-

ва открыть в Web-обозревателе Web-страницу, с которой оно было установ-

лено, щелкнуть где-либо на присутствующем в данной Web-странице прило-

жении правой кнопкой мыши и выбрать в появившемся на экране контекст-

ном меню пункт Remove this application. Среда исполнения Silverlight

выведет окно-предупреждение, спрашивающее нас, действительно ли мы хо-

тим удалить это приложение; нажмем кнопку Да для его удаления и Нет для

отказа от этого.

Вот и все о распространении Silverlight-приложений. И вообще о платформе

Silverlight!

Page 446: Дронов В. Самоучитель Silverlight 3 (2010)

434 Часть VII. Последние штрихи

Page 447: Дронов В. Самоучитель Silverlight 3 (2010)

Заключение

Вот и подошла к концу книга-самоучитель Microsoft Silverlight 3. Мы изучи-ли основы программирования на этой платформе, языки XAML и C#, среду

разработки Microsoft Visual Web Developer 2008 Express Edition, создали множество клиентских приложений и даже одно серверное — Web-службу.

Мы вызубрили множество новых терминов, без которых в программировании никуда. Мы стали программистами.

Книга закончилась. Автор успел рассказать в ней о многом, о чем-то — под-

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

Silverlight 3 из-за недостатка места. Ведь самоучитель — книга по определе-нию тонкая...

Далее перечислены возможности, не рассмотренные на страницах этой книги.

� Внутренние механизмы работы среды исполнения Silverlight: обработка

откомпилированного MSIL-кода, управление памятью, потоками, ресур-сами операционной системы, сборками и пр.

� Механизм событий: типы событий, делегаты (своего рода представители

методов, которые выступают как обработчики событий; это еще один из типов данных Silverlight), создание собственных событий и их генерация

и пр.

� Работа с удаленными данными. Мы рассмотрели только загрузку файлов

по сети и "общение" с Web-службами, но сетевые возможности Silverlight

гораздо богаче.

� Работа с потоками: создание, управление, синхронизация и завершение.

И вообще написание многопоточных приложений, которые выполняют различные фрагменты своего кода в разных потоках. Мы этого вообще не

касались.

� Оптимизация кода для быстрейшего выполнения.

� Стили — мощнейшее средство для изменения внешнего вида компо-

нентов.

Page 448: Дронов В. Самоучитель Silverlight 3 (2010)

436 Заключение

� Шаблоны — для тех, кому недостаточно стилей. Шаблоны мы рассмотре-ли довольно-таки поверхностно, но на самом деле это гораздо более раз-витый инструмент.

� Создание собственных компонентов.

� Взаимодействие Silverlight-приложения со средой исполнения Silverlight.

� Взаимодействие Silverlight-приложения с Web-обозревателем и Web-страницей, на которой оно размещено, а также взаимодействие Web-страницы с Silverlight-приложением.

� И еще множество полезных мелочей, оставшихся "за бортом"...

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

Но кто такой программист? Программист — это тот, кто пишет программы (по определению). А еще тот, кто постоянно учится. К его услугам — книги, электронные руководства, справочники, примеры исходного кода. Все это можно без особого труда найти в книжных магазинах и Интернете.

В табл. З1 приведен список полезных интернет-ресурсов по теме Silverlight.

Таблица З1. Полезные интернет-ресурсы

Интернет-адрес Описание

http://www.silverlight.net/ Основной сайт Microsoft, посвященный Silverlight. Инструменты, документация, примеры и блоги разра-ботчиков

http://msdn.microsoft.com /ru-ru/default.aspx

Сайт MSDN (MicroSoft Developers Network). Большое собрание инструментов, документации и примеров на все случаи жизни. Альфа и омега программистов, использующих решения Microsoft

http://www.microsoft.com/ visualstudio/en-us/default.mspx

Раздел "домашнего" сайта Microsoft, посвященный Visual Studio

http://www.microsoft.com /silverlight/default.aspx

Раздел "домашнего" сайта Microsoft, посвященный Silverlight. К сожалению, больше похож на рекламу платформы, чем на серьезный программистский ре-сурс...

Вероятно, в Сети существуют и другие ресурсы, посвященные Silverlight. Перечисленные в табл. З1 — эта та "печка", от которой следует "плясать".

Так или иначе, автор прощается с вами!

Успешного Silverlight-программирования!

Владимир Дронов

Page 449: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель

A

Abs() 180

Abstract 182, 184

AcceptsReturn 100

Acos() 180

ActualHeight 97

ActualWidth 97

Add() 194, 202, 207, 419

AddDays() 194

AddHours() 194

AddLink() 407

AddMilliseconds() 194

AddMinutes() 194

AddMonths() 194

AddObject() 403

AddSeconds() 194

AddYears() 194

AlignmentX 292

AlignmentY 292

Angle 301

AngleX 302

AngleY 302

Application 416

ApplicationSettings 418

ArcSegment 281

ArgumentException 215

ArgumentNullException 216

ArgumentOutOfRangeException 216

ArithmeticException 216

ArrayTypeMismatchException 215

Asin() 180

AsyncState 397

Atan() 180

AutoGenerateColumns 243

AutoPlay 125

AutoReverse 311

AvailableFreeSpace 355

Average() 261

B

Background 284

Balance 125

Base 185

Begin() 319

BeginExecute() 396, 400

BeginLoadProperty() 402

BeginSaveChanges() 404

BeginTime 312

BezierSegment 282

Binding 228, 244

BindingMode 229

BitmapImage 122

BlurEffect 298

BlurRadius 298

Border 284

BorderBrush 284

BorderThickness 284

Brush 272

Button 103

By 258, 309

C

Calendar 110

CalendarClosed 111

Page 450: Дронов В. Самоучитель Silverlight 3 (2010)

438 Предметный указатель

CalendarOpened 111

CancelAsync() 370

Cancelled 370

CanGoBack 327

CanGoForward 327

CanUserReorder 243

CanUserReorderColumns 242

CanUserResize 243

CanUserResizeColumns 242

CanUserSort 243

CanUserSortColumns 243

Canvas 89

Canvas.Left 90

Canvas.Top 90

Canvas.ZIndex 91

Case 157

Catch 217

Ceiling() 180

CellEditingTemplate 244

CellTemplate 244

Center 277, 289

CenterOfRotationX 304

CenterOfRotationY 304

CenterOfRotationZ 304

CenterX 301, 302

CenterY 301, 302

Chars 178

CheckBox 105

Checked 106

Child 284

Children 212, 278, 303, 308

ChildWindow 335

Class 181

Clear() 203, 208, 420

Click 104

ClickMode 104

Clip 295

Close() 337, 352

Closed 338, 415

Color 286, 288

ColorAnimation 309

ColorAnimationUsingKeyFrames 313

ColorKeyFrame 313

ColorKeyFrameCollection 313

ColumnDefinition 87

ColumnDefinitionCollection 87

ColumnDefinitions 87

Columns 243

ComboBox 109

ComboBoxItem 109

Completed 320

Const 189

Contains() 204, 419

ContainsKey() 209

ContainsValue() 209

Content 103, 105, 106, 108, 113, 115,

414, 417

ContentControl 103

Convert() 234

ConvertBack() 234

Converter 235

CornerRadius 284

Cos() 180

Count 202, 207, 420

Count() 261

CreateDirectory() 347

CreateQuery() 400

CultureInfo 234

Current 416

CurrentState 125

CurrentStateChanged 126

D

Data 276

DataContext 240

DataGrid 241

DataGridCheckBoxColumn 243

DataGridColumn 243

DataGridGridLinesVisibility 242

DataGridHeadersVisibility 242

DataGridTemplateColumn 243

DataGridTextColumn 243

DataService 391

DataServiceContext 395, 396

DataServiceQuery 399

DataTemplate 239

Date 193

DatePicker 111

DateTime 193

DateValidation 111

Day 193

DayOfWeek 193

DayOfYear 193

Days 195

Default 157

Page 451: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель 439

DefaultExt 360

DefaultIfEmpty() 266

DeleteDirectory() 354

DeleteFile() 354

DeleteObject() 405

Dequeue() 210

Descending 256

DialogResult 337

Dictionary 207

Direction 298

DirectoryExists() 353

DiscreteColorKeyFrame 314

DiscreteDoubleKeyFrame 314

DiscreteObjectKeyFrame 316

DiscretePointKeyFrame 314

DisplayIndex 243

DisplayMemberPath 238

Dispose() 356

DivideByZeroException 216

do 160

Dock 115

DoubleAnimation 309

DoubleAnimationUsingKeyFrames 313

DoubleKeyFrame 313

DoubleKeyFrameCollection 313

DownloadProcessChangedEventArgs 369

DownloadProgressCompleted 369

DropDownClosed 109

DropDownOpened 109

DropShadowEffect 298

Duration 309

E

EasingFunction 312

Effect 298

ElementName 233

Ellipse 273

EllipseGeometry 277

Else 156

EndExecute() 397, 401

EndLoadProperty() 402

EndOfStream 351

EndPoint 276, 287

EndSaveChanges() 404

Enqueue() 210

EntitySetRights 392

Enum 199

Epsilon 192

Equals 258

Error 370

Exception 215

Exists 362

Exp() 180

F

Figures 279

File 362

FileAccess 348

FileExists() 353

FileInfo 362

FileMode 348

FileStream 362

Fill 272

FillBehavior 311

FillRule 275

Filter 359

FilterIndex 359

Finally 218

Flash 18

Floor() 180

Focus() 101

FontFamily 95

FontSize 95

FontStretch 95

FontStretches 95

FontStyle 96

FontStyles 96

FontWeight 96

FontWeights 96

For 159

Foreach 165

Foreground 285

Frame 323

FrameworkElement 107

From 253, 309

FullScreenChanged 417

G

Geometry 276

GeometryCollection 278

GeometryGroup 277

GetDirectoryNames() 354

GetFileNames() 353

Page 452: Дронов В. Самоучитель Silverlight 3 (2010)

440 Предметный указатель

GetUserStoreForApplication() 346

GlobalOffsetX 304

GlobalOffsetY 304

GlobalOffsetZ 304

GoBack() 327

GoForward() 327

GradientOrigin 289

GradientSpreadMethod 289

GradientStop 288

GradientStopCollection 288

GradientStops 288

Grid 82

Grid.Column 87

Grid.ColumnSpan 88

Grid.Row 87

Grid.RowSpan 88

GridLinesVisibility 242

Group 258

GroupName 107

H

HasCloseButton 336

Header 115, 243

HeadersVisibility 242

Height 83, 86

HorizontalAlignment 84

HorizontalOffset 415

HorizontalScrollBarVisibility 100, 114,

242

Host 416

Hour 193

Hours 195

HTML 9, 12

HyperlinkButton 330

I

IAsyncResult 397

IDataServiceConfiguration 392

IEasingFunction 312

IEnumerable 251

If 156

IGrouping 259

Image 121

ImageBrush 292

ImageFailed 122

ImageOpened 122

ImageSource 122, 292

In 165, 253, 258

IncreaseQuotaTo() 355

Indeterminate 106

IndexOf() 178, 204

IndexOutOfRangeException 215

Inline 97

InlineCollecton 97

INotifyCollectionChanged 236

INotifyPropertyChanged 230

Insert() 202

Internal 181, 183

Into 263

InvalidCastException 215

InvalidOperationException 215

IsChecked 105

IsClosed 279

IsDirectionReversed 112

IsEnabled 100

IsFilled 279

IsFullScreen 417

IsIndeterminate 113

IsInfinity() 191

IsLargeArc 281

IsMuted 125

IsNaN() 191

IsNegativeInfinity() 191

IsolatedStorageFile 347

IsolatedStorageFileStream 349

IsolatedStorageSettings 418

IsOpen 415

IsPositiveInfinity() 191

IsReadOnly 100, 242

IsSelected 108

IsTabStop 101

IsThreeState 105, 244

IsTodayHighlighted 110

Item 203, 208

ItemCollection 107

Items 107, 109, 114

ItemsSource 237

ItemTemplate 239

IValueConverter 234

J

Java 19

Join 258

Page 453: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель 441

K

Key 209, 259

KeyFrames 313

KeySpline 315

KeyTime 314

KeyValuePair 209

L

LargeChange 112

LastIndexOf() 179, 204

Length 178, 362

Let 264

Line 271

LinearColorKeyFrame 314

LinearDoubleKeyFrame 314

LinearGradientBrush 286

LinearPointKeyFrame 314

LineBreak 98

LineGeometry 276

LineHeight 96

LineSegment 280

LINQ 250

List 201

ListBox 107, 108

ListBoxItem 107

Loaded 91

Loading 328

LocalOffsetX 304

LocalOffsetY 304

LocalOffsetZ 304

Log() 180

Log10() 180

LongCount() 261

M

Margin 83

Math 179

Max() 180, 261

MaxDropDownHeight 109

MaxHeight 83

Maximum 112, 113

MaxLength 99

MaxValue 191, 192

MaxWidth 83

MediaElement 124

MediaElementState 125

MediaEnded 126

MediaFailed 126

MediaOpened 126

Message 216

MessageBox 341

MessageBoxButton 341

MessageBoxResult 341

Millisecond 193

Milliseconds 195

Min() 180, 261

MinHeight 83

Minimum 112, 113

Minute 193

Minutes 195

MinValue 191, 192

MinWidth 83

Mode 229

Month 193

MSIL 34

N

Name 97, 362

NaN 192

Navigate() 327

Navigated 328

NavigateUri 330

Navigating 328

NavigationContext 329

NavigationFailed 328

NavigationService 327

NavigationStopped 328

NegativeInfinity 192

NotEmplementedException 235

NotFiniteNumberException 216

Now 193

Null 177

NullReferenceException 215

O

Object 91, 178

ObjectAnimationUsingKeyFrames 316

ObjectKeyFrame 316

ObjectKeyFrameCollection 316

ObservableCollection 236

Offset 288

Page 454: Дронов В. Самоучитель Silverlight 3 (2010)

442 Предметный указатель

On 258

Opacity 272, 286

OpacityMask 297

Opened 415

OpenFile() 347, 360

OpenFileDialog 361

OpenRead() 362

OpenReadAsync() 366

OpenReadCompleted 367

OpenReadCompletedEventArgs 367

OpenText() 362

Orderby 255

Orientation 89, 112

Out 175, 184

OutOfMemoryException 215

OverflowException 216

Override 184

P

Padding 95

Page 325

Partial 182

Password 103

PasswordBox 103

PasswordChanged 103

PasswordChar 103

Path 228, 276

PathFigure 279

PathFigureCollection 279

PathGeometry 279

PathSegment 279

PathSegmentCollection 279

Pause() 126, 319

Peek() 210, 211

PI 179

PlaneProjection 304

Play() 126

Point 274, 276, 280

Point1 282

Point2 282

Point3 282

PointAnimation 309

PointAnimationUsingKeyFrames 313

PointCollection 274

PointKeyFrame 313

PointKeyFrameCollection 313

Points 274, 280

PolyBezierSegment 282

Polygon 274

Polyline 276

PolyLineSegment 280

PolyQuadraticBezierSegment 283

Pop() 211

Position 126

PositiveInfinity 192

Pow() 180

Private 181, 183

ProgressBar 112

ProgressPercentage 369

Projection 304

Protected 183

Protected internal 183

Public 181, 183

Push() 211

Q

QuadraticBezierSegment 283

QueryOperationResponse 402

QueryString 329

Queue 210

Quota 355

R

RadialGradientBrush 289

RadioButton 106

Radius 298

RadiusX 272, 277, 289

RadiusY 273, 277, 289

ReadLine() 351

Readonly 183

ReadToEnd() 351

Rect 277

Rectangle 272

RectangleGeometry 277

RelativeTransform 299

Remove() 203, 208, 356, 420

RemoveAt() 203

RenderTransform 299

RepeatBehavior 312

ResourceDictionary 227

Resources 227

Result 367

Resume() 319

Page 455: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель 443

RotateTransform 301

RotationAngle 281

RotationX 304

RotationY 304

RotationZ 304

Round() 180

RoutedEventArgs 91

RowDefinition 86

RowDefinitionCollection 86

RowDefinitions 86

RowEditEnded 405

Run 97

S

SafeFileName 360

Save() 420

SaveFileDialog 359

ScaleTransform 301

ScaleX 301

ScaleY 301

ScrollBarVisibility 101

ScrollViewer 113

Sealed 181, 185

Second 194

Seconds 195

Seek() 319

SeekAlignedToLastTick() 319

Segments 279

Select 253

Select() 102

SelectAll() 101

SelectedDate 110

SelectedDatesChanged 110

SelectedIndex 108, 115

SelectedItem 240

SelectedText 99

SelectionChanged 102, 109, 116

SelectionLength 100

SelectionStart 100

SetEntitySetAccessRule 392

SetLink() 407

SetSource() 367

ShadowDepth 298

Show() 336, 341

ShowDialog() 360

ShowGridLines 85

Sign() 180

SilverlightHost 416

Sin() 180

Size 281

SkewTransform 302

SkipToFill() 319

Slider 111

SmallChange 111

SolidColorBrush 286

Source 121, 122, 124, 126, 228, 323

SourceName 293

SpeedRatio 318

SplineColorKeyFrame 314

SplineDoubleKeyFrame 314

SplinePointKeyFrame 314

SpreadMethod 289

SQL 250

Sqrt() 180

Stack 210

StackOverflowException 215

StackPanel 89

StartPoint 276, 279, 287

Static 182, 183

Stop() 126, 319

StopLoading() 328

Storyboard 308

Storyboard.TargetName 310

Storyboard.TargetProperty 310

Stream 348

StreamReader 350

StreamWriter 349

Stretch 121, 292

String 178

Stroke 271

StrokeThickness 271

Struct 196, 197

Substring() 179

Sum() 261

SweepDirection 281

Switch 157

SystemException 215

T

TabControl 114

TabIndex 101

TabItem 115

TabStripPlacement 115

Tan() 180

Page 456: Дронов В. Самоучитель Silverlight 3 (2010)

444 Предметный указатель

TargetName 331

Text 93, 99, 111

TextAlignment 94

TextBlock 93

TextBox 99

TextChanged 102

TextDecorationCollection 94

TextDecorations 94

TextWrapping 94

Thickness 284

This 174

Throw 219

Timeline 308

TimelineCollection 308

TimeOfDay 194

TimeSpan 195

Title 325, 335

To 309

Today 194

ToLower() 179

ToolTip 414

ToolTipService.ToolTip 414

ToString() 191

TotalDays 195

TotalHours 195

TotalMilliseconds 195

TotalMinutes 195

TotalSeconds 195

ToUpper() 179

Transform 299

TransformCollection 303

TransformGroup 303

TranslateTransform 300

Try 217

TryGetValue() 208

TryParse() 191

Type 234

U

UIElement 212

UIElementCollection 212

Unchecked 106

Unicode 141

UpdateObject() 405

Uri 123

UriKind 123

UserControl 47, 91

UserState 368

V

ValidatesOnExceptions 232

Value 111, 112, 209, 259, 314

ValueChanged 112

Var 254

VerticalAlignment 84

VerticalOffset 415

VerticalScrollBarVisibility 100, 114, 242

VideoBrush 293

Virtual 184

Visibility 85

Void 184

Volume 125

W

WebClient 365

Web-обозреватель 9

Web-приложение

◊ клиентское 16

◊ серверное 13

Web-сайт 10

Web-сервер 11

Web-служба 375

◊ контекст 395

Web-страница 9

◊ по умолчанию 12

Web-сценарий 12

Web-форма 14

Where 255

While 160, 161

Width 83, 87

World Wide Web 9

WriteLine() 350

X

X 300

X:Class 47

X:Key 227

X:Name 57

X1 271

X2 271

Page 457: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель 445

XAML 33, 44

XML 33

Xmlns 79

Y

Y 300

Y1 271

Y2 271

Year 194

Z

Z-индекс 90

А

Автоподстановка 49

Агрегатные данные 261

Анимация

◊ покадровая 307

◊ продолжительность 306

◊ трансформационная 306

Атрибут тега 45, 46, 58

Б

База данных 376

◊ процессор 376

◊ реляционная 377

Безусловный переход 162

Библиотека 16, 73, 74

Блок 155

В

Версия 421

◊ номер 422

◊ отладочная 422

◊ распространяемая 422

Видеоцвет 293

Внедренный элемент 12

Выражение 60, 137

◊ блочное 155

◊ выбора 157

◊ сложное 155

◊ условное 64, 155

Г

Гиперссылка 9

Градиент 286, 289

Группа 258

◊ анимаций 307

◊ преобразований 303

◊ путей 277

Группировка 258

Д

Декремент 149

Делегат 435

Диалоговое окно

◊ Add Connection 379

◊ Add Existing Item 129

◊ Add New Item 324

◊ Add Reference 74

◊ Add Service Reference 394

◊ Assembly Information 427

◊ Change Data Source 379

◊ Choose Name 383

◊ Configuration Manager 423

◊ Entity Data Model Wizard 388

◊ Foreign Key Relationships 384

◊ Install application 432

◊ New Project 40

◊ New Silverlight Application 41, 393

◊ Open File 67

◊ Open Project 66

Page 458: Дронов В. Самоучитель Silverlight 3 (2010)

446 Предметный указатель

Диалоговое окно (прод.)

◊ Out-of-Browser Settings 430

◊ Tables And Columns 385

З

Завершение 52

Закрытие 352

Запись 377

Запрос 250

◊ вложенный 263

Запуск 52

И

Изолированное хранилище 346

Имя 31, 57, 76, 146

◊ полное 77, 78

◊ сокращенное 77

Индекс 163

Инициализатор 253

Инкремент 149

Интернет-адрес 10

Интерфейс 22, 196

Исключение 214

Исполняемый код 18

История 325

Исходный код 10

◊ форматирование 47

К

Кадр ключевой 307

Квота 354

Класс 30, 168

◊ абстрактный 182

◊ внутренний 181

◊ иерархия 32

◊ обобщенный 201

◊ потомок 32

◊ публичный 181

◊ родитель 32

◊ статический 182

◊ частный 181

Ключ 207

◊ группировки 258

Ключевое слово 57, 139

Кодировка 141

Коллекция 53, 200, 258

Комментарий 166

Компиляция 18

Компонент 25

◊ дочерний 26

Конвертер 234

Константа 138

◊ внутренняя 189

◊ защищенная 189

◊ защищенная внутренняя 189

◊ именованная 171

◊ публичная 189

◊ частная 190

Конструктор 57, 169, 186, 187

Контейнер 26

◊ "резиновый" 83

◊ вложенный 26

◊ главный 26

Конфигурация 422

Кривая Безье 282

◊ квадратичная 283

Кэширование 395

Л

Логика 23

М

Маска прозрачности 296

Массив 163, 173

◊ многомерный 164

◊ размер 163

◊ элемент 163

Метка 162

Метод 30, 169

◊ get 169

◊ set 169

◊ абстрактный 184

◊ виртуальный 184

◊ внутренний 184

◊ вызов 31

◊ защищенный 184

◊ защищенный внутренний 184

◊ обобщенный 397

◊ перегруженный 185

Page 459: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель 447

◊ перекрытый 184

◊ публичный 184

◊ статические 62

◊ статический 171

◊ тело 184

◊ унаследованный 32

◊ частный 184

Модель данных 388

Модификатор 181

Н

Навигация 322

Наследование 32

О

Объект 29, 168

Объектная запись 53, 86

Окно

◊ активное 42

◊ вторичное 332

◊ диалоговое 332

◊ документа 42

◊ закрытие 66

◊ переключение 43

◊ предупреждение 340

Операнд 138

Оператор 61, 138

◊ арифметический 149

◊ безусловного перехода 162

◊ бинарный 150

◊ возврата 185

◊ генерирования исключения 219

◊ комментария 166

◊ конкатенации 61, 150

◊ логические 152

◊ объединения строк 61, 150

◊ перезапуска 161

◊ преобразования ссылочных типов 176

◊ преобразования типов 145

◊ прерывания 158, 161

◊ присваивания 61

◊ присваивания простого 150

◊ присваивания сложного 150

◊ проверки типа 176

◊ создания объекта 172

◊ сравнения 151

◊ унарный 149

◊ условный 153

Открытие 346

Очередь 210

Ошибка 65

П

Панель 36, 113

◊ Database Explorer 378

◊ Error List 65

◊ Output 65

◊ Properties 130

◊ Solution Explorer 43, 74

◊ Toolbox 48

◊ закрытие 37

◊ инструментов 38

◊ скрытая 37

Папка, корневая 11

Параметр 31, 169, 328

Переменная 60, 137

◊ временная запроса 263, 264

◊ запроса 251

◊ объявление 60, 147

◊ элемента 252

Переопределение 172

Перечисление 84, 85, 199

◊ элемент 84, 199

Платформа 16

Подзапрос 262

Подстраница 322

◊ название 325

◊ начальная 322

Поле 169, 377

◊ внутреннее 183

◊ защищенное 183

◊ защищенное внутреннее 183

◊ ключевое 377

◊ обязательное 383

◊ публичное 183

◊ статическое 171

◊ счетчика 377

◊ частное 183

Полигон 274

Page 460: Дронов В. Самоучитель Silverlight 3 (2010)

448 Предметный указатель

Полноэкранный режим 416

Поток 333, 348

Преобразование 299

Префикс 80

Привязка 224, 413

Приложение 22

◊ серверное 13

Приоритет оператора 153

Приращение 306

Проект 39

◊ закрытие 66

◊ открытие 66

◊ создание 40

◊ стартовый 398

Пространство имен 75

◊ вложенное 75

◊ отображение 78

◊ по умолчанию 79

◊ подключение 79

Путь 276

Р

Расширенная запись 228

Результат 31, 169

Ресурс сборки 129

◊ включенный 130

◊ невключенный 130

Ресурсы 227

◊ приложения 227

◊ страницы 227, 308

Решение 40, 41, 387

С

Сайт 10

Сборка 73

◊ библиотечная 73

Свойство 30, 169

◊ внутреннее 188

◊ защищенное 188

◊ защищенное внутреннее 188

◊ по умолчанию 203

◊ публичное 188

◊ статическое 94, 171

◊ унаследованное 32

◊ частное 188

Связывание 258, 265, 266

Сегмент 279

Символ

◊ код 141

◊ специальный 141

Словарь 207

Событие 28, 30, 170

◊ обработчик 29, 58, 92, 171

◊ статическое 171

◊ унаследованное 32

Среда исполнения 16

Стек 210

Страница 23

◊ главная 24

◊ стартовая 38

Строгая типизация 147

Строка 140

Структура 95, 190

Счетчик цикла 159

Т

Таблица 377, 380, 384,

Тег

◊ HTML 10

◊ XAML 45

◊ вложенный 47

◊ закрывающий 45

◊ открывающий 45

◊ парный 45

Тик 307

Тип 60, 139

◊ анонимный 253

◊ вложенный 171

◊ значимый 144

◊ логический 143

◊ неявное указание 254

◊ объектный 173

◊ преобразование 62

◊ преобразование неявное 64, 144

◊ преобразование явное 145

◊ символьный 143

◊ ссылочный 173

◊ строковый 140

◊ целочисленный 142

◊ число с плавающей точкой 142

Page 461: Дронов В. Самоучитель Silverlight 3 (2010)

Предметный указатель 449

Точка

◊ базовая 301

◊ ключевая 288

◊ контрольная 282

У

Указатель 173

Условие 155

Ф

Файл

◊ дополнительных параметров

проекта 72

◊ дополнительных параметров

решения 72

◊ закрытие 66

◊ запускаемый 73

◊ исходного кода 39, 43, 72

◊ манифеста 73

◊ открытие 44, 66

◊ пакета 73

◊ проекта 40, 66, 71

◊ решения 72

◊ сохранение 66

Фильтр 255

Фокус ввода 31

Фрейм 322

Х

Хостинг-провайдер 366

Хранилище настроек 418

Ц

Цвет

◊ градиентный 286

◊ градиентный линейный 286

◊ градиентный радиальный 289

◊ графический 291

◊ предопределенный 285

◊ сплошной 285

Цикл 158

◊ перезапуск 161

◊ прерывание 161

◊ просмотра 165, 204, 209

◊ с постусловием 160

◊ с предусловием 161

◊ со счетчиком 158

◊ тело 159

Ш

Шаблон 239

◊ ввода 247

◊ подключение 239

Э

Элемент управления 14

Эффект 295

Я

Язык программирования 12

◊ интерпретируемый 17

◊ компилируемый 18

Язык разметки 33