Silverlight 集成到 ASP.NET AJAX 控件 (十五数码游戏)






4.28/5 (12投票s)
将 Silverlight 集成到 ASP.NET AJAX 控件中 (十五数码游戏)。
引言
最近,我对一项名为 Silverlight 的非常棒的技术产生了兴趣,并且我正在尝试学习它,因为它非常强大。
事实上,Silverlight 有两个版本
- Silverlight 1.0 发布
- Silverlight 2.0 Beta 1
1.0 和 2.0b1 之间的区别很大
- 支持客户端的 .NET 语言 (C#, VB.NET,..) 而非 JavaScript
- 来自 WPF 的大量控件
- Framework 3.5 的子集 (LINQ, ...)
有关差异的完整列表,请参见 此处。
不幸的是,要轻松使用 Silverlight 2.0 Beta 1,需要 Framework 3.5 和 Visual Studio 2008 Personal 或更高版本。
我的想法是将 Silverlight 控件集成到 ASP.NET AJAX 控件中,但如果没有 Visual Studio 2008,这似乎相当困难。因此,我使用 Visual Studio 2005 将 Silverlight 1.0 控件集成到了 ASP.NET AJAX 控件中。
背景
通常,当我开始学习一项新技术时,我总是尝试实现一个非常简单的示例,但为了使其更有趣,我会实现一个完整的应用程序或游戏。
在我小时候,家里没有电脑,我花了一些空闲时间玩一款名为“十五数码游戏”的游戏。这是一款非常简单的游戏,但一开始并不绝对容易解决。有关该游戏的更多详细信息,请参见此 链接。我在 Silverlight 1.0 中实现了它,并将其集成到了 ASP.NET AJAX 控件中。
Using the Code
解决方案分为以下项目
- Web 应用程序:包含托管该控件的 ASP.NET 页面
- 类库:包含该控件
在 Web 应用程序中,我们有一个 ASP.NET Web 窗体,其中包含该控件和 ScriptManager
(ASP.NET AJAX 框架的核心)。
在控件中,我们有以下文件
- FifteenPuzzle.cs:服务器端控件,继承自
ScriptControl
; - FifteenPuzzle.js:包含 JavaScript 类的 JavaScript,该类将 Silverlight 控件集成到 ASP.NET AJAX 控件中;
- FifteenPuzzle.xaml:描述控件静态部分的 XAML (动态部分在 JavaScript 类中生成);
- Helper.js:包含相同有用函数的 JavaScript;
- Silverlight.js:用于将 Silverlight 控件实例化到 HTML div 中的标准 JavaScript;
类 FifteenPuzzle
继承自 ScriptControl
(服务器端所有 ASP.NET AJAX 控件的基类) 并重写了两个方法
GetScriptDescriptors
:用于使用 JSON 格式将参数传递给 JavaScript 类的 \u53d8\u91cf\u3002xamlUrl
:用于生成 Silverlight 中 XAML 静态部分的资源 URL。width
,height
:控件的尺寸。imageUrl
:在游戏处于puzzleRenderMode.Image
模式时使用。puzzleRenderMode
:用于定义渲染游戏的两种不同模式 (Number
或Image
) 的枚举。GetScriptReferences
:用于传递脚本 URL 的方法 (我使用了GetWebResourceUrl
,因为我将所有脚本嵌入到了程序集中)- Controls.Resources.FifteenPuzzle.js:包含 AJAX 控件的 JavaScript 类,继承自
Sys.UI.Control
。 - Controls.Resources.Helper.js:包含一些实用函数。
- Controls.Resources.Silverlight.js:用于将插件实例化到 div 中的默认 Silverlight JavaScript。
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor =
new ScriptControlDescriptor("Controls.FifteenPuzzle", this.ClientID);
descriptor.AddProperty("xamlUrl", this.Page.ClientScript.GetWebResourceUrl(
this.GetType(), "Controls.Resources.FifteenPuzzle.xaml"));
descriptor.AddProperty("width", this.Width.ToString());
descriptor.AddProperty("height", this.Height.ToString());
descriptor.AddProperty("imageUrl", this.ImageUrl);
descriptor.AddProperty("puzzleRenderMode", this.PuzzleRenderMode);
yield return descriptor;
}
protected override IEnumerable<ScriptReference> GetScriptReferences()
{
List<ScriptReference> scripts = new List<ScriptReference>();
ScriptReference scriptReference1 = new ScriptReference();
scriptReference1.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Controls.Resources.FifteenPuzzle.js");
scripts.Add(scriptReference1);
ScriptReference scriptReference2 = new ScriptReference();
scriptReference2.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Controls.Resources.Helper.js");
scripts.Add(scriptReference2);
ScriptReference scriptReference3 = new ScriptReference();
scriptReference3.Path = this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Controls.Resources.Silverlight.js");
scripts.Add(scriptReference3);
return scripts;
}
文件 Controls.Resources.FifteenPuzzle.js 包含 JavaScript 类 Controls.FifteenPuzzle
,该类封装了大部分逻辑。
每个拼图块的位置包含在一个 4x4 的数组中。开始时,数组被初始化为正常顺序,并使用 JavaScript 动态渲染拼图块。
当 puzzleRenderMode
设置为 Number
时,为每个拼图块生成的 XAML 如下
<Canvas Name="FifteenPuzzle1_Cell_15" Canvas.Left="219"
Canvas.Top="370" Canvas.ZIndex="3" Width="100"
Height="100" Background="White" Cursor="Hand">
<Rectangle Fill="#80000000" Width="98" Height="98"
Canvas.Top="2" Canvas.Left="2"
RadiusX="15" RadiusY="15">
</Rectangle>
<Rectangle Width="100" Height="100"
Canvas.Top="0" Canvas.Left="0"
RadiusX="15" RadiusY="15">
<Rectangle.Fill><LinearGradientBrush
StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Green"/>
<GradientStop Offset="0.6" Color="Lime"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle><Rectangle Width="98" Height="98"
Canvas.Top="1" Canvas.Left="1"
RadiusX="14" RadiusY="14">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#FFFFFFFF"/>
<GradientStop Offset="1" Color="#00000000"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Name="FifteenPuzzle1_TextBlock_3_2"
Canvas.Left="0" Canvas.Top="5"
Canvas.ZIndex="1" FontFamily="Verdana"
FontSize="70" Text="15"
FontWeight="Bold" Cursor="Hand" />
</Canvas>
当 puzzleRenderMode
设置为 Image
时,为每个拼图块生成的 XAML 如下
<Image Name="FifteenPuzzle1_Cell_15" Canvas.Left="19"
Canvas.Top="70" Canvas.ZIndex="3" Width="400"
Height="400" Cursor="Hand" Source="Resources/Photo.jpg">
<Image.Clip>
<RectangleGeometry Rect="200,300,100,100"
RadiusX="15" RadiusY="15">
</RectangleGeometry>
</Image.Clip>
<Image.RenderTransform>
<TranslateTransform Name="FifteenPuzzle1_Cell_15_Transform"
X="0" Y="0" />
</Image.RenderTransform>
</Image>
用于打乱拼图块的算法非常简单,可能没有真正优化,但似乎运行良好。我只是创建一个介于 1 和 15 之间的随机数,并尝试该移动是否可用。此算法的问题在于,理论上它可能需要很长时间,但根据“大数定律”,它运行得很好。:)
JavaScript 类中最重要的部分是与 Silverlight 的集成。
在 _renderControl
函数中调用 Silverlight.createObject
,它实例化附加到 HTML DIV 的插件。插件实例化后,将调用事件处理程序 _onXamlLoaded
,并渲染 XAML 的动态部分。
_renderControl : function()
{
this.get_element().innerHTML = String.format("<div id='{0}_Content' " +
"style='width:{1};height:{2}'></div>",
this.get_id(), this.get_width(), this.get_height());
var hostId = String.format("{0}_Host", this.get_id());
var bounds = Sys.UI.DomElement.getBounds(this._getContentElement());
Silverlight.createObject(this.get_xamlUrl(), this._getContentElement(), hostId,
{ width:bounds.width.toString(), height:bounds.height.toString(), version:'1.0' },
{ onError:null, onLoad:Function.createDelegate(this, this._onXamlLoaded) },
null);
},
_onXamlLoaded : function(plugIn, userContext, rootElement)
{
this._plugIn = plugIn;
this._rootElement = rootElement;
this._renderNumbers();
this._drawShuffleButton();
},
我将插件和 rootElement
的引用存储在本地 JavaScript 变量中,因为我使用它来动态生成 XAML,方法是使用 createFromXaml
。实际上,当我生成拼图块时,我使用了以下代码将其附加到 XAML 的静态部分
var numberElement = this._plugIn.content.createFromXaml(sb.toString(), false);
this._rootElement.children.add(numberElement);
以及以下代码通过名称获取元素并附加鼠标单击事件处理程序
var elementNumber = this._rootElement.findName(String.format("{0}_Cell_{1}",
this.get_id(), n));
elementNumber.AddEventListener("MouseLeftButtonDown", this._onNumberClick)
关注点
我知道这是一个非常简单的示例,考虑到使用此框架可用的可能性,但它可以帮助开始理解这项技术,并了解将其集成到现有 ASP.NET 应用程序的可能性。
历史
- 2008 年 5 月 26 日 - 首次发布。
- 2008 年 5 月 30 日 - 添加了在
Number
和Image
之间切换的可能性,并改进了文章详情。