среда, 5 мая 2010 г.

Coloroid: Silverlight Profiling by Example

Привет, друзья!

Предлагаю вам игру: Coloroid. Суть проста: у вас есть летающий мячик, отбивающий блоки. Выпустите мячик за пределы сцены - и вы проиграли. Звучит знакомо? Что же, уточнения. Размер летающего мячика - один пиксел. Размер блока - один пиксел. Если вам удается поймать отбитый осколок - он так же превращается в мячик.

Coloroid Start

Через некоторое время у вас в арсенале несколько тысяч мячиков, а число оставшейся работы уменьшается с завидной скоростью:

lookma

В игре есть 100 000 пикселей, которые предстоит разбить. В худшие времена, когда картинка почти вся разобрана по пикселям, а по сцене носятся десятки тысяч мячиков, на моем двухгигагерцевом пеньке FPS не опускается ниже 20. Как же достичь такой скорости?

Профилирование Silverlight приложений



Профилировать Silverlight приложения можно стандартными средствами Visual Studio 2010. Проблема лишь в том, что эти стандартные средства не имеют стандартных кнопочек, чтобы их запустить из IDE. Вместо этого - вооружимся консолью Visual Studio:

LaunchTools


Последовательность действий такова. Запускаем консоль VS 2010 (от имени администратора под Vista и выше), и выполняем команды:
cd {папка, где находятся скомпилированные Silverlihgt,  (не Web!) исходники}
VSPerfClrEnv /sampleon
"%ProgramFiles%\Internet Explorer\iexplore.exe" {Полный путь к тестoвой страничке SL приложения}
VSPerfCmd /start:sample /output:{Имя Файла с результатами профилирования} /attach:{PID процесса iexplore.exe}

{Играемся с приложением}

VSPerfCmd /detach
VSPerfCmd /shutdown
VSPerfClrEnv /off
Все, что находится в фигурных скобках, вы заполняете сами и без скобок. В моем случае эта последовательность выглядела так:

cd c:\Projects\Coloroid\Coloroid\Bin\Debug
VSPerfClrEnv /sampleon
"%ProgramFiles%\Internet Explorer\iexplore.exe" C:\Projects\Coloroid\Coloroid\Bin\Debug\TestPage.html
VSPerfCmd /start:sample /output:ProfilingName /attach:8816

Играемся с приложением

VSPerfCmd /detach
VSPerfCmd /shutdown
VSPerfClrEnv /off
Тройка важных моментов.

Момент 1. Запускать профайлер нужно именно из той папки, где находятся скомпилированные файлы сильверлайта. Иначе вы не увидите имена функций в файле профайлера:

Launch foloder is important

Момент 2.  Если вы запускаете приложение под IE8+, то вы должны указать идентификатор именно того процесса, в котором запущен Silverlight. Иначе вы будете профилировать код процесса-контейнера. Определить, какой именно процесс выполняет Silverlight - легко. Откройте диспетчер задач и поработайте со своим приложением. Кто загрузит процессор - тот и будет нашим кандидатом.

Момент 3. Как определить идентификатор процесса? - В диспетчере задач зайдите в  Вид-> Выбрать столбцы...


Pid

Если все сделано верно, то на выходе мы получим .vsp файл, который по умолчанию не ассоциирован с 2010-й студией, но легко открывается в ней. Вот, что получилось у меня:

Don't use DP's when do intensive calculations

Сюрприз! Функция, проверяющая столкнулся ли мячик с доской внизу, занимала львиную долю вычислений. В чем же дело?  Дело в том, что мы часто обращаемся к Dependency Properties прямоугольника (Width и Height). Это самая распространенная причина медлительности Silverlight/WPF приложений: использование dependency properties в интенсивных вычислениях. Решается простым кешированием значений перед выполнением алгоритма:
/* Где-то в классе Bar */
/* .. */

/// <summary>
/// Creates a new instance of <see cref="Bar"/>.
/// </summary>
/// <param name="barVisual">Visual presentation of the bar.</param>
public Bar(Rectangle barVisual)
{
    _barVisual = barVisual;
    _barVisual.Width = GameSettings.BarWidth;
    _barWidth = GameSettings.BarWidth;
    _barHeight = _barVisual.Height;

    _barTop = GameSettings.SceneHeight - 50;
    Canvas.SetTop(_barVisual, _barTop);
}

