【IT168专稿】在上篇Silverlight4应用Bing Maps构建程序 中,我们探讨了如何使用Bing Maps Silverlight控件并借助Bing提供的地理信息服务呈现针对指定代码的地图信息。在本篇中,我们将继续构建这个案例程序,使之支持浏览器外执行功能并将进一步细化这个实例,使之实现当鼠标移动到指定地理位置时显示相关提示信息。【提示】本文示例开发环境为:
- WINDDOWS XP Professional(SP3);
- .NET 4.0;
- VS2010+Microsoft Silverlight 4 Tools for Visual Studio 2010;
- Bing Maps Silverlight Control SDK。
一、定位和标示地图上的机场
现在,地球服务Geoservice已经启动并运行,地图也初步显示出来了。那么,接下来我们可以标记地图上的机场位置。
首先,我们要创建一个MapLayer类型的私有变量imageLayer:
然后,我们在GetAirports()方法的开头处添加下面的代码:
myMAP.Children.Add (imageLayer);
第一行创建一个类MapLayer的实例。这个地图层类十分重要,可以创建于地图之上,并使用一些给定信息定制。在本例中,我们通过把它添加为地图控件myMAP的子控件的方式实现。
接下来,我们来分析本示例中最为重要的调用Bing服务的代码:
if (_geoservice == null) {
_geoservice = new geoService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
_geoservice.GeocodeCompleted += new EventHandler<geoService.GeocodeCompletedEventArgs>(_geoservice_GeocodeCompleted);
}
_geocodeRequest.Query = id;
_geoservice.GeocodeAsync(_geocodeRequest);
}
上面是典型的以异步方式在Silverlight客户端调用一个外部WCF服务的代码。没有太多需要说明的。但注意在调用方法中传递了一个重要参数id。此参数正是要查询的飞机场的id标记。
接下来,我们来看服务调用成功时的处理返回数据的相关代码:
{
if (e.Result.ResponseSummary.StatusCode == geoService.ResponseStatusCode.Success && e.Result.Results[0].EntityType == "Airport")
{
Image img = new Image();
img.Source = new BitmapImage(new Uri("Flag2_Green.png", UriKind.Relative));
img.Width = 20;
img.Height = 20;
imageLayer.AddChild(img, e.Result.Results[0].Locations[0]);
var airport = Airports.Where(p => p.Name == e.Result.Results[0].DisplayName).FirstOrDefault();
airport.Location = e.Result.Results[0].Locations[0];
}
}
首先,我们需要核实返回的结果状态。如果成功的话(即结果的类型是Airport),我们将创建一个类型为Image的对象,改变了该对象的图像来源(一面绿色的小旗子)及大小属性,然后添加此图像到定制地图层的相应位置。接下来,我们根据服务返回的Airport实例数据,把它添加到前面定义的字典uiElements中。
现在,我们已经在地图上标记好了飞机场的位置。我们的目的是,当选择下拉列表框中的飞机场名时应用程序会在地图中显示此位置对应的数据。为此,我们再回到文件MainPage.xaml中定位到ListBox控件lstbAirports处创建一个SelectionChanged事件处理器。
接下来创建上面的SelectionChanged事件处理器代码,如下所示:
{
myMAP.SetView((lstbAirports.SelectedItem as Airport).Location, 14);
}
在这里,我们仅是调用了地图控件的SetView方法以便把地图的当前焦点设置为你从列表中选择的飞机场所在位置。另外,传递给这个方法的第二个参数代表了要放大显示的倍数。
现在,让我们来补充解释一个前面提到的称为uiElements的变量,其数据类型为Dictionary。这个结构能够把一幅图像映射到飞机场数据以方便后面返回飞机场状态数据时的处理。其初始化代码如下所示:
现在,再切换到事件处理器_geoservice_GeocodeCompleted中。请注意下面的代码:
uiElements.Add(airport, img);
在这段代码中,我们首先确定是否当前飞机场名称位于界面中的下拉列表中。如果不在,则把图像与飞机场数据添加到前面新创建的字典结构中(它们将用于后面要讨论的状态映射)。
二、观察运行时快照
现在,再次运行示例程序,你会看到如下图所示的快照。
图1. 美国飞机场完整列表(其位置完全显示于地图之上:以绿色小旗标识)。
之后,当你进一步放大显示并选择某一个特定的机场(例如Las Vegas机场)时,这个特定地理的信息将得到进一步的放大。
图2. 当你选择Las Vegas机场时地图焦点自动切换到机场并进行放大显示。
三、启动浏览器外执行功能
至此,一个基本的支持Bing Maps的Silverlight应用程序已经能够完整运行了。从现在开始,我们要使其支持浏览器外(Out Of Browser)运行功能。如果你观察应用程序的布局,你会注意到右上角有一个“Install”按钮。该按钮将会把应用程序安装在本地计算机上。这样一来,我们可以在不考虑本机安全策略的情况下消费RSS回馈并从中获得所需的机场的现状信息。
为此,打开工程的属性对话框并切换到Silverlight选项卡,勾选“Enable application running out of the browser”。然后,点击“Out-Of-Browser Settings”按钮,在对应的子对话框中勾选“Require elevated trust When running outside the browser”选项。
图3. 把Silverlight应用程序配置成以Out of Browser方式运行。
现在,把Silverlight应用程序配置成以Out of Browser方式运行。关闭工程属性对话框,并切换到文件MainPage.xaml,并且添加Install按钮相关的Click事件处理器btnInstall_Click。
现在,打开文件MainPage.xaml.cs并定位到btnInstall_Click方法,添加如下代码:
{
if (Application.Current.InstallState != InstallState.Installed)
Application.Current.Install();
}
以上是检查应用程序是否已经安装否则执行OOB安装的典型的Silverlight代码,在此不赘述。现在,你可以再次运行系统来观察程序运行时的快照。
四、获取飞机场RSS状态信息
现在,应用程序已经能够在浏览器外运行。接下来,我们将访问网站http://www.airportfact.com/提供的RSS回馈信息并用之检索全美国飞机场当前状态信息。
首先,我们要编写一个方法ReplaceHTMLChars来修整一下原始返回的RSS回馈信息。
{
StringBuilder sb = new StringBuilder(str);
sb.Replace("\n", string.Empty);
sb.Replace("<b>", string.Empty);
sb.Replace("</b>", string.Empty);
sb.Replace("<strong>", string.Empty);
sb.Replace("</strong>", string.Empty);
sb.Replace("<small>", string.Empty);
sb.Replace("</small>", string.Empty);
sb.Replace("<br />", "\n");
sb.Replace("<br>", "\n");
sb.Replace("<p>", "\n");
sb.Replace("/", "-");
sb.Replace("</p>", string.Empty);
return sb.ToString();
}
接下来,我们还要编写另一个方法GetDescription以便请求适当的RSS回馈信息:
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(
client_DownloadStringCompleted);
string url = "http://www.airportfact.com/feeds/airport/" + search;
client.DownloadStringAsync(new Uri(url));
}
上述GetDescription方法打开我们感兴趣的RSS回馈信息所在的顶层URL位置,并检索指定通过列表框选择的代码相应的机场的标题信息。注意,因为我们把程序修改为浏览器外运行,所以,上述以异步方式调用跨域中的RSS数据是完全没有问题的。
接下来,我们还要编写上面的事件处理器方法client_DownloadStringCompleted,以便处理返回的RSS信息并将其添加到上面创建的地图控件中:
if (e.Error == null)
{
XDocument doc = XDocument.Parse(e.Result);
var item = doc.Elements("rss").Elements("channel").Elements("item")
.FirstOrDefault();
var airport = Airports.Where(p => p.ID == item.Element("title")
.Value.Substring(0, 3)).FirstOrDefault();
try
{
ToolTipService.SetToolTip(uiElements[airport], ReplaceHTMLChars(
item.Element("description").Value));
}
catch (Exception)
{
}
}
}
这个client_DownloadStringCompleted事件处理器负责从返回的XML列表中检索特定机场信息,并更新此飞机场的当前属性值。
为了调用包含在方法GetDescription (string search)中的服务,请切换到_geoservice_GeocodeCompleted方法,并注意观察在下面语句的后面的内容:
正是调用了GetDescription方法,并把飞机场ID属性信息以参数传递过去:
下图给出了本文示例最终的某一运行时快照。图中,当用户单击下拉列表框中的某一个飞机场代码时并把鼠标停放在相应的小旗标记上时,对应的提示信息即显示于当前位置。
图4.最终版本的示例程序运行时某一快照。
五、结论
其实,本文中仅仅涉及到了Bing Maps及其提供的服务的开发一个Silverlight 4应用程序最基本的内容,读者可以把它作为有关这些内容编程的一个入门级教程。然而,你也看到了,如果结合更复杂的多数据源操作和多图层操作以及更细致具体的地图数据提供,你完全可以把本文实例扩展成一个企业级应用的核心模块。
代码下载:UserFiles/File/SL4BingMashup.rar