В предыдущих лекциях вы познакомились с основными элементами управления, используемыми для создания насыщенных интерактивных Интернет-приложений. В данной главе будет рассмотрено три элемента управления, предоставляющие специальную функциональность. Сначала будет представлен элемент управления MediaElement, который обеспечивает API для управления аудио- и видеоданными, позволяя тем самым создавать замечательные мультимедийные приложения. Затем вы рассмотрите элемент управления InkPresenter (Элемент отображения рукописного ввода), который позволяет вводить рукописное или рисованное содержимое посредством рукописных примечаний, т.е. обеспечивает возможность использования приложения на компьютерах с перьевым вводом. Наконец, вашему вниманию будет представлен достаточно новый элемент управления Silverlight 2, MultiScaleImage (Масштабируемое изображение), который добавляет в ваши приложения новое измерение - возможность глубокого масштабирования.
Одно из наиболее важных применений Silverlight в Веб - обеспечение поддержки мультимедиа следующего поколения, которое может воспроизводиться на разных платформах. Для этого Silverlight поддерживает элемент управления MediaElement, которому посвящен данный раздел. В нем поэтапно рассматривается процесс создания простого медиаплейера, обеспечивающего возможность прогрессивной загрузки и воспроизведения видеоданных. Кроме того, вы научитесь закрашивать поверхности с помощью видео-кисти, что позволит добавлять интересные графические эффекты. Элемент управления MediaElement поддерживает следующие форматы:
Видео
Аудио
Кроме этих форматов, элемент управления MediaElement также поддерживает списки воспроизведения ASX и протоколы http, https и .
Если говорить о потоковой передаче видео- и/или аудио, MediaElement поддерживает потоковое вещание и потоковую передачу по запросу от Windows Media Server. Если для URI задан протокол , потоковая передача включена; в противном случае, загрузка и воспроизведение файла будут осуществляться путем прогрессивной загрузки, при которой воспроизведение начинается только после заполнения буфера воспроизведения и затем эти два процесса выполняются параллельно.
Если определен протокол http или https, происходит обратное. MediaElement сначала делает попытку выполнить прогрессивную загрузку, и уже в случае неудачи пытается применить потоковую передачу файла.
Элемент управления MediaElement в MediaElement.
Чтобы начать работу с элементом управления MediaElement, добавьте его на свою страницу и задайте в качестве значения его атрибута Source URL видеофайл, который хотите воспроизвести. Рассмотрим пример XAML-кода Silverlight 2 приложения:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SilverlightApplication5.Page"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" Background="White">
<MediaElement Source="balls.wmv" />
</Grid>
</UserControl>
Это обеспечит автоматическую загрузку и воспроизведение видеоданных. Размер области воспроизведения мультимедиа определяется следующими правилами:
Height и Width элемента управления MediaElement, MediaElement использует их.MediaElement растянет мультимедиа, сохраняя пропорции изображения.Height, ни Width, элемент управления MediaElement будет воспроизводить видео в стандартных размерах. Если они превышают размер области просмотра элемента управления Silverlight, MediaElement обрежет видео соответственно размеру области просмотра.Рассмотрим пример. Используемая в данной лекции (и доступная для скачивания на Веб-сайте компании) видеозапись balls. имеет размер 480 х 360. Если указать MediaElement воспроизводить это видео и не задать его Height и Width, воспроизводимое видеоизображение будет размером 480 х 360. Если у UserControl выставить размер 200 х 200, на экране будет видна только часть видеоизображения размером 200 х 200 пикселов, начиная от верхнего левого угла. Обрезанная таким образом часть видео представлена на рис. 11.1.
(рис 11.1) Видеоизображение, обрезанное по размеру элемента управления Silverlight
Как было показано в предыдущем разделе, размер MediaElement важен при воспроизведении видео. Если размеры элемента управления не заданы, и видеоизображение превышает размеры элемента управления Silverlight, оно будет обрезано.
Управление шириной и высотой самого MediaElement осуществляется через его свойства Height и Width. При формировании визуального представления элемента управления, мультимедиа будет растягиваться (или сжиматься) так, чтобы поместиться в элемент управления. Если заданный размер элемента управления мультимедиа больше элемента управления Silverlight, изображение будет обрезано размерами элемента управления Silverlight.
Далее представлен пример элемента управления MediaElement, для которого заданы размеры 200 х 200. На рис. 11.2 можно увидеть, как будет отображаться видео:
<MediaElement Source="balls.wmv"
Height="200" Width="200" />
(рис 11.2) Отображение видео после задания размеров MediaElement
В предыдущем примере видеоизображение сворачивалось до размера элемента управления MediaElement (200 х 200). Как видно на рис. 11.2, размер видеоизображения (исходный размер которого, как вы помните, 480 х 360 пикселов) меняется так, чтобы оно поместилось в MediaElement, но с сохранением его пропорций. Это может привести к появлению черных полей вверху и внизу видеоизображения, создавая эффект просмотра широкоформатного видео. Переопределить это поведение можно с помощью свойства Stretch элемента управления MediaElement. Это свойство принимает четыре разных значения:
Uniform Это режим растяжения по умолчанию, он обеспечивает сохранение пропорций видеоизображения и добавление полей вверху, внизу или по бокам в случае необходимости.Регулировать прозрачность MediaElement позволяет свойство Opacity. Это нормализованное свойство, т.е. значение 0 эквивалентно полной прозрачности, 1 - полной непрозрачности, все остальные значения в этом диапазоне представляют разные степени прозрачности. Видеоизображение будет отображаться с заданной прозрачностью, при этом элементы, находящиеся позади элемента мультимедиа на экране, будут видны.
Далее представлен пример XAML, обеспечивающий отображение красного прямоугольника и MediaElement. Для MediaElement задана прозрачность 0,5, что делает видеоизображение полупрозрачным. Поскольку визуальное представление MediaElement формируется вторым, он размещается ближе в направлении по оси Z и располагается поверх прямоугольника. На рис. 11.6 можно увидеть результат выполнения этого примера.
<Rectangle Fill="Red" Height="100" Width="200" />
<MediaElement Source="balls.wmv" Height="200" Width="200"
Stretch="Fill" Opacity="0.5" />
(рис 11.6) Применение свойства Opacity для видеоизображения
Подробно трансформации рассматриваются в лекц. 5, "Трансформация и анимация в XAML", здесь хотелось бы отметить лишь то, что трансформации могут применяться и к элементу управления MediaElement. При этом отображаемое видео также трансформируется. Это может привести к созданию некоторых очень интересных эффектов. Например, далее представлен MediaElement, к которому применена трансформация наклонение:
<MediaElement Source="balls.wmv" Height="200" Width="200"
Stretch="Fill" >
<MediaElement.RenderTransform> <SkewTransform AngleX="45"/>
</MediaElement.RenderTransform>
</MediaElement>
Как это выглядит, можно увидеть на рис. 11.7.
(рис 11.7) Наклонение видео с помощью SkewTransform
Silverlight позволяет размещать содержимое, включая текст и графические элементы, поверх видеоизображения. Это можно сделать либо с помощью свойств ZOrder (Z-порядок) элементов пользовательского интерфейса (UI) (смотрите лекц. 1, "Введение в Silverlight 2"), либо просто размещать элементы UI в том же месте, что и элемент мультимедиа, и объявлять их в XAML после него. Далее представлен пример MediaElement, и Canvas с прямоугольником и текстовым блоком, который перекрывает видеоизображение:
<MediaElement Source="balls.wmv" Height="200"
Width="200" Stretch="Fill" />
<Canvas Margin="20,140,0,0">
<Rectangle Fill= "Red" Height="40" Width="160" />
<TextBlock>Subtitle on Video</TextBlock>
</Canvas>
На рис. 11.8 показано, как это будет выглядеть на экране.
(рис 11.8) Отображение содержимого поверх видео
В лекц. 4, "Основы XAML" были рассмотрены вырезание и геометрические элементы. Эти возможности также могут применяться к MediaElement, для которого с помощью фигур или контуров можно определить контур вырезания. Например, следующий XAML в качестве контура вырезания для MediaElement определяет эллипс:
<MediaElement Source="balls.wmv" Height="200"
Width="200" Stretch="Fill" >
<MediaElement.Clip>
<EllipseGeometry RadiusX="100" RadiusY="75" Center="100,75"/>
</MediaElement.Clip>
</MediaElement>
Результаты представлены на рис. 11.9.
(рис 11.9) Вырезание мультимедиа геометрическими формами
По умолчанию, если задан источник, MediaElement автоматически воспроизводит мультимедиа. Этим поведением можно управлять посредством свойства (Автоматическое воспроизведение). По умолчанию его значение true, но его можно переопределить и задать false. Можно воспроизвести мультимедиа позже, используя метод Play (Воспроизвести). Этот и другие методы и события, используемые при программировании элемента мультимедиа, рассматриваются в следующем разделе.
С помощью свойства IsMuted (Без звука) элемента управления MediaElement можно определить, будет ли аудиозапись сопровождать воспроизведение или нет. Это свойство Булевого типа, и если задать ему значение true, звук будет отключен.
Кроме того, с помощью свойства Volume (Громкость) можно управлять громкостью звука. Оно принимает нормализованное значение, где 0 эквивалентно отсутствию звука, 1 - полной громкости, и промежуточные значения представляют относительные уровни громкости. Итак, например, значение 0,43 обозначало бы воспроизведение звука с громкостью, составляющей 43% от полной громкости.
Наконец, балансом звука можно управлять с помощью свойства Balance (Баланс), которое принимает значения в диапазоне от -1 до +1. Значение -1 приводит к смещению баланса звука полностью влево, т.е. левый динамик будет воспроизводить звук со 100% громкостью, и в правом динамике будет 0% громкости. Значение +1 обеспечивает прямо противоположный эффект - баланс звука будет полностью смещен вправо, при этом правый динамик будет воспроизводить звук со 100% громкостью. Значение 0 обеспечивает равномерное распределение громкости между двумя динамиками.
Например, свойству Balance задано значение 0,8, тогда правый динамик будет воспроизводить звук с 80% громкостью и левый динамик- с 20%. Если используется значение -0,8, левый динамик будет воспроизводить звук с громкостью 80%, и правый динамик - с громкостью 20%.
Далее представлен некоторый XAML, определяющий, что звук не выключен, общая громкость составляет 50% и что баланс звука смещен вправо:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch = "Fill"
IsMuted = "False" Volume="0.5" Balance="0.8" />
MediaElement предлагает насыщенную модель программирования, которая позволяет управлять воспроизведением методами Play, Stop (Остановить) и Pause (Приостановить). Также вы имеете возможность взаимодействовать с видео, отслеживая процесс буферизации и загрузки, а также отвечать на маркеры, размещенные в видеофайле. Кроме того, можно обработать события для перехвата, такие как поведение мыши.
Основными методами управления видео являются Play, Stop и Pause. Если свойству элемента управления MediaElement задано значение false, для начала воспроизведения видео необходимо применить Play. Даже если задано значение true, и воспроизведение начинается автоматически, с помощью этих методов его можно остановить или приостановить. Далее представлен пример XAML, в котором имеется элемент мультимедиа и три простых элемента управления воспроизведением, реализованных как элементы TextBlock:
<MediaElement x:Name="vid" Source="balls.wmv" Height="200"
Width = "200" Stretch="Fill" />
<Canvas Canvas.Top="160">
<Rectangle Fill= "Black" Width = "200" Height="24" Opacity="0.7"/>
<TextBlock Foreground= "White" Canvas.Left="20">Play</TextBlock>
<TextBlock Foreground="White" Canvas. Left="80">Stop</TextBlock>
<TextBlock Foreground="White" Canvas. Left=" 140" >Pause</TextBlock>
</Canvas>
На рис. 11.10 показано, как эти элементы управления будут выглядеть на видеоизображении.
(рис 11.10) Добавление элементов управления в видеоизображениеДля создания элементов управления вы задаете имя функции, которая должна выполняться в ответ на событие мыши, используя атрибут самого элемента управления видео. Намного более подробно обработка событий Silverlight с помощью JavaScript рассматривается в лекц. 6, "Элемент управления браузера Silverlight". Однако, в данном случае, просто требуется начать, остановить или приостановить воспроизведение видео, когда пользователь щелкнет соответствующий текстовый блок. Это реализуется путем обработки события MouseLeftButtonDown текстового блока, которое ассоциируется с функциями, воспроизводящими, приостанавливающими или останавливающими воспроизведение.
При программировании на JavaScript необходимо обеспечить, чтобы эти функции были доступны со страницы, на которой размещается элемент управления Silverlight. Итак, они могут быть реализованы либо путем использования элементов JavaScript на странице, либо в файле js, который включен в страницу с помощью элемента JavaScript, свойству Src которого задано соответствующее значение.
Если для сведения воедино содержимого XAML используется Expression Blend, он обеспечивает для Page.xaml псевдо-файл выделенного кода Page.xaml.js. Это идеальное место для реализации функциональности JavaScript.
Создадим проект Silverlight 1.0 Site. Далее представлен XAML, описывающий похожий UI, как был создан в предыдущем примере, но с объявлениями обработчиков событий:
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480" Background="White" x:Name="Page">
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch="Fill" />
<Canvas Canvas.Top="160">
<Rectangle Fill="Black" Width="200" Height="24" Opacity="0.7"/>
<TextBlock MouseLeftButtonDown="doPlay"
Foreground="White"
Canvas.Left="20">Play</TextBlock>
<TextBlock MouseLeftButtonDown="doStop"
Foreground="White"
Canvas.Left="80">Stop</TextBlock>
<TextBlock MouseLeftButtonDown="doPause"
Foreground="White"
Canvas.Left="140">Pause</TextBlock>
</Canvas>
</Canvas>
Теперь можно написать кода на JavaScript для начала, остановки и приостановки воспроизведения. Вот этот код:
function doPlay(sender, args)
{
var meVid = sender.findName("vid");
meVid.Play();
}
function doStop(sender, args)
{
var meVid = sender.findName("vid");
meVid.Stop();
}
function doPause(sender, args)
{
var meVid = sender.findName("vid");
meVid.Pause();
}
Когда функция JavaScript определяется как обработчик события, она должна принимать два параметра. Первый, sender, - это объект, который формирует событие. Второй, args, содержит аргументы, которые включаются как часть события.
Теперь в этой JavaScript-функции необходимо получить ссылку на объект MediaElement, управление которым осуществляется. Выполняется это с помощью метода findName объекта sender. Хотя отправителем является текстовый блок, который щелкнул пользователь, выполнение его метода findName все равно обеспечит выполнение поиска по всему XAML-документу до тех пор, пока не будет найден элемент (который был создан путем применения атрибута x:Name к нашему объекту MediaElement ). Если вернуться к XAML, можно увидеть, что в MediaElement был элемент x:Name со значением , так что поиск должен быть успешным. Ссылка на соответствующий объект MediaElement будет сохранена в переменной JavaScript meVid. Теперь для управления воспроизведением осталось просто вызывать методы Play, Stop или Pause для этого объекта.
При создании проекта приложения для Silverlight 2 и использовании .NET все даже еще проще: не надо использовать findName для доступа к элементу, и ему уже присвоено имя , так что код будет выглядеть так:
private void doPlay(object sender, MouseButtonEventArgs e)
{
vid.Play();
}
private void doPause(object sender, MouseButtonEventArgs e)
{
vid.Pause();
}
private void doStop(object sender, MouseButtonEventArgs e)
{
vid.Stop();
}
При использовании прогрессивной загрузки видео подсистема мультимедиа определяет, сколько видеоданных необходимо кэшировать, прежде чем будет возможно воспроизведение. Итак, в зависимости от того, какая полоса пропускания необходима для загрузки видео, и какая полоса пропускания доступна, создается буфер для размещения объема видеоданных, достаточного для начала воспроизведения, в ходе которого загрузка видео продолжается в фоновом режиме.
Когда буфер полон на 100%, начинается воспроизведение. Воспроизведение может прерываться при изменении состояния связи и дозагрузки буфера. Silverlight позволяет отслеживать это поведение с помощью события BufferingProgressChanged (Показатель процесса буферизации изменился) и свойства BufferingProgress (Показатель процесса буферизации). Используя эти событие и свойство, можно показывать пользователям текущее состояние буферизации или выполнять логику для улучшения взаимодействия с пользователем (UX) как результат условий буферизации. Например, в условиях плохого соединения буферизация может не превышать 50%. Вы можете перехватить это значение и предоставить соответствующую обратную связь своему клиенту.
Для управления буферизацией задайте в MediaElement обработчик событий, определяющий функцию для обработки события BufferingProgressChanged:
<MediaElement x:Name="vid" Source="balls.wmv" Height="200" Width="200" Stretch="Fill" Buffering Prog ressChanged = "doBuff"/> <TextBlock x:Name="txtBuff"></TextBlock>
Таким образом, функция doBuff будет выполняться каждый раз при изменении наполненности буфера. Это событие тесно связано со свойством BufferingProgress. Данное свойство принимает значения в диапазоне от 0 до 1, где 0 соответствует пустому буферу, и 1 - полному. Событие будет формироваться при изменении наполнения буфера на 5% (т.е. 0,05) или более и когда буфер заполнен полностью.
Далее представлен код, который может использоваться для ответа на это событие и обеспечения обратной связи о текущем состоянии буфера пользователям.
Сначала JavaScript-версия:
function doBuff(sender, args)
{
var theText = sender.findName("txtBuff");
var meVid = sender.findName("vid");
var prog = meVid.BufferingProgress * 100;
prog = "Buffering % " + prog;
theText.Text = prog;
}
И вот, как это было бы сделано в выделенном коде .NET (на C#):
private void doBuff(object sender, RoutedEventArgs e)
{
double prog = vid.BufferingProgress * 100;
txtBuff.Text = "Buffering % " + prog;
}
Если у вас не видно текст с отображением буферизации в Silverlight-приложении, обратите внимание на используемый элемент компоновки. Если вы использовали <Grid>, текст может находиться под "кнопками" Play, Stop и Pause. В этом случае задайте для текстовых блоков небольшой отступ с помощью свойства Margin.
Можно переопределить автоматическую буферизацию, задавая конкретную продолжительность буферизованного видео. Например, если необходимо всегда иметь 10-секундный буфер видео и, таким образом, снизить риск прерывания воспроизведения из-за повторной синхронизации буфера при ухудшении состояния связи, можно задать свойство BufferingTime (Размер буфера). В качестве его значения задается временной диапазон. Так, например, чтобы задать 10-секундный буфер, BufferingTime задается значение 0:0:10, как показано в следующем примере:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width = "200" Stretch="Fill"
BufferingProgressChanged = "doBuff"
BufferingTime="0:0:10"/>
<TextBlock x:Name="txtDown"></TextBlock>
Если прогрессивная загрузка недоступна или не поддерживается, придется загружать весь видеофайл целиком и только потом начинать его воспроизведение. В этом случае событие DownloadPro-gressChanged и свойство DownloadProgress могут использоваться для получения состояния загрузки. Принципы работы с ними аналогичны функциям буферизации. Дале представлен XAML, определяющий событие DownloadProgressChanged:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch="Fill"
BufferingProgressChanged = "doBuff"
BufferingTime="0:0:10"
DownloadProgressChanged = "doDown"/>
<TextBlock x:Name="txtDown"></TextBlock>
И теперь код функции doDown, обрабатывающей это событие. Опять же, сначала рассмотрим JavaScript-версию:
function doDown(sender, args)
{
var theText = sender.findName("txtDown");
var meVid = sender.findName("vid");
var prog = meVid.DownloadProgress * 100;
prog = "Downloading % " + prog;
theText.Text = prog;
}
И
private void doDown(object sender, RoutedEventArgs e)
{
double prog = vid.DownloadProgress * 100;
txtBuff.Text = "Downloading % " + prog;
}
Silverlight представляет свойство CurrentState (Текущее состояние) и связанное с ним событие Cur-rentStateChanged (Текущее состояние изменилось), которое может использоваться для ответа на изменение состояния мультимедиа.
Действительными состояниями свойства CurrentState являются:
Buffering ( Буферизация ) Буфер не заполнен на 100%, поэтому воспроизведение приостановлено до полного заполнения буфера.Closed ( Закрыто ) Мультимедиа закрыто.Error ( Ошибка ) Возникла проблема с загрузкой, буферизацией или воспроизведением мультимедиа.Opening ( Открывается ) Мультимедийный файл найден, и буферизация или загрузка должны вот-вот начаться.Paused ( Приостановлено ) Воспроизведение приостановлено.Playing ( Воспроизводится ) Идет воспроизведение мультимедиа.Stopped ( Остановлено ) Воспроизведение остановлено.Вот как событие CurrentStateChanged элемента управления MediaElement определяется в XAML:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch="Fill"
CurrentStateChanged= "doState" BufferingTime="0:0:10" />
Здесь задается функция doState, которая будет вызываться в ответ на изменение текущего состояния. Далее представлен пример такой JavaScript-функции, которая использует значение свойства Cur-rentState элемента управления MediaElement в окне уведомления:
function doState(sender, args)
{
var meVid = sender.findName("vid");
alert(meVid.CurrentState);
}
В C#, вместо окна уведомления MessageBox, для вывода содержимого свойства CurrentState на экран можно использовать TextBlock:
private void doState(object sender, RoutedEventArgs e)
{
txtBuff.Text = vid.CurrentState.ToString();
}
Для управления текущим положением курсора воспроизведения используются свойства NaturalDuration (Номинальная продолжительность) и Position (Положение) элемента мультимедиа. После того, как свойство CurrentState устанавливается в значение Opened, становится возможным получение значения и свойства NaturalDuration. Продолжительность видео в секундах можно получить с помощью Natu-ralDuration.Seconds и потом преобразовать это значение в часы, минуты и секунды.
В этом примере событие CurrentStateChanged элемента управления MediaElement ассоциировано с функцией doState (из предыдущего примера). Однако теперь эта функция перехватывает значение свойства NaturalDuration. В JavaScript-версии используется вспомогательная функция convertDT для преобразования возвращаемого значения в строку. Рассмотрим код на JavaScript:
function doState(sender, args)
{
var meVid = sender.findName("vid");
var txtStat = sender.findName("txtStat");
var datetime = new Date(0, 0, 0, 0, 0, meVid.naturalDuration.Seconds)
durationString = convertDT(datetime);
txtStat.Text = durationString.toString();
}
function convertDT(datetime)
{
var hours = datetime.getHours();
var minutes = datetime.getMinutes();
var seconds = datetime.getSeconds();
if (seconds < 10) {
seconds = "0" + seconds;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
var durationString;
if (hours > 0) {
durationString = hours.toString() + ":" + minutes + ":" + seconds;
} else {
durationString = minutes + ":" + seconds;
}
return durationString;
}
Далее следует замечательный пример того, как .NET Framework в Silverlight 2 существенно упрощает разработку: все это может быть реализовано одной строкой кода на C#:
private void doState(object sender, RoutedEventArgs e)
{ txtStat.Text = vid.NaturalDuration.ToString();
}
Вот XAML, В котором используется представленная выше функция:
<Canvas x:Name="sample9" Opacity="1">
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch = "Fill"
CurrentStateChanged="doState" BufferingTime="0:0:10" />
<TextBlock x:Name="txtStat">
</TextBlock>
</Canvas>
Получить текущее положение курсора воспроизведения можно с помощью свойства Position. В этом примере позиция выводится в текстовом блоке при приостановке воспроизведения (Для тестирования можете оставить в XAML-коде три "кнопки" Play, Stop и Pause и соответствующие им обработчики событий doPlay и doStop с предыдущего примера, но закомментировать тело функции doState ). Необходимый код на JavaScript для функции doPause выглядел бы примерно так:
function doPause(sender, args) {
var meVid = sender.findName("vid");
meVid.Pause();
var txtStat = sender.findName("txtStat");
var datetime = new Date(0,0,0,0,0, meVid.Position.Seconds);
positionString = convertDT(datetime);
txtStat.Text = positionString.toString();
}
И, как и ранее, эквивалентный код на C# намного проще:
private void doPause(object sender, MouseButtonEventArgs e) {
vid.Pause();
txtStat.Text = vid.Position.ToString(); }
Маркер временной шкалы - это
Silverlight поддерживает эти маркеры и формирует событие MarkerReached (Маркер достигнут) при встрече с маркером в ходе воспроизведения. Это событие можно перехватывать и использовать как триггер для действий, которые должны выполняться при достижении данной отметки.
Далее представлен фрагмент XAML, в котором с помощью атрибута MarkerReached задается обработчик события встречи с маркером, JavaScript-функция handleMarker:
<MediaElement x:Name="vid" Source="balls.wmv" Height="200" Width = "200" MarkerReached = "handleMarker" />
Среди аргументов данного события имеется объект marker (маркер). Он содержит объект TimeSpan (Диапазон времени) с меткой времени, в которой установлен маркер. В предыдущем разделе, "Управление положением курсора воспроизведения", представлен пример преобразования объекта Time-Span в формат, удобный для восприятия пользователем. Также marker содержит свойство Type (Тип).
Его значением является строка, которую задает человек, кодирующий видео. Наконец, имеется параметр Text, допускающий в качестве своего значения текст в свободном формате и обычно используемый для описания параметра. Далее представлен JavaScript-код для чтения всех трех параметров и создания строки, отображаемой в окне уведомления:
function handleMarker(sender, args) {
var strMarkerStatus = args.marker.time.seconds.toString();
strMarkerStatus += " : ";
strMarkerStatus += args.marker.type;
strMarkerStatus += " : ";
strMarkerStatus += args.marker.text;
alert(strMarkerStatus); }
Код на C# очень похож на предыдущий; заметьте, что args типа TimeLineMarkerRoutedEventArgs:
private void handleMarker(object sender, TimelineMarkerRoutedEventArgs e) {
string strMarkerStatus = e.Marker.Time.ToString();
strMarkerStatus += " : ";
strMarkerStatus += e.Marker.Type;
strMarkerStatus += " : ";
strMarkerStatus += e.Marker.Text; }
Как это выглядит, можно увидеть на рис. 11.11.
(рис 11.11) Перехват маркеров временной шкалы в SilverlightМаркеры временной шкалы можно добавлять в медиафайл также динамически, используя код на Silverlight. При этом поисковые метки можно задавать через процентное соотношение положения метки к общей продолжительности файла, например.
Далее представлен пример, в котором XAML для MediaElement определяет функцию handleOpen (Обработка открытия), выполняющуюся при открытии файла мультимедиа. Она вставляет новый объект временной шкалы в видео на 10-секундной отметке. Этот элемент не сохраняется в видео постоянно, только до завершения сеанса.
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width = "200"
MarkerReached = "handleMarker" MediaOpened= "handleOpened" />
Обработчик должен создать новый элемент временной шкалы в XAML и добавить его в коллекцию маркеров MediaElement. В данном случае, задается маркер временной шкалы на отметке 10 секунд, для которого Type определен как 'My Temp Marker' (мой временный маркер) и Text- 'Dynamically Added Marker' (Динамически добавляемый маркер).
Вот как это выглядело бы в JavaScript:
function handleOpened(sender, args) {
var marker =
sender.getHost().content.createFromXam l(
"<TimelineMarker Time='0:0:10'" +
" Type='My Temp Marker'
Text='Dynamically Added Marker' />");
sender. markers.add(marker);
}
И вот аналогичный код в .NET:
private void handleOpened(object sender, RoutedEventArgs e)
{
TimelineMarker t = new TimelineMarker();
t.Time = new TimeSpan(0, 0, 0, 10);
t.Type = "My Temp Marker";
t.Text = "Dynamically Added Marker";
vid.Markers.Add(t);
}
Коллекцию маркеров MediaElement составляют объекты TimeLineMarker (Маркер временной шкалы), поэтому для введения нового маркера просто создается новый объект TimeLineMarker, задаются его свойства Time, Type и Text, и этот объект добавляется в коллекцию. Silverlight имеет достаточно развитую логику и знает, когда должно быть сформировано событие, исходя из заданного Time, поэтому при создании новых маркеров нет необходимости добавлять их в порядке расположения на временной шкале.
Теперь, когда MediaElement достигает 10-секундной отметки при воспроизведении, появляется диалоговое окно уведомления, как показано на рис. 11.12.
(рис 11.12) Использование динамически добавляемого маркера
Особенно замечательная возможность Silverlight - использование видео для заливки поверхности с помощью VideoBrush. Сделать это очень просто. Во-первых, необходим элемент мультимедиа, который загрузит видео. Он должен быть скрыт и не должен принимать события мыши. Для этого его прозрачность задается равной 0 и свойству IsHitTestVisible (Объект виден для механизма обработки кликов) присваивается значение false. Также элементу мультимедиа должно быть задано имя посредством свойства x:Name. Рассмотрим пример:
<MediaElement x:Name="vid" Source="balls.wmv" Opacity="0" IsHitTestVisible="False" />
Теперь объект VideoBrush можно применять к любому объекту точно так же, как любую другую кисть. В качестве источника кисти задается MediaElement (вот почему ему обязательно должно быть присвоено имя). Также можно задать свойство Stretch для управления визуальными эффектами кисти.
Например, здесь представлен TextBlock, для закрашивания Foreground которого используется Video-Brush:
<TextBlock FontFamily="Verdana" FontSize="80"
FontWeight="Bold" TextWrapping = "Wrap"
Text="Video">
<TextBlock.Foreground >
<VideoBrush SourceName="vid"/>
</TextBlock.Foreground >
</TextBlock>
Silverlight теперь будет формировать визуальное представление текста, используя VideoBrush. На рис. 11.13 показано, как это будет выглядеть.
(рис 11.13) Использование VideoBrush для закрашивания текстаКак было показано в данном курсе до сих пор, Silverlight расширяет возможности следующего поколения Веб-приложений, предоставляя инструменты для добавления насыщенных видео, аудио, векторной графики, анимации и других элементов, которые улучшают UX. Рукописные примечания - создание рукописного или нарисованного от руки содержимого с использованием устройств, разработанных для этих целей, - еще один замечательный способ сделать приложения еще более интерактивными и индивидуальными. Обеспечиваемая Silverlight поддержка программирования рукописного содержимого приносит эту функциональность в Веб. В данном разделе мы рассмотрим поддержку рукописных примечаний в Silverlight.
Существует несколько разных типов устройств, которые могут использоваться для приложений с поддержкой рукописных примечаний:
Ink (Рукописный фрагмент), который может быть интегрирован в Веб-страницы Silverlight, таким образом, обеспечивая поддержку рукописного ввода, рисования, рукописных примечаний и прочих форматов ввода в Веб.На сайте http://silverlight.net предлагается замечательный пример приложения, поддерживающего рукописные примечания. Это приложение для чтения книг можно найти по адресу http://silverlight.net/samples/1.0/Page-Turn/default.html и увидеть на рис. 11.14.
Данное приложение демонстрирует, как можно загружать источник данных и просматривать изображения и другие ресурсы, имитируя перелистывание страниц книги. Оно расширено возможностью введения рукописных примечаний, что позволяет комментировать изображения, и добавляемые комментарии остаются связанными с изображением. Пример страницы с комментарием показан на рис. 11.15.
(рис 11.14) Пример PageTurn на Silverlight
(рис 11.15) Комментирование изображения в Silverlight
Поддержка рукописного ввода в Silverlight очень проста. Каждый раз при проведении устройством ввода по экрану формируется один или более объектов StylusPoint (Штрих стилуса). Эти объекты объединяются в коллекцию StylusPointCollection (Коллекция штрихов стилуса), которая формирует основу Stroke. Обводки собираются в коллекцию StrokeCollection (Коллекция обводок). Эта коллекция составляет список графических элементов, используемых для создания рукописного ввода, визуальное предоставление которого формирует InkPresenter. Итак, если взглянуть на рис. 11.15, буква "С" в слове - это обводка, образованная рядом точек, объединенных в StrokeCollection. Обе буквы "О", буква "L", линия и точка в восклицательном знаке, и линии, образующие стрелку и круг, - все это обводки. Каждая из этих обводок является участником коллекции StrokeCollection в InkPresenter, и каждая из них образована объектами StylusPoint, входящими в состав коллекции StylusPointCollection соответствующей обводки. Каждый из этих типов предоставляет объектно-ориентированный интерфейс со свойствами и методами, обеспечивающими возможность их программирования. Рассмотрим основные типы и некоторые их свойства и методы.
Объект InkPresenter имеет свойство Strokes типа StrokeCollection. Эта коллекция, в свою очередь, содержит все метаданные, необходимые для представления рукописного ввода пользователя.
При использовании JavaScript для программирования Silverlight, StrokeCollection предоставляет свойство Count (Количество), которое возвращает количество обводок, находящихся в коллекции в настоящий момент.
StrokeCollection предоставляет следующие методы:
Add ( Добавить ) Позволяет добавлять новую обводку в коллекцию.Clear ( Очистить ) Очищает коллекцию обводок, что приводит к немедленному повторному формированию визуального отображения (т.е. удалению рукописного фрагмента, созданного ранее).GetBounds ( Получить границы ) Возвращает прямоугольник (в виде структуры Rect ), который представляет блок, ограничивающий обводки.GetItem(индекс) ( Получить элемент ) Обеспечивает извлечение обводки, хранящейся под заданным индексом.HitTest ( Проверка совпадения ) Если этот метод передается в StylusPointCollection, он возвращает подмножество обводок в StrokeCollection, которые пересекаются с данными штрихами.Insert ( Вставить ) Аналогичен Add за исключением того,что позволяет вставлять новые обводки в коллекцию по заданному индексу.Remove ( Удалить ) Обеспечивает удаление заданного элемента из StrokeCollection.StrokeCollection по заданному индексу.Представленный в предыдущем разделе StrokeCollection - это коллекция объектов Stroke. Объект Stroke представляет коллекцию штрихов обводки; каждый штрих соответствует одному движению стилуса: стилус опускается (
Объект Stroke предоставляет следующие свойства:
DrawingAttributes ( Атрибуты рисования ) Каждая обводка имеет собственную независимую высоту, ширину, цвет и цвет контура. Эти параметры задаются посредством типа DrawingAttributes. Чтобы использовать этот тип, создается экземпляр объекта DrawingAttributes, задаются значения свойств, и новый объект DrawingAttributes добавляется в обводку.StylusPoints ( Штрихи стилуса ) Это StylusPointCollection, содержащий коллекцию объектов StylusPoint, образующих обводку. StylusPoint обсуждается в следующем разделе.Объект Stroke обладает следующими методами для работы с рукописным вводом:
GetBounds Возвращает Stroke в виде структуры Rect.HitTest Если передать этот метод в StylusPointCollection и если какой-либо из этих штрихов пересекает Stroke, метод возвратит true; в противном случае, возвращается false.Эта коллекция объединяет в себе объекты StylusPoint. Она используется для хранения штрихов стилуса для Stroke или, в некоторых случаях, может передаваться в такие методы, как HitTest, для выявления пересечения обводок.
StylusPointCollection предоставляет свойство - Count, которое возвращает количество штрихов стилуса в данной коллекции.
Объект StylusPointCollection имеет следующие методы:
Add Этот метод позволяет добавлять новый StylusPoint в конец коллекции.AddStylusPoints ( Добавить штрихи стилуса ) Этот метод позволяет добавлять существующий объект StylusPointCollection в конец данной коллекции.Clear Этот метод удаляет все объекты StylusPoint из коллекции.GetItem Этот метод получает заданный StylusPoint из коллекции. Используется со значением типа integer, представляющим индекс интересующего элемента, например, getItem(5 ).Insert Этот метод вставляет новый StylusPoint в коллекцию по заданному индексу.Remove Этот метод удаляет заданный StylusPoint из коллекции.RemoveAt Этот метод удаляет из коллекции StylusPoint по заданному индексу.Тип StylusPoint представляет отдельный штрих, создаваемый, когда пользователь выполняет ввод с помощью устройства рукописного ввода: пера, мыши или сенсорного экрана. Штрих предоставляет ряд свойств и методов.
StylusPoint предоставляет следующие свойства:
Name позволяет задавать имя штриха. Это уникальный идентификатор. Когда штрихи формируются в процессе ввода пользователем, имена им не присваиваются.PressureFactor (Коэффициент нажима) обозначает нажим, с которым пользователь водит пером или касается экрана при формировании штриха. При использовании мыши применяется нажим по умолчанию. Это значение типа double в диапазоне от 0.0 до 1.0, значение по умолчанию - 0,5. На основании коэффициента нажима можно менять высоту и ширину Stroke программно через свойство DrawingAttributes, чтобы предоставить обратную связь пользователю.Координаты обводки можно получить с помощью свойств X и Y. Они измеряются в пикселах.
При использовании рукописного ввода формируемые устройством ввода события трактуются как события мыши, и аргументы, получаемые обработчиками событий, будут типа MouseEventArgs.
Объект MouseEventArgs содержит методы, которые позволяют запрашивать сведения об устройстве ввода (является ли оно мышью, стилусом или каким-то другим устройством) и коллекцию объектов StylusPoint, связанную с этим событием.
Обратите внимание, что это тот же объект MouseEventArgs, который используется с событиями и MouseMove.
Объект MouseEventArgs предоставляет следующие свойства Булевого типа: ctrl и shift. Они принимают значение true, если пользователь удерживает соответствующую клавишу при формировании события.
Объект MouseEventArgs предоставляет следующие методы:
GetPosition (Получить положение) принимает в качестве параметра элемент и возвращает объект Point, который представляет координаты х и у курсора мыши относительно этого элемента. Если вызов метода осуществляется без параметра, Point содержит координаты относительно положения элемента управления, сформировавшего событие.GetStylusInfo (Получить сведения о стилусе) возвращает объект StylusInfo (Сведения о стилусе), содержащий информацию о состоянии стилуса. StylusInfo имеет следующие свойства:
1.true в этом случае.
2.GetStylusPoints возвращает копию штрихов стилуса, добавленных в коллекцию с момента формирования последнего события мыши. Это будет объект типа StylusPointCollection.При написании приложений инструментами Microsoft под термином рукописный ввод (Ink) обычно подразумевается надпись или рисунок от руки, создаваемый пользователем с помощью описанных ранее устройств рукописного ввода, таких как цифровое перо, сенсорный экран или мышь. При использовании в приложении Silverlight эти устройства обеспечивают заполнение объекта StrokeCollec-tion отдельными объектами Stroke. В свою очередь, Stroke выполняет запись действий устройства, например пера, к которым относятся действия: перо опускается (pen-down), перо перемещается (pen-move) и перо поднимается (pen-up). Stroke может представлять точку, прямую линию или кривую. Это осуществляется с помощью объекта StylusPointCollection, который содержит объекты StylusPoint, создаваемые преобразователем графической информации, связанным с пером, DrawingAttributes.
Как говорилось, Silverlight принимает элементы рукописного ввода с помощью класса InkPresenter. Это подкласс элемента Canvas, который также содержит коллекцию обводок в StrokeCollection. При добавлении обводок в StrokeCollection, InkPresenter автоматически формирует их визуальное представление, используя соответствующие DrawingAttributes.
Обычно InkPresenter добавляется в XAML приложения во время разработки дизайна, но объекты Stroke, составляющие StrokeCollection, будут добавляться во время выполнения с помощью JavaScript.
Далее представлен пример использования перекрывающего изображение InkPresenter на странице:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xaml">
<Image Source="sushi.jpg" Width="600" Height="400"></Image>
<InkPresenter x:Name="inkEl" Background="transparent"
Width="600" Height="400"
MouseLeftButtonDown="inkMouseDown"
MouseMove="inkMouseMove"
MouseLeftButtonUp="inkMouseUp"/>
</Canvas>
InkPresenter определяет обработчики событий MouseLeftButtonDown, MouseMove и MouseUp. Мы рассмотрим, как это делается в JavaScript, для чего создадим простое приложение для рукописного ввода. Затем далее в этом разделе то же самое будет сделано на C#. Сходство необыкновенное! Для этих событий понадобятся функции-обработчики действий "начало рукописного ввода", "рукописный ввод" и "прекращение рукописного ввода".
Прежде чем перейти к функциям-обработчикам событий JavaScript, требуется выполнить некоторые вспомогательные операции по объявлению глобальных переменных, необходимых для поддержки этих действий:
var theInk; // Ссылка на модуль отображения рукописного ввода
var newStroke; // Ссылка на обводку
var theControl; // Ссылка на элемент управления Silverlight
function handleLoad(control, userContext, rootElement)
{
// Событие Load возвращает ссылку на элемент управления,
// но другие обработчики событий нет. Поэтому мы собираемся
// создать ссылку на элемент управления здесь
theControl = control;
// Здесь мы создадим ссылку на элемент рукописного ввода
theInk = control.content. findName("inkEl");
}
Эти вспомогательные переменные для InkPresenter, текущей обводки и самого элемента управления Silverlight будут использоваться функциями, поддерживающими описанные выше действия рукописного ввода. При загрузке элемент управления Silverlight запускает функцию handleLoad. В качестве параметра она принимает ссылку на элемент управления Silverlight, но поскольку реализованные нами обработчики событий мыши не делают этого, данную ссылку необходимо сохранить в handleLoad. В ходе выполнения handleLoad можно также получить ссылку на InkPresenter, проведя его поиск по имени ( in-kEl ). Тогда не придется вызывать getHost для получения ссылки на родительский элемент UI, чтобы найти InkPresenter, при каждом выполнении обработчика событий.
Теперь можно приступать к рассмотрению обработчиков событий. Сначала разберемся, что происходит при формировании события MouseLeftButtonDown, результатом чего является выполнение функции inkMouseDown (Нажатие кнопки мыши при рукописном вводе). Мы будем отслеживать перемещения мыши аналогично тому, как это происходило при обработке перемещения методом "drag-and-drop", что рассматривалось в лекц. 6. При получении входных данных от мыши создается новый объект Stroke, содержащий объект DrawingAttributes, который определяет визуальные характеристики этой обводки. В представленном здесь примере DrawingAttributes будет обеспечивать следующие параметры обводки: Width - 2, Height-2, цвет заливки - White (Белый), и цвет контура - White. Тип MouseEven-tArgs в Silverlight поддерживает метод getStylusPoints, как было показано ранее в этой лекции, который принимает InkPresenter в качестве единственного параметра. Этот метод возвращает тип StylusPointCol-lection, который используется в методе AddStylusPoints обводки. Теперь можно добавить обводку в StrokesCollection InkPresenter. Посмотрим на код:
function inkMouseDown(sender,args)
{
// Перехватываем событие мыши.
theInk.CaptureMouse();
// Создаем новую обводку.
newStroke = theControl.content.createFromXaml('<Stroke/>');
// Задаем новый элемент атрибутов рисования для обводки.
// Он, как следует из его имени, определяет внешний вид обводки
var da = theControl.content.CreateFromXaml('<DrawingAttributes/>');
newStroke.DrawingAttributes = da;
// Теперь, когда обводка имеет атрибуты рисования,
// зададим их значения...
newStroke.DrawingAttributes.Width = 2;
newStroke.DrawingAttributes.Height = 2;
newStroke.DrawingAttributes.Color = "White";
newStroke.DrawingAttributes.OutlineColor = "White";
newStroke.StylusPoints.Add StylusPoints(args.GetS tylusPoints(theInk));
theInk.Strokes.Add (newStroke);
}
Теперь перемещение курсора мыши по холсту в состоянии отрисовки обводки (т.е. когда newStroke не null ) означает добавление новых штрихов в эту обводку и отображается в виде следа, оставляемого мышью. Вот код, реализующий это:
// Добавляем новые штрихи в Stroke, с которым работаем в данный момент.
function inkMouseMove(sender,args)
{
if (newStroke != null)
{
newStroke.StylusPoints.AddStylusPoints(args.GetStylusPoints(theInk));
}
}
Наконец, по завершении отрисовки обводки, когда вы отпускаете кнопку мыши (или отрываете перо от экрана), формируется событие MouseLeftButtonUp. После этого требуется очистить обводку и прекратить отслеживание мыши. После того, как переменной newStroke задается значение null, движение мыши (или пера) по экрану больше не будет обеспечивать добавление штрихов в обводку, и, следовательно, обводка не будет отрисовываться. Рассмотрим код:
function inkMouseUp(sender,args)
{
// Задаем newStroke значение null
newStroke = null;
// Прекращаем отслеживать перемещения мыши
theInk.releaseMouseCapture();
}
На рис. 11.16 показан пример приложения до добавления в него рукописного примечания. На рис. 11ly представлено то же приложение с рукописным примечанием, которое было отрисовано с помощью мыши или пера.
(рис 11.16) Выполнение приложения Silverlightc возможностью рукописного ввода
(рис 11.17) Приложение Silverlight с возможностью рукописного ввода с рукописным примечаниемВ предыдущем примере было продемонстрировано, как программировать рукописный ввод, используя Silverlight и JavaScript. Конечно же, то же самое можно сделать с помощью управляемого кода. Вот полный выделенный код на C#, обеспечивающий создание такого же приложения:
using System;
using System. Collections.Generic;
using System.Linq;
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;
using System.Windows.Ink;
namespace NetInkSample
{
public partial class Page : UserControl
{
Stroke newStroke; public Page()
{
InitializeComponent();
}
private void inkEl MouseLeftButtonDown( object sender, MouseButtonEventArgs e)
{
inkEl.CaptureMouse();
// Создаем новую обводку.
newStroke = new Stroke();
DrawingAttributes da = new DrawingAttributes();
newStroke.DrawingAttributes = da;
newStroke.DrawingAttributes.Width=2;
newStroke.DrawingAttributes.Height=2;
newStroke.DrawingAttributes.Color= Colors.White;
newStroke.DrawingAttributes.OutlineColor= Colors. White;
newStroke. StylusPoints.Add(e.Sty lusDevice.GetStylusPoints(inkEl));
inkEl.Strokes.Add (newStroke); }
private void inkEl MouseMove(object sender, MouseEventArgs e) {
if (newStroke != null) {
newStroke.StylusPoints.Add ( e.Sty lusDevice. GetS tylusPoints(inkEl));
}
}
private void inkEl MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
newStroke = null; inkEl.ReleaseMouseCapture();
}
}
}
И в XAML-коде, соответственно, необходимо сделать изменения:
<Image Source="sushi.jpg" Width="600" Height="400">
</Image>
<InkPresenter x:Name="inkEl" Background="transparent"
Width="600" Height="400"
MouseLeftButtonDown="inkEl MouseLeftButtonDown"
MouseMove="inkEl MouseMove"
MouseLeftButtonUp="inkEl MouseLeftButtonUp"/>
Кроме демонстрации гибкости Silverlight, который позволяет разрабатывать приложения как на языке сценариев JavaScript, так и на компилируемых языках программирования .NET, это также показывает простоту перехода от JavaScript к .NET, что позволяет без труда обновить существующие Silverlight-приложения на JavaScript до Silverlight 2 и перейти к управляемому коду.
MultiScaleImage, который, как следует из его имени, предоставляет возможность управлять размером и масштабом изображений, a Silverlight, в свою очередь, обеспечивает огромное виртуальное пространство, в котором эти изображения могут отрисовываться.
Лучше всего возможность
(рис 11.18) Изображение в Deep ZoomПока что ничего сверхъестественного, казалось бы, обычная фотография. Так чем же вызван весь этот шум вокруг
(рис 11.19) Исходное изображение в другом масштабеПосмотрите внимательно на апельсин на рис. 11.19. В его центре можно увидеть изображение с рис. 11.18. Увеличьте масштаб, и вы опять получите изображение, представленное на рис. 11.20.
(рис 11.20) Еще более глубокое масштабированиеКак видите, все второе изображение - не более чем пиксел на третьем (оно находится в зрачке глаза на рис. 11.20), а размер первого изображения всего несколько пикселов на втором рисунке. По этим нескольким статическим рисункам сложно оценить возможность
Но как создать приложение, которое позволит уменьшать и увеличивать масштаб изображений таким образом? Базовое приложение для этого написать довольно просто. Возьмем элемент управления MultiScaleImage и ассоциируем его с файлом, содержащим метаданные изображений. Этот файл создается в редакторе
(рис 11.21) Редактор Deep ZoomЭтот инструмент выполняет простую последовательность операций, включающую Import (Импорт), за которым следует Compose (Компоновка) с последующим Export (Экспорт). Итак, начинаем с вкладки Import, щелкаем Add Image (Добавить изображение), чтобы выбрать изображение, и повторяем этот
шаг для всех изображений, которые вы хотите использовать. На рис. 11.21 можно увидеть, что выбраны три изображения.
Следующий шаг - Compose, который осуществляется с помощью опций на вкладке Compose. На этой вкладке изображения размещаются на соответствующих поверхностях. Вы можете менять масштаб изображений, что разместить в них другие изображения. Например, на рис. 11.22 можно увидеть, что я взял одно из изображений и увеличил его масштаб так, что на экране виден только глаз.
(рис 11.22) Компоновка изображений для просмотра с разными уровнями увеличения в редакторе Deep ZoomТеперь, следующее изображение с его нормальным разрешением будет помещено на данное, масштаб которого увеличен. Итак, если посмотреть на рис. 11.22, вы увидите, куда вставлено второе изображение (в зрачок глаза). Позже, при выполнении приложения, увидеть это изображение можно будет, только увеличив первое. На рис. 11.23 показано, куда помещается второе изображение на первом.
(рис 11.23) Добавление нового изображения на увеличенное первоеЭто простой пример, мы просто добавили одно изображение, которое можно увидеть, только увеличив первое.
(рис 11.24) Экспорт метаданных Deep ZoomЧтобы экспортировать метаданные изображений, скомпонованных в SparseImageSceneGraph.xml и является конфигурационным файлом, который определяет каждое изображение и его местоположение на других изображениях с разными уровнями увеличения. Например, рассмотрим граф сцены для XAML с двумя изображениями:
<?xml version = "1.0"?> <SceneGraph version = "1"> <AspectRatio>1.33333333333334</AspectRatio> <SceneNode> <FileName>C:\Code\SLBook\Chapter11\DZCSample \source images\ dzcsample\DSCN2961JPG</FileName> <x>0</x> <y>0</y> < Width >1</Width> <Height>1</Height> <ZOrder> 1 </ZOrder> </SceneNode> <SceneNode> <FileName>C:\Code\SLBook\Chapter11\DZCSample \source images\dzcsample\DSCN2959JPG</FileName> <x>0.451782754964542</x> <y> 0.313488814592021</y> < Width >0.00099432659277551</Width> <Height>0.00099432659277551</Height> <ZOrder>2</ZOrder> </SceneNode> </SceneGraph>
Как видите, это довольно простой XAML-код. В нем указывается соотношение сторон основного изображения (полученное, исходя из размеров первого изображения), и каждое изображение превращается в SceneNode (Узел сцены). Первое изображения является первым SceneNode. Оно располагается в позиции (0,0) и является нормализованным изображением, т.е. его ширина и высота принимаются за 1. Размеры и расположение всех остальных изображений устанавливаются относительного первого.
Второе изображение размещается в точке с координатами 0,45 по оси х и 0,31 по оси у. Его размер составляет, примерно, 0,00099 размеров первого изображения. Таким образом, если увеличить первое изображение, примерно, в 10000 раз, можно будет увидеть второе изображение. Его Z-порядок равен 2 (Z-порядок первого изображения - 1), т.е. он будет отрисовываться поверх первого изображения.
Кроме того, редактор
Другой экспортируемый файл будет называется dzc output.xml. В нем содержатся все сведения о фрагментах и их положении относительно основного изображения. Он будет располагаться в каталоге, созданном редактором
Чтобы использовать ClientBin в проекте Веб-приложения. Сделав это, закройте решение.
Теперь в Windows Explorer скопируйте каталог, содержащий файл dzc output.xml и подпапки с фраг-ментированными изображениями, в папку ClientBin Веб-приложения. Сделав это, вновь откройте решение. Solution Explorer вашего проекта будет выглядеть примерно так, как показано на рис. 11.25.
(рис 11.25) Добавление данных Deep Zoom в Веб приложениеТеперь, чтобы сформировать визуальное представление содержимого MultiScaleImage в Page.xaml и в качестве значения его свойства Source задать путь к файлу dzc output.xml (в данном случае, этот файл располагается в /dzcsample/dzc output.xml ), а также необходимые ширину и высоту. Посмотрим, как будет выглядеть ваш Page.xaml:
<UserControl x:Class="DZCSampleApp.Page" xmlns="http://schemas.microsoft.com/client/2007" xm lns:x=" http://schemas.microsoft.com/winfx/2006/xam l" Width = "400" Height="300"> <Canvas> <MultiScaleImage Source="dzcsample/dzc_output.xml" Height="300" Width="400" /> </Canvas> </UserControl>
Итак, при выполнении этого приложения элемент управления MultiScaleImage будет формировать визуальное представление верхнего элемента в SceneGraph, увеличивая его от оригинального масштаба изображения до заданных ширины и высоты (в данном случае, 400 * 300).
Как видите, в приложении нет автоматической реализации управления мышью, поэтому мы не можем перемещаться по изображению или менять его масштаб. Как это делается, будет рассмотрено в следующем разделе
Как и все остальные элементы управления Silverlight, MultiScaleImage может объявлять функции для обработки событий. Например, для перемещения изображения используются обычные события мыши: MouseLeftButtonDown, MouseLeftButtonUp и Mouse Move, - и обрабатываются они так же, как это делается при перемещении любого элемента методом "drag-and-drop". Сначала, рассмотрим XAML для MultiScaleImage, определяющий эти события:
<UserControl x:Class="DeepZoomSample.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xa ml/presentation" xm lns:x=" http://schemas.microsoft.com/winfx/2006/xam l" Width = "640" Height="480"> <Canvas> <MultiScaleImage x:Name="dz" Source="dzcsample/dzc_output.xml" MouseLeftButtonDown = "MultiScaleImage MouseLeftButtonDown" MouseLeftButtonUp="MultiScaleImage MouseLeftButtonUp" MouseMove="MultiScaleImage MouseMove" Height="480" Width = "640"></MultiScaleImage> </Canvas> </UserControl>
И теперь рассмотрим каждый из этих обработчиков событий более подробно. Прежде всего, есть код, общий для них всех, который обеспечивает отслеживание текущего состояния мыши и просматриваемых в настоящий момент координат MultiScaleImage:
bool dragging = false; double dx = 0; double dy = 0; Point p0; Point p1; Point pLast;
Теперь посмотрим, что происходит, когда пользователь щелкает изображение кнопкой мыши:
private void MultiScaleImage MouseLeftButtonDown( object sender, MouseButtonEventArgs e)
{
dragging = true;
p0 = dz.ElementToLogicalPoint(new Point(0, 0));
p1 = dz.ElementToLogicalPoint(new Point(640, 480));
dx = 0;
dy = 0;
double x = e.GetPosition(null).X;
double y = e.GetPosition(null).Y;
pLast = dz.ElementToLogicalPoint(new Point(x, y));
}
Предполагается, что пользователь удерживает кнопку мыши и двигает мышью, поэтому задаем Буле-вому свойству dragging (перетягивание) значение true. Теперь необходимо получить текущие координаты видимой части изображения. Вспомним, что для верхнего изображения задана позиция (0,0), или x = 0 and y = 0, и его высота и ширина заданы равными 1. Это логические координаты и логические размеры.
В Silverlight логические координаты верхнего левого угла и нижнего правого угла окна можно получить с помощью метода ElementToLogicalPoint (Логическая точка элемента), передав в него физические координаты. Итак, представим, что изображение было увеличено и немного передвинуто. Теперь если вызвать этот API для физической точки (0,0), т.е. верхнего левого угла видимой в данный момент части изображения,будет возвращена логическая точка, представляющая положение это точки на полном изображении. Чтобы получить координаты нижнего правого угла, делаем то же самое для точки, координаты которой соответствуют ширине и высоте физического экрана (в данном случае, 640 * 480).
Мы хотим отслеживать изменение размеров относительно осей х и у, поэтому задаем переменным dx и dy значение 0. Зачем это сделано, станет понятно через мгновение.
Кроме того, необходимо получать логические координаты курсора мыши. Для этого вызывается API ElementToLogicalPoint, в него передается текущее положение мыши (которое можно получить из объекта MouseEventArgs, передаваемого в эту функцию) и возвращаемые результаты сохраняются в переменной pLast.
Итак, что происходит, когда пользователь начинает перетягивание, удерживая кнопку мыши нажатой? Мы хотим получить эффект перемещения по изображению. Вот код, реализующий это:
private void MultiScaleImage MouseMove(
object sender, MouseEventArgs e) { if (dragging) { if (e!=null)
{ double x = e.GetPosition(null).X; double y = e.GetPosition(null).Y;
Point pCurrent = dz.ElementToLogicalPoint(new Point(x, y)); if (pLast! = null)
{ dx += (pCurrent.X - pLast.X); dy += (pCurrent.Y - pLast.Y); Point origin =
new Point(p0.X - dx, p0.Y - dy); dz.ViewportOrigin = origin; } pLast = pCurrent; } } }
Событие MouseMove формируется независимо от того, удерживается кнопка мыши или нет, поэтому для обозначения перетягивания используем переменную dragging. Итак, если пользователь выполняет перетягивание, и MouseEventArgs не null, будет выполняться весь остальной код.
В данном случае, мы извлекаем текущие координаты мыши и сохраняем их логические эквиваленты (полученные с помощью метода ElementToLogicalPoint элемента управления) в точке pCurrent. Теперь если pLast не null, значит, пользователь выполняет перетягивание, и мышь переместилась относительно ее предыдущего положения. Итак, чтобы получить изменение логических координат, произошедшее в результате перемещения мыши из предыдущей точки в текущую, мы просто вычисляем разницу (дельту) между предыдущими и текущими координатами относительно обеих осей (х и у). После этого задаем для свойства Origin (Начало отсчета) элемента управления MultiScaleImage исходные координаты с учетом дельта по х и у. Это обеспечит эффект "перемещения" изображения при перемещении мышью, хотя мы, по сути, просто изменяем координаты верхнего левого угла отображаемой части изображения. По завершении перемещения необходимо просто задать положение Last (Последний) как Current, чтобы, когда пользователь вновь начнет двигать мышью, вычисления производились относительно этой точки, а не той, в которой пользователь нажмет кнопку мыши.
Наконец, необходимо, чтобы после того, как пользователь отпустит кнопку мыши, все вернулось в исходное состояние. Сделать это довольно просто. Рассмотрим код:
private void MultiScaleImage MouseLeftButtonUp( object sender, MouseButtonEventArgs e) {
dragging = false;
p0 = new Point();
p1 = new Point();
pLast = new Point();
dx = 0;
dy = 0; }
Эти три простые функции обеспечивают пользователю возможность перетягивать изображение и перемещаться по нему независимо от степени его увеличения. В следующем разделе будет показано, как с помощью колеса мыши увеличивать и уменьшать изображение и, таким образом, открывать части изображения, скрытые при других степенях увеличения.
Проблемой при создании приложений
Прежде всего, необходимо обеспечить возможность использования API браузера в Silverlight, поэтому не забудьте включить в код следующую строку:
using System.Windows.Browser;
Затем в конструкторе Page элемент управления должен быть зарегистрирован как поддерживающий сценарии. Методы элементы управления, обозначенные как ScriptableMember (Участник, поддерживающий сценарии), доступны из JavaScript. Вот как это делается:
public Page() {
InitializeComponent();
HtmlPage.RegisterScriptableObject("MySilverlightObject", this); }
Далее требуется перехватывать события колеса мыши на этой странице. Для этого сначала добавляем вызов handleLoad в тег <Body>, чтобы обеспечить выполнение JavaScript-кода при формировании визуального представления страницы:
<body style=" height: 100%; margin :0;" onload = "handleLoad();"> ... </body>
Затем в JavaScript-функции handleLoad задается событие:
function handleLoad()
{
window.onmousewheel = document.onmousewheel = onMouseWheel;
if (window.addEventListener)
{
window.addEventListener('DOMMouseScroll', onMouseWheel, false);
}
}
Тем самым определяем, что JavaScript-функция onMouseWheel (При прокручивании колеса мыши) будет выполняться при любом прокручивании колеса мыши.
function onMouseWheel()
{
if(!event)
{ event = window.event;
}
var slPlugin = $get("Xaml1");
slPlugin.content.MySilverlightObject.dz_MouseWheel( event.clientX, event.clientY, event.wheelDelta);
}
Эта функция просто принимает ссылку на элемент управления Silverlight (в данном случае, при создании ему присваивается имя Xaml1) и затем вызывает функцию выделенного кода, определенную для него. Для вызова используется синтаксис <ИмяКомпонента>.content.<ИмяОбъекта>.<ИмяФункции>.
<ИмяКомпонента> - это переменная, определенная в JavaScript как ссылка на объект Silverlight. <ИмяОбъекта> - это имя, заданное для объекта при его регистрации (ее можно увидеть в конструкторе Page() ). <ИмяФункции> - это имя вызываемой функции. Эта функция должна быть соответствующим образом описана, чтобы JavaScript "видел" ее. В данном случае, вызывается функция dz MouseWheel, рассмотрим ее:
[ScriptableMember]
public void dz MouseWheel(double x, double y, int delta)
{
double dZoomFactor=1.33;
if (delta < 0) dZoomFactor= 1/1.33;
Point pz = dz.ElementToLogicalPoint(new Point(x, y));
dz.ZoomAboutLogicalPoint(dZoomFactor, pz.X, pz.Y);
}
Прежде всего, как видите, она обозначена ScriptableMember, т.е. JavaScript может видеть и вызывать ее. Функция JavaScript передает текущие координаты мыши и дельта, возникающие в результате изменения масштаба. Дельта будет иметь положительное значение при прокручивании колеса вперед и отрицательное значение при прокручивании назад.
Масштабный коэффициент каждого прокручивания колеса задан равным 33% (но он может быть любым), таким образом, переменной dZoomFactor (Масштабный коэффициент) задается значение 1.33 при увеличении и 1/1.33 при уменьшении масштаба.
Далее требуется, чтобы изменение размеров происходило вокруг текущего положения курсора мыши на изображении. Это очень просто сделать, преобразовав координаты курсора мыши в логическую точку с помощью метода ElementToLogicalPoint элемента управления MultiScaleImage. Имея координаты и коэффициент масштабирования, вызываем метод ZoomAboutLogicalPoint (Масштабирование вокруг логической точки), чтобы обеспечить изменение размеров MultiScaleImage.
Кроме возможности увеличения и уменьшения изображения, можно также создавать коллекции изображений и работать с ними в масштабируемой среде.
Возможно, вы заметили, что при экспорте изображений из редактора
Для работы с изображениями коллекции Silverlight предлагает коллекцию SubImages (Множество изображений). Обратите внимание, что при формировании визуального представления страницы полный набор изображений не будет загружаться, поэтому эта коллекция будет пустой в этот момент. Чтобы иметь возможность работать с коллекцией, необходимо описать событие ImageOpenSucceeded (Изображение открыто успешно). Это событие будет формироваться в случае успешной загрузки и отображения.
(рис 11.26) Создание коллекции Deep ZoomРассмотрим XAML:
<MultiScaleImage x:Name="dz"
Source="dzcsample/dzc_output .xml"
ImageOpenSucceeded = "dz ImageOpenSucceeded"
Height="480" Width="640">
</MultiScaleImage>
В этот момент коллекция SubImages будет заполнена и может использоваться для работы с отдельными рисунками, ее составляющими. Рассмотрим пример изменения месторасположения изображений:
private void dz ImageOpenSucceeded(object sender, RoutedEventArgs e)
{
int nImages = dz.SubImages.Count;
for (int lp = 0; lp < nImages; lp++)
{
dz.SubImages[lp].ViewportOrigin = new Point(lp*0.1, lp*0.1);
}
}
Как видите, процесс абсолютно аналогичен тому, который использовался ранее для работы с одним рисунком. В данном случае, каждое изображение перемещается путем использования SubImages[lp,], где lp - это переменная цикла, которая может принимать значения от 0 до числа, соответствующего количеству изображений в коллекции. Результаты представлены на рис. 11.27. Обратите внимание на разницу между этой и исходной компоновками, которая представлена на рис. 11.26.
(рис 11.27) Компоновка коллекции изображений
В этой длинной лекции представлены три наиболее мощных элемента управления, предлагаемые Silver-light 2. Сначала был подробно рассмотрен элемент управления MediaElement и то, как можно использовать Silverlight для воспроизведения и трансформации видео- и/или аудиосодержимого, включая программирование прозрачности, маркеры, динамические маркеры и т.д. Затем вы познакомились с рукописными примечаниями и устройствами, поддерживающими их, а также тем, как Silverlight может обеспечить поддержку рукописного ввода на Веб-страницах. Наконец, вашему вниманию была представлена технология
В предыдущих лекциях вы познакомились с основными элементами управления, используемыми для создания насыщенных интерактивных Интернет-приложений. В данной главе будет рассмотрено три элемента управления, предоставляющие специальную функциональность. Сначала будет представлен элемент управления MediaElement, который обеспечивает API для управления аудио- и видеоданными, позволяя тем самым создавать замечательные мультимедийные приложения. Затем вы рассмотрите элемент управления InkPresenter (Элемент отображения рукописного ввода), который позволяет вводить рукописное или рисованное содержимое посредством рукописных примечаний, т.е. обеспечивает возможность использования приложения на компьютерах с перьевым вводом. Наконец, вашему вниманию будет представлен достаточно новый элемент управления Silverlight 2, MultiScaleImage (Масштабируемое изображение), который добавляет в ваши приложения новое измерение - возможность глубокого масштабирования.
Одно из наиболее важных применений Silverlight в Веб - обеспечение поддержки мультимедиа следующего поколения, которое может воспроизводиться на разных платформах. Для этого Silverlight поддерживает элемент управления MediaElement, которому посвящен данный раздел. В нем поэтапно рассматривается процесс создания простого медиаплейера, обеспечивающего возможность прогрессивной загрузки и воспроизведения видеоданных. Кроме того, вы научитесь закрашивать поверхности с помощью видео-кисти, что позволит добавлять интересные графические эффекты. Элемент управления MediaElement поддерживает следующие форматы:
Видео
Аудио
Кроме этих форматов, элемент управления MediaElement также поддерживает списки воспроизведения ASX и протоколы http, https и .
Если говорить о потоковой передаче видео- и/или аудио, MediaElement поддерживает потоковое вещание и потоковую передачу по запросу от Windows Media Server. Если для URI задан протокол , потоковая передача включена; в противном случае, загрузка и воспроизведение файла будут осуществляться путем прогрессивной загрузки, при которой воспроизведение начинается только после заполнения буфера воспроизведения и затем эти два процесса выполняются параллельно.
Если определен протокол http или https, происходит обратное. MediaElement сначала делает попытку выполнить прогрессивную загрузку, и уже в случае неудачи пытается применить потоковую передачу файла.
Элемент управления MediaElement в MediaElement.
Чтобы начать работу с элементом управления MediaElement, добавьте его на свою страницу и задайте в качестве значения его атрибута Source URL видеофайл, который хотите воспроизвести. Рассмотрим пример XAML-кода Silverlight 2 приложения:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SilverlightApplication5.Page"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" Background="White">
<MediaElement Source="balls.wmv" />
</Grid>
</UserControl>
Это обеспечит автоматическую загрузку и воспроизведение видеоданных. Размер области воспроизведения мультимедиа определяется следующими правилами:
Height и Width элемента управления MediaElement, MediaElement использует их.MediaElement растянет мультимедиа, сохраняя пропорции изображения.Height, ни Width, элемент управления MediaElement будет воспроизводить видео в стандартных размерах. Если они превышают размер области просмотра элемента управления Silverlight, MediaElement обрежет видео соответственно размеру области просмотра.Рассмотрим пример. Используемая в данной лекции (и доступная для скачивания на Веб-сайте компании) видеозапись balls. имеет размер 480 х 360. Если указать MediaElement воспроизводить это видео и не задать его Height и Width, воспроизводимое видеоизображение будет размером 480 х 360. Если у UserControl выставить размер 200 х 200, на экране будет видна только часть видеоизображения размером 200 х 200 пикселов, начиная от верхнего левого угла. Обрезанная таким образом часть видео представлена на рис. 11.1.
(рис 11.1) Видеоизображение, обрезанное по размеру элемента управления Silverlight
Как было показано в предыдущем разделе, размер MediaElement важен при воспроизведении видео. Если размеры элемента управления не заданы, и видеоизображение превышает размеры элемента управления Silverlight, оно будет обрезано.
Управление шириной и высотой самого MediaElement осуществляется через его свойства Height и Width. При формировании визуального представления элемента управления, мультимедиа будет растягиваться (или сжиматься) так, чтобы поместиться в элемент управления. Если заданный размер элемента управления мультимедиа больше элемента управления Silverlight, изображение будет обрезано размерами элемента управления Silverlight.
Далее представлен пример элемента управления MediaElement, для которого заданы размеры 200 х 200. На рис. 11.2 можно увидеть, как будет отображаться видео:
<MediaElement Source="balls.wmv"
Height="200" Width="200" />
(рис 11.2) Отображение видео после задания размеров MediaElement
В предыдущем примере видеоизображение сворачивалось до размера элемента управления MediaElement (200 х 200). Как видно на рис. 11.2, размер видеоизображения (исходный размер которого, как вы помните, 480 х 360 пикселов) меняется так, чтобы оно поместилось в MediaElement, но с сохранением его пропорций. Это может привести к появлению черных полей вверху и внизу видеоизображения, создавая эффект просмотра широкоформатного видео. Переопределить это поведение можно с помощью свойства Stretch элемента управления MediaElement. Это свойство принимает четыре разных значения:
Uniform Это режим растяжения по умолчанию, он обеспечивает сохранение пропорций видеоизображения и добавление полей вверху, внизу или по бокам в случае необходимости.Регулировать прозрачность MediaElement позволяет свойство Opacity. Это нормализованное свойство, т.е. значение 0 эквивалентно полной прозрачности, 1 - полной непрозрачности, все остальные значения в этом диапазоне представляют разные степени прозрачности. Видеоизображение будет отображаться с заданной прозрачностью, при этом элементы, находящиеся позади элемента мультимедиа на экране, будут видны.
Далее представлен пример XAML, обеспечивающий отображение красного прямоугольника и MediaElement. Для MediaElement задана прозрачность 0,5, что делает видеоизображение полупрозрачным. Поскольку визуальное представление MediaElement формируется вторым, он размещается ближе в направлении по оси Z и располагается поверх прямоугольника. На рис. 11.6 можно увидеть результат выполнения этого примера.
<Rectangle Fill="Red" Height="100" Width="200" />
<MediaElement Source="balls.wmv" Height="200" Width="200"
Stretch="Fill" Opacity="0.5" />
(рис 11.6) Применение свойства Opacity для видеоизображения
Подробно трансформации рассматриваются в лекц. 5, "Трансформация и анимация в XAML", здесь хотелось бы отметить лишь то, что трансформации могут применяться и к элементу управления MediaElement. При этом отображаемое видео также трансформируется. Это может привести к созданию некоторых очень интересных эффектов. Например, далее представлен MediaElement, к которому применена трансформация наклонение:
<MediaElement Source="balls.wmv" Height="200" Width="200"
Stretch="Fill" >
<MediaElement.RenderTransform> <SkewTransform AngleX="45"/>
</MediaElement.RenderTransform>
</MediaElement>
Как это выглядит, можно увидеть на рис. 11.7.
(рис 11.7) Наклонение видео с помощью SkewTransform
Silverlight позволяет размещать содержимое, включая текст и графические элементы, поверх видеоизображения. Это можно сделать либо с помощью свойств ZOrder (Z-порядок) элементов пользовательского интерфейса (UI) (смотрите лекц. 1, "Введение в Silverlight 2"), либо просто размещать элементы UI в том же месте, что и элемент мультимедиа, и объявлять их в XAML после него. Далее представлен пример MediaElement, и Canvas с прямоугольником и текстовым блоком, который перекрывает видеоизображение:
<MediaElement Source="balls.wmv" Height="200"
Width="200" Stretch="Fill" />
<Canvas Margin="20,140,0,0">
<Rectangle Fill= "Red" Height="40" Width="160" />
<TextBlock>Subtitle on Video</TextBlock>
</Canvas>
На рис. 11.8 показано, как это будет выглядеть на экране.
(рис 11.8) Отображение содержимого поверх видео
В лекц. 4, "Основы XAML" были рассмотрены вырезание и геометрические элементы. Эти возможности также могут применяться к MediaElement, для которого с помощью фигур или контуров можно определить контур вырезания. Например, следующий XAML в качестве контура вырезания для MediaElement определяет эллипс:
<MediaElement Source="balls.wmv" Height="200"
Width="200" Stretch="Fill" >
<MediaElement.Clip>
<EllipseGeometry RadiusX="100" RadiusY="75" Center="100,75"/>
</MediaElement.Clip>
</MediaElement>
Результаты представлены на рис. 11.9.
(рис 11.9) Вырезание мультимедиа геометрическими формами
По умолчанию, если задан источник, MediaElement автоматически воспроизводит мультимедиа. Этим поведением можно управлять посредством свойства (Автоматическое воспроизведение). По умолчанию его значение true, но его можно переопределить и задать false. Можно воспроизвести мультимедиа позже, используя метод Play (Воспроизвести). Этот и другие методы и события, используемые при программировании элемента мультимедиа, рассматриваются в следующем разделе.
С помощью свойства IsMuted (Без звука) элемента управления MediaElement можно определить, будет ли аудиозапись сопровождать воспроизведение или нет. Это свойство Булевого типа, и если задать ему значение true, звук будет отключен.
Кроме того, с помощью свойства Volume (Громкость) можно управлять громкостью звука. Оно принимает нормализованное значение, где 0 эквивалентно отсутствию звука, 1 - полной громкости, и промежуточные значения представляют относительные уровни громкости. Итак, например, значение 0,43 обозначало бы воспроизведение звука с громкостью, составляющей 43% от полной громкости.
Наконец, балансом звука можно управлять с помощью свойства Balance (Баланс), которое принимает значения в диапазоне от -1 до +1. Значение -1 приводит к смещению баланса звука полностью влево, т.е. левый динамик будет воспроизводить звук со 100% громкостью, и в правом динамике будет 0% громкости. Значение +1 обеспечивает прямо противоположный эффект - баланс звука будет полностью смещен вправо, при этом правый динамик будет воспроизводить звук со 100% громкостью. Значение 0 обеспечивает равномерное распределение громкости между двумя динамиками.
Например, свойству Balance задано значение 0,8, тогда правый динамик будет воспроизводить звук с 80% громкостью и левый динамик- с 20%. Если используется значение -0,8, левый динамик будет воспроизводить звук с громкостью 80%, и правый динамик - с громкостью 20%.
Далее представлен некоторый XAML, определяющий, что звук не выключен, общая громкость составляет 50% и что баланс звука смещен вправо:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch = "Fill"
IsMuted = "False" Volume="0.5" Balance="0.8" />
MediaElement предлагает насыщенную модель программирования, которая позволяет управлять воспроизведением методами Play, Stop (Остановить) и Pause (Приостановить). Также вы имеете возможность взаимодействовать с видео, отслеживая процесс буферизации и загрузки, а также отвечать на маркеры, размещенные в видеофайле. Кроме того, можно обработать события для перехвата, такие как поведение мыши.
Основными методами управления видео являются Play, Stop и Pause. Если свойству элемента управления MediaElement задано значение false, для начала воспроизведения видео необходимо применить Play. Даже если задано значение true, и воспроизведение начинается автоматически, с помощью этих методов его можно остановить или приостановить. Далее представлен пример XAML, в котором имеется элемент мультимедиа и три простых элемента управления воспроизведением, реализованных как элементы TextBlock:
<MediaElement x:Name="vid" Source="balls.wmv" Height="200"
Width = "200" Stretch="Fill" />
<Canvas Canvas.Top="160">
<Rectangle Fill= "Black" Width = "200" Height="24" Opacity="0.7"/>
<TextBlock Foreground= "White" Canvas.Left="20">Play</TextBlock>
<TextBlock Foreground="White" Canvas. Left="80">Stop</TextBlock>
<TextBlock Foreground="White" Canvas. Left=" 140" >Pause</TextBlock>
</Canvas>
На рис. 11.10 показано, как эти элементы управления будут выглядеть на видеоизображении.
(рис 11.10) Добавление элементов управления в видеоизображениеДля создания элементов управления вы задаете имя функции, которая должна выполняться в ответ на событие мыши, используя атрибут самого элемента управления видео. Намного более подробно обработка событий Silverlight с помощью JavaScript рассматривается в лекц. 6, "Элемент управления браузера Silverlight". Однако, в данном случае, просто требуется начать, остановить или приостановить воспроизведение видео, когда пользователь щелкнет соответствующий текстовый блок. Это реализуется путем обработки события MouseLeftButtonDown текстового блока, которое ассоциируется с функциями, воспроизводящими, приостанавливающими или останавливающими воспроизведение.
При программировании на JavaScript необходимо обеспечить, чтобы эти функции были доступны со страницы, на которой размещается элемент управления Silverlight. Итак, они могут быть реализованы либо путем использования элементов JavaScript на странице, либо в файле js, который включен в страницу с помощью элемента JavaScript, свойству Src которого задано соответствующее значение.
Если для сведения воедино содержимого XAML используется Expression Blend, он обеспечивает для Page.xaml псевдо-файл выделенного кода Page.xaml.js. Это идеальное место для реализации функциональности JavaScript.
Создадим проект Silverlight 1.0 Site. Далее представлен XAML, описывающий похожий UI, как был создан в предыдущем примере, но с объявлениями обработчиков событий:
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480" Background="White" x:Name="Page">
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch="Fill" />
<Canvas Canvas.Top="160">
<Rectangle Fill="Black" Width="200" Height="24" Opacity="0.7"/>
<TextBlock MouseLeftButtonDown="doPlay"
Foreground="White"
Canvas.Left="20">Play</TextBlock>
<TextBlock MouseLeftButtonDown="doStop"
Foreground="White"
Canvas.Left="80">Stop</TextBlock>
<TextBlock MouseLeftButtonDown="doPause"
Foreground="White"
Canvas.Left="140">Pause</TextBlock>
</Canvas>
</Canvas>
Теперь можно написать кода на JavaScript для начала, остановки и приостановки воспроизведения. Вот этот код:
function doPlay(sender, args)
{
var meVid = sender.findName("vid");
meVid.Play();
}
function doStop(sender, args)
{
var meVid = sender.findName("vid");
meVid.Stop();
}
function doPause(sender, args)
{
var meVid = sender.findName("vid");
meVid.Pause();
}
Когда функция JavaScript определяется как обработчик события, она должна принимать два параметра. Первый, sender, - это объект, который формирует событие. Второй, args, содержит аргументы, которые включаются как часть события.
Теперь в этой JavaScript-функции необходимо получить ссылку на объект MediaElement, управление которым осуществляется. Выполняется это с помощью метода findName объекта sender. Хотя отправителем является текстовый блок, который щелкнул пользователь, выполнение его метода findName все равно обеспечит выполнение поиска по всему XAML-документу до тех пор, пока не будет найден элемент (который был создан путем применения атрибута x:Name к нашему объекту MediaElement ). Если вернуться к XAML, можно увидеть, что в MediaElement был элемент x:Name со значением , так что поиск должен быть успешным. Ссылка на соответствующий объект MediaElement будет сохранена в переменной JavaScript meVid. Теперь для управления воспроизведением осталось просто вызывать методы Play, Stop или Pause для этого объекта.
При создании проекта приложения для Silverlight 2 и использовании .NET все даже еще проще: не надо использовать findName для доступа к элементу, и ему уже присвоено имя , так что код будет выглядеть так:
private void doPlay(object sender, MouseButtonEventArgs e)
{
vid.Play();
}
private void doPause(object sender, MouseButtonEventArgs e)
{
vid.Pause();
}
private void doStop(object sender, MouseButtonEventArgs e)
{
vid.Stop();
}
При использовании прогрессивной загрузки видео подсистема мультимедиа определяет, сколько видеоданных необходимо кэшировать, прежде чем будет возможно воспроизведение. Итак, в зависимости от того, какая полоса пропускания необходима для загрузки видео, и какая полоса пропускания доступна, создается буфер для размещения объема видеоданных, достаточного для начала воспроизведения, в ходе которого загрузка видео продолжается в фоновом режиме.
Когда буфер полон на 100%, начинается воспроизведение. Воспроизведение может прерываться при изменении состояния связи и дозагрузки буфера. Silverlight позволяет отслеживать это поведение с помощью события BufferingProgressChanged (Показатель процесса буферизации изменился) и свойства BufferingProgress (Показатель процесса буферизации). Используя эти событие и свойство, можно показывать пользователям текущее состояние буферизации или выполнять логику для улучшения взаимодействия с пользователем (UX) как результат условий буферизации. Например, в условиях плохого соединения буферизация может не превышать 50%. Вы можете перехватить это значение и предоставить соответствующую обратную связь своему клиенту.
Для управления буферизацией задайте в MediaElement обработчик событий, определяющий функцию для обработки события BufferingProgressChanged:
<MediaElement x:Name="vid" Source="balls.wmv" Height="200" Width="200" Stretch="Fill" Buffering Prog ressChanged = "doBuff"/> <TextBlock x:Name="txtBuff"></TextBlock>
Таким образом, функция doBuff будет выполняться каждый раз при изменении наполненности буфера. Это событие тесно связано со свойством BufferingProgress. Данное свойство принимает значения в диапазоне от 0 до 1, где 0 соответствует пустому буферу, и 1 - полному. Событие будет формироваться при изменении наполнения буфера на 5% (т.е. 0,05) или более и когда буфер заполнен полностью.
Далее представлен код, который может использоваться для ответа на это событие и обеспечения обратной связи о текущем состоянии буфера пользователям.
Сначала JavaScript-версия:
function doBuff(sender, args)
{
var theText = sender.findName("txtBuff");
var meVid = sender.findName("vid");
var prog = meVid.BufferingProgress * 100;
prog = "Buffering % " + prog;
theText.Text = prog;
}
И вот, как это было бы сделано в выделенном коде .NET (на C#):
private void doBuff(object sender, RoutedEventArgs e)
{
double prog = vid.BufferingProgress * 100;
txtBuff.Text = "Buffering % " + prog;
}
Если у вас не видно текст с отображением буферизации в Silverlight-приложении, обратите внимание на используемый элемент компоновки. Если вы использовали <Grid>, текст может находиться под "кнопками" Play, Stop и Pause. В этом случае задайте для текстовых блоков небольшой отступ с помощью свойства Margin.
Можно переопределить автоматическую буферизацию, задавая конкретную продолжительность буферизованного видео. Например, если необходимо всегда иметь 10-секундный буфер видео и, таким образом, снизить риск прерывания воспроизведения из-за повторной синхронизации буфера при ухудшении состояния связи, можно задать свойство BufferingTime (Размер буфера). В качестве его значения задается временной диапазон. Так, например, чтобы задать 10-секундный буфер, BufferingTime задается значение 0:0:10, как показано в следующем примере:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width = "200" Stretch="Fill"
BufferingProgressChanged = "doBuff"
BufferingTime="0:0:10"/>
<TextBlock x:Name="txtDown"></TextBlock>
Если прогрессивная загрузка недоступна или не поддерживается, придется загружать весь видеофайл целиком и только потом начинать его воспроизведение. В этом случае событие DownloadPro-gressChanged и свойство DownloadProgress могут использоваться для получения состояния загрузки. Принципы работы с ними аналогичны функциям буферизации. Дале представлен XAML, определяющий событие DownloadProgressChanged:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch="Fill"
BufferingProgressChanged = "doBuff"
BufferingTime="0:0:10"
DownloadProgressChanged = "doDown"/>
<TextBlock x:Name="txtDown"></TextBlock>
И теперь код функции doDown, обрабатывающей это событие. Опять же, сначала рассмотрим JavaScript-версию:
function doDown(sender, args)
{
var theText = sender.findName("txtDown");
var meVid = sender.findName("vid");
var prog = meVid.DownloadProgress * 100;
prog = "Downloading % " + prog;
theText.Text = prog;
}
И
private void doDown(object sender, RoutedEventArgs e)
{
double prog = vid.DownloadProgress * 100;
txtBuff.Text = "Downloading % " + prog;
}
Silverlight представляет свойство CurrentState (Текущее состояние) и связанное с ним событие Cur-rentStateChanged (Текущее состояние изменилось), которое может использоваться для ответа на изменение состояния мультимедиа.
Действительными состояниями свойства CurrentState являются:
Buffering ( Буферизация ) Буфер не заполнен на 100%, поэтому воспроизведение приостановлено до полного заполнения буфера.Closed ( Закрыто ) Мультимедиа закрыто.Error ( Ошибка ) Возникла проблема с загрузкой, буферизацией или воспроизведением мультимедиа.Opening ( Открывается ) Мультимедийный файл найден, и буферизация или загрузка должны вот-вот начаться.Paused ( Приостановлено ) Воспроизведение приостановлено.Playing ( Воспроизводится ) Идет воспроизведение мультимедиа.Stopped ( Остановлено ) Воспроизведение остановлено.Вот как событие CurrentStateChanged элемента управления MediaElement определяется в XAML:
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch="Fill"
CurrentStateChanged= "doState" BufferingTime="0:0:10" />
Здесь задается функция doState, которая будет вызываться в ответ на изменение текущего состояния. Далее представлен пример такой JavaScript-функции, которая использует значение свойства Cur-rentState элемента управления MediaElement в окне уведомления:
function doState(sender, args)
{
var meVid = sender.findName("vid");
alert(meVid.CurrentState);
}
В C#, вместо окна уведомления MessageBox, для вывода содержимого свойства CurrentState на экран можно использовать TextBlock:
private void doState(object sender, RoutedEventArgs e)
{
txtBuff.Text = vid.CurrentState.ToString();
}
Для управления текущим положением курсора воспроизведения используются свойства NaturalDuration (Номинальная продолжительность) и Position (Положение) элемента мультимедиа. После того, как свойство CurrentState устанавливается в значение Opened, становится возможным получение значения и свойства NaturalDuration. Продолжительность видео в секундах можно получить с помощью Natu-ralDuration.Seconds и потом преобразовать это значение в часы, минуты и секунды.
В этом примере событие CurrentStateChanged элемента управления MediaElement ассоциировано с функцией doState (из предыдущего примера). Однако теперь эта функция перехватывает значение свойства NaturalDuration. В JavaScript-версии используется вспомогательная функция convertDT для преобразования возвращаемого значения в строку. Рассмотрим код на JavaScript:
function doState(sender, args)
{
var meVid = sender.findName("vid");
var txtStat = sender.findName("txtStat");
var datetime = new Date(0, 0, 0, 0, 0, meVid.naturalDuration.Seconds)
durationString = convertDT(datetime);
txtStat.Text = durationString.toString();
}
function convertDT(datetime)
{
var hours = datetime.getHours();
var minutes = datetime.getMinutes();
var seconds = datetime.getSeconds();
if (seconds < 10) {
seconds = "0" + seconds;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
var durationString;
if (hours > 0) {
durationString = hours.toString() + ":" + minutes + ":" + seconds;
} else {
durationString = minutes + ":" + seconds;
}
return durationString;
}
Далее следует замечательный пример того, как .NET Framework в Silverlight 2 существенно упрощает разработку: все это может быть реализовано одной строкой кода на C#:
private void doState(object sender, RoutedEventArgs e)
{ txtStat.Text = vid.NaturalDuration.ToString();
}
Вот XAML, В котором используется представленная выше функция:
<Canvas x:Name="sample9" Opacity="1">
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width="200" Stretch = "Fill"
CurrentStateChanged="doState" BufferingTime="0:0:10" />
<TextBlock x:Name="txtStat">
</TextBlock>
</Canvas>
Получить текущее положение курсора воспроизведения можно с помощью свойства Position. В этом примере позиция выводится в текстовом блоке при приостановке воспроизведения (Для тестирования можете оставить в XAML-коде три "кнопки" Play, Stop и Pause и соответствующие им обработчики событий doPlay и doStop с предыдущего примера, но закомментировать тело функции doState ). Необходимый код на JavaScript для функции doPause выглядел бы примерно так:
function doPause(sender, args) {
var meVid = sender.findName("vid");
meVid.Pause();
var txtStat = sender.findName("txtStat");
var datetime = new Date(0,0,0,0,0, meVid.Position.Seconds);
positionString = convertDT(datetime);
txtStat.Text = positionString.toString();
}
И, как и ранее, эквивалентный код на C# намного проще:
private void doPause(object sender, MouseButtonEventArgs e) {
vid.Pause();
txtStat.Text = vid.Position.ToString(); }
Маркер временной шкалы - это
Silverlight поддерживает эти маркеры и формирует событие MarkerReached (Маркер достигнут) при встрече с маркером в ходе воспроизведения. Это событие можно перехватывать и использовать как триггер для действий, которые должны выполняться при достижении данной отметки.
Далее представлен фрагмент XAML, в котором с помощью атрибута MarkerReached задается обработчик события встречи с маркером, JavaScript-функция handleMarker:
<MediaElement x:Name="vid" Source="balls.wmv" Height="200" Width = "200" MarkerReached = "handleMarker" />
Среди аргументов данного события имеется объект marker (маркер). Он содержит объект TimeSpan (Диапазон времени) с меткой времени, в которой установлен маркер. В предыдущем разделе, "Управление положением курсора воспроизведения", представлен пример преобразования объекта Time-Span в формат, удобный для восприятия пользователем. Также marker содержит свойство Type (Тип).
Его значением является строка, которую задает человек, кодирующий видео. Наконец, имеется параметр Text, допускающий в качестве своего значения текст в свободном формате и обычно используемый для описания параметра. Далее представлен JavaScript-код для чтения всех трех параметров и создания строки, отображаемой в окне уведомления:
function handleMarker(sender, args) {
var strMarkerStatus = args.marker.time.seconds.toString();
strMarkerStatus += " : ";
strMarkerStatus += args.marker.type;
strMarkerStatus += " : ";
strMarkerStatus += args.marker.text;
alert(strMarkerStatus); }
Код на C# очень похож на предыдущий; заметьте, что args типа TimeLineMarkerRoutedEventArgs:
private void handleMarker(object sender, TimelineMarkerRoutedEventArgs e) {
string strMarkerStatus = e.Marker.Time.ToString();
strMarkerStatus += " : ";
strMarkerStatus += e.Marker.Type;
strMarkerStatus += " : ";
strMarkerStatus += e.Marker.Text; }
Как это выглядит, можно увидеть на рис. 11.11.
(рис 11.11) Перехват маркеров временной шкалы в SilverlightМаркеры временной шкалы можно добавлять в медиафайл также динамически, используя код на Silverlight. При этом поисковые метки можно задавать через процентное соотношение положения метки к общей продолжительности файла, например.
Далее представлен пример, в котором XAML для MediaElement определяет функцию handleOpen (Обработка открытия), выполняющуюся при открытии файла мультимедиа. Она вставляет новый объект временной шкалы в видео на 10-секундной отметке. Этот элемент не сохраняется в видео постоянно, только до завершения сеанса.
<MediaElement x:Name="vid" Source="balls.wmv"
Height="200" Width = "200"
MarkerReached = "handleMarker" MediaOpened= "handleOpened" />
Обработчик должен создать новый элемент временной шкалы в XAML и добавить его в коллекцию маркеров MediaElement. В данном случае, задается маркер временной шкалы на отметке 10 секунд, для которого Type определен как 'My Temp Marker' (мой временный маркер) и Text- 'Dynamically Added Marker' (Динамически добавляемый маркер).
Вот как это выглядело бы в JavaScript:
function handleOpened(sender, args) {
var marker =
sender.getHost().content.createFromXam l(
"<TimelineMarker Time='0:0:10'" +
" Type='My Temp Marker'
Text='Dynamically Added Marker' />");
sender. markers.add(marker);
}
И вот аналогичный код в .NET:
private void handleOpened(object sender, RoutedEventArgs e)
{
TimelineMarker t = new TimelineMarker();
t.Time = new TimeSpan(0, 0, 0, 10);
t.Type = "My Temp Marker";
t.Text = "Dynamically Added Marker";
vid.Markers.Add(t);
}
Коллекцию маркеров MediaElement составляют объекты TimeLineMarker (Маркер временной шкалы), поэтому для введения нового маркера просто создается новый объект TimeLineMarker, задаются его свойства Time, Type и Text, и этот объект добавляется в коллекцию. Silverlight имеет достаточно развитую логику и знает, когда должно быть сформировано событие, исходя из заданного Time, поэтому при создании новых маркеров нет необходимости добавлять их в порядке расположения на временной шкале.
Теперь, когда MediaElement достигает 10-секундной отметки при воспроизведении, появляется диалоговое окно уведомления, как показано на рис. 11.12.
(рис 11.12) Использование динамически добавляемого маркера
Особенно замечательная возможность Silverlight - использование видео для заливки поверхности с помощью VideoBrush. Сделать это очень просто. Во-первых, необходим элемент мультимедиа, который загрузит видео. Он должен быть скрыт и не должен принимать события мыши. Для этого его прозрачность задается равной 0 и свойству IsHitTestVisible (Объект виден для механизма обработки кликов) присваивается значение false. Также элементу мультимедиа должно быть задано имя посредством свойства x:Name. Рассмотрим пример:
<MediaElement x:Name="vid" Source="balls.wmv" Opacity="0" IsHitTestVisible="False" />
Теперь объект VideoBrush можно применять к любому объекту точно так же, как любую другую кисть. В качестве источника кисти задается MediaElement (вот почему ему обязательно должно быть присвоено имя). Также можно задать свойство Stretch для управления визуальными эффектами кисти.
Например, здесь представлен TextBlock, для закрашивания Foreground которого используется Video-Brush:
<TextBlock FontFamily="Verdana" FontSize="80"
FontWeight="Bold" TextWrapping = "Wrap"
Text="Video">
<TextBlock.Foreground >
<VideoBrush SourceName="vid"/>
</TextBlock.Foreground >
</TextBlock>
Silverlight теперь будет формировать визуальное представление текста, используя VideoBrush. На рис. 11.13 показано, как это будет выглядеть.
(рис 11.13) Использование VideoBrush для закрашивания текстаКак было показано в данном курсе до сих пор, Silverlight расширяет возможности следующего поколения Веб-приложений, предоставляя инструменты для добавления насыщенных видео, аудио, векторной графики, анимации и других элементов, которые улучшают UX. Рукописные примечания - создание рукописного или нарисованного от руки содержимого с использованием устройств, разработанных для этих целей, - еще один замечательный способ сделать приложения еще более интерактивными и индивидуальными. Обеспечиваемая Silverlight поддержка программирования рукописного содержимого приносит эту функциональность в Веб. В данном разделе мы рассмотрим поддержку рукописных примечаний в Silverlight.
Существует несколько разных типов устройств, которые могут использоваться для приложений с поддержкой рукописных примечаний:
Ink (Рукописный фрагмент), который может быть интегрирован в Веб-страницы Silverlight, таким образом, обеспечивая поддержку рукописного ввода, рисования, рукописных примечаний и прочих форматов ввода в Веб.На сайте http://silverlight.net предлагается замечательный пример приложения, поддерживающего рукописные примечания. Это приложение для чтения книг можно найти по адресу http://silverlight.net/samples/1.0/Page-Turn/default.html и увидеть на рис. 11.14.
Данное приложение демонстрирует, как можно загружать источник данных и просматривать изображения и другие ресурсы, имитируя перелистывание страниц книги. Оно расширено возможностью введения рукописных примечаний, что позволяет комментировать изображения, и добавляемые комментарии остаются связанными с изображением. Пример страницы с комментарием показан на рис. 11.15.
(рис 11.14) Пример PageTurn на Silverlight
(рис 11.15) Комментирование изображения в Silverlight
Поддержка рукописного ввода в Silverlight очень проста. Каждый раз при проведении устройством ввода по экрану формируется один или более объектов StylusPoint (Штрих стилуса). Эти объекты объединяются в коллекцию StylusPointCollection (Коллекция штрихов стилуса), которая формирует основу Stroke. Обводки собираются в коллекцию StrokeCollection (Коллекция обводок). Эта коллекция составляет список графических элементов, используемых для создания рукописного ввода, визуальное предоставление которого формирует InkPresenter. Итак, если взглянуть на рис. 11.15, буква "С" в слове - это обводка, образованная рядом точек, объединенных в StrokeCollection. Обе буквы "О", буква "L", линия и точка в восклицательном знаке, и линии, образующие стрелку и круг, - все это обводки. Каждая из этих обводок является участником коллекции StrokeCollection в InkPresenter, и каждая из них образована объектами StylusPoint, входящими в состав коллекции StylusPointCollection соответствующей обводки. Каждый из этих типов предоставляет объектно-ориентированный интерфейс со свойствами и методами, обеспечивающими возможность их программирования. Рассмотрим основные типы и некоторые их свойства и методы.
Объект InkPresenter имеет свойство Strokes типа StrokeCollection. Эта коллекция, в свою очередь, содержит все метаданные, необходимые для представления рукописного ввода пользователя.
При использовании JavaScript для программирования Silverlight, StrokeCollection предоставляет свойство Count (Количество), которое возвращает количество обводок, находящихся в коллекции в настоящий момент.
StrokeCollection предоставляет следующие методы:
Add ( Добавить ) Позволяет добавлять новую обводку в коллекцию.Clear ( Очистить ) Очищает коллекцию обводок, что приводит к немедленному повторному формированию визуального отображения (т.е. удалению рукописного фрагмента, созданного ранее).GetBounds ( Получить границы ) Возвращает прямоугольник (в виде структуры Rect ), который представляет блок, ограничивающий обводки.GetItem(индекс) ( Получить элемент ) Обеспечивает извлечение обводки, хранящейся под заданным индексом.HitTest ( Проверка совпадения ) Если этот метод передается в StylusPointCollection, он возвращает подмножество обводок в StrokeCollection, которые пересекаются с данными штрихами.Insert ( Вставить ) Аналогичен Add за исключением того,что позволяет вставлять новые обводки в коллекцию по заданному индексу.Remove ( Удалить ) Обеспечивает удаление заданного элемента из StrokeCollection.StrokeCollection по заданному индексу.Представленный в предыдущем разделе StrokeCollection - это коллекция объектов Stroke. Объект Stroke представляет коллекцию штрихов обводки; каждый штрих соответствует одному движению стилуса: стилус опускается (
Объект Stroke предоставляет следующие свойства:
DrawingAttributes ( Атрибуты рисования ) Каждая обводка имеет собственную независимую высоту, ширину, цвет и цвет контура. Эти параметры задаются посредством типа DrawingAttributes. Чтобы использовать этот тип, создается экземпляр объекта DrawingAttributes, задаются значения свойств, и новый объект DrawingAttributes добавляется в обводку.StylusPoints ( Штрихи стилуса ) Это StylusPointCollection, содержащий коллекцию объектов StylusPoint, образующих обводку. StylusPoint обсуждается в следующем разделе.Объект Stroke обладает следующими методами для работы с рукописным вводом:
GetBounds Возвращает Stroke в виде структуры Rect.HitTest Если передать этот метод в StylusPointCollection и если какой-либо из этих штрихов пересекает Stroke, метод возвратит true; в противном случае, возвращается false.Эта коллекция объединяет в себе объекты StylusPoint. Она используется для хранения штрихов стилуса для Stroke или, в некоторых случаях, может передаваться в такие методы, как HitTest, для выявления пересечения обводок.
StylusPointCollection предоставляет свойство - Count, которое возвращает количество штрихов стилуса в данной коллекции.
Объект StylusPointCollection имеет следующие методы:
Add Этот метод позволяет добавлять новый StylusPoint в конец коллекции.AddStylusPoints ( Добавить штрихи стилуса ) Этот метод позволяет добавлять существующий объект StylusPointCollection в конец данной коллекции.Clear Этот метод удаляет все объекты StylusPoint из коллекции.GetItem Этот метод получает заданный StylusPoint из коллекции. Используется со значением типа integer, представляющим индекс интересующего элемента, например, getItem(5 ).Insert Этот метод вставляет новый StylusPoint в коллекцию по заданному индексу.Remove Этот метод удаляет заданный StylusPoint из коллекции.RemoveAt Этот метод удаляет из коллекции StylusPoint по заданному индексу.Тип StylusPoint представляет отдельный штрих, создаваемый, когда пользователь выполняет ввод с помощью устройства рукописного ввода: пера, мыши или сенсорного экрана. Штрих предоставляет ряд свойств и методов.
StylusPoint предоставляет следующие свойства:
Name позволяет задавать имя штриха. Это уникальный идентификатор. Когда штрихи формируются в процессе ввода пользователем, имена им не присваиваются.PressureFactor (Коэффициент нажима) обозначает нажим, с которым пользователь водит пером или касается экрана при формировании штриха. При использовании мыши применяется нажим по умолчанию. Это значение типа double в диапазоне от 0.0 до 1.0, значение по умолчанию - 0,5. На основании коэффициента нажима можно менять высоту и ширину Stroke программно через свойство DrawingAttributes, чтобы предоставить обратную связь пользователю.Координаты обводки можно получить с помощью свойств X и Y. Они измеряются в пикселах.
При использовании рукописного ввода формируемые устройством ввода события трактуются как события мыши, и аргументы, получаемые обработчиками событий, будут типа MouseEventArgs.
Объект MouseEventArgs содержит методы, которые позволяют запрашивать сведения об устройстве ввода (является ли оно мышью, стилусом или каким-то другим устройством) и коллекцию объектов StylusPoint, связанную с этим событием.
Обратите внимание, что это тот же объект MouseEventArgs, который используется с событиями и MouseMove.
Объект MouseEventArgs предоставляет следующие свойства Булевого типа: ctrl и shift. Они принимают значение true, если пользователь удерживает соответствующую клавишу при формировании события.
Объект MouseEventArgs предоставляет следующие методы:
GetPosition (Получить положение) принимает в качестве параметра элемент и возвращает объект Point, который представляет координаты х и у курсора мыши относительно этого элемента. Если вызов метода осуществляется без параметра, Point содержит координаты относительно положения элемента управления, сформировавшего событие.GetStylusInfo (Получить сведения о стилусе) возвращает объект StylusInfo (Сведения о стилусе), содержащий информацию о состоянии стилуса. StylusInfo имеет следующие свойства:
1.true в этом случае.
2.GetStylusPoints возвращает копию штрихов стилуса, добавленных в коллекцию с момента формирования последнего события мыши. Это будет объект типа StylusPointCollection.При написании приложений инструментами Microsoft под термином рукописный ввод (Ink) обычно подразумевается надпись или рисунок от руки, создаваемый пользователем с помощью описанных ранее устройств рукописного ввода, таких как цифровое перо, сенсорный экран или мышь. При использовании в приложении Silverlight эти устройства обеспечивают заполнение объекта StrokeCollec-tion отдельными объектами Stroke. В свою очередь, Stroke выполняет запись действий устройства, например пера, к которым относятся действия: перо опускается (pen-down), перо перемещается (pen-move) и перо поднимается (pen-up). Stroke может представлять точку, прямую линию или кривую. Это осуществляется с помощью объекта StylusPointCollection, который содержит объекты StylusPoint, создаваемые преобразователем графической информации, связанным с пером, DrawingAttributes.
Как говорилось, Silverlight принимает элементы рукописного ввода с помощью класса InkPresenter. Это подкласс элемента Canvas, который также содержит коллекцию обводок в StrokeCollection. При добавлении обводок в StrokeCollection, InkPresenter автоматически формирует их визуальное представление, используя соответствующие DrawingAttributes.
Обычно InkPresenter добавляется в XAML приложения во время разработки дизайна, но объекты Stroke, составляющие StrokeCollection, будут добавляться во время выполнения с помощью JavaScript.
Далее представлен пример использования перекрывающего изображение InkPresenter на странице:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xaml">
<Image Source="sushi.jpg" Width="600" Height="400"></Image>
<InkPresenter x:Name="inkEl" Background="transparent"
Width="600" Height="400"
MouseLeftButtonDown="inkMouseDown"
MouseMove="inkMouseMove"
MouseLeftButtonUp="inkMouseUp"/>
</Canvas>
InkPresenter определяет обработчики событий MouseLeftButtonDown, MouseMove и MouseUp. Мы рассмотрим, как это делается в JavaScript, для чего создадим простое приложение для рукописного ввода. Затем далее в этом разделе то же самое будет сделано на C#. Сходство необыкновенное! Для этих событий понадобятся функции-обработчики действий "начало рукописного ввода", "рукописный ввод" и "прекращение рукописного ввода".
Прежде чем перейти к функциям-обработчикам событий JavaScript, требуется выполнить некоторые вспомогательные операции по объявлению глобальных переменных, необходимых для поддержки этих действий:
var theInk; // Ссылка на модуль отображения рукописного ввода
var newStroke; // Ссылка на обводку
var theControl; // Ссылка на элемент управления Silverlight
function handleLoad(control, userContext, rootElement)
{
// Событие Load возвращает ссылку на элемент управления,
// но другие обработчики событий нет. Поэтому мы собираемся
// создать ссылку на элемент управления здесь
theControl = control;
// Здесь мы создадим ссылку на элемент рукописного ввода
theInk = control.content. findName("inkEl");
}
Эти вспомогательные переменные для InkPresenter, текущей обводки и самого элемента управления Silverlight будут использоваться функциями, поддерживающими описанные выше действия рукописного ввода. При загрузке элемент управления Silverlight запускает функцию handleLoad. В качестве параметра она принимает ссылку на элемент управления Silverlight, но поскольку реализованные нами обработчики событий мыши не делают этого, данную ссылку необходимо сохранить в handleLoad. В ходе выполнения handleLoad можно также получить ссылку на InkPresenter, проведя его поиск по имени ( in-kEl ). Тогда не придется вызывать getHost для получения ссылки на родительский элемент UI, чтобы найти InkPresenter, при каждом выполнении обработчика событий.
Теперь можно приступать к рассмотрению обработчиков событий. Сначала разберемся, что происходит при формировании события MouseLeftButtonDown, результатом чего является выполнение функции inkMouseDown (Нажатие кнопки мыши при рукописном вводе). Мы будем отслеживать перемещения мыши аналогично тому, как это происходило при обработке перемещения методом "drag-and-drop", что рассматривалось в лекц. 6. При получении входных данных от мыши создается новый объект Stroke, содержащий объект DrawingAttributes, который определяет визуальные характеристики этой обводки. В представленном здесь примере DrawingAttributes будет обеспечивать следующие параметры обводки: Width - 2, Height-2, цвет заливки - White (Белый), и цвет контура - White. Тип MouseEven-tArgs в Silverlight поддерживает метод getStylusPoints, как было показано ранее в этой лекции, который принимает InkPresenter в качестве единственного параметра. Этот метод возвращает тип StylusPointCol-lection, который используется в методе AddStylusPoints обводки. Теперь можно добавить обводку в StrokesCollection InkPresenter. Посмотрим на код:
function inkMouseDown(sender,args)
{
// Перехватываем событие мыши.
theInk.CaptureMouse();
// Создаем новую обводку.
newStroke = theControl.content.createFromXaml('<Stroke/>');
// Задаем новый элемент атрибутов рисования для обводки.
// Он, как следует из его имени, определяет внешний вид обводки
var da = theControl.content.CreateFromXaml('<DrawingAttributes/>');
newStroke.DrawingAttributes = da;
// Теперь, когда обводка имеет атрибуты рисования,
// зададим их значения...
newStroke.DrawingAttributes.Width = 2;
newStroke.DrawingAttributes.Height = 2;
newStroke.DrawingAttributes.Color = "White";
newStroke.DrawingAttributes.OutlineColor = "White";
newStroke.StylusPoints.Add StylusPoints(args.GetS tylusPoints(theInk));
theInk.Strokes.Add (newStroke);
}
Теперь перемещение курсора мыши по холсту в состоянии отрисовки обводки (т.е. когда newStroke не null ) означает добавление новых штрихов в эту обводку и отображается в виде следа, оставляемого мышью. Вот код, реализующий это:
// Добавляем новые штрихи в Stroke, с которым работаем в данный момент.
function inkMouseMove(sender,args)
{
if (newStroke != null)
{
newStroke.StylusPoints.AddStylusPoints(args.GetStylusPoints(theInk));
}
}
Наконец, по завершении отрисовки обводки, когда вы отпускаете кнопку мыши (или отрываете перо от экрана), формируется событие MouseLeftButtonUp. После этого требуется очистить обводку и прекратить отслеживание мыши. После того, как переменной newStroke задается значение null, движение мыши (или пера) по экрану больше не будет обеспечивать добавление штрихов в обводку, и, следовательно, обводка не будет отрисовываться. Рассмотрим код:
function inkMouseUp(sender,args)
{
// Задаем newStroke значение null
newStroke = null;
// Прекращаем отслеживать перемещения мыши
theInk.releaseMouseCapture();
}
На рис. 11.16 показан пример приложения до добавления в него рукописного примечания. На рис. 11ly представлено то же приложение с рукописным примечанием, которое было отрисовано с помощью мыши или пера.
(рис 11.16) Выполнение приложения Silverlightc возможностью рукописного ввода
(рис 11.17) Приложение Silverlight с возможностью рукописного ввода с рукописным примечаниемВ предыдущем примере было продемонстрировано, как программировать рукописный ввод, используя Silverlight и JavaScript. Конечно же, то же самое можно сделать с помощью управляемого кода. Вот полный выделенный код на C#, обеспечивающий создание такого же приложения:
using System;
using System. Collections.Generic;
using System.Linq;
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;
using System.Windows.Ink;
namespace NetInkSample
{
public partial class Page : UserControl
{
Stroke newStroke; public Page()
{
InitializeComponent();
}
private void inkEl MouseLeftButtonDown( object sender, MouseButtonEventArgs e)
{
inkEl.CaptureMouse();
// Создаем новую обводку.
newStroke = new Stroke();
DrawingAttributes da = new DrawingAttributes();
newStroke.DrawingAttributes = da;
newStroke.DrawingAttributes.Width=2;
newStroke.DrawingAttributes.Height=2;
newStroke.DrawingAttributes.Color= Colors.White;
newStroke.DrawingAttributes.OutlineColor= Colors. White;
newStroke. StylusPoints.Add(e.Sty lusDevice.GetStylusPoints(inkEl));
inkEl.Strokes.Add (newStroke); }
private void inkEl MouseMove(object sender, MouseEventArgs e) {
if (newStroke != null) {
newStroke.StylusPoints.Add ( e.Sty lusDevice. GetS tylusPoints(inkEl));
}
}
private void inkEl MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
newStroke = null; inkEl.ReleaseMouseCapture();
}
}
}
И в XAML-коде, соответственно, необходимо сделать изменения:
<Image Source="sushi.jpg" Width="600" Height="400">
</Image>
<InkPresenter x:Name="inkEl" Background="transparent"
Width="600" Height="400"
MouseLeftButtonDown="inkEl MouseLeftButtonDown"
MouseMove="inkEl MouseMove"
MouseLeftButtonUp="inkEl MouseLeftButtonUp"/>
Кроме демонстрации гибкости Silverlight, который позволяет разрабатывать приложения как на языке сценариев JavaScript, так и на компилируемых языках программирования .NET, это также показывает простоту перехода от JavaScript к .NET, что позволяет без труда обновить существующие Silverlight-приложения на JavaScript до Silverlight 2 и перейти к управляемому коду.
MultiScaleImage, который, как следует из его имени, предоставляет возможность управлять размером и масштабом изображений, a Silverlight, в свою очередь, обеспечивает огромное виртуальное пространство, в котором эти изображения могут отрисовываться.
Лучше всего возможность
(рис 11.18) Изображение в Deep ZoomПока что ничего сверхъестественного, казалось бы, обычная фотография. Так чем же вызван весь этот шум вокруг
(рис 11.19) Исходное изображение в другом масштабеПосмотрите внимательно на апельсин на рис. 11.19. В его центре можно увидеть изображение с рис. 11.18. Увеличьте масштаб, и вы опять получите изображение, представленное на рис. 11.20.
(рис 11.20) Еще более глубокое масштабированиеКак видите, все второе изображение - не более чем пиксел на третьем (оно находится в зрачке глаза на рис. 11.20), а размер первого изображения всего несколько пикселов на втором рисунке. По этим нескольким статическим рисункам сложно оценить возможность
Но как создать приложение, которое позволит уменьшать и увеличивать масштаб изображений таким образом? Базовое приложение для этого написать довольно просто. Возьмем элемент управления MultiScaleImage и ассоциируем его с файлом, содержащим метаданные изображений. Этот файл создается в редакторе
(рис 11.21) Редактор Deep ZoomЭтот инструмент выполняет простую последовательность операций, включающую Import (Импорт), за которым следует Compose (Компоновка) с последующим Export (Экспорт). Итак, начинаем с вкладки Import, щелкаем Add Image (Добавить изображение), чтобы выбрать изображение, и повторяем этот
шаг для всех изображений, которые вы хотите использовать. На рис. 11.21 можно увидеть, что выбраны три изображения.
Следующий шаг - Compose, который осуществляется с помощью опций на вкладке Compose. На этой вкладке изображения размещаются на соответствующих поверхностях. Вы можете менять масштаб изображений, что разместить в них другие изображения. Например, на рис. 11.22 можно увидеть, что я взял одно из изображений и увеличил его масштаб так, что на экране виден только глаз.
(рис 11.22) Компоновка изображений для просмотра с разными уровнями увеличения в редакторе Deep ZoomТеперь, следующее изображение с его нормальным разрешением будет помещено на данное, масштаб которого увеличен. Итак, если посмотреть на рис. 11.22, вы увидите, куда вставлено второе изображение (в зрачок глаза). Позже, при выполнении приложения, увидеть это изображение можно будет, только увеличив первое. На рис. 11.23 показано, куда помещается второе изображение на первом.
(рис 11.23) Добавление нового изображения на увеличенное первоеЭто простой пример, мы просто добавили одно изображение, которое можно увидеть, только увеличив первое.
(рис 11.24) Экспорт метаданных Deep ZoomЧтобы экспортировать метаданные изображений, скомпонованных в SparseImageSceneGraph.xml и является конфигурационным файлом, который определяет каждое изображение и его местоположение на других изображениях с разными уровнями увеличения. Например, рассмотрим граф сцены для XAML с двумя изображениями:
<?xml version = "1.0"?> <SceneGraph version = "1"> <AspectRatio>1.33333333333334</AspectRatio> <SceneNode> <FileName>C:\Code\SLBook\Chapter11\DZCSample \source images\ dzcsample\DSCN2961JPG</FileName> <x>0</x> <y>0</y> < Width >1</Width> <Height>1</Height> <ZOrder> 1 </ZOrder> </SceneNode> <SceneNode> <FileName>C:\Code\SLBook\Chapter11\DZCSample \source images\dzcsample\DSCN2959JPG</FileName> <x>0.451782754964542</x> <y> 0.313488814592021</y> < Width >0.00099432659277551</Width> <Height>0.00099432659277551</Height> <ZOrder>2</ZOrder> </SceneNode> </SceneGraph>
Как видите, это довольно простой XAML-код. В нем указывается соотношение сторон основного изображения (полученное, исходя из размеров первого изображения), и каждое изображение превращается в SceneNode (Узел сцены). Первое изображения является первым SceneNode. Оно располагается в позиции (0,0) и является нормализованным изображением, т.е. его ширина и высота принимаются за 1. Размеры и расположение всех остальных изображений устанавливаются относительного первого.
Второе изображение размещается в точке с координатами 0,45 по оси х и 0,31 по оси у. Его размер составляет, примерно, 0,00099 размеров первого изображения. Таким образом, если увеличить первое изображение, примерно, в 10000 раз, можно будет увидеть второе изображение. Его Z-порядок равен 2 (Z-порядок первого изображения - 1), т.е. он будет отрисовываться поверх первого изображения.
Кроме того, редактор
Другой экспортируемый файл будет называется dzc output.xml. В нем содержатся все сведения о фрагментах и их положении относительно основного изображения. Он будет располагаться в каталоге, созданном редактором
Чтобы использовать ClientBin в проекте Веб-приложения. Сделав это, закройте решение.
Теперь в Windows Explorer скопируйте каталог, содержащий файл dzc output.xml и подпапки с фраг-ментированными изображениями, в папку ClientBin Веб-приложения. Сделав это, вновь откройте решение. Solution Explorer вашего проекта будет выглядеть примерно так, как показано на рис. 11.25.
(рис 11.25) Добавление данных Deep Zoom в Веб приложениеТеперь, чтобы сформировать визуальное представление содержимого MultiScaleImage в Page.xaml и в качестве значения его свойства Source задать путь к файлу dzc output.xml (в данном случае, этот файл располагается в /dzcsample/dzc output.xml ), а также необходимые ширину и высоту. Посмотрим, как будет выглядеть ваш Page.xaml:
<UserControl x:Class="DZCSampleApp.Page" xmlns="http://schemas.microsoft.com/client/2007" xm lns:x=" http://schemas.microsoft.com/winfx/2006/xam l" Width = "400" Height="300"> <Canvas> <MultiScaleImage Source="dzcsample/dzc_output.xml" Height="300" Width="400" /> </Canvas> </UserControl>
Итак, при выполнении этого приложения элемент управления MultiScaleImage будет формировать визуальное представление верхнего элемента в SceneGraph, увеличивая его от оригинального масштаба изображения до заданных ширины и высоты (в данном случае, 400 * 300).
Как видите, в приложении нет автоматической реализации управления мышью, поэтому мы не можем перемещаться по изображению или менять его масштаб. Как это делается, будет рассмотрено в следующем разделе
Как и все остальные элементы управления Silverlight, MultiScaleImage может объявлять функции для обработки событий. Например, для перемещения изображения используются обычные события мыши: MouseLeftButtonDown, MouseLeftButtonUp и Mouse Move, - и обрабатываются они так же, как это делается при перемещении любого элемента методом "drag-and-drop". Сначала, рассмотрим XAML для MultiScaleImage, определяющий эти события:
<UserControl x:Class="DeepZoomSample.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xa ml/presentation" xm lns:x=" http://schemas.microsoft.com/winfx/2006/xam l" Width = "640" Height="480"> <Canvas> <MultiScaleImage x:Name="dz" Source="dzcsample/dzc_output.xml" MouseLeftButtonDown = "MultiScaleImage MouseLeftButtonDown" MouseLeftButtonUp="MultiScaleImage MouseLeftButtonUp" MouseMove="MultiScaleImage MouseMove" Height="480" Width = "640"></MultiScaleImage> </Canvas> </UserControl>
И теперь рассмотрим каждый из этих обработчиков событий более подробно. Прежде всего, есть код, общий для них всех, который обеспечивает отслеживание текущего состояния мыши и просматриваемых в настоящий момент координат MultiScaleImage:
bool dragging = false; double dx = 0; double dy = 0; Point p0; Point p1; Point pLast;
Теперь посмотрим, что происходит, когда пользователь щелкает изображение кнопкой мыши:
private void MultiScaleImage MouseLeftButtonDown( object sender, MouseButtonEventArgs e)
{
dragging = true;
p0 = dz.ElementToLogicalPoint(new Point(0, 0));
p1 = dz.ElementToLogicalPoint(new Point(640, 480));
dx = 0;
dy = 0;
double x = e.GetPosition(null).X;
double y = e.GetPosition(null).Y;
pLast = dz.ElementToLogicalPoint(new Point(x, y));
}
Предполагается, что пользователь удерживает кнопку мыши и двигает мышью, поэтому задаем Буле-вому свойству dragging (перетягивание) значение true. Теперь необходимо получить текущие координаты видимой части изображения. Вспомним, что для верхнего изображения задана позиция (0,0), или x = 0 and y = 0, и его высота и ширина заданы равными 1. Это логические координаты и логические размеры.
В Silverlight логические координаты верхнего левого угла и нижнего правого угла окна можно получить с помощью метода ElementToLogicalPoint (Логическая точка элемента), передав в него физические координаты. Итак, представим, что изображение было увеличено и немного передвинуто. Теперь если вызвать этот API для физической точки (0,0), т.е. верхнего левого угла видимой в данный момент части изображения,будет возвращена логическая точка, представляющая положение это точки на полном изображении. Чтобы получить координаты нижнего правого угла, делаем то же самое для точки, координаты которой соответствуют ширине и высоте физического экрана (в данном случае, 640 * 480).
Мы хотим отслеживать изменение размеров относительно осей х и у, поэтому задаем переменным dx и dy значение 0. Зачем это сделано, станет понятно через мгновение.
Кроме того, необходимо получать логические координаты курсора мыши. Для этого вызывается API ElementToLogicalPoint, в него передается текущее положение мыши (которое можно получить из объекта MouseEventArgs, передаваемого в эту функцию) и возвращаемые результаты сохраняются в переменной pLast.
Итак, что происходит, когда пользователь начинает перетягивание, удерживая кнопку мыши нажатой? Мы хотим получить эффект перемещения по изображению. Вот код, реализующий это:
private void MultiScaleImage MouseMove(
object sender, MouseEventArgs e) { if (dragging) { if (e!=null)
{ double x = e.GetPosition(null).X; double y = e.GetPosition(null).Y;
Point pCurrent = dz.ElementToLogicalPoint(new Point(x, y)); if (pLast! = null)
{ dx += (pCurrent.X - pLast.X); dy += (pCurrent.Y - pLast.Y); Point origin =
new Point(p0.X - dx, p0.Y - dy); dz.ViewportOrigin = origin; } pLast = pCurrent; } } }
Событие MouseMove формируется независимо от того, удерживается кнопка мыши или нет, поэтому для обозначения перетягивания используем переменную dragging. Итак, если пользователь выполняет перетягивание, и MouseEventArgs не null, будет выполняться весь остальной код.
В данном случае, мы извлекаем текущие координаты мыши и сохраняем их логические эквиваленты (полученные с помощью метода ElementToLogicalPoint элемента управления) в точке pCurrent. Теперь если pLast не null, значит, пользователь выполняет перетягивание, и мышь переместилась относительно ее предыдущего положения. Итак, чтобы получить изменение логических координат, произошедшее в результате перемещения мыши из предыдущей точки в текущую, мы просто вычисляем разницу (дельту) между предыдущими и текущими координатами относительно обеих осей (х и у). После этого задаем для свойства Origin (Начало отсчета) элемента управления MultiScaleImage исходные координаты с учетом дельта по х и у. Это обеспечит эффект "перемещения" изображения при перемещении мышью, хотя мы, по сути, просто изменяем координаты верхнего левого угла отображаемой части изображения. По завершении перемещения необходимо просто задать положение Last (Последний) как Current, чтобы, когда пользователь вновь начнет двигать мышью, вычисления производились относительно этой точки, а не той, в которой пользователь нажмет кнопку мыши.
Наконец, необходимо, чтобы после того, как пользователь отпустит кнопку мыши, все вернулось в исходное состояние. Сделать это довольно просто. Рассмотрим код:
private void MultiScaleImage MouseLeftButtonUp( object sender, MouseButtonEventArgs e) {
dragging = false;
p0 = new Point();
p1 = new Point();
pLast = new Point();
dx = 0;
dy = 0; }
Эти три простые функции обеспечивают пользователю возможность перетягивать изображение и перемещаться по нему независимо от степени его увеличения. В следующем разделе будет показано, как с помощью колеса мыши увеличивать и уменьшать изображение и, таким образом, открывать части изображения, скрытые при других степенях увеличения.
Проблемой при создании приложений
Прежде всего, необходимо обеспечить возможность использования API браузера в Silverlight, поэтому не забудьте включить в код следующую строку:
using System.Windows.Browser;
Затем в конструкторе Page элемент управления должен быть зарегистрирован как поддерживающий сценарии. Методы элементы управления, обозначенные как ScriptableMember (Участник, поддерживающий сценарии), доступны из JavaScript. Вот как это делается:
public Page() {
InitializeComponent();
HtmlPage.RegisterScriptableObject("MySilverlightObject", this); }
Далее требуется перехватывать события колеса мыши на этой странице. Для этого сначала добавляем вызов handleLoad в тег <Body>, чтобы обеспечить выполнение JavaScript-кода при формировании визуального представления страницы:
<body style=" height: 100%; margin :0;" onload = "handleLoad();"> ... </body>
Затем в JavaScript-функции handleLoad задается событие:
function handleLoad()
{
window.onmousewheel = document.onmousewheel = onMouseWheel;
if (window.addEventListener)
{
window.addEventListener('DOMMouseScroll', onMouseWheel, false);
}
}
Тем самым определяем, что JavaScript-функция onMouseWheel (При прокручивании колеса мыши) будет выполняться при любом прокручивании колеса мыши.
function onMouseWheel()
{
if(!event)
{ event = window.event;
}
var slPlugin = $get("Xaml1");
slPlugin.content.MySilverlightObject.dz_MouseWheel( event.clientX, event.clientY, event.wheelDelta);
}
Эта функция просто принимает ссылку на элемент управления Silverlight (в данном случае, при создании ему присваивается имя Xaml1) и затем вызывает функцию выделенного кода, определенную для него. Для вызова используется синтаксис <ИмяКомпонента>.content.<ИмяОбъекта>.<ИмяФункции>.
<ИмяКомпонента> - это переменная, определенная в JavaScript как ссылка на объект Silverlight. <ИмяОбъекта> - это имя, заданное для объекта при его регистрации (ее можно увидеть в конструкторе Page() ). <ИмяФункции> - это имя вызываемой функции. Эта функция должна быть соответствующим образом описана, чтобы JavaScript "видел" ее. В данном случае, вызывается функция dz MouseWheel, рассмотрим ее:
[ScriptableMember]
public void dz MouseWheel(double x, double y, int delta)
{
double dZoomFactor=1.33;
if (delta < 0) dZoomFactor= 1/1.33;
Point pz = dz.ElementToLogicalPoint(new Point(x, y));
dz.ZoomAboutLogicalPoint(dZoomFactor, pz.X, pz.Y);
}
Прежде всего, как видите, она обозначена ScriptableMember, т.е. JavaScript может видеть и вызывать ее. Функция JavaScript передает текущие координаты мыши и дельта, возникающие в результате изменения масштаба. Дельта будет иметь положительное значение при прокручивании колеса вперед и отрицательное значение при прокручивании назад.
Масштабный коэффициент каждого прокручивания колеса задан равным 33% (но он может быть любым), таким образом, переменной dZoomFactor (Масштабный коэффициент) задается значение 1.33 при увеличении и 1/1.33 при уменьшении масштаба.
Далее требуется, чтобы изменение размеров происходило вокруг текущего положения курсора мыши на изображении. Это очень просто сделать, преобразовав координаты курсора мыши в логическую точку с помощью метода ElementToLogicalPoint элемента управления MultiScaleImage. Имея координаты и коэффициент масштабирования, вызываем метод ZoomAboutLogicalPoint (Масштабирование вокруг логической точки), чтобы обеспечить изменение размеров MultiScaleImage.
Кроме возможности увеличения и уменьшения изображения, можно также создавать коллекции изображений и работать с ними в масштабируемой среде.
Возможно, вы заметили, что при экспорте изображений из редактора
Для работы с изображениями коллекции Silverlight предлагает коллекцию SubImages (Множество изображений). Обратите внимание, что при формировании визуального представления страницы полный набор изображений не будет загружаться, поэтому эта коллекция будет пустой в этот момент. Чтобы иметь возможность работать с коллекцией, необходимо описать событие ImageOpenSucceeded (Изображение открыто успешно). Это событие будет формироваться в случае успешной загрузки и отображения.
(рис 11.26) Создание коллекции Deep ZoomРассмотрим XAML:
<MultiScaleImage x:Name="dz"
Source="dzcsample/dzc_output .xml"
ImageOpenSucceeded = "dz ImageOpenSucceeded"
Height="480" Width="640">
</MultiScaleImage>
В этот момент коллекция SubImages будет заполнена и может использоваться для работы с отдельными рисунками, ее составляющими. Рассмотрим пример изменения месторасположения изображений:
private void dz ImageOpenSucceeded(object sender, RoutedEventArgs e)
{
int nImages = dz.SubImages.Count;
for (int lp = 0; lp < nImages; lp++)
{
dz.SubImages[lp].ViewportOrigin = new Point(lp*0.1, lp*0.1);
}
}
Как видите, процесс абсолютно аналогичен тому, который использовался ранее для работы с одним рисунком. В данном случае, каждое изображение перемещается путем использования SubImages[lp,], где lp - это переменная цикла, которая может принимать значения от 0 до числа, соответствующего количеству изображений в коллекции. Результаты представлены на рис. 11.27. Обратите внимание на разницу между этой и исходной компоновками, которая представлена на рис. 11.26.
(рис 11.27) Компоновка коллекции изображений
В этой длинной лекции представлены три наиболее мощных элемента управления, предлагаемые Silver-light 2. Сначала был подробно рассмотрен элемент управления MediaElement и то, как можно использовать Silverlight для воспроизведения и трансформации видео- и/или аудиосодержимого, включая программирование прозрачности, маркеры, динамические маркеры и т.д. Затем вы познакомились с рукописными примечаниями и устройствами, поддерживающими их, а также тем, как Silverlight может обеспечить поддержку рукописного ввода на Веб-страницах. Наконец, вашему вниманию была представлена технология
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.