/// <summary>
/// Checks whether point with given coordinates hits the bar.
/// </summary>
public bool Hit(int x, int y)
{
    // Using _barVisual.Width & _barVisual.Heihgt was a bottleneck.
    // since they are dependency properties...

    //return x > _barLeft && x < _barVisual.Width + _barLeft &&
    //       y >= _barTop && y < _barTop + _barVisual.Height;

    return x > _barLeft && x < _barLeft + _barWidth &&
            y >= _barTop && y < _barTop + _barHeight;
}

Такое простое изменение позволило увеличить скорость в два раза.

Спасибо!


Вдохновение к этой игре я нашел на сайте wonderfl.net - потрясающий сайт, где собирается много талантливых Flash-разработчиков. Мне нравится копировать оттуда flash работы. Не потому, что я тешу себя фразой Пикассо: "Хорошие художники копируют, великие - воруют". Нет. Просто таким образом ты не только сравниваешь две технологии, но и неизбежно учишь новое. Желаю вам, друзья, легкого профилирования, быстрых программ и неиссякаемого вдохновения!

PS: Исходники к игре я выложил под MS-PL здесь. Вдруг вам будет интересно :).

воскресенье, 25 апреля 2010 г.

Chess Url: Немного шахматных задач

Привет :)!

За неделю собралось немного красивых и сложных задачек (спасибо Андрею Котенко :)).

Задача 1


Белые начинают и дают мат в два хода.


Задача 2


Опять начинают белые и дают мат в опять два хода.


Задача 3


Наверное, вы уже начали догадываться :)... Начинают белые и дают мат в два хода.

вторник, 20 апреля 2010 г.

Chess Url (Чешурл) - Easy Chess Puzzles Sharing

Привет, Друзья!

Недавно стал играть в шахматы с Другом. Занятие это настолько увлекательное, что начал решать шахматные задачки. Но вот беда - решаю я их на смартфоне (ну нету у меня ифона, нету), а поделиться интересной задачкой хочется. Как быть?

Как всегда, пошел в Google, в поисках подходящего сервиса, а там этих сервисов... что Кот наплакал. То, что удалось найти имело фантастически убогий интерфейс и потрясающее неудобство использования. Вобщем, почему бы не сделать свой сервис? - Сделал. О нем и речь в сегодняшней статье.

Знакомьтесь - Chess Url


Чешурл позволяет расставить фигуры на доске и получить URL к картинке, которая соответствует расстановке. Например, в следующем примере у белых есть прекрасный ход. Какой?

White to Move
Ход белых

Эта картинка имеет следующий URL:

http://chessurl.appspot.com/?RA8,PA7,KB4,PC5,PF6/RA6,PB5,PC6,PD5,KF7

До слеша идут белые фигуры (RA8,PA7,KB4,PC5,PF6), после слеша - черные (RA6,PB5,PC6,PD5,KF7). Фигура начинается с имени (K - King, Q - Queen, R - Rook (ладья), B - Bishop, N - Knight (конь), P - Pawn). Следующие два символа - координаты фигуры на доске.

Как делался Chess Url?


Программа состоит из двух частей: дизайнера и просмотрщика. Дизайнер написан на Silverlight 4.0, просмотрщик на... Python в Google App Engine. Суть дизайнера - дать пользователю интерактивность при создании шахматной комбинации. Просмотрщик отвечает за генерацию картинки по указанным параметрам строки запроса.

Исходники дизайнера (Silverlight части) можно скачать здесь. Я решил открыть их под Microsoft Public License. Что касается исходников просмотрщика - позвольте оставить их за кулисами... Дело в том, что мои познания в Python настолько глубоки, что написанный код стыдно показывать blush. Отсутствие Python-исходников попробую компенсировать, поделившись полученным опытом.

  • Откуда картинки? - Изображения шахматных фигур я нашел на Википедии, в формате SVG.

  • Как конвертировать SVG в XAML? - Нужно скачать последнюю версию Inkscape. Редактор позволяет экспортировать SVG в XAML, и является абсолютно бесплатным. Единственный минус, у меня некорректно обработалась одна трансформация в SVG файле. Пришлось дотачивать напильником, но процесс этот безболезненный, т.к. форматы очень похожи.

  • Как происходит отрисовка картинки по строке запроса? - В состав Google App Engine входит Images API, который позволяет производить трансформации над картинками. Однако тут не без сюрпризов. Когда объединяешь .PNG файлы с прозрачностью, App Engine игнорирует прозрачность, заливая все белым цветом (как на эмуляторе сервера, так и на сервере). Решилась проблема использованием .GIF файлов. Благо Paint.NET легко конвертирует файлы.

  • В чем разрабатывалось Python приложение? - Мне очень понравилась Aptana. Это IDE, с открытым исходным кодом. Для Python устанавливается плагин, и вы получаете хороший user experience, имея полноценный intellisense в Python. Кстати, этим же IDE я пользуюсь для создания JavaScript приложений (прости меня Студия, но ты и рядом не стояло).

  • Сколько времени ушло на разработку? - около 4-х часов. Но я молчу о поиске изображений и их конвертировании. Так же промолчу и о поисках багов и их "конвертировании" :). Суммарных усилий хватило на два выходных дня.

Самым приятным сюрпризом был App Engine. Во-первых, очень либеральные квоты (один гиг трафика на вход и один на выход... в день). Во-вторых, простота программирования. Я заметил, что много потребляю ресурсов на конвертирование изображений (а это тоже входит в квоту: 2 592 000 трансформаций в день). Решение в три строчки кода: прикрутил Memcache. Теперь число трансформаций упало в разы.

Что вы думаете?


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

Спасибо за ваше внимание! Отличного программирования :)!

понедельник, 8 марта 2010 г.

Pro Debugging: А вы умеете?

Привет, Друзья!

Сегодня мы отвлечемся от Silverlight'a и пройдемся по самому популярному инструменту .NET разработчика: по отладчику Visual Studio, в контексте языка C#. Прежде чем вы подумаете, что я окончательно спятил, если собираюсь рассказывать очевидные вещи, позвольте возразить. Мы, разработчики ПО, склонны останавливаться на выученном, после того, как думаем, что знаний достаточно для выполнения работы. В то же время, копнув глубже, можно найти более эффективные способоы решения проблемы. Думаю, это одно из отличительных свойств настоящих Специалистов - желание всегда копать глубже, чем большинство, не останавливаясь на достигнутом.  

Итак, приятного вам "копания". Да не испугает вас размер скроллбара - тут картинок больше, чем текста :).

Содержание


Для вашего удобства, вот список всех полезностей, рассмотренных в этом посте:

Наблюдение за экземплярами классов или Make Object ID


Когда вы работаете с несколькими экземплярами класса, как можно отличить один от другого? Если это неуправляемый код, можно посмотреть на адрес объекта. Если вы в управляемом коде, то можно попросить GetHashCode() у объекта, и надеятся, что его реализация не подкачает. В отладчике есть более простой способ: правый клик на объекте в окне Watch/Locals/Autos -> Make Object ID.

Context menu: Make Object Id

После этого объекту присваивается уникальная метка {1#}, {2#}, и т.д. Метка дается объекту на все время жизни, и именно так объект будет появляться во всех окошках, в том числе, в Immediate Window, и даже в тултипах.

1. Делаем Object ID:

Make object id

2. Объект получает идентификатор:

Got Id

3. Теперь всюду, где встречается "меченный" объект, студия даст нам знать об этом:

Id everywhere

Что еще более интересно, так это возможность использовать метку для ссылки на объект. Т.е. можно написать в окне Watch 1# и получить мгновенный доступ к содержимому меченного объекта. Также метку можно использовать в условных (conditional) точках останова. Например, условие this == 1# остановит выполнение программы только если это наш меченный объект.

Улучшаем читабельность в Watch window


По умолчанию, значение объекта в окне Watch - имя его типа. Хочешь больше - раскрывай дерево. Но ведь для списков мы видим число элементов в списке. Значит, должен быть способ повысить читабельность нашего класса Customer?

Default Watch Value

ToString() - подумаете вы, и будете практически правы. Можно перекрыть метод ToString() и выводить отладочную информацию, которая будет показана в Watch. Но ToString() не всегда является лучшим выбором. Например, WPF использует его для вывода элементов без определенного DataTemplate на UI. Более удобным методом будет использование атрибута DebuggerDisplay.

[System.Diagnostics.DebuggerDisplay("Customer: {Name}")]
public class Customer
{
  public string Name { get; set; }
  // ...
}        

Перекомпилируем приложение, и под отладчиком получаем:

I Can Read This

Когда определены и атрибут DebuggerDisplay и ToString(), отладчик использует атрибут. Однако будьте аккуратны, в некоторых случаях вы можете получить интересное насекомое, зарегистрированное под именем Гейзенбаг.


Поймать все исключения? - Легко!


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

try
{
  DoSomethingBuggy();
}
catch
{
  // Sorry. 
}
Все, что нужно для остановки, это зайти в меню Debug -> Exceptions... и выбрать  на чем именно вам интересно было бы остановиться:

Catch them all

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


Исключение, ты кто? - $exception.


В предыдущем примере, когда мы останавливались на всех исключениях, вы могли встретить код следующего типа:
try
{
  // Do something
} catch
{
  // Catch 
}
Как теперь быть, если вы хотите посмотреть на исключение в окне Watch? Экземпляр исключения не указан в блоке catch, что написать? Конечно, можно добавить переменную исключения, перекомпилировать код, и посмотреть на красавца. Но ни в коем случае не делайте так. Просто введите в окно Watch строку $exception:

No Name

PS: Какие еще псеводпеременные поддерживает отладчик? Для C# мне известна только $user, которая показывает текущего пользователя. Для неуправляемого кода их чуть-чуть больше.


Debugger.Break(), или Стоп машина!


Чтобы мгновенно провалиться в отладчик, даже когда программа запущена не в режиме отладке, и даже когда она запущена в Release версии, достаточно вызвать Debugger.Break().


Поток, тебя как зовут?


Когда создаете новый поток, не поленитесь дать ему имя (Thread.Name). Так будет легче отличать потоки друг от друга во время отладки: на скриншоте ниже Happy thread получил имя.

Thread Name


Позвольте к вам прикрепиться: Attach to Process.


Расставили все брякпоинты, прошли первые десять шагов багорепа, и вдруг вспомнили, что программа запущена не под отладчиком? Ничего страшного. Выбираете в меню Debug -> Attach to Proces или просто Alt + D, P.

Attach to process

После этого, выбираем процесс, который хотим отладить, и жмем Attach. Если все загруженные символы соответсвуют прикрепленному процессу, вы попадаете в мир полноценной отладки.


Отладка без отладки: Design Time Debugging.


Immediate Window (Ctrl + Alt + I, или Debug -> Windows -> Immediate) позволяет выполнить метод даже если программа не запущена. А если еще поставить точку останова в методе, то мы попадем в отладчик:

 Design Time Debugging

Для того, чтобы отладить функцию Sum() достаточно ввести в Immediate Window'e выражение ?Sum(x, y), и нажать Enter. Эта фича называется "Отладка в режиме дизайна", и работает во всех редакциях студии, кроме Express'a.


Хочу остановиться здесь, если только...


Не можете понять как эта переменная становится null'ом? Или как этот счетчик выходит за пределы? Установите условный breakpoint. Для этого нужно добавить точку останова и тыкунть правой кнопкой мыши по ней. В контекстном меню выбираем "Condition...":

Conditional Context

И указываем при каких условиях должна сработать точка останова:

Debug Conditional Set

В следующий раз, когда вы войдете в функцию, и x будет меньше сработает точка останова.

Совместно с метками объектов условные точки останова представляют отличное подспорье, когда вы пытаетесь отладить конкретный экземпляр коллекции. Если вы хотите, чтобы точка останова срабатывала только для данного элемента коллекции достаточно пометить элемент коллекции и использовать его метку в условии. Например условие 1# == this остановит выполнение только в случае если текущей объект обладает меткой 1#.


Вывод информации, без вывода информации: Trace Points.


Приходилось ли вам когда-нибудь писать следующий код для упрощения отладки?
private static int Sum(int x,  int y)  int y)
{
  Trace.WriteLine(string.Format("Enetring function Sum(). X = {0}; Y = {1}; Time = {2}", x, y, DateTime.Now));
  // ...
}
Да? - Поздравляю, вы делали ненужную работу. Я тоже так делал :). Ведь это очень полезно, особенно когда ищешь баги, связанные с фокусом ввода - переключение в отладчик влечет за собой потерю фокуса, и усложняет отладку. А так - написал и смотришь в Output Window, что там куда приходит. Но есть способ сделать это легче.

Устанавливаем breakpoint, кликаем по нему правой кнопкой, выбираем "When Hit..." и в появившемся окошке пишем, что бы мы хотели увидеть:

Trace points

Когда отладчик войдет в эту функцию, в окошке Output появится следующее сообщение:
Entering ConsoleApplication1.Program.Sum(int, int); X == 0; Y = 1; Time = {19.02.2010 8:30:34}
Как видите, можно выводить не только значения переменных, но и значения вызовов функций или выражений. Безусловный плюс - не нужно перекомпилировать приложение, если хотите изменить что-либо.

PS: Это не значит, что Trace.WriteLine() становится вовсе бесполезным. Он все так же бесценен для сопровождения приложений.


Когда очень плохо: Отладка .NET framework'a.


Я люблю копаться в framework'e. Эта страсть к археологии позволяет не только лучше понять работу Библиотеки но и понять почему вызов этого метода не привел к ожидаемым результатам. Для отладки  .NET Framework вам нужны символы, флажок и желание поискать на форумах, почему имея первые два пункта вы до сих пор не можете отлаживать исходники .NET'a.

В самом простом случае, достаточно установить флажок в настройках студии Enable .NET Framework source stepping (Tools -> Options -> Debugging -> General):

Enable .NET Source Code

Я также вЫключил флаг Requre source files to exactly match the original version. Это увеличивает шансы на успешный вход в исходники .NET, в случае если загруженные символы по какой-то прчине отличаются от загруженной библиотеки.

Далее соглашайтесь на все предложения студии. Запускайте программу в дебаге, и попробуйте войти внутрь вызова системного метода. Если в Output окне вы видите сообщение "Step into: Stepping over method without symbols '...'":

Step into failed

Значит нужно проверить, что символы загружены. Идем в Call Stack и смотрим на методы без символов (серые):

Load Symbols

Тыкаем правой кнопкой по любому из них и выбираем Load Symbols From -> Microsoft symbols server. После этого студия начинает скачивать символы:

Load symbols progress

Загрузить и проверить статус дополнительных символов можно так же из окна Modules (Debug -> Windows -> Modules). Итак, символы загружены, пробуем войти в функцию еще раз:

Hurray

Что самое приятное - можно прочитать комментарии разработчиков библиотеки, которые нельзя найти ни в одном Reflector'e и MSDN'e.

Напоследок хочу предупредить, что не для всех сборок .NET Framework Microsoft позволяет отлаживать исходники. Смотрите в Output Window.

Отладка запуска сервисов/приложений.


Иногда нужно отладить код запуска сервисов или приложений,  которые стартует другой процесс (например, installer). Нажать F5 для запуска сервиса вы не можете, а прикрепиться к процессу - уже слишком поздно. Как быть?

А быть надо с редактором реестра. Идем в раздел "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" и добавляем новый подраздел, с названием процесса, который будем  отлаживать (e.g. explorer.exe). Затем, в этом разделе создаем строковый параметр с ключом debugger и значением - указывающм на отладчик:

HP Health Monitor

На картинке выше я решил отладить сервис HP Health Check (не потому, что у моей хапешки проблемы со здоровьем, тьфу-тьфу-тьфу, просто ради примера). Имя сервиса hphc_service.exe - именно так я назвал подраздел в Image File Execution Options. VSJitDebugger - это процесс запускающий отладчик Visual Studio. Теперь, если кто-то запустит процесс мы получим окошко предлагающее отладку:

Debug it

Кстати 1. Этот подход помогает не только отладить приложения, но и выследить какой засранец (извините) запускает исполняемые файлы вирусов...

Кстати 2. Если вы когда-нибудь устанавливали Process Explorer от SysInternals, и заменяли им стандартный диспетчер задач, то теперь вы знаете как он подменяет запуск taskmgr.exe :).


Отладка Silverlight приложений.


Мой основной браузер - Google Chrome, но разработку я веду под Firefox. Как же отлаживать Silverlight в таком случае? Один способ - это в настройках веб проекта указать каким браузером вы хотите запустить сайт, но я предпочитаю просто прикрепляться к процессу Firefox.

В четвертом Silverlight появилась удобная возможность отлаживать Out of Browser приложения. Достаточно указать в настройках Silverlight проекта какое именно приложение нужно отладить:

OOB Silverlight Debugging


Знайте свой инструмент, или блог Сары Форд.


Sara Ford - Program Manager CodePlex, автор книги MS Visual Studio Tips, очень жизнерадостная и веселая женщина. Она ведет потрясающий блог, огромная часть советов из которого и составила книгу. Если вы думаете, что отлично знаете Visual Studio - посетите ее блог, чтобы понять, насколько глубоко заблуждаетесь :).

Спасибо!


Друзья, спасибо вам, что прочитали или просто пролистали до этого места. Надеюсь, вы нашли что-нибудь новое для себя. Буду очень рад, если вы поделитесь своими открытиями, которые не вошли в пост. Напоследок, хотелось бы упомянуть слова мудреца: If debugging is the process of removing bugs, then programming must be the process of putting them in... Отличного настроения и программирования :)!

суббота, 19 декабря 2009 г.

SilverNotes: Drag'n'Drop + Print + RightClick

Привет, Друзья!

Сегодняшний пост посвящен сразу трем новинкам Silvelright 4.0: Printing, Drag'n'Drop и Right Click.

Надо отдать должное разработчикам Silverlight: они сделали настолько простой API для рассматриваемых новинок, что их описание влезло бы в два-три твита (интересно, твит - это уже стандартизированная единица размера информации :)? ).

В сумме эта тройка позволила мне за пару часов написать маленькое, но полезное приложение: SliverNotes (исходники, демо - для демо нужен 4-й сильверлайт). Эта программка позволяет сделать из обычного листка формата A4 карманную книжечку на 8 министраниц. Формат и содержание каждой странички определяется пользователем. Например, это может быть список покупок, подготовленный любимой женой забывчевому мужу, или же каждая страница может быть нотоносцем для талантливого музыканта, или... все что захотите. Вот пример распечатанной карманной книжки:



Итак к делу! (Иначе я рискую сделать введение самой длинной и сложной частью этого поста :)...)

Печать


Все, что нужно от разработчика для вывода содержимого на печать, это вызов функции Print() у объекта типа PrintDocument:
// PrintDocument - тот, кто нам нужен для печати.
var doc = new PrintDocument { DocumentName = "Notes" };

doc.PrintPage += (o, e) =>
                   {
                     e.PageVisual = uie;
                     e.HasMorePages = false;
                   };

doc.Print();
В обработчике события PrintPage  мы говорим Silverlight'у что именно нужно напечатать (e.PageVisual = uie). Здесь uie - это объект типа UIElement. Если бы мы хотели распечатать больше, чем одну страницу, то свойство e.HasMorePages нужно установить в true.

Чуть-чуть деталей

  • Если элемент, который вы выводите на печать "обрезается" родительским контейнером, он будет обрезанным и на бумаге. Чтобы это обойти, просто измените родительский контейнер, например, на Canvas.
  • Если вы применяете трансформации прямо к элементу, который служит PageVisual'ом, то они не будут применены на печати. Чтобы это обойти, устанавливайте трансформации ниже по дереву: вместо самого PageVisual'a, трансформируйте его первого ребенка (жуть-то какая :) ).
  • PageVisual не обязательно должен находиться в Visual Tree. Это может быть просто объект в памяти.
  • Инициировать печать можно только по запросу пользователя (например, в обработчике Click'a). Эй, а где еще его можно инициировать-то, умник? - Ну, у меня было желание стартовать печать в событии Loaded, но Silverlight пожелал мне удачи в виде приветливого Exception'a. Даже если обработчик Loaded - это лямбда в обработчике Click, вы получите исключение...

Drag'n'Drop


Эта новинка позволяет пользователям перетаскивать в плагин [пока только] файлы из операционной системы. Стоит ли говорить, на какой уровень это повышает UX (user experience)? . Фантазия - ваш предел. В SilverNotes вы можете перетащить на пустую страничку картинку, тем самым сделав свой шаблон для печати:


1. Слева - окошко Windows Explorer'a. Я схватил фотографию леса из моего родного города Лебедина, и тащу ее на SilverNotes. Превью фото делает Explorer.

2. Теперь указатель мышки находится над пустой страничкой SilverNote's. Я замечаю это в событии DragEnter, и начинаю анимировать цвет фона, как бы говоря: "Давай, бросай!"
 
3. Ну и, наконец, я отпускаю левую кнопку, и фото оказывается в Silverlight приложении...

Для того, чтобы разрешить бросать файл на UIElement, достаточно установить свойство AllowDrop="True". После этого у UIElement'a будут срабатывать обработчики событий DragEnter, DragLeave, DragOver и Drop. Вот пример кода, который считывает брошенную картинку на UIElement с установленным AllowDrop'ом:

private void DoDrop(object sender, DragEventArgs e)
{
  if (e.Data == null)
  {
    return; // И почему мы так не любим nulls?
  }
  
  // Куда именно это мы бросаем?
  Point dropPosition = e.GetPosition(_cnvDropTarget);

  // Ну-ка, что нам тут подбросили?
  var files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];

  foreach (var file in files)
  {
    using (var stream = file.OpenRead())
    {
      // Обрабатываем файл:
      ProcessFileContent(stream, dropPosition);
    }
  }
}

Чуть-чуть деталей

  • Не пытайтесь получить доступ к данным во время событий DragEnter/DragLeave. Только разочарование и SecurityException ждут вас. Правильное место для считывания данных - обработчик события Drop.
  • Не пытайтесь перетаскивать файл на Silverlight приложение, с установленным windowless режимом. Также проблематичным будет использование Drag'n'Drop под Маками (см. детали).

ПКМ ака RightClick


О поддержке правого клика в сильверлайт приложениях раньше приходилось только мечтать (или извращаться с html'ем поверх сильверлайта). Теперь же, все что нужно сделать - это подписаться на событие MouseRightButtonDown (или MouseRightButtonUp). Если вы хотите показать свое контекстное меню по правому клику, то нужно... написать свое контекстное меню и показать его. Что?! В сильверлайте нет стандартной реализации класса ContextMenu? - Да, покамест нет. Но не удивлюсь, если с выходом четверки (не беты) оно там появится. Пока же Microsoft предлагает пользоваться классом Popup, и в него встраивать контекстное содержимое.

Для демонстрации на IT Jam'e мы воспользовались кодом Jesse Bishop'a, и чуть-чуть изменили его, сделав более похожим на стандартное WPF меню:



В SilverNotes контекстное меню для Grid'a устанавливает следующий код:

<Grid x:Name="LayoutRoot" Background="Transparent">
  <menu:ContextMenu.ContextMenu>
 <menu:ContextMenu>
   <menu:MenuItem Click="OnDeleteClick">
  <menu:MenuItem.Content>
    <StackPanel Orientation="Horizontal">
   <Image Margin="0, 0, 8, 0" Source="/SilverNotes;Component/Resources/delete.png" Width="16" Height="16"/>
   <TextBlock Text="Delete"/>
    </StackPanel>
  </menu:MenuItem.Content>
   </menu:MenuItem>
 </menu:ContextMenu>
  </menu:ContextMenu.ContextMenu>
  <!-- Содержимое Grid. -->
</Grid>

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

Чуть-чуть деталей

  • Чтобы полностью скрыть стандартное меню сильверлайта, не забудьте установить e.Handled = true, в обработчике MouseRightButtonDown.
  • Событие MouseRightButtonUp не прийдет к вам, до тех пор пока вы не установите e.Handled = true в MouseRightButtonDown.

Итак


Все три новинки наглядно показаны в SilverNotes. Я выложил этот проект на Google Code. Если у вас есть желание усовершенствовать проект, дописать шаблоны для печати - добро пожаловать :). Кстати, создавать свои собственные шаблоны в SilverNotes так же просто, как создавать обычный UserControl на Silverlight'e. Шаблоны загружаются при помощи MEF (Managed Extensibility Framework), который так же входит в поставку четвертого Silverlight. Но об этом чуть-чуть попозже :).

Отличного настроения и прекрасного программирования в предновогодние морозы :)!

понедельник, 7 декабря 2009 г.

Webcam Fun

Привет, Друзья!

На всеукраинской сходке айтишников IT Jam, нам с Сергеем Лутаем посчастливилось делать доклад по новинкам четвертого Сильверлайта. Пока обрабатывается видео с презентации, выкладываю код, который мы использовали для демонстрации работы с веб камерой. Кроме кода, мы посмотрим на основы работы с камерой  и на несколько веселых эффектов, которые можно сделать при помощи пиксельных шейдеров.

Исходники к этому посту можно скачать здесь. Живое демо находится здесь. Для запуска демо, вам, кроме камеры, понадобится установить Silverlight 4 Beta 1. Рантайм есть только для разработчиков, но ведь это про вас, верно :)?

Основы


Для старта нам понадобится... камера. Чтобы получить доступ к камере из Silverlight'a, достаточно спросить у пользователя разрешения, и получив оное, начать видеозахват:

private CaptureSource _captureSource;

private CaptureSource CaptureSource
{
  get { return _captureSource ?? (_captureSource = new CaptureSource()); }
}

private void StartCapture()
{
  // Можно мне попользоваться вашей камерой?
  if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
  {
    // Урра! Начинаем захват: 
    CaptureSource.Start();
    
    var brush = new VideoBrush();
    brush.SetSource(CaptureSource);
    VideoBrush = brush; // К этому свойству можно привязать любой Brush: <Border Background="{Binding VideoBrush}" .../>
  }
}
Видео является обычной кисточкой (Brush), а значит, мы можем разукрасить выходом с камеры любой элемент, который можно разукрасить :). Пожалуй, это и все. Просто, не так ли?

Не Основы


К "продвинутым" темам можно отнести сохранение рисунка с камеры. Тут есть два варианта: воспользоваться функцией CaptureSource.AsyncCaptureImage() или сделать снимок UIElement'a, как обычно, при помощи WriteableBitmap'a: new WriteableBitmap(uie, null). Разница в том, что WriteableBitmap фотографирует содержимое со всеми эффектами, а CaptureSource выдаст именно то, что есть на выходе камеры, без трансформаций, эффектов и прочего.

Если есть несколько камер (скорее всего, у вас, как и у меня, их нет, но мало ли :)?), то можно всегда выбрать с какой камеры вести захват: CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices();

Для того, чтобы сохранить WriteableBitmap в Jpeg формат нужно написать свой энкодер, который будет последоватльно, байт за байтом, запаковывать цвета в формат Jpeg. Для этого, конечно, нужно сначала достать спецификацию Jpeg'a, изучить ее, и заимплементить... Шутка :). Все уже написано до нас: библиотека FJCore позволяет легко сохранять jpeg'и. Для просмотра примера использования, загляните в исходники к этому посту.

Спецэффекты


Использование спецэффектов радует больше всего. В реальном времени увидеть себя с необычной стороны, и я говорю не о спине, - оказывается очень увлектально :)! О создании собственных эффектов на шейдерах, я уже писал. А на презентации мы показали четыре эффекта:

Эффект находит границы на рисунке и закрашивает их черным цветом. Все остальное выводится белым. На этой фотке знакомые могут узнать Мишу Чалого :).
С этой фотки из паралельной вселенной дружественный привет землянам шлет головастый и чертовски умный двойник  автора этого блога :).
Этот эффект-страшилка просто инвертирует цвета. Как-то так выглядел мир по ту сторону кольца, когда Фродо надевал его :).
И, наконец, последний эффект, "скрытая фича Sivlerlight 4.0", он же знакомый нам по передачам типа "Прогноз погоды": подмена вывода. Как только цвет точки становится ярче определенного порога, вместа оригинала выводится точка из другого сэмплера (читай - brush'a). Здесь я направил включенный фонарик в ладошку, открывая секретный кусочек лица Билла.

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

Выводы


Пользоваться камерой в Silverlight  4.0 очень легко. Все операции с камерой не требуют  участия сервер-сайда. Можно легко сохранять изображения.

Для тех, кто любит просто читать с конца, еще раз привожу ссылку на исходники и демо. Не забудьте про рантайм: Silverlight 4 Beta 1. Отличного программирования и прекрасного настроения!

ЗЫЖ спасибо всем, кто пришел к нам на презентацию. И, конечно, всем тем, кто поставил за нас "наляпочки" на доске голосования :). Нам было очень приятно!

среда, 8 июля 2009 г.

July 10: See the Light

Привет :)!

Немножко пафосное название, но из домена букв не выбросишь. http://www.seethelight.com/ анонсирует выпуск третьего сильверлайта и бленда уже 10-го июля. Согласно MSDN Flash'у, виртуальный запуск состоится именно там.

Ждем-ждем тяпницы!