目录
Silverlight 应用程序的大小
动态生成的 XAML
动态生成的 XAP
请求内容
缓存下载的内容
下载工具
使用 XAP 程序包
处理 XAP 内容
总结
下载 Silverlight 插件并不是问题的核心所在,因为此操作只需几秒钟且只能运行一次。但是所下载应用程序的大小会有什么影响呢?
Silverlight 应用程序的大小
动态生成的 XAML
Silverlight 插件本质上设计用于显示 XAML 内容。如果 XAML 附带了一些代码隐藏,则该插件会对代码进行处理以生成用户界面并支持任何编码行为或效果。如果您要下载的是 XAML,那么可以通过 URL 直接指向该 XAML;如果不是,则可以通过 XAP 扩展名来引用 Silverlight 程序包。
图 1 返回 XAML 的 HTTP 处理程序
<%@ WebHandler Language="C#" Class="XamlGenHandler" %>
using System;
using System.Web;
public class XamlGenHandler : IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
// Prevent caching of the response
context.Response.Expires = -1;
// Set the type of data we're returning
context.Response.ContentType = "text/xaml";
// Create some XAML and return it down the wire
context.Response.Write("<Canvas xmlns=
'http://schemas.microsoft.com/client/2007' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>" +
"<TextBlock Foreground='black' Padding='10' FontSize='20'>
<Run>XAML content</Run><LineBreak/>" +
"<Run>[generated " + DateTime.Now.ToLongTimeString() + "]</Run>" +
"</TextBlock></Canvas>");
}
public bool IsReusable
{
get {return true;}
}
}
动态生成的 XAP
图 2 返回 XAP 程序包的 HTTP 处理程序
<%@ WebHandler Language="C#" Class="XapGenHandler" %>
using System;
using System.Web;
public class XapGenHandler : IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
// XAP file to return
string xapFile = "...";
// Set the type of data we're returning
context.Response.ContentType = "application/octet-stream";
// Create some XAML and return it down the wire
content.Response.WriteFile(xapFile);
}
public bool IsReusable
{
get {return true;}
}
}
如果想返回 XAP 内容,可将响应的内容类型设置为 application/octet-stream,这是一种 MIME 类型,通常用于标识通用的二进制内容。
<asp:Silverlight ID="Xaml1" runat="server"
Source="~/xap.ashx"
MinimumVersion="2.0.30523"
Width="100%"
Height="100%" />
在这两个示例中,Silverlight 应用程序的工厂均位于 Web 服务器上。如果宿主页面需要动态指示要下载的内容,则这不失为一种不错的方法。
但是,这只是一种可能的情况。还有另一种情况可能更常见,即需要下载当前 Silverlight 应用程序的可选组件。在这种情况下,选择和下载外部内容的逻辑全部位于客户端上运行的 Silverlight 插件中。
请求内容
Silverlight 2 为按需下载代码和/或 XAML 提供了一个丰富而又强大的 API,可用来下载内容并将其插入到现有的 XAML 文档对象模型中。
StackPanel1.Children.Add(downloadedContent);
由参数表示的用户控件被添加到 StackPanel XAML 元素的 Children 集合中。呈现是即时性的,而用户界面是实时更新的。
此方法使您能够永久存储下载的内容。但在某些情况下,这可能有些多余。另一种简单的方法不需要任何额外的工作:让浏览器为您缓存 XAP
缓存下载的内容
从 Web 服务器获得的 XAP 程序包对浏览器而言没有任何特殊含义。因此,浏览器在缓存从 Web 服务器获得的任何其他内容的同时也会缓存它,但这一切都遵守在宿主 HTML 页面中由请求或类似 meta 标记中的 cache-control 和 "expires" HTTP 标头所确定的请求缓存策略。
最后还要注意,保存在浏览器缓存中的 XAP 程序包的存留完全取决于用户。如果用户决定在某个时间清除缓存,则其中的所有内容都将丢失(包括 XAP 程序包)。要永久存储 Silverlight XAP 程序包,必须求助于独立存储(此主题也安排在第 2 部分进行介绍)。
下载工具
在编写 Silverlight 应用程序时,要记住届时需要用到但却未打包到应用程序的 XAP 中的所有资源都必须从服务器显式
WebClient wc = new WebClient();
wc.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(callback);
wc.DownloadStringAsync(address);
DownloadStringAsync 方法操控 HTTP GET 并捕获 URL 响应作为一个字符串。此字符串由相关联的回调接收,如下所示:
void callback(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
return;
string response = e.Result;
...
}
WebClient wc = new WebClient();
wc.OpenReadCompleted +=
new OpenReadCompletedEventHandler(callback);
wc.OpenReadAsync(address);
关联回调的结构与上一示例中的完全相同。最后,使用 DownloadStringAsync 方法获取一个简单的字符串;使用 OpenReadAsync 方法获取任意数据的流。无论您决定下载字符串还是流,都属于个人喜好问题,在本质上取决于您打算如何使用接收到的数据。
您可以使用 Headers 属性来指定附加标头,因为默认情况下该类并不指定任何标头。但应注意,您设置的某些标头会被 Framework 剥离而改为进行内部管理。这些标头包括 Referer、Connection 和 User-Agent。Content-Type 标头(如果已设置)会被保留。
XAML 字符串下载完毕后,可使用 XamlReader 类将其转换为 UI 元素,然后即可添加到现有文档对象模型中。下面的代码显示了如何以编程方式从某个 URL 下载 XAML 字符串。请注意,您需要提供 URL 作为 Uri 对象:
WebClient client = new WebClient();
client.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(OnDownloadCompleted);
Uri uri = new Uri("xaml.ashx", UriKind.Relative);
client.DownloadStringAsync(uri);
该 URL 可以指向纯 XAML 资源或指向返回 text/xaml 响应的端点。下面的代码将处理下载的 XAML 并将其附加到可视树中的一个占位符上:
void OnDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
// Parse XAML to a UI element
string xaml = e.Result;
UIElement dom = XamlReader.Load(xaml) as UIElement;
// Append to the DOM
Placeholder.Children.Clear();
Placeholder.Children.Add(dom);
}
如前所述,占位符可以是插件中当前呈现的文档对象模型中的任何元素。请注意,UI 元素的子元素会形成一个集合,它们将呈现为一个序列。这意味着,为避免元素之间产生不必要的重叠,在更新时应首先删除这些元素然后再重新添加。
使用 XAP 程序包
XAP 程序包内含整个 Silverlight 应用程序,其用户界面主要由一个用户控件构成,此控件实际上只是 XAML 标记和代码的容器。
图 3 动态下载 XAP 程序包
public partial class Page : UserControl
{
private UIElement content = null;
private TabItem item = null;
public Page()
{
InitializeComponent();
}
private void chkNewContent_Click(object sender, RoutedEventArgs e)
{
bool shouldDisplay = (sender as CheckBox).IsChecked.Value;
if (shouldDisplay)
{
if (!IsContentAvailable())
DownloadContent();
else
ShowContent();
}
else
{
HideContent();
}
}
private bool IsContentAvailable()
{
return (content != null);
}
private void DownloadContent()
{
Downloader dl = new Downloader();
dl.XapDownloaded +=
new EventHandler<XapEventArgs>(OnPackageDownload);
dl.LoadPackage("more.xap", "more.dll", "More.ExtraTab");
}
void OnPackageDownload(object sender, XapEventArgs e)
{
content = e.DownloadedContent;
ShowContent();
}
private void HideContent()
{
this.TabList.Items.Remove(item);
}
private void ShowContent()
{
item = new TabItem();
item.Header = "Extra tab";
item.Content = content;
this.TabList.Items.Add(item);
}
}