пятница, 13 марта 2009 г.

Дружим canvas и asp .net ajax под ie7

Известно, что ie7 - особенный браузер со взглядами далекими от общепринятых стандартов. Одним из таких стандартов, незаметных для ie7, является html5. В частности, его элемент canvas.
Одним из решений для поддержки страниц, использвующих тег canvas, в брузере ie7 является библиотека excanvas от Google. Библиотека реализует требуемый стандартом объект CanvasRenderingContext2D использованием Microsoft VML с одной стороны, с другой стороны предоставляя клиентским приложениям интерфейс, описанный стандартом html5.
Указанное решение позволяет вполне комфортно вести разработку страниц с тегом canvas до тех пор, пока не начинают возникать вопросы построения изображений, зависимых от результатов выполнения серверных сценариев, в режиме реального времени.
Если разработка ведется по технологиям, близким LAMP - проблем нет никаких. Рисование осуществляется средствами javascript. Поэтому заменить исходные данные асинхронным http-запросом с последующей перерисовкой - дело не сложное. Подробнее с примером это описано в статье "Canvas, Ajax, and the Supertrain".
Совсем другое дело, если разработка ведется в рамках ASP с использвоанием asp .net ajax. В данной ситуации очень не хочется воротить собственную реализацию ajax, которая при асинхронных запросах обновляла бы только исходные данные для графического отображения. Вполне нормально в рамках ASP желать обновления пользовательского элемента управления прозрачно, использованием UpdatePanel.
На перепутье canvas, asp, ajax и ie7 возникают следующие сложности:
  • После асинхронной перезагрузки элемента canvas на странице, excanvas его не воспринимает;
  • При использовании RegisterStartupScript объекта ClientScriptManager для построения javascript-кода инициализации элемента в обработчике Page_PreRender, код не выполняется по завершении асинхронного запроса.
Решить первую помогает изучение исходного кода библиотеки excanvas. В библиотеке при завершении загрузки страницы (по значению complete свойства document.readyState) осуществляется связывание элементов DOM страницы, подходящих под описание элемента canvas, с VML-реализацией функционала тега canvas из стандарта html5. Занимается этим метод init_ некоторого менеджера - G_vmlCanvasManager. Ниже представлен псевдокод библиотеки:
if (!window.CanvasRenderingContext2D) {
(function () {
Описание vmlCanvasManager
...
G_vmlCanvasManager.init()
...
Описание интерфейса canvas
...
Определение CanvasRenderingContext2D
})();
}

Метод init связывает вызов метода init_ с обработчиком onreadystatechange. Тут и кроется подвох. Ведь document.readyState всегда complete после загрузки страницы, несмотря на асинхронные запросы - onreadystatechange не происходит. Поэтому определить контекст нового элемента документа, приходящего с результатом выполнения асинхронного запроса, необходимо вызовом метода init_ по завершении передачи данных от сервера. Тут мы сталкиваемся со второй проблемой.
А вторая проблема решается путем использования метода RegisterStartupScript класса ScriptManager. Пример представлен ниже:
protected void Page_PreRender ( object sender, EventArgs e )
{
StringBuilder sbStartScript = new StringBuilder( );
sbStartScript.Append( "window.alert('ку-ку');" );
sbStartScript.Append( "G_vmlCanvasManager.init_();" );
ScriptManager.RegisterStartupScript( this, Page.GetType( ), ID + "StartupScript", sbStartScript.ToString( ), true );
}

Более подробное решение второй проблемы описано в заметке "Inline Script inside an ASP.NET AJAX UpdatePanel".

Комментариев нет:

Отправить комментарий