Silverlight 1.0 全JavaScript智能感知






3.81/5 (16投票s)
一篇关于Silverlight 1.0 全JavaScript智能感知的文章
我们今天在这里所做的一切都记录在一个40分钟的网络直播中,网址是: mms://ttvv.tv/users/publicftp/justinangel/intellisense.wmv
让我们在Expression Blend 2 August preview中创建一个新的Silverlight 1.0项目,将第一页的背景颜色更改为“LightGreen”,然后绘制一个小矩形。这是我们当前的XAML文件
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480"
Background="#FF90EE90" <-- LightGreen
x:Name="Page" <-- Name of Canvas
>
<Rectangle x:Name="myRectangle" Width="240" Height="144" Stroke="#FF000000"
Canvas.Left="200" Canvas.Top="144"> <-- Small Rectangle
</Rectangle>
</Canvas>
这个基本的XAML文件包含一个Canvas
和一个rectangle
。让我们修改rectangle
,使其看起来像这样
<Rectangle x:Name="myRectangle" Width="240" Height="144" Stroke="#FF000000"
Canvas.Left="200" Canvas.Top="144">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#FF1F4AB5" Offset="0.053"/>
<GradientStop Color="#FFD97F38" Offset="0.779"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
以及一个Storyboard
,使rectangle
在两秒钟内改变背景颜色。
<Canvas.Resources>
<Storyboard x:Name="changeBackground">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty=
"(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="00:00:00" Value="#FFD97F38"/>
<SplineColorKeyFrame KeyTime="00:00:02.1000000" Value="#FF6BD938"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty=
"(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="00:00:00" Value="#FF1F4AB5"/>
<SplineColorKeyFrame KeyTime="00:00:02.1000000" Value="#FF5B0E54"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Canvas.Resources>
最后,我们希望在Canvas
加载时触发一个名为CanvasLoaded
的JavaScript函数。
<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480"
Background="#FF90EE90"
x:Name="Page"
Loaded="CanvasLoaded"
>
<Canvas.Resources>
...
</Canvas.Resources>
<Rectangle x:Name="myRectangle" Width="240" Height="144" Stroke="#FF000000"
Canvas.Left="200" Canvas.Top="144">
...
</Rectangle>
</Canvas>
让我们看看在Expression Blend 2 August Preview中这个屏幕的样子
很简单。现在让我们使用Visual Studio 2008 Beta2查看位于我们Default_html.js文件中的CanvasLoaded
函数。
嗯,这是canvas
加载事件,所以发送者必须是Canvas
类型!
我们肯定能获得Canvas
的智能感知。
等等。什么??没有Canvas
的智能感知??
嗯,我想打印出我们Canvas
的背景颜色。
那么我们会写什么呢?
嗯..好吧,我猜我可以在没有智能感知的情况下工作,然后写点东西。
好了,我们需要Silverlight JavaScript智能感知!
如何在Visual Studio 2008 Beta 2中使用Visual Studio Extensions for Silverlight设置Silverlight 1.0 JavaScript智能感知
- 请访问这里并下载最新的“JavaScript Silverlight Intellisense”版本。
- 打开zip文件。我们得到两个文件
- 将这两个文件拖放到Visual Studio 2008 Beta 2项目中。
- 在JS文件的顶部添加一个脚本引用。
/// <reference path="intellisense.js" />
- 在我们的HTML页面中,为intellisense.js添加一个script src。
<script type="text/javascript" src="intellisense.js"></script>
就是这样!
我们完成了。
现在您已经拥有Silverlight 1.0的JavaScript智能感知。
我们要做的第一件事是将我们的sender
转换为一个强类型Canvas
对象。
现在让我们看看从这个奇怪的Convert.ToCanvas
函数中得到了什么。
我们在JavaScript代码中获得了Silverlight对象的完整智能感知!
我们获得了属性、事件、方法、集合、索引器,它们都是强类型的!
处理属性
所以让我们打印出我们Canvas
的背景。
我们甚至在JavaScript中获得了SDK注释!
但是这是什么?...这个背景属性给了我们一个Brush
?什么是brush
?
等等,我们用的是SolidColorBrush
,那么它和这个Brush
有什么关系?
好的,那么让我们把我们的Brush
Convert
成SolidColorBrush
。
我们得到了新的Color
属性!
让我们打印出来。
在我们的屏幕上,我们得到
我们得到-7278960,这是一个RGB值,代表LightGreen。
所以,让我们做一些更棘手的事情,让我们改变我们Canvas
的背景。
我们将创建一个新的SolidColorBrush
。
我们的背景颜色已经改变了!
处理方法
还记得我们添加了一个动画,让我们的Storyboard
改变背景颜色吗?
让我们启动那个Storyboard
。
<Canvas.Resources>
<Storyboard x:Name="changeBackground">
...
</Storyboard>
</Canvas.Resources>
让我们在我们的canvas
上使用findName
方法。
您注意到我们的FindName
方法返回一个DependencyObject
类型的类了吗?
我们需要将通用的DependencyObject
转换回Storyb
。oard
以前
操作后
处理事件和全局类型变量
让我们捕获鼠标左键单击事件,让我们的canvas
将不透明度更改为0.3。
function CanvasLoaded(sender, eventArgs)
{
var someElement = Convert.ToCanvas(sender);
...
someElement.add_MouseLeftButtonDown(OnMouseLeftButtonDown);
}
function OnMouseLeftButtonDown(sender, eventArgs)
{
}
我们可以看到Canvas
元素在新的OnMouseLeftButtonDown
方法中超出了作用域,所以我们必须将someElement
移到更高的作用域。
var someElement;
function CanvasLoaded(sender, eventArgs)
{
someElement = Convert.ToCanvas(sender);
...
}
function OnMouseLeftButtonDown(sender, eventArgs)
{
}
让我们看看我们在智能感知方面是如何处理的。
在我们将sender
转换为Canvas
之后,我们在CanvasLoaded
中获得了someElement
的智能感知,但在OnMouseLeftButtonDown
中却没有。
我们需要将someElement
定义为一个类型为Canvas
的全局变量。
现在让我们把不透明度改为0.3,就像我们想要的。
最终结果...
在点击鼠标左键之前
操作后
处理内联函数(匿名委托)和附加属性
当我们在键盘上按下箭头键时,让我们的rectangle
在Canvas
周围移动。
这与C# 2.0匿名委托使用的语法非常相似。
为了获取按下的键,我们需要eventArgs.Key
属性,所以让我们转换我们的eventArgs
。
我碰巧知道(因为Jon Galloway告诉我们)“15”代表向上箭头键。
好的,现在我们需要将我们的Rectangle
向上移动...所以让我们改变它的Canvas.Top
附加属性。
请注意,我们正在使用DependencyObject
的setValue
和getValue
,它们并非仅限于Rectangle
。
让我们创建我们的Canvas.Top
依赖属性。
someElement.add_KeyUp(function(sender, eventArgs)
{
var e = Convert.ToKeyboardEventArgs(eventArgs);
if (e.get_key() == 15)
{
var rect = someElement.findName("myRectangle");
var top = Convert.ToDependencyProperty("Canvas.Top");
rect.setValue(top, rect.getValue(top) - 10);
}
});
在点击向上箭头按钮之前
操作后
多态性和Is/As模式
如果您曾经使用过C#,您将非常熟悉类型转换的Is/As模式。
Is类型转换模式

As类型转换模式
我们在这里看到的是基本的多态性。我们获得了一个Perctangle类型,将其引用类型改回其父类DependencyObject
,然后再改回Rectangle
。
我们在JavaScript代码中也有类似C# Is/As模式的模式。
JavaScript Is模式
JavaScript As模式
“Convert.IsXXX”方法返回一个值,无论元素是否可以转换为XXX。
“Convert.ToXXX”方法在值不可转换为XXX时返回null
。
处理扩展方法
假设我们经常改变前面看到的Rectangle
的Canvas.top
附加属性。
如此普遍,它应该是一个扩展方法。
我们希望将其添加到Rectangle
类的智能感知中,而无需更改原始类代码。
C# 3.0语法如下
我们向Rectangle
类添加了一个新方法,而无需更改其代码!
我们在JavaScript Silverlight对象中做同样的事情。
我们将创建一个名为Extensions.js的新文件(因为它是一个好名字)
我们需要向我们的intellisense.js文件添加一个引用。
我将通过在解决方案资源管理器中选择intellisense.js文件并将其拖放到我们的Extensions.js文件中来实现。
让我们将我们的Extension
方法添加到Rectangle
。
我们需要确保ChangeByHowMuch
是一个已知的Number
类型,所以我们将添加一个param
语句。
现在让我们改变我们Rectangle
的Canvas.Top
。
现在让我们将这个Extensions.js文件作为script标签引用到我们的HTML页面。
并且我们需要在需要智能感知的地方引用我们的Extensions.js文件。
请注意,我们不再直接引用intellisense.js文件。我们的extensions.js引用它,所以我们不需要。
运行此代码后,我们可以看到矩形位于我们Canvas
的底部
类型安全、隐藏元素字段和JavaScript调试
让我们看一下这个方法
它期望一个Double
类型的Number
。所以这个参数是可以接受的
canvas.set_opacity(0.5);
但是关于...
canvas.set_opacity("really shiny opacity please!");
让我们先将此代码直接运行到Canvas
Silverlight CLR类型。
我们可以通过使用我们Silverlight JavaScript类上的Element
隐藏字段来访问它。
请注意,我们没有获得我们元素类型的任何智能感知。
var canvas = Convert.ToCanvas(sender);
canvas.element.opacity = "really shiny opacity please!";
我们直接从Silverlight 1.0 CLR那里得到了这个有些神秘的错误消息
让我们用我们的Silverlight JavaScript智能感知对象试试。
var canvas = Convert.ToCanvas(sender);
// canvas.element.opacity = "really shiny opacity please!";
canvas.set_opacity("really shiny opacity please!");
然后我们得到
让我们打开JavaScript调试的调用堆栈窗口。
在调用堆栈中,我们可以看到
我们可以看到是TypeSafety
检查失败了。
双击第二行
我们在编辑器中可以看到
现在,只是为了好玩,让我们打开我们的快速监视窗口(自VS2003以来是CRTL + ALT + Q)并评估this
。
我们可以看到我们Silverlight JavaScript智能感知对象上的所有属性、方法和事件。
让我们检查this.element
,这是普通的Silverlight CLR 1.0对象。
什么也没有,我们没有原生Silverlight对象的JavaScript调试监视。
现在,让我们尝试将一个字符串添加到我们的Canvas.Children
集合中。
var canvas = Convert.ToCanvas(sender);
canvas.get_children().add("blue shiny rectangle");
我们得到了以下运行时错误
让我们尝试一些更微妙的东西。还记得这段代码吗?
我们使用了多态性,将一个继承自Brush
的SolidColorBrush
作为Brush
参数发送。
让我们将SolidColorBrush
转换回DependencyObject
并将其发送到方法
这工作正常,没有引发错误。
让我们尝试发送一个被引用为DependencyObject
的Rectangle
。
我们得到以下错误
我们需要一个Brush
。
我们可以得到一个SolidColorBrush
,因为它继承自Brush
。
我们可以得到一个SolidColorBrush
的DependencyObject
,因为SolidColorBrush
仍然继承自Brush
。
我们不能将Rectangle
的DependencyObject
Convert
成Brush
。
部署
使用自定义智能感知JavaScript语法进行开发会导致部署问题。
我们是将intellisense.js部署到客户端浏览器吗?还是从JavaScript代码中剥离智能感知功能?
我们可以根据需要做任何事情。
带有Intellisense.js文件的部署
看看intellisense.js的文件大小。
它超过1MB!太大了!
而且大部分实际上是注释、空格和变量名。
所以,通过下载intellisense.js文件,您将获得一个名为intellisense.compressed.js的被剥离了内容的版本。
您可以将脚本标签HTML引用更改为这个压缩后的JavaScript文件,您的客户只会下载压缩版本,而您仍然可以获得完整的智能感知。
在生成Intellisense.js文件的过程中,我们使用了Atif Aziz的JavaScript压缩器来创建这个压缩版本。
您可以看到它只压缩到完整intellisense.js文件的40%左右。
市面上有成百上千的JavaScript压缩器,有些将我们的intellisense.js文件压缩成了**仅仅100KB的文件**。
如果您想找到它们,只需在Google上搜索:“JavaScript compressor”或“JavaScript shrink”。
但是,通过下载,我只能提供一个基于.NET的软件的压缩文件。
但是您可以随意使用您自己的压缩器处理这个文件,并根据需要进行部署。
如果您找到一个具有更好结果并且是用.NET编写的压缩器,我很乐意使用它。
不带Intellisense.js文件的部署
项目Codeplex页面上有两个下载在此。
第一个只包含前面提到的两个JavaScript文件。
第二个下载包含生成intellisense.js的.NET软件的代码。
不用担心,您不需要它。
在该软件内部,有以下屏幕
(您现在可能在想:“所以,这是那个告诉我如何使用Silverlight的人?那一定是史上最丑的屏幕!”)
让我们使用今天编写的以下JavaScript代码
var someElement = Convert.ToCanvas(null);
function CanvasLoaded(sender, eventArgs)
{
someElement = Convert.ToCanvas(sender);
var brush = Convert.ToSolidColorBrush(someElement.get_background());
alert(brush.get_color();
var newBrush = SolidColorBrush.createFromXaml(sender.getHost());
newBrush.set_color(Convert.ToColor("Aqua"));
var newBrushAsDependencYObject = Convert.ToDependencyObject(newBrush);
someElement.set_background(newBrushAsDependencYObject);
Convert.ToStoryboard(someElement.findName("changeBackground")).begin();
someElement.add_MouseLeftButtonDown(OnMouseLeftButtonDown);
someElement.add_KeyUp(function(sender, eventArgs)
{
var e = Convert.ToKeyboardEventArgs(eventArgs);
if (e.get_key() == 15)
{
var rect = someElement.findName("myRectangle");
var top = Convert.ToDependencyProperty("Canvas.Top");
rect.setValue(top, rect.getValue(top) - 10);
}
});
}
并将其复制到“JavaScript with intellisense”文本框中。
我们得到相同的JavaScript代码,但没有任何智能感知功能。
它使用普通的Silverlight CLR对象。
这是完整的翻译代码
var someElement = null;
function CanvasLoaded(sender, eventArgs)
{
someElement = sender;
var brush = someElement.background;
alert(brush.color);
var newBrush = sender.getHost().content.createFromXaml("<SolidColorBrush />");
newBrush.color = "Aqua";
someElement.background = newBrush;
someElement.findName("changeBackground").begin();
someElement.addEventListener("MouseLeftButtonDown", OnMouseLeftButtonDown);
someElement.addEventListener("KeyUp", function(sender, eventArgs)
{
var e = eventArgs;
if (e.key == 15)
{
var rect = someElement.findName("myRectangle");
var top = "Canvas.Top";
rect.setValue(top, rect.getValue(top) - 10);
}
});
}
}
所以我们甚至不需要在部署场景中包含任何JavaScript文件。
这个小工具只是一个从JavaScript中移除智能感知代码的简单示例。
我们可以对Visual Studio 2008插件使用相同的机制,该插件可以在智能感知代码和非智能感知代码之间进行转换。
我们可以创建一个小型的.NET IHttpHandler
,它将只向浏览器请求提供已移除智能感知代码的JavaScript文件。
所以关于部署,取决于您。
如果您需要我的任何帮助,我都在。
问题、后续和建议
Bug
您会在此智能感知中发现bug。
说真的,我没聪明到能在10小时内构建一个完美的类型系统。
请访问项目Codeplex页面在此并创建一个新问题。
写下您收到的错误,添加相关的最小化代码。
如果某项功能不按预期工作,请告诉我您的预期以及实际发生的情况。
如果可能,附加截图。
功能请求
此外,您可能还需要其他功能,例如我提到的HttpHandler
,或者更多需要强类型化的内容(例如DependencyProperties
或Keys
)。
同样,打开一个新问题,我会尽力而为。
检查更新
此项目在发布后的前30-60天内必将经历持续的变更。
如果您正在使用此项目,请订阅我们的RSS feed,以便及时收到通知。
RSS feed可以在此处找到。
万一Codeplex页面不够,这里是我的个人联系方式
Justin-Josef Angel,
高级.NET顾问,Microsoft C# MVP
电子邮件: J@JustinAngel.Net
电话: +972 546 567789
我对此是认真的,请不要犹豫与我联系。
好了,就这样。
历史
- 2007年12月20日:发布于The Code Project
- 2007年8月1日:发布于CodePlex