В лекц. 1, "Введение в Silverlight 2", был представлен Silverlight 2 и его архитектура, благодаря которой можно разрабатывать Silverlight-приложения, используя всю мощь .NET Framework. Было показано, как используется XAML для описания представления элементов пользовательского интерфейса (UI), взаимодействий и анимаций. Кроме того, вы увидели, как Silverlight-приложение может размещаться в браузере и реализовываться с помощью JavaScript, или размещаться в .NET Runtime для браузера и реализовываться на C# или других языках программирования .NET.
Затем в лекц. 2, "Использование Expression Blend с Silverlight 2", был более подробно рассмотрен Expression Blend, инструмент для дизайна, используемый для создания приложений Silverlight.
В данной лекции этот процесс будет подан с точки зрения разработчика и приведен практический пример использования Microsoft Visual Studio 2008 для построения простой игры-головоломки с перемещением фрагментов (по типу игры в пятнашки). В последующих лекциях этот пример будет дорабатываться. К концу данной лекции вы получите прочное понимание того, как использовать C# и Visual Studio 2008 для построения .NET-приложений.
Пакет Visual Studio tools for Silverlight (инструменты Visual Studio для Silverlight) включают программу установки, которая обеспечивает установку среды выполнения (для Windows), средств разработки ПО (SDK) Silverlight 2 и инструментов самой Visual Studio в правильной последовательности. Его можно скачать по адресу http://www.silverlight.net
Пожалуйста, обратите внимание, что для установки этих инструментальных средств необходимо выполнить некоторые предварительные условия:
Если эти требования не будут выполнены, программа установки даст сбой, и будет выведено диалоговое окно, представленное на рис. 3.1.
(рис 3.1) Сбой установки инструментов Visual Studio.Если все предварительные условия выполнены, будет выведено следующее диалоговое окно (рис. 3.2).
(рис 3.2) Диалоговое окно, указывающее на готовность к установке Visual Studio Tools для Silverlight.Щелкните Next (Далее), и появится экран с лицензионным соглашением. Сразу после принятия лицензионного соглашения начнется установка. Окно, отображаемое в процессе установки, показано на рис. 3.3.
(рис 3.3) Установка инструментов Silverlight.Когда программа установки завершит загрузку и установку, вы получите готовые к работе среду выполнения Silverlight, SDK и Visual Studio Tools. Обратите внимание, что предыдущие версии SDK не интегрируют службу Help (Справка) непосредственно в Visual Studio, но есть файл Readme, расположенный в каталоге \Program Files\Microsoft SDKs\Silverlight\v2.0\Documentation\VS-Help, в котором можно найти инструкции по подключению ее вручную.
Теперь, когда Visual Studio Tools для Silverlight установлены, пора научиться использовать их для проектирования и построения приложений. рис. 3.4 показывает в действии игру-головоломку на Silverlight. Это приложение полностью написано на C# и XAML и запущено в браузере.
(рис 3.4) Игра головоломка с перемещением фрагментов.В последующих разделах будет показано, как использовать Visual Studio 2008 и Silverlight для создания такого приложения на языке программирования C#.
Установив Visual Studio, а также все необходимые инструменты и шаблоны для Silverlight, вы сможете создавать Silverlight-приложения. Для этого выберите пункт New Project в меню File. Откроется диалоговое окно New Project (рис. 3.5).
(рис 3.5) Диалоговое окно New Project Visual Studio 2008.Убедитесь, что в верхнем правом углу диалога выбран пункт .NET Framework 3.5 (рис. 3.5), и в списке Project Type (Тип проекта) выберите Silverlight. Вы увидите доступные шаблоны Silverlight Application (Приложение Silverlight) и Silverlight Class Library (Библиотека классов Silverlight).
Выберите шаблон Silverlight Application и задайте имя и местоположение своего проекта. Щелкните OK, и Visual Studio запустит Silverlight Application Wizard (Мастер приложений Silverlight) (рис. 3.6).
Этот мастер предлагает несколько вариантов создания и управления Silverlight-приложением. Все Silverlight-приложения в версии 2.0 создаются как пользовательские элементы управления, экземпляры которых затем могут быть созданы и размещены на странице. Таким образом, данное обсуждение сводится к тому, как это сделать.
(рис 3.6) Мастер приложений Silverlight.Предлагаются такие опции:
Примите настройки по умолчанию, как показано на рис. 3.6, и для вас будет создано новое решение Visual Studio, содержащее элемент управления Silverlight и ASP.NET Веб-приложение, в котором будет размещаться этот элемент управления. Структуру проекта можно увидеть на рис. 3.7.
(рис 3.7) Стандартная структура проекта Silverlight.Как видно на рис. 3.7, было создано два решения: базовый элемент управления Silverlight ( SlidingBlocks ) и Веб-приложение. В следующем разделе вы рассмотрите эти проекты и узнаете, что они включают. После этого перейдем к созданию приложения.
Созданный шаблоном базовый проект содержит ряд файлов, включая манифест приложения, XAML-файл приложения с файлом выделенного кода, страницу-пример с файлом выделенного кода, информационный файл сборки и некоторые ссылки. Рассмотрим каждый из этих файлов по очереди. В данном разделе представлены некоторые сложные моменты проекта Silverlight, которые, вероятно, особо нетерпеливые читатели захотят пропустить, чтобы быстрее перейти к написанию кода. Однако я бы рекомендовал задержаться и ознакомиться с этой информацией, чтобы понимать все взаимосвязи и зависимости.
Лучше всего начать с рассмотрения свойств проекта. Для этого щелкните правой кнопкой мыши проект SlidingBlocks в обозревателе решения и выберите Properties. Появится диалоговое окно Project Properties (рис. 3.8).
(рис 3.8) Диалоговое окно Project Properties Silverlight.Те, кто знаком с этим окном, заметит, что в нем появилась дополнительная вкладка для Silverlight. Она выбрана на рис. 3.8 и представляет опции Silverlight.
Опции Assembly Name (Имя сборки) по умолчанию присваивается имя проекта. Это имя будет использоваться при компиляции приложения в DLL
Опции Default Namespace (Пространство имен по умолчанию) по умолчанию также присваивается имя проекта. Если вы ссылаетесь на классы из этого проекта, их имена будут начинаться с имени этого пространства имен.
Опции Startup Object (Объект, с которого начинается запуск проекта) по умолчанию присваивается имя проекта с .App в конце (т.е. SilverlightBlocks.App). Это класс в приложении, который будет выполняться первым. Шаблон определяет этот класс в App.xaml и связанный с ним файл выделенного кода App.xaml.cs, который будет рассматриваться в одном из последующих разделов.
По щелчку кнопки Assembly Information (Сведения о сборке) будет вызван диалог Assembly Information (рис. 3.9). В нем можно определить метаданные своей сборки, включая Title (Название), Description (Описание), Copyright (Авторское право) и Trademark (Торговая марка). Вся эта информация хранится в файле AssemblyInfo.cs и компилируется в приложение Silverlight.
Опция XAP file name (Имя ХАР-файла) позволяет задавать файл .xap, в который будут записываться все выходные данные компиляции проекта. Обычно это DLL, содержащая ваш компонент Silverlight и его манифест приложения. Это удобно для Веб-приложений, поскольку компоненту Silverlight для загрузки и использования множества компонентов необходимо сослаться только на один файл (XAP-файл).
(рис 3.9) Задание сведений о сборке.Наконец, предлагается опция для создания самого файла манифеста Silverlight. Этот файл содержит данные по всем элементам пакета, которые будут использоваться приложением Silverlight, таким как дополнительные компоненты или элементы управления, которые необходимы для выполнения приложения. Мы еще будем возвращаться к этому по ходу курса.
Первой папкой проекта является папка Properties, содержащая файлы свойств: AppManifest.xml и Assemblyinfo.cs.
AppManifest.xml создается при компиляции проекта. Если проект имеет какие-либо зависимости во время выполнения, такие как внешние элементы управления, ссылки на них размещаются в этом файле.
Файл Assemblyinfo.cs содержит метаданные, которые должны быть скомпилированы в вашу DLL, заданную в диалоговом окне Assembly Information (рис. 3.9). Код в этом файле можно редактировать вручную, но рекомендуется использовать для этого диалоговое окно.
Папка References содержит ссылки на ряд сборок. Это основные сборки Silverlight, которые необходимы для запуска приложения.
mscorlib В эту сборку входят основные типы, используемые приложениями Silverlight.System В эту сборку входят многие высокоуровневые типы, используемые для разработки и отладки приложений Silverlight, такие как компилятор и классы для отладки и диагностики.System.Core Эта сборка включает функциональность работы с данными LINQ.System.Xml В эту сборку входят библиотеки Silverlight для обработки XMLSystem.Windows В эту сборку входят основные функциональные возможности Windows и Silver-light, включая элементы управления Silverlight.System.Windows.Browser В эту сборку входят библиотеки, используемые для взаимодействия с браузером.System.Net В эту сборку входят классы необходимые для работы с сетью, например, для получения файлов по протоколу HTTP.В Silverlight также имеется ряд сборок, не входящих в стандартную поставку, которые можно добавлять для обеспечения необходимой функциональности. Некоторые из них будут рассмотрены в данном курсе. Примером такой функциональности является
Файл App.xaml создается интегрированной средой разработки (IDE) при создании проекта Silverlight по шаблону. Обычно он используется для хранения общей информации всего приложения.
App.xaml содержит объявления, определяющие поведение приложения. Вот пример App.xaml, создаваемого шаблоном по умолчанию:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xa ml"
x:Class="Sliding Blocks. App">
<Application.Resources>
</Application.Resources>
</Application>
Первое, на что следует обратить внимание, - атрибут x:Class, который определяет имя класса, в который будет скомпилирован этот XAML и связанный с ним файл выделенного кода. Как видите, в данном случае это SlidingBlocks.App, который, как вы, возможно, помните из рис. 3.8, был задан в диалоговом окне Project Properties как объект запуска для этого приложения. Таким образом, функциональность запуска проекта Silverlight будет находиться в этом классе.
С помощью атрибута Startup (Запуск) можно задать функцию, которая будет выполняться при запуске приложения. Для этого просто указывается имя необходимой функции, описанной в файле выделенного кода. Также в атрибуте Exit (Выход) можно задать имя функции из файла выделенного кода, которая будет выполняться при завершении приложения.
Далее приведен код стандартного файла выделенного кода, созданного шаблоном:
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;
namespace SlidingBlocks
{
public partial class App : Application
{
public App()
{
this.Startup += this.Application Startup;
this.Exit += this.Application Exit;
this.UnhandledException += this.Application UnhandledException;
InitializeComponent();
}
private void Application Startup(object sender, StartupEventArgs e)
{
// Загружаем основной элемент управления
this.RootVisual = new Page();
}
private void Application Exit(object sender, EventArgs e)
{
}
private void Application UnhandledException(object sender,
ApplicationUnhandledExceptionEventArgs e)
{
if (!System.Diagnostics.Debugger.IsAttached)
{
e.Handled = true;
Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
}
}
private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
{
try
{
string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
System.Windows.Browser.HtmlPage.Window.Eval
("throw new Error(\"Unhandled Error in Silverlight 2 Application " + errorMsg + "\");");
}
catch (Exception)
{
}
}
}
}
Сначала рассмотрим конструктор (это функция с именем, аналогичным имени модуля кода, в данном случае это App() ). Он используется для подключения функций Startup и Exit. Это уже было сделано в файле XAML, поэтому нет необходимости делать это в коде. Однако это демонстрирует замечательную гибкость модели XAML/выделенный код, которая позволяет подключать события во время разработки (задавая их в XAML) или во время выполнения (объявляя их в коде).
Далее можно проверить функции Application Startup и Application Exit. Обратите внимание, что они принимают два параметра: объект, формирующий событие, и объект аргументов. Такая сигнатура функций очень распространена в приложениях Silverlight.
В функции Application Startup происходит присвоение свойству RootVisual (Корневой визуальный элемент) приложения нового объекта Page (Страница). Тем самым объявляется, что UI объекта Page является первым экраном UI, который должно создать это приложение. Если предполагается использовать другие экраны UI, объявленные в XAML, они будут запускаться уже из объекта Page.
Объект Page является XAML-объектом по умолчанию, создаваемым шаблоном для размещения UI вашего приложения, который рассматривается в следующем разделе.
Файл Page.xaml является для приложения UI по умолчанию. При компиляции вместе с ассоциированным с ним файлом выделенного кода он формирует класс Page, экземпляр которого будет объектом Page. Если вспомнить из предыдущего раздела, в качестве значения свойства RootVisual приложения был задан новый объект Page, что обеспечило данному классу UI по умолчанию.
Вот XAML, формируемый по умолчанию для Page.xaml:
<UserControl x:Class="SlidingBlocks.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x=" http://schemas.microsoft.com/winfx/2006/xa ml" Width = "400" Height="300"> <Grid x:Name="LayoutRoot" Background ="White" > </Grid> </UserControl>
Прежде всего, обратите внимание, что контейнером XAML является UserControl. Этого не было в Silverlight 1.0. Как говорилось ранее, при построении приложений Silverlight 2 в Visual Studio, фактически, создаются элементы управления, которые компилируются в DLL и помещаются в XAP, который затем открывает и исполняет Silverlight.
В данном случае, видим, что экземпляр этого UserControl называется SlidingBlocks.Page. SlidingBlocks- это пространство имен (чтобы увидеть это, вернитесь и взгляните на свойства проекта), и Page - имя класса из этого пространства имен.
Объявления xmlns и xmlns:x задают пространство имен по умолчанию и дополнительное пространство имен, соответственно, которые должны использоваться для проверки корректности XAML Ранее был рассмотрен атрибут x:Class, используемый для описания класса этого элемента управления, который также является примером применения дополнительного пространства имен, запись которого начинается с префикса x:.
Наконец, ширине и высоте присвоены значения по умолчанию: 640 х 480.
Далее идет корневой Grid. В Silverlight 2 корневым элементом должен быть Container (Контейнер), которым в данном случае является Grid под именем LayoutRoot. Соответственно, все элементы дизайна UI будут дочерними элементами этого узла.
Вот выделенный код для данного XAML:
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;
namespace SlidingBlocks {
public partial class Page : UserControl {
public Page()
{
// Необходима для инициализации переменных
InitializeComponent();
}
}
}
Те, кто знаком с C#, видят, что этот код очень похож на код, который, возможно, вы использовали ранее. По сути, это стандартный класс Page, унаследованный от типа UserControl. В конструкторе класса выполняется общая настройка с помощью вызова метода InitializeComponent(). В разделе "Написание игры на Silverlight 2" данной лекции, в котором мы займемся созданием головоломки с перемещением фрагментов, вы добавите в этот модуль характерный для вашей страницы код.
Кроме проекта элемента управления, шаблон создает Веб-проект, в котором размещается ваше приложение Silverlight. Этот Веб-проект включает два ASPX-файла: Default.aspx, являющийся пустой Веб-формой, на базе которой можно построить свое приложение; и тестовую страницу <ИмяПриложения> TestPage.aspx (например, SlidingBlocksTestPage.aspx ), которая содержит все необходимое для запуска Silverlight из ASP.NET.
Хотя Silverlight не имеет никаких зависимостей на стороне сервера, ASP.NET предлагает элементы управления, обеспечивающие возможность создания JavaScript и HTML на стороне клиента, необходимых для размещения Silverlight в браузере.
Файл TestPage включает ссылки на эти элементы управления. Далее приведена разметка для ASPX-файла:
<%@ Page="" Language="C#" AutoEventWireup="true" %>
<%@ Register="" Assembly="System.Web.Silverlight"
Namespace="System.Web.UI.SilverlightControls" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"style="height: 100%;">
<head runat="server">
<title>SlidingBlocks</title>
</head>
<body style="height: 100%;margin:0;">
<form id = "form1" runat="server" style="height:100%;">
<asp:ScriptManager ID="ScriptManager1" runat= "server">
</asp:ScriptManager>
<div style="height:100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SlidingBlocks.xap"
MinimumVersion = "2.0.31005.0" Width = "100%" Height="100%" />
</div>
</form>
</body>
</html>
Заметьте, что данная технология является развивающейся, поэтому атрибуты номера версии и открытого ключа могут немного меняться. Если для создания кода вы использовали шаблон, не стоит волноваться, все будет в порядке.
На этой странице используются два элемента управления ASP.NET. Первый - (Диспетчер сценариев), который является артефактом AJAX ASP.NET и превосходным средством управления загрузкой и использованием всех необходимых библиотек JavaScript в соответствующее время и в соответствующем месте.
Второй -элемент управления Silverlight. Обратите внимание, что в качестве параметра он принимает рассмотренный выше XAP. Этот элемент управления будет формировать правильный HTML-код для создания функций DIV и CreateSilverlight.
После запуска этой страницы будет сформировано много кода на HTML и JavaScript. Элемент управления Silverlight создается и получает файл XAP ближе к концу кода. Вот фрагмент кода:
<script type="text/javascript">
//<![CDATA[
Sys. Application. initialize();
Sys.Application.add
init(function() {
$create(Sys.UI.Silverlight.Control,
{"source": ClientBin/SlidingBlocks.xap"},
null, null, $get("Xaml1 parent"));
});
//]]>
</script>
Браузер интерпретирует этот сценарий и создает элемент управления Silverlight. Обратите внимание, что корректную работу элементов управления Silverlight ASP.NET обеспечит ASP.NET. Если экземпляр Silverlight создается не из ASP.NET, по-прежнему могут использоваться сценарии JavaScript, входящие в SDK. Это подробно рассматривается в лекц. 6, "Элемент управления браузера Silverlight".
Silverlight поддерживает модель разделения дизайна и процесса разработки, при которой дизайн представляется с помощью XAML и код, обычно (хотя это не обязательно) на C#, создается в выделенном файле.
В данном примере XAML будет использоваться совсем немного, лишь для размещения Canvas, в котором будут находиться фрагменты рисунка и его полное изображение. Если вернуться к рис. 3.4, эти компоненты можно увидеть слева и справа, соответственно. Обратите внимание, что хотя главным контейнером страницы по умолчанию является Grid, для простоты в этой игре мы будем использовать Canvas.
Как видно на рис. 3.4, UI этой игры очень прост. Он состоит из области экрана (занимаемой элементом Canvas ), в которой располагаются перемещаемые фрагменты собираемого изображения, и области, в которой формируется визуальное представление собранного рисунка.
Вот пример XAML, который обеспечивает отображение Canvas и завершенного рисунка:
<UserControl x:Class="SlidingBlocks.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xam l/presentation"
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xa ml"
Width = "640" Height="480">
<Canvas x:Name="LayoutRoot">
<Canvas x:Name="GameContainer" />
<Image Source="sl.jpg" Canvas.Left="500"
Height="400" Width="400" Stretch="UniformToFill">
</Image>
</Canvas>
</UserControl>
Как видите, все очень просто. Canvas, в котором будут размещаться фрагменты собираемого изображения, называется GameContainer (Контейнер игры). Изображение, которое будет формировать визуальное представление собранного рисунка, является статическим, таким образом, не нуждается в имени. Изначально оно заполнено изображением sl.jpg из этого же Веб-проекта. Вот и весь дизайн. В следующем разделе займемся кодом.
Код игры в данном примере написан на C#. Его без труда можно было бы транслировать на VB.NET, IronPython или любой другой поддерживаемый язык программирования, но в большинстве случаев в данном курсе будет использоваться C#.
Первый шаг в написании кода для этой игры - инициализация структур данных, которые будут использоваться приложением. В данном случае мы собираемся разбить рисунок на 16 фрагментов, 15 из которых будут выставлены на поле в произвольном порядке. Один фрагмент не используется, вместо него оставляем пустое пространство, обеспечивающее возможность перемещения фрагментов по игровому полю.
В Silverlight нельзя создать фрагмент существующего изображения, но можно вырезать его по определенному контуру. Не следует путать вырезание с обрезкой. В первом случае вы указываете, какие части рисунка должны быть отрисованы; во втором -удаляете все, кроме необходимой части изображения. Silverlight не поддерживает обрезку, поэтому приходится вырезать изображение.
Проблема использования вырезания состоит в том, что при его применении остальная часть рисунка остается доступной и может быть активизирована щелчком мыши. Например, если имеется изображение 400 х 400 и из него вырезается квадратный фрагмент 100 х 100 в позиции (100,100), мы видим квадрат 100 х 100 в позиции (100,100), но объект400 х 400 никуда не исчез: он стал пустым, но по-прежнему может быть активизирован щелчком мыши. Это не отвечает нашим требованиям для создания головоломки с перемещаемыми фрагментами. Но что можно сделать?
Решение состоит во введении в Canvas изображения, размеры которого соответствуют размерам вырезанного фрагмента, и последующем использовании трансформации транслирования на него, чтобы вырезанная часть изображения располагалась в верхнем левом углу данного Canvas, и, таким образом, Canvas формировал визуальное отображение только вырезанной области.
Итак, для головоломки 4x4, образованной 16 блоками необходимо создать 16 изображений и 16 объектов Canvas. Также потребуется как-то представлять поле, чтобы вы знали, какое изображение в каком квадрате находится. Вот код для описания этого:
Canvas[] cI = new Canvas[16]; Image[] i = new Image[16]; int[] board = new int[16];
Чтобы создать фрагменты головоломки, понадобиться загрузить изображение sl.jpg в каждый элемент массива изображений. Для этого с помощью универсального идентификатора ресурса (uniform resource identifier, URI) мы указываем на необходимое изображение и определяем класс BitmapImage (из пространства имен System.Windows.Media.Imaging ), в который считываются данные по этому URI. И присваиваем этот класс элементу управления изображение:
i[nx].Source = new BitmapImage(uri);
Из конструктора можно вызвать функцию ( InitBoard ), которая будет использоваться для создания экземпляров фрагментов головоломки:
public Page() {
// Необходимо для инициализации переменных
InitializeComponent();
InitBoard(); }
Эту функцию можно увидеть здесь:
void InitBoard() {
Uri uri = new Uri("sl.jpg", UriKind.Relative);
int nx = 0;
for (int ix = 0; ix < 4; ix++)
for (int iy = 0; iy < 4; iy++) {
nx = (ix * 4) + iy;
i[nx] = new Image();
i[nx].Height = 400;
i[nx].Width = 400;
i[nx].Stretch = Stretch.UniformToFill;
RectangleGeometry r = new RectangleGeometry();
r.Rect = new Rect((ix * 100), (iy * 100), 100, 100);
i[nx].Clip = r;
i[nx].Source = new BitmapImage(uri);
i[nx].SetValue(Canvas.TopProperty, Convert.toDouble(iy * 100 * -1));
i[nx].SetValue(Canvas.LeftProperty, Convert.toDouble(ix * 100 * -1));
cI[nx] = new Canvas();
cI[nx].Width = 100;
cI[nx].Height = 100;
cI[nx].Children.Add(i[nx]);
cI[nx].SetValue(Canvas.NameProperty, "C" + nx.ToString());
cI[nx].MouseLeftButtonDown += new
MouseButtonEventHandler(Page MouseLeftButtonDown);
if (nx < 15) GameContainer.Children.Add(cI[nx]);
}
// Перемешиваем фрагменты shuffle();
// Отрисовываем поле drawBoard();
}
Не забудьте добавить ссылку на System.Windows.Media.Imaging в начале кода:
using System.Windows.Media.Imaging;
На первый взгляд все это может показаться немного сложным, но при более близком рассмотрении все предельно просто. Задается вложенный цикл для прохождения по осям х и у с диапазоном значений от 0 до 3 . Это, как можно догадаться, используется для организации массива 4x4 для изображений.
Массивы Image и Canvas, используемые для хранения фрагментов, являются одномерными массивами по 16 элементов каждый (при такой структуре для их хранения требуется меньше памяти и кода). Чтобы спроецировать двумерные координаты ix, iy на одномерный массив, необходимо выполнить следующие вычисления:
nx = (ix * 4) + iy;
Для каждого элемента задаются размеры 400 х 400 с помощью значения UniformToFill (Равномерное заполнение) свойства Stretch (Растяжение). Более подробно об использовании изображений рассказывается в лекц. 4, "Основы XAML".
Далее вычисляется область вырезания. Для этого используется объект RectangleGeometry (Геометрический элемент прямоугольник):
RectangleGeometry r = new RectangleGeometry(); r.Rect=new Rect((ix*100), (iy*100) ,100,100); i[nx].Clip = r;
Таким образом, определяется прямоугольник с соответствующими координатами (полученными путем умножения ix и iy на 100) соответствующего размера (100 х 100), и он задается как область вырезания для текущего изображения ( i[nx] ).
Далее изображение загружается в BitmapImage, инициализация которого происходит путем задания URI этого изображения, и помещается в заданное положение. Чтобы изображение, вырезанное в позиции (100,100), появилось в верхнем левом углу, оно должно быть передвинуто на (-100,-100) относительно вырезанной области. Таким образом, свойствам Top (Сверху) и Left (Слева) должно быть задано значение -100 и умножено на значения iy и ix, соответственно.
i[nx].Source = new BitmapImage(uri); i[nx].SetValue(Canvas.TopProperty, Convert.toDouble(iy * 100 * -1)); i[nx].SetValue(Canvas.LeftProperty, Convert.toDouble(ix * 100 * -1));
Изображения до сих пор не добавлены в родительский Canvas. Это следующий шаг. Canvas необходимо инициализировать, задать его размеры и добавить в него в качестве дочернего элемента соответствующее изображение. Вот код:
cI[nx] = new Canvas(); cI[nx] .Width = 100; cI[nx].Height = 100; cI[nx].Children.Add(i[nx]);
Теперь мы получили наш Canvas, и он содержит наше вырезанное изображение. Завершаем инициализацию, присваивая Canvas имя (чтобы его можно было отслеживать, когда мы щелкаем его), определяем обработчик событий для описания действий, которые будут выполняться по щелчку пользователя по нему, и, наконец, добавляем его в родительский Canvas. Пока нет необходимости задавать местоположение фрагмента. Это будет сделано после того, как фрагменты будут перемешаны на поле.
Обратите внимание, последнее изображение не добавляется на поле, потому что мы хотим, чтобы осталось свободное пространство.
cI[nx].SetValue(Canvas.NameProperty, "C" + nx.ToString());
cI[nx].MouseLeftButtonDown += new
MouseButtonEventHandler(Page MouseLeftButtonDown);
if(nx<15)
GameContainer.Children.Add(cI[nx]);
Наконец, необходимо перемешать фрагменты и отрисовать игровое поле. Код для реализации этого представлен в следующем разделе.
Фрагменты головоломки перемешиваются с помощью довольно простого алгоритма перемешивания, который перебирает массив 100 раз, выбирая два случайных элемента каждый раз; если эти два элемента разные, он меняет местами их содержимое. В конце он загружает в последний элемент значение -1, чтобы обозначить пустой квадрат.
Здесь представлен алгоритм перемешивания:
void shuffle()
{ // Инициализируем игровое поле
for (int n = 0; n < 15; n ++) {
board[n] = n;
}
Random rand = new Random(System.DateTime.Now.Second);
for (int n = 0; n < 100; n++) {
int n1 = rand.Next(15);
int n2 = rand.Next(15);
if (n1 != n2) {
int tmp = board[n1];
board[n1] = board[n2];
board[n2] = tmp;
}
}
board[15] = -1;
}
Следующий шаг после того, как фрагменты перемешаны, - отрисовка игрового поля.
На данный момент мы имеем все фрагменты головоломки, определенные как элементы Image в элементе Canvas, и массив целых чисел, значение индекса n которого соответствует фрагменту, отображаемому в данном месте. Вы также перемешали этот массив целых чисел, поэтому пришло время отрисовать игровое поле. Это делается следующим образом:
void drawBoard() {
int nx = 0;
int ny = 0;
for (int n = 0; n < 15; n ++) {
nx = n / 4;
ny = n % 4;
if(board[n]> = 0) {
cI[board[n]].SetValue(Canvas.TopProperty, Convert.toDouble(ny * 100));
cI[board[n]].SetValue(Canvas.LeftProperty, Convert.toDouble(nx * 100));
}
}
}
В данном случае выполняется цикл с изменением индекса от 0 до 14 (в головоломке 15 фрагментов) и вычисление координат x,y для каждого блока в сетке 4 х 4. Значение x -это просто результат целочисленного деления индекса массива на 4, и значение y -это модуль индекса, деленного на 4. Умножаем эти значения на 100 и получаем необходимые координаты для отрисовки элемента Canvas.
Теперь мы имеем полностью инициализированную игру. Рисунок располагается справа, а игровое поле с перемешанными фрагментами рисунка - слева.
Теперь необходимо заняться обработкой действий пользователя. В играх подобного рода предполагается, что пользователь будет щелкать фрагмент изображения, и если этот фрагмент находится рядом с пустой областью, он плавно переместится в эту область, оставляя пустым занимаемое до этого место. Итак, мы должны обработать щелчки по элементу Canvas, содержащего фрагмент изображения. Если вернуться к инициализации фрагментов, можно вспомнить такую строку:
cI[nx].MouseLeftButtonDown += new MouseButtonEventHandler(Page MouseLeftButtonDown);
Здесь описывается, что по щелчку Canvas будет запускаться обработчик события
PageMouseLeftButtonDown. Этот обработчик события задан для каждого из блоков Canvas.
Код этого обработчика события состоит из двух разделов. Первый определяет, который из Canvas сформировал событие и положение этого Canvas на игровом поле:
void PageMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Canvas c = sender as Canvas;
int nCanvasID = -1;
int nBoardLoc = -1;
int nEmptyLoc = -1;
for (int i = 0; i < 16; i++) {
if (c == cI[i]) { nCanvasID = i; break; }
}
for (int i = 0; i < 16; i++) {
if (board[i] == nCanvasID) {
nBoardLoc = i;
}
else if (board[i] == -1)
{ nEmptyLoc = i; }
}
Итак, например, вы щелкнули фрагмент, представляющий верхний левый угол полного изображения (блок 0), но в настоящий момент он находится в нижнем левом углу игрового поля (позиция 12). При возникновении события щелчка, выполняется перебор всего массива элементов Canvas, представляющих фрагменты головоломки, до тех пор, пока не будет найден фрагмент, соответствующий Canvas, по которому было произведен щелчок. И здесь вы можете получить индекс этого элемента в массиве, сохранив его в переменной nCanvasID (в нашем гипотетическом примере он равнялся бы 0). После этого, просканировав игровое поле, можно определить местоположение элемента 0 на нем и присвоить значение его позиции переменной nBoardLoc (которое в нашем гипотетическом примере равно 12). И, находясь в этой позиции, найдите местоположение пустой ячейки на игровом поле и сохраните его в nEmptyLoc.
Вторая часть кода проверяет возможность перемещения фрагмента, и, если перемещение возможно, перемещает фрагмент в пустую ячейку и соответствующим образом обновляет игровое поле.
// Проверяем возможность перемещения
if ((nBoardLoc == nEmptyLoc + 1) ||
(nBoardLoc == nEmptyLoc - 1) ||
(nBoardLoc == nEmptyLoc + 4) ||
(nBoardLoc == nEmptyLoc - 4)) {
int nx = nEmptyLoc/4;
int ny = nEmptyLoc%4;
cI[nCanvasID].SetValue(Convert.toDouble(Canvas.TopProperty, ny * 100));
cI[nCanvasID].SetValue(Convert.toDouble(Canvas.LeftProperty, nx * 100));
board[nEmptyLoc] = nCanvasID; board[nBoardLoc] = -1;
checkWinner();
}
else {
// не делаем ничего }
}
Для этого сначала проверим местоположение пустой ячейки относительно положения блока, по которому был произведен щелчок. Если они располагаются непосредственно рядом друг с другом (над, под, слева или справа), перемещение возможно. Поскольку игровое поле 4x4 является одномерным массивом, сделать это несложно. Позиции элементов, располагающихся слева и справа от текущего элемента, отличаются от текущей на -1 и +1, соответственно, и позиции элементов сверху и снизу - на -4 и +4, соответственно. Так что если результат проверки фрагментов указывает на пустую ячейку, вы знаете, что можете двигаться.
Чтобы выполнить перемещение, необходимо получить координаты x и y относительно положения пустого фрагмента и задать их как координаты блока, по которому был произведен щелчок. Затем присвоить позиции поля, на которой до этого находилась пустая ячейка, значение Canvas, по которому был произведен щелчок, и позиции поля, на которой до этого располагался Canvas, по которому был произведен щелчок, значение -1, указывая на то, что теперь эта ячейка пуста.
Наконец, вызовем функцию checkWinner(), чтобы проверить успешность решения головоломки.
Функция checkWinner (Проверка победителя) проверяет успешность решения головоломки. Опять же, все очень просто. Головоломка решена, если каждый n -ный элемент игрового поля равен n, т.е. Canvas 0 имеет индекс 0, Canvas1 - индекс 1 и т.д.
Самый эффективный способ сделать это - предположить, что головоломка решена успешно, и проска-нировать игровое поле в поисках несоответствия board[n] значению n, что будет свидетельствовать о неверном решении и приводить к завершению цикла. Если все индексы совпадают, у нас есть победитель!
void checkWinner() {
bool bCompleted = true;
for (int n = 0; n < 15; n ++) {
if (n != board[n]) {
bCompleted = false; break; }
}
if (bCompleted) {
// Игрок выиграл....сделайте для него что-нибудь приятное.
}
}
Теперь вы получили все элементы простой игры-головоломки с перемещением фрагментов, написанной на С# для Silverlight 2. И для этого понадобилось всего лишь 100 строк кода. По ходу чтения этого курса данный пример можно расширять, добавляя анимацию перемещения блоков, сохранение лучших результатов, возможность загрузки изображений в приложение и т.д. Нет пределов совершенству!
В данной лекции мы рассмотрели Visual Studio 2008 и предлагаемые ею различные инструменты и шаблоны для разработки приложений Silverlight с использованием языков программирования .NET. Был детально разобран проект, созданный на базе стандартных шаблонов Silverlight, рассмотрены все его файлы и то, как он используется для разработки и развертывания приложения Silverlight. После этого мы применили теорию на практике и создали полнофункциональную игру-головоломку с перемещением фрагментов, используя XAML и C#. Это позволило почувствовать возможности Silverlight 2, но мы лишь слегка прикоснулись к ним. В части 2 данного курса, "Программирование в Silverlight 2", будут более глубоко рассмотрены функциональные возможности, доступные в .NET Framework, включая создание собственных элементов управления, работу с сетью и обмен информацией, данные и XML, динамические языки программирования и серверные элементы управления ASP.NET.
В лекц. 1, "Введение в Silverlight 2", был представлен Silverlight 2 и его архитектура, благодаря которой можно разрабатывать Silverlight-приложения, используя всю мощь .NET Framework. Было показано, как используется XAML для описания представления элементов пользовательского интерфейса (UI), взаимодействий и анимаций. Кроме того, вы увидели, как Silverlight-приложение может размещаться в браузере и реализовываться с помощью JavaScript, или размещаться в .NET Runtime для браузера и реализовываться на C# или других языках программирования .NET.
Затем в лекц. 2, "Использование Expression Blend с Silverlight 2", был более подробно рассмотрен Expression Blend, инструмент для дизайна, используемый для создания приложений Silverlight.
В данной лекции этот процесс будет подан с точки зрения разработчика и приведен практический пример использования Microsoft Visual Studio 2008 для построения простой игры-головоломки с перемещением фрагментов (по типу игры в пятнашки). В последующих лекциях этот пример будет дорабатываться. К концу данной лекции вы получите прочное понимание того, как использовать C# и Visual Studio 2008 для построения .NET-приложений.
Пакет Visual Studio tools for Silverlight (инструменты Visual Studio для Silverlight) включают программу установки, которая обеспечивает установку среды выполнения (для Windows), средств разработки ПО (SDK) Silverlight 2 и инструментов самой Visual Studio в правильной последовательности. Его можно скачать по адресу http://www.silverlight.net
Пожалуйста, обратите внимание, что для установки этих инструментальных средств необходимо выполнить некоторые предварительные условия:
Если эти требования не будут выполнены, программа установки даст сбой, и будет выведено диалоговое окно, представленное на рис. 3.1.
(рис 3.1) Сбой установки инструментов Visual Studio.Если все предварительные условия выполнены, будет выведено следующее диалоговое окно (рис. 3.2).
(рис 3.2) Диалоговое окно, указывающее на готовность к установке Visual Studio Tools для Silverlight.Щелкните Next (Далее), и появится экран с лицензионным соглашением. Сразу после принятия лицензионного соглашения начнется установка. Окно, отображаемое в процессе установки, показано на рис. 3.3.
(рис 3.3) Установка инструментов Silverlight.Когда программа установки завершит загрузку и установку, вы получите готовые к работе среду выполнения Silverlight, SDK и Visual Studio Tools. Обратите внимание, что предыдущие версии SDK не интегрируют службу Help (Справка) непосредственно в Visual Studio, но есть файл Readme, расположенный в каталоге \Program Files\Microsoft SDKs\Silverlight\v2.0\Documentation\VS-Help, в котором можно найти инструкции по подключению ее вручную.
Теперь, когда Visual Studio Tools для Silverlight установлены, пора научиться использовать их для проектирования и построения приложений. рис. 3.4 показывает в действии игру-головоломку на Silverlight. Это приложение полностью написано на C# и XAML и запущено в браузере.
(рис 3.4) Игра головоломка с перемещением фрагментов.В последующих разделах будет показано, как использовать Visual Studio 2008 и Silverlight для создания такого приложения на языке программирования C#.
Установив Visual Studio, а также все необходимые инструменты и шаблоны для Silverlight, вы сможете создавать Silverlight-приложения. Для этого выберите пункт New Project в меню File. Откроется диалоговое окно New Project (рис. 3.5).
(рис 3.5) Диалоговое окно New Project Visual Studio 2008.Убедитесь, что в верхнем правом углу диалога выбран пункт .NET Framework 3.5 (рис. 3.5), и в списке Project Type (Тип проекта) выберите Silverlight. Вы увидите доступные шаблоны Silverlight Application (Приложение Silverlight) и Silverlight Class Library (Библиотека классов Silverlight).
Выберите шаблон Silverlight Application и задайте имя и местоположение своего проекта. Щелкните OK, и Visual Studio запустит Silverlight Application Wizard (Мастер приложений Silverlight) (рис. 3.6).
Этот мастер предлагает несколько вариантов создания и управления Silverlight-приложением. Все Silverlight-приложения в версии 2.0 создаются как пользовательские элементы управления, экземпляры которых затем могут быть созданы и размещены на странице. Таким образом, данное обсуждение сводится к тому, как это сделать.
(рис 3.6) Мастер приложений Silverlight.Предлагаются такие опции:
Примите настройки по умолчанию, как показано на рис. 3.6, и для вас будет создано новое решение Visual Studio, содержащее элемент управления Silverlight и ASP.NET Веб-приложение, в котором будет размещаться этот элемент управления. Структуру проекта можно увидеть на рис. 3.7.
(рис 3.7) Стандартная структура проекта Silverlight.Как видно на рис. 3.7, было создано два решения: базовый элемент управления Silverlight ( SlidingBlocks ) и Веб-приложение. В следующем разделе вы рассмотрите эти проекты и узнаете, что они включают. После этого перейдем к созданию приложения.
Созданный шаблоном базовый проект содержит ряд файлов, включая манифест приложения, XAML-файл приложения с файлом выделенного кода, страницу-пример с файлом выделенного кода, информационный файл сборки и некоторые ссылки. Рассмотрим каждый из этих файлов по очереди. В данном разделе представлены некоторые сложные моменты проекта Silverlight, которые, вероятно, особо нетерпеливые читатели захотят пропустить, чтобы быстрее перейти к написанию кода. Однако я бы рекомендовал задержаться и ознакомиться с этой информацией, чтобы понимать все взаимосвязи и зависимости.
Лучше всего начать с рассмотрения свойств проекта. Для этого щелкните правой кнопкой мыши проект SlidingBlocks в обозревателе решения и выберите Properties. Появится диалоговое окно Project Properties (рис. 3.8).
(рис 3.8) Диалоговое окно Project Properties Silverlight.Те, кто знаком с этим окном, заметит, что в нем появилась дополнительная вкладка для Silverlight. Она выбрана на рис. 3.8 и представляет опции Silverlight.
Опции Assembly Name (Имя сборки) по умолчанию присваивается имя проекта. Это имя будет использоваться при компиляции приложения в DLL
Опции Default Namespace (Пространство имен по умолчанию) по умолчанию также присваивается имя проекта. Если вы ссылаетесь на классы из этого проекта, их имена будут начинаться с имени этого пространства имен.
Опции Startup Object (Объект, с которого начинается запуск проекта) по умолчанию присваивается имя проекта с .App в конце (т.е. SilverlightBlocks.App). Это класс в приложении, который будет выполняться первым. Шаблон определяет этот класс в App.xaml и связанный с ним файл выделенного кода App.xaml.cs, который будет рассматриваться в одном из последующих разделов.
По щелчку кнопки Assembly Information (Сведения о сборке) будет вызван диалог Assembly Information (рис. 3.9). В нем можно определить метаданные своей сборки, включая Title (Название), Description (Описание), Copyright (Авторское право) и Trademark (Торговая марка). Вся эта информация хранится в файле AssemblyInfo.cs и компилируется в приложение Silverlight.
Опция XAP file name (Имя ХАР-файла) позволяет задавать файл .xap, в который будут записываться все выходные данные компиляции проекта. Обычно это DLL, содержащая ваш компонент Silverlight и его манифест приложения. Это удобно для Веб-приложений, поскольку компоненту Silverlight для загрузки и использования множества компонентов необходимо сослаться только на один файл (XAP-файл).
(рис 3.9) Задание сведений о сборке.Наконец, предлагается опция для создания самого файла манифеста Silverlight. Этот файл содержит данные по всем элементам пакета, которые будут использоваться приложением Silverlight, таким как дополнительные компоненты или элементы управления, которые необходимы для выполнения приложения. Мы еще будем возвращаться к этому по ходу курса.
Первой папкой проекта является папка Properties, содержащая файлы свойств: AppManifest.xml и Assemblyinfo.cs.
AppManifest.xml создается при компиляции проекта. Если проект имеет какие-либо зависимости во время выполнения, такие как внешние элементы управления, ссылки на них размещаются в этом файле.
Файл Assemblyinfo.cs содержит метаданные, которые должны быть скомпилированы в вашу DLL, заданную в диалоговом окне Assembly Information (рис. 3.9). Код в этом файле можно редактировать вручную, но рекомендуется использовать для этого диалоговое окно.
Папка References содержит ссылки на ряд сборок. Это основные сборки Silverlight, которые необходимы для запуска приложения.
mscorlib В эту сборку входят основные типы, используемые приложениями Silverlight.System В эту сборку входят многие высокоуровневые типы, используемые для разработки и отладки приложений Silverlight, такие как компилятор и классы для отладки и диагностики.System.Core Эта сборка включает функциональность работы с данными LINQ.System.Xml В эту сборку входят библиотеки Silverlight для обработки XMLSystem.Windows В эту сборку входят основные функциональные возможности Windows и Silver-light, включая элементы управления Silverlight.System.Windows.Browser В эту сборку входят библиотеки, используемые для взаимодействия с браузером.System.Net В эту сборку входят классы необходимые для работы с сетью, например, для получения файлов по протоколу HTTP.В Silverlight также имеется ряд сборок, не входящих в стандартную поставку, которые можно добавлять для обеспечения необходимой функциональности. Некоторые из них будут рассмотрены в данном курсе. Примером такой функциональности является
Файл App.xaml создается интегрированной средой разработки (IDE) при создании проекта Silverlight по шаблону. Обычно он используется для хранения общей информации всего приложения.
App.xaml содержит объявления, определяющие поведение приложения. Вот пример App.xaml, создаваемого шаблоном по умолчанию:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xa ml"
x:Class="Sliding Blocks. App">
<Application.Resources>
</Application.Resources>
</Application>
Первое, на что следует обратить внимание, - атрибут x:Class, который определяет имя класса, в который будет скомпилирован этот XAML и связанный с ним файл выделенного кода. Как видите, в данном случае это SlidingBlocks.App, который, как вы, возможно, помните из рис. 3.8, был задан в диалоговом окне Project Properties как объект запуска для этого приложения. Таким образом, функциональность запуска проекта Silverlight будет находиться в этом классе.
С помощью атрибута Startup (Запуск) можно задать функцию, которая будет выполняться при запуске приложения. Для этого просто указывается имя необходимой функции, описанной в файле выделенного кода. Также в атрибуте Exit (Выход) можно задать имя функции из файла выделенного кода, которая будет выполняться при завершении приложения.
Далее приведен код стандартного файла выделенного кода, созданного шаблоном:
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;
namespace SlidingBlocks
{
public partial class App : Application
{
public App()
{
this.Startup += this.Application Startup;
this.Exit += this.Application Exit;
this.UnhandledException += this.Application UnhandledException;
InitializeComponent();
}
private void Application Startup(object sender, StartupEventArgs e)
{
// Загружаем основной элемент управления
this.RootVisual = new Page();
}
private void Application Exit(object sender, EventArgs e)
{
}
private void Application UnhandledException(object sender,
ApplicationUnhandledExceptionEventArgs e)
{
if (!System.Diagnostics.Debugger.IsAttached)
{
e.Handled = true;
Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
}
}
private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
{
try
{
string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
System.Windows.Browser.HtmlPage.Window.Eval
("throw new Error(\"Unhandled Error in Silverlight 2 Application " + errorMsg + "\");");
}
catch (Exception)
{
}
}
}
}
Сначала рассмотрим конструктор (это функция с именем, аналогичным имени модуля кода, в данном случае это App() ). Он используется для подключения функций Startup и Exit. Это уже было сделано в файле XAML, поэтому нет необходимости делать это в коде. Однако это демонстрирует замечательную гибкость модели XAML/выделенный код, которая позволяет подключать события во время разработки (задавая их в XAML) или во время выполнения (объявляя их в коде).
Далее можно проверить функции Application Startup и Application Exit. Обратите внимание, что они принимают два параметра: объект, формирующий событие, и объект аргументов. Такая сигнатура функций очень распространена в приложениях Silverlight.
В функции Application Startup происходит присвоение свойству RootVisual (Корневой визуальный элемент) приложения нового объекта Page (Страница). Тем самым объявляется, что UI объекта Page является первым экраном UI, который должно создать это приложение. Если предполагается использовать другие экраны UI, объявленные в XAML, они будут запускаться уже из объекта Page.
Объект Page является XAML-объектом по умолчанию, создаваемым шаблоном для размещения UI вашего приложения, который рассматривается в следующем разделе.
Файл Page.xaml является для приложения UI по умолчанию. При компиляции вместе с ассоциированным с ним файлом выделенного кода он формирует класс Page, экземпляр которого будет объектом Page. Если вспомнить из предыдущего раздела, в качестве значения свойства RootVisual приложения был задан новый объект Page, что обеспечило данному классу UI по умолчанию.
Вот XAML, формируемый по умолчанию для Page.xaml:
<UserControl x:Class="SlidingBlocks.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x=" http://schemas.microsoft.com/winfx/2006/xa ml" Width = "400" Height="300"> <Grid x:Name="LayoutRoot" Background ="White" > </Grid> </UserControl>
Прежде всего, обратите внимание, что контейнером XAML является UserControl. Этого не было в Silverlight 1.0. Как говорилось ранее, при построении приложений Silverlight 2 в Visual Studio, фактически, создаются элементы управления, которые компилируются в DLL и помещаются в XAP, который затем открывает и исполняет Silverlight.
В данном случае, видим, что экземпляр этого UserControl называется SlidingBlocks.Page. SlidingBlocks- это пространство имен (чтобы увидеть это, вернитесь и взгляните на свойства проекта), и Page - имя класса из этого пространства имен.
Объявления xmlns и xmlns:x задают пространство имен по умолчанию и дополнительное пространство имен, соответственно, которые должны использоваться для проверки корректности XAML Ранее был рассмотрен атрибут x:Class, используемый для описания класса этого элемента управления, который также является примером применения дополнительного пространства имен, запись которого начинается с префикса x:.
Наконец, ширине и высоте присвоены значения по умолчанию: 640 х 480.
Далее идет корневой Grid. В Silverlight 2 корневым элементом должен быть Container (Контейнер), которым в данном случае является Grid под именем LayoutRoot. Соответственно, все элементы дизайна UI будут дочерними элементами этого узла.
Вот выделенный код для данного XAML:
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;
namespace SlidingBlocks {
public partial class Page : UserControl {
public Page()
{
// Необходима для инициализации переменных
InitializeComponent();
}
}
}
Те, кто знаком с C#, видят, что этот код очень похож на код, который, возможно, вы использовали ранее. По сути, это стандартный класс Page, унаследованный от типа UserControl. В конструкторе класса выполняется общая настройка с помощью вызова метода InitializeComponent(). В разделе "Написание игры на Silverlight 2" данной лекции, в котором мы займемся созданием головоломки с перемещением фрагментов, вы добавите в этот модуль характерный для вашей страницы код.
Кроме проекта элемента управления, шаблон создает Веб-проект, в котором размещается ваше приложение Silverlight. Этот Веб-проект включает два ASPX-файла: Default.aspx, являющийся пустой Веб-формой, на базе которой можно построить свое приложение; и тестовую страницу <ИмяПриложения> TestPage.aspx (например, SlidingBlocksTestPage.aspx ), которая содержит все необходимое для запуска Silverlight из ASP.NET.
Хотя Silverlight не имеет никаких зависимостей на стороне сервера, ASP.NET предлагает элементы управления, обеспечивающие возможность создания JavaScript и HTML на стороне клиента, необходимых для размещения Silverlight в браузере.
Файл TestPage включает ссылки на эти элементы управления. Далее приведена разметка для ASPX-файла:
<%@ Page="" Language="C#" AutoEventWireup="true" %>
<%@ Register="" Assembly="System.Web.Silverlight"
Namespace="System.Web.UI.SilverlightControls" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"style="height: 100%;">
<head runat="server">
<title>SlidingBlocks</title>
</head>
<body style="height: 100%;margin:0;">
<form id = "form1" runat="server" style="height:100%;">
<asp:ScriptManager ID="ScriptManager1" runat= "server">
</asp:ScriptManager>
<div style="height:100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SlidingBlocks.xap"
MinimumVersion = "2.0.31005.0" Width = "100%" Height="100%" />
</div>
</form>
</body>
</html>
Заметьте, что данная технология является развивающейся, поэтому атрибуты номера версии и открытого ключа могут немного меняться. Если для создания кода вы использовали шаблон, не стоит волноваться, все будет в порядке.
На этой странице используются два элемента управления ASP.NET. Первый - (Диспетчер сценариев), который является артефактом AJAX ASP.NET и превосходным средством управления загрузкой и использованием всех необходимых библиотек JavaScript в соответствующее время и в соответствующем месте.
Второй -элемент управления Silverlight. Обратите внимание, что в качестве параметра он принимает рассмотренный выше XAP. Этот элемент управления будет формировать правильный HTML-код для создания функций DIV и CreateSilverlight.
После запуска этой страницы будет сформировано много кода на HTML и JavaScript. Элемент управления Silverlight создается и получает файл XAP ближе к концу кода. Вот фрагмент кода:
<script type="text/javascript">
//<![CDATA[
Sys. Application. initialize();
Sys.Application.add
init(function() {
$create(Sys.UI.Silverlight.Control,
{"source": ClientBin/SlidingBlocks.xap"},
null, null, $get("Xaml1 parent"));
});
//]]>
</script>
Браузер интерпретирует этот сценарий и создает элемент управления Silverlight. Обратите внимание, что корректную работу элементов управления Silverlight ASP.NET обеспечит ASP.NET. Если экземпляр Silverlight создается не из ASP.NET, по-прежнему могут использоваться сценарии JavaScript, входящие в SDK. Это подробно рассматривается в лекц. 6, "Элемент управления браузера Silverlight".
Silverlight поддерживает модель разделения дизайна и процесса разработки, при которой дизайн представляется с помощью XAML и код, обычно (хотя это не обязательно) на C#, создается в выделенном файле.
В данном примере XAML будет использоваться совсем немного, лишь для размещения Canvas, в котором будут находиться фрагменты рисунка и его полное изображение. Если вернуться к рис. 3.4, эти компоненты можно увидеть слева и справа, соответственно. Обратите внимание, что хотя главным контейнером страницы по умолчанию является Grid, для простоты в этой игре мы будем использовать Canvas.
Как видно на рис. 3.4, UI этой игры очень прост. Он состоит из области экрана (занимаемой элементом Canvas ), в которой располагаются перемещаемые фрагменты собираемого изображения, и области, в которой формируется визуальное представление собранного рисунка.
Вот пример XAML, который обеспечивает отображение Canvas и завершенного рисунка:
<UserControl x:Class="SlidingBlocks.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xam l/presentation"
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xa ml"
Width = "640" Height="480">
<Canvas x:Name="LayoutRoot">
<Canvas x:Name="GameContainer" />
<Image Source="sl.jpg" Canvas.Left="500"
Height="400" Width="400" Stretch="UniformToFill">
</Image>
</Canvas>
</UserControl>
Как видите, все очень просто. Canvas, в котором будут размещаться фрагменты собираемого изображения, называется GameContainer (Контейнер игры). Изображение, которое будет формировать визуальное представление собранного рисунка, является статическим, таким образом, не нуждается в имени. Изначально оно заполнено изображением sl.jpg из этого же Веб-проекта. Вот и весь дизайн. В следующем разделе займемся кодом.
Код игры в данном примере написан на C#. Его без труда можно было бы транслировать на VB.NET, IronPython или любой другой поддерживаемый язык программирования, но в большинстве случаев в данном курсе будет использоваться C#.
Первый шаг в написании кода для этой игры - инициализация структур данных, которые будут использоваться приложением. В данном случае мы собираемся разбить рисунок на 16 фрагментов, 15 из которых будут выставлены на поле в произвольном порядке. Один фрагмент не используется, вместо него оставляем пустое пространство, обеспечивающее возможность перемещения фрагментов по игровому полю.
В Silverlight нельзя создать фрагмент существующего изображения, но можно вырезать его по определенному контуру. Не следует путать вырезание с обрезкой. В первом случае вы указываете, какие части рисунка должны быть отрисованы; во втором -удаляете все, кроме необходимой части изображения. Silverlight не поддерживает обрезку, поэтому приходится вырезать изображение.
Проблема использования вырезания состоит в том, что при его применении остальная часть рисунка остается доступной и может быть активизирована щелчком мыши. Например, если имеется изображение 400 х 400 и из него вырезается квадратный фрагмент 100 х 100 в позиции (100,100), мы видим квадрат 100 х 100 в позиции (100,100), но объект400 х 400 никуда не исчез: он стал пустым, но по-прежнему может быть активизирован щелчком мыши. Это не отвечает нашим требованиям для создания головоломки с перемещаемыми фрагментами. Но что можно сделать?
Решение состоит во введении в Canvas изображения, размеры которого соответствуют размерам вырезанного фрагмента, и последующем использовании трансформации транслирования на него, чтобы вырезанная часть изображения располагалась в верхнем левом углу данного Canvas, и, таким образом, Canvas формировал визуальное отображение только вырезанной области.
Итак, для головоломки 4x4, образованной 16 блоками необходимо создать 16 изображений и 16 объектов Canvas. Также потребуется как-то представлять поле, чтобы вы знали, какое изображение в каком квадрате находится. Вот код для описания этого:
Canvas[] cI = new Canvas[16]; Image[] i = new Image[16]; int[] board = new int[16];
Чтобы создать фрагменты головоломки, понадобиться загрузить изображение sl.jpg в каждый элемент массива изображений. Для этого с помощью универсального идентификатора ресурса (uniform resource identifier, URI) мы указываем на необходимое изображение и определяем класс BitmapImage (из пространства имен System.Windows.Media.Imaging ), в который считываются данные по этому URI. И присваиваем этот класс элементу управления изображение:
i[nx].Source = new BitmapImage(uri);
Из конструктора можно вызвать функцию ( InitBoard ), которая будет использоваться для создания экземпляров фрагментов головоломки:
public Page() {
// Необходимо для инициализации переменных
InitializeComponent();
InitBoard(); }
Эту функцию можно увидеть здесь:
void InitBoard() {
Uri uri = new Uri("sl.jpg", UriKind.Relative);
int nx = 0;
for (int ix = 0; ix < 4; ix++)
for (int iy = 0; iy < 4; iy++) {
nx = (ix * 4) + iy;
i[nx] = new Image();
i[nx].Height = 400;
i[nx].Width = 400;
i[nx].Stretch = Stretch.UniformToFill;
RectangleGeometry r = new RectangleGeometry();
r.Rect = new Rect((ix * 100), (iy * 100), 100, 100);
i[nx].Clip = r;
i[nx].Source = new BitmapImage(uri);
i[nx].SetValue(Canvas.TopProperty, Convert.toDouble(iy * 100 * -1));
i[nx].SetValue(Canvas.LeftProperty, Convert.toDouble(ix * 100 * -1));
cI[nx] = new Canvas();
cI[nx].Width = 100;
cI[nx].Height = 100;
cI[nx].Children.Add(i[nx]);
cI[nx].SetValue(Canvas.NameProperty, "C" + nx.ToString());
cI[nx].MouseLeftButtonDown += new
MouseButtonEventHandler(Page MouseLeftButtonDown);
if (nx < 15) GameContainer.Children.Add(cI[nx]);
}
// Перемешиваем фрагменты shuffle();
// Отрисовываем поле drawBoard();
}
Не забудьте добавить ссылку на System.Windows.Media.Imaging в начале кода:
using System.Windows.Media.Imaging;
На первый взгляд все это может показаться немного сложным, но при более близком рассмотрении все предельно просто. Задается вложенный цикл для прохождения по осям х и у с диапазоном значений от 0 до 3 . Это, как можно догадаться, используется для организации массива 4x4 для изображений.
Массивы Image и Canvas, используемые для хранения фрагментов, являются одномерными массивами по 16 элементов каждый (при такой структуре для их хранения требуется меньше памяти и кода). Чтобы спроецировать двумерные координаты ix, iy на одномерный массив, необходимо выполнить следующие вычисления:
nx = (ix * 4) + iy;
Для каждого элемента задаются размеры 400 х 400 с помощью значения UniformToFill (Равномерное заполнение) свойства Stretch (Растяжение). Более подробно об использовании изображений рассказывается в лекц. 4, "Основы XAML".
Далее вычисляется область вырезания. Для этого используется объект RectangleGeometry (Геометрический элемент прямоугольник):
RectangleGeometry r = new RectangleGeometry(); r.Rect=new Rect((ix*100), (iy*100) ,100,100); i[nx].Clip = r;
Таким образом, определяется прямоугольник с соответствующими координатами (полученными путем умножения ix и iy на 100) соответствующего размера (100 х 100), и он задается как область вырезания для текущего изображения ( i[nx] ).
Далее изображение загружается в BitmapImage, инициализация которого происходит путем задания URI этого изображения, и помещается в заданное положение. Чтобы изображение, вырезанное в позиции (100,100), появилось в верхнем левом углу, оно должно быть передвинуто на (-100,-100) относительно вырезанной области. Таким образом, свойствам Top (Сверху) и Left (Слева) должно быть задано значение -100 и умножено на значения iy и ix, соответственно.
i[nx].Source = new BitmapImage(uri); i[nx].SetValue(Canvas.TopProperty, Convert.toDouble(iy * 100 * -1)); i[nx].SetValue(Canvas.LeftProperty, Convert.toDouble(ix * 100 * -1));
Изображения до сих пор не добавлены в родительский Canvas. Это следующий шаг. Canvas необходимо инициализировать, задать его размеры и добавить в него в качестве дочернего элемента соответствующее изображение. Вот код:
cI[nx] = new Canvas(); cI[nx] .Width = 100; cI[nx].Height = 100; cI[nx].Children.Add(i[nx]);
Теперь мы получили наш Canvas, и он содержит наше вырезанное изображение. Завершаем инициализацию, присваивая Canvas имя (чтобы его можно было отслеживать, когда мы щелкаем его), определяем обработчик событий для описания действий, которые будут выполняться по щелчку пользователя по нему, и, наконец, добавляем его в родительский Canvas. Пока нет необходимости задавать местоположение фрагмента. Это будет сделано после того, как фрагменты будут перемешаны на поле.
Обратите внимание, последнее изображение не добавляется на поле, потому что мы хотим, чтобы осталось свободное пространство.
cI[nx].SetValue(Canvas.NameProperty, "C" + nx.ToString());
cI[nx].MouseLeftButtonDown += new
MouseButtonEventHandler(Page MouseLeftButtonDown);
if(nx<15)
GameContainer.Children.Add(cI[nx]);
Наконец, необходимо перемешать фрагменты и отрисовать игровое поле. Код для реализации этого представлен в следующем разделе.
Фрагменты головоломки перемешиваются с помощью довольно простого алгоритма перемешивания, который перебирает массив 100 раз, выбирая два случайных элемента каждый раз; если эти два элемента разные, он меняет местами их содержимое. В конце он загружает в последний элемент значение -1, чтобы обозначить пустой квадрат.
Здесь представлен алгоритм перемешивания:
void shuffle()
{ // Инициализируем игровое поле
for (int n = 0; n < 15; n ++) {
board[n] = n;
}
Random rand = new Random(System.DateTime.Now.Second);
for (int n = 0; n < 100; n++) {
int n1 = rand.Next(15);
int n2 = rand.Next(15);
if (n1 != n2) {
int tmp = board[n1];
board[n1] = board[n2];
board[n2] = tmp;
}
}
board[15] = -1;
}
Следующий шаг после того, как фрагменты перемешаны, - отрисовка игрового поля.
На данный момент мы имеем все фрагменты головоломки, определенные как элементы Image в элементе Canvas, и массив целых чисел, значение индекса n которого соответствует фрагменту, отображаемому в данном месте. Вы также перемешали этот массив целых чисел, поэтому пришло время отрисовать игровое поле. Это делается следующим образом:
void drawBoard() {
int nx = 0;
int ny = 0;
for (int n = 0; n < 15; n ++) {
nx = n / 4;
ny = n % 4;
if(board[n]> = 0) {
cI[board[n]].SetValue(Canvas.TopProperty, Convert.toDouble(ny * 100));
cI[board[n]].SetValue(Canvas.LeftProperty, Convert.toDouble(nx * 100));
}
}
}
В данном случае выполняется цикл с изменением индекса от 0 до 14 (в головоломке 15 фрагментов) и вычисление координат x,y для каждого блока в сетке 4 х 4. Значение x -это просто результат целочисленного деления индекса массива на 4, и значение y -это модуль индекса, деленного на 4. Умножаем эти значения на 100 и получаем необходимые координаты для отрисовки элемента Canvas.
Теперь мы имеем полностью инициализированную игру. Рисунок располагается справа, а игровое поле с перемешанными фрагментами рисунка - слева.
Теперь необходимо заняться обработкой действий пользователя. В играх подобного рода предполагается, что пользователь будет щелкать фрагмент изображения, и если этот фрагмент находится рядом с пустой областью, он плавно переместится в эту область, оставляя пустым занимаемое до этого место. Итак, мы должны обработать щелчки по элементу Canvas, содержащего фрагмент изображения. Если вернуться к инициализации фрагментов, можно вспомнить такую строку:
cI[nx].MouseLeftButtonDown += new MouseButtonEventHandler(Page MouseLeftButtonDown);
Здесь описывается, что по щелчку Canvas будет запускаться обработчик события
PageMouseLeftButtonDown. Этот обработчик события задан для каждого из блоков Canvas.
Код этого обработчика события состоит из двух разделов. Первый определяет, который из Canvas сформировал событие и положение этого Canvas на игровом поле:
void PageMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Canvas c = sender as Canvas;
int nCanvasID = -1;
int nBoardLoc = -1;
int nEmptyLoc = -1;
for (int i = 0; i < 16; i++) {
if (c == cI[i]) { nCanvasID = i; break; }
}
for (int i = 0; i < 16; i++) {
if (board[i] == nCanvasID) {
nBoardLoc = i;
}
else if (board[i] == -1)
{ nEmptyLoc = i; }
}
Итак, например, вы щелкнули фрагмент, представляющий верхний левый угол полного изображения (блок 0), но в настоящий момент он находится в нижнем левом углу игрового поля (позиция 12). При возникновении события щелчка, выполняется перебор всего массива элементов Canvas, представляющих фрагменты головоломки, до тех пор, пока не будет найден фрагмент, соответствующий Canvas, по которому было произведен щелчок. И здесь вы можете получить индекс этого элемента в массиве, сохранив его в переменной nCanvasID (в нашем гипотетическом примере он равнялся бы 0). После этого, просканировав игровое поле, можно определить местоположение элемента 0 на нем и присвоить значение его позиции переменной nBoardLoc (которое в нашем гипотетическом примере равно 12). И, находясь в этой позиции, найдите местоположение пустой ячейки на игровом поле и сохраните его в nEmptyLoc.
Вторая часть кода проверяет возможность перемещения фрагмента, и, если перемещение возможно, перемещает фрагмент в пустую ячейку и соответствующим образом обновляет игровое поле.
// Проверяем возможность перемещения
if ((nBoardLoc == nEmptyLoc + 1) ||
(nBoardLoc == nEmptyLoc - 1) ||
(nBoardLoc == nEmptyLoc + 4) ||
(nBoardLoc == nEmptyLoc - 4)) {
int nx = nEmptyLoc/4;
int ny = nEmptyLoc%4;
cI[nCanvasID].SetValue(Convert.toDouble(Canvas.TopProperty, ny * 100));
cI[nCanvasID].SetValue(Convert.toDouble(Canvas.LeftProperty, nx * 100));
board[nEmptyLoc] = nCanvasID; board[nBoardLoc] = -1;
checkWinner();
}
else {
// не делаем ничего }
}
Для этого сначала проверим местоположение пустой ячейки относительно положения блока, по которому был произведен щелчок. Если они располагаются непосредственно рядом друг с другом (над, под, слева или справа), перемещение возможно. Поскольку игровое поле 4x4 является одномерным массивом, сделать это несложно. Позиции элементов, располагающихся слева и справа от текущего элемента, отличаются от текущей на -1 и +1, соответственно, и позиции элементов сверху и снизу - на -4 и +4, соответственно. Так что если результат проверки фрагментов указывает на пустую ячейку, вы знаете, что можете двигаться.
Чтобы выполнить перемещение, необходимо получить координаты x и y относительно положения пустого фрагмента и задать их как координаты блока, по которому был произведен щелчок. Затем присвоить позиции поля, на которой до этого находилась пустая ячейка, значение Canvas, по которому был произведен щелчок, и позиции поля, на которой до этого располагался Canvas, по которому был произведен щелчок, значение -1, указывая на то, что теперь эта ячейка пуста.
Наконец, вызовем функцию checkWinner(), чтобы проверить успешность решения головоломки.
Функция checkWinner (Проверка победителя) проверяет успешность решения головоломки. Опять же, все очень просто. Головоломка решена, если каждый n -ный элемент игрового поля равен n, т.е. Canvas 0 имеет индекс 0, Canvas1 - индекс 1 и т.д.
Самый эффективный способ сделать это - предположить, что головоломка решена успешно, и проска-нировать игровое поле в поисках несоответствия board[n] значению n, что будет свидетельствовать о неверном решении и приводить к завершению цикла. Если все индексы совпадают, у нас есть победитель!
void checkWinner() {
bool bCompleted = true;
for (int n = 0; n < 15; n ++) {
if (n != board[n]) {
bCompleted = false; break; }
}
if (bCompleted) {
// Игрок выиграл....сделайте для него что-нибудь приятное.
}
}
Теперь вы получили все элементы простой игры-головоломки с перемещением фрагментов, написанной на С# для Silverlight 2. И для этого понадобилось всего лишь 100 строк кода. По ходу чтения этого курса данный пример можно расширять, добавляя анимацию перемещения блоков, сохранение лучших результатов, возможность загрузки изображений в приложение и т.д. Нет пределов совершенству!
В данной лекции мы рассмотрели Visual Studio 2008 и предлагаемые ею различные инструменты и шаблоны для разработки приложений Silverlight с использованием языков программирования .NET. Был детально разобран проект, созданный на базе стандартных шаблонов Silverlight, рассмотрены все его файлы и то, как он используется для разработки и развертывания приложения Silverlight. После этого мы применили теорию на практике и создали полнофункциональную игру-головоломку с перемещением фрагментов, используя XAML и C#. Это позволило почувствовать возможности Silverlight 2, но мы лишь слегка прикоснулись к ним. В части 2 данного курса, "Программирование в Silverlight 2", будут более глубоко рассмотрены функциональные возможности, доступные в .NET Framework, включая создание собственных элементов управления, работу с сетью и обмен информацией, данные и XML, динамические языки программирования и серверные элементы управления ASP.NET.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.