Стояла задача – сделать анимацию в Silverlight, формирование страницы в зависимости от некоторых условий (например, в зависимости от пользователя), и чтобы все хорошо смотрелось в IE и FF.
Создадим новый проект Silverlight 1.0. По-умолчанию он состоит из следующих файлов: Default.html и Default.html.js Scene.xaml и Scene.xaml.js Silverlight.js
Default.html – содержит ссылки на все скриптовые файлы в заголовке и вызов функции createSilverlight() в теле. Scene.xaml – собственно xaml-код приложения. Silverlight.js и другие *.js-файлы содержат java-script, необходимый для работы.
Увы, в данном случае возможно показывать только заранее созданную xaml-форму. Но часто требуется, чтобы наполнение формы зависело от каких-то условий. Например, разным пользователям нужно показывать разные пункты меню. Попробуем сделать это.
Для начала создадим в проекте еще aspx-форму с именем XamlForm.aspx и в редакторе заменим стандартный код на следующий:
Код XAML: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="XamlForm.aspx.cs"
Inherits="SilverlightJSApplication1.XamlForm" ContentType="text/xaml" %>
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480"
Background="#FFF6F4F0"
x:Name="Page">
<Canvas x:Name="Menu" Width="400" Height="420"
Canvas.Top="200" Canvas.Left="320">
<Rectangle Width="400" Height="420" Fill="#FFFFFFFF"
Stroke="#FF000000" RadiusX="10" RadiusY="10"/>
</Canvas>
</Canvas>
Также добавим в проект файл XamlForm.js и в него перенесем код из Scene.xaml.js, очистив от всего лишнего:
Код JavaScript:
if (!window.SilverlightJSApplication1)
window.SilverlightJSApplication1 = {};
SilverlightJSApplication1.Scene = function()
{
}
SilverlightJSApplication1.Scene.prototype =
{
handleLoad: function(plugIn, userContext, rootElement)
{
this.plugIn = plugIn;
}
}
Файлы Scene.xaml и Scene.xaml.js можно удалить из проекта, они больше не понадобятся.
Чтобы увидеть, что у нас получилось, нужно в файле Default.html заменить путь к Scene.xaml.js на путь к новому XamlForm.js и добавить атрибуты в :
leftmargin=”0″ topmargin=”0″ marginheight=”0″ marginwidth=”0″
А в Default.html.js заменить строку source c ‘Scene.xaml’ на ‘ XamlForm.aspx’. Теперь в качестве xaml-файла у нас обыкновенная страница aspx, и мы имеем возможность динамически ее наполнять. Поправим aspx код на:
Код XAML: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="XamlForm.aspx.cs"
Inherits="SilverlightJSApplication1.XamlForm"
ContentType="text/xaml" %>
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480"
Background="#FFF6F4F0"
x:Name="Page">
<Canvas.Resources>
<Storyboard x:Name="animation">
<%= createRectAnimation() %>
</Storyboard>
</Canvas.Resources>
<Canvas x:Name="Menu" Width="330" Height="170"
Canvas.Top="200" Canvas.Left="320">
<Rectangle Width="330" Height="170" Fill="#FFFFFFFF"
Stroke="#FF000000" RadiusX="10" RadiusY="10"/>
</Canvas>
<%= createRectangles()%>
</Canvas>
В cs-коде напишем:
Код C#:
using System;
using System.Collections;
using System.Text;
namespace SilverlightJSApplication1
{
public partial class XamlForm : System.Web.UI.Page
{
// {0} - TargetName
// {1} - end X value
// {2} - end Y value
private string xamlAnimation = @"<DoubleAnimationUsingKeyFrames BeginTime=""00:00:00""
Storyboard.TargetName=""{0}""
Storyboard.TargetProperty=""(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"">
<SplineDoubleKeyFrame KeyTime=”"00:00:02″” Value=”"{1}”"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime=”"00:00:00″” Storyboard.TargetName=”"{0}”"
Storyboard.TargetProperty=”"(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)”">
<SplineDoubleKeyFrame KeyTime=”"00:00:02″” Value=”"{2}”"/>
</DoubleAnimationUsingKeyFrames>”;
// {0} - Name
// {1} - Width
// {2} - Height
// {3} - Left
// {4} - Top
// {5} - Color (#FFFFFFFF)
// {6} - Visibility (Visible | Collapsed)
private string xamlRectString = @"<Rectangle Width=""{1}"" Height=""{2}"" Fill=""{5}""
Stroke=""#FF000000"" Canvas.Left=""{3}"" Canvas.Top=""{4}""
RenderTransformOrigin=""0.5,0.5"" x:Name=""{0}"" Visibility=""{6}"">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX=”"1″” ScaleY=”"1″”/>
<SkewTransform AngleX=”"0″” AngleY=”"0″”/>
<RotateTransform Angle=”"0″”/>
<TranslateTransform X=”"0″” Y=”"0″”/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>”;
private ArrayList arrayObjects;
protected void Page_Load(object sender, EventArgs e)
{
arrayObjects = new ArrayList(4);
arrayObjects.Add(new XamlRectangle("rect1", 160, 80, 40, 40, 260, 180, "#FFC42B2B", "Visible"));
arrayObjects.Add(new XamlRectangle("rect2", 160, 80, 440, 40, -260, 180, "#FF33AB23", "Visible"));
arrayObjects.Add(new XamlRectangle("rect3", 160, 80, 40, 360, 260, -180, "#FF3A2BC4", "Visible"));
arrayObjects.Add(new XamlRectangle("rect4", 160, 80, 440, 360, -260, -180, "#FFDEBA10", "Visible"));
}
protected string createRectAnimation()
{
StringBuilder result = new StringBuilder();
foreach (XamlRectangle rect in arrayObjects)
{
result.AppendFormat(xamlAnimation, rect.Name, rect.EndX, rect.EndY);
}
return result.ToString();
}
protected string createRectangles()
{
StringBuilder result = new StringBuilder();
foreach (XamlRectangle rect in arrayObjects)
{
result.AppendFormat(xamlRectString, rect.Name, rect.Width, rect.Height, rect.StartX, rect.StartY,
rect.Color, rect.Visibility);
}
return result.ToString();
}
}
}
Да, еще потребуется новый класс XamlRectangle.cs. Его код:
Код C#:
using System;
namespace SilverlightJSApplication1
{
public class XamlRectangle
{
public string Name;
public int Width;
public int Height;
public int StartX;
public int StartY;
public int EndX;
public int EndY;
public string Color; // #FFFFFFFF
public string Visibility; // Visible | Collapsed
public XamlRectangle(string name, int width, int height, int startX, int startY, int endX, int endY,
string color, string visibility)
{
this.Name = name;
this.Width = width;
this.Height = height;
this.StartX = startX;
this.StartY = startY;
this.EndX = endX;
this.EndY = endY;
this.Color = color;
this.Visibility = visibility;
}
}
}
Теперь мы можем генерировать нужные элементы «на лету», в зависимости от начальных условий. Но то, что получилось, пока выглядит не очень красиво. Добавим анимацию и автоматическое изменение размера под всю страницу. Для этого в файле XamlForm.js допишем:
Код JavaScript:
SilverlightJSApplication1.Scene.prototype =
{
handleLoad: function(plugIn, userContext, rootElement)
{
this.plugIn = plugIn;
this.plugIn.content.onResize = Silverlight.createDelegate(this, this.onResize);
this.border = 5;
this.indent = 100;
this.animate = false;
rootElement.addEventListener("MouseLeftButtonDown", Silverlight.createDelegate(this, this.handleMouseDown));
},
// Sample event handler
handleMouseDown: function(sender, eventArgs)
{
if (this.animate == false)
{
this.animation = sender.findName("animation");
this.animation.begin();
this.animate = true;
}
},
onResize: function(sender, args) {
var targetWidth = sender.getHost().content.actualWidth;
var targetHeight = sender.getHost().content.actualHeight;
var page = sender.findName("Page");
page.Width = targetWidth;
page.Height = targetHeight;
var menu = sender.findName("Menu");
menu["Canvas.Top"] = (targetHeight - menu["Height"]) / 2;
menu["Canvas.Left"] = (targetWidth - menu["Width"]) / 2;
var rect1 = sender.findName("rect1");
rect1["Canvas.Top"] = menu["Canvas.Top"] - rect1["Height"] - this.indent + this.border;
rect1["Canvas.Left"] = menu["Canvas.Left"] - rect1["Width"] - this.indent + this.border;
var rect2 = sender.findName("rect2");
rect2["Canvas.Top"] = menu["Canvas.Top"] - rect2["Height"] - this.indent + this.border;
rect2["Canvas.Left"] = menu["Canvas.Left"] + menu["Width"] + this.indent - this.border;
var rect3 = sender.findName("rect3");
rect3["Canvas.Top"] = menu["Canvas.Top"] + menu["Height"] + this.indent - this.border;
rect3["Canvas.Left"] = menu["Canvas.Left"] - rect3["Width"] - this.indent + this.border;
var rect4 = sender.findName("rect4");
rect4["Canvas.Top"] = menu["Canvas.Top"] + menu["Height"] + this.indent - this.border;
rect4["Canvas.Left"] = menu["Canvas.Left"] + menu["Width"] + this.indent - this.border;
}
}
А в файле Default.html.js исправим свойства width и height на 100% Вот теперь все стало совсем красиво. :)
Увы, в данном случае без ложки дегтя не обошлось. То, что красиво выглядит в IE далеко не всегда также выглядит в Firefox. Точнее, если запустить Default.html, в FF, то все будет выглядеть аналогично IE. Но стоит заменить Default.html на Default.aspx, и тут же FF отказывается воспроизводить наш пример. На всякий случай приведу код Default.aspx:
КОД HTML: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>SilverLight Application</title>
<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="Default.html.js"></script>
<script type="text/javascript" src="XamlForm.js"></script>
</head>
<body leftmargin="0" topmargin="0" marginheight="0" marginwidth="0">
<form id="form1" runat="server">
<div id="SilverlightPlugInHost">
<script type="text/javascript">
createSilverlight();
</script>
</div>
</form>
</body>
</html>
Экспериментальным путем выяснил, что FF плохо реагирует на width и height в Default.html.js указанные в %. Поэтому придется от них избавиться. Чтобы в обоих браузерах была полная аналогия придется поправить некоторый код. В файле Default.html.js добавить строку:
Код JavaScript:
var plugIn = null;
Файл XamlForm.js переписать следующим образом:
SilverlightJSApplication1.Scene.prototype =
{
handleLoad: function(plugIn, userContext, rootElement)
{
this.plugIn = plugIn;
this.plugIn.content.onResize = Silverlight.createDelegate(this, this.onResize);
top.plugIn = plugIn;
this.border = 5;
this.indent = 100;
this.animate = false;
rootElement.addEventListener("MouseLeftButtonDown", Silverlight.createDelegate(this, this.handleMouseDown));
if (window.navigator.userAgent.indexOf("MSIE ") > 0)
{
this.plugIn.width = window.document.body.offsetWidth;
this.plugIn.height = window.document.body.offsetHeight;
}
else
{
this.plugIn.width = window.innerWidth;
this.plugIn.height = window.innerHeight;
}
},
// Sample event handler
handleMouseDown: function(sender, eventArgs)
{
if (this.animate == false)
{
this.animation = sender.findName("animation");
this.animation.begin();
this.animate = true;
}
},
onResize: function(sender, args)
{
var page = sender.findName("Page");
page.Width = window.document.body.offsetWidth;
page.Height = window.document.body.offsetHeight;
},
function onBodyResize(body)
{
if (window.navigator.userAgent.indexOf("MSIE ") > 0)
{
top.plugIn.width = window.document.body.offsetWidth;
top.plugIn.height = window.document.body.offsetHeight;
}
else
{
top.plugIn.width = body.innerWidth;
top.plugIn.height = body.innerHeight;
}
}
}
И добавить пару атрибутов в файла Default.aspx:
style=”width: 100%; height: 100%; overflow: hidden;” onresize=”onBodyResize(this)”
Вот теперь все! :)
|