ArcGIS API For Silverlight开发入门

love_lxl 贡献于2011-03-12

作者   创建于2008-12-26 08:45:00   修改者杨成斌  修改于2010-05-15 01:39:00字数70446

文档摘要:其实是ArcGIS API forSilverlight/WPF(以下专注Siverlight部分),那么Silverlight和WPF的关系如何呢?Silverlight原来叫WPF/E,E就是Everywhere,从命名可以看出它们的关系:Silverlight是WPF的一个子集。WPF是.NETFramework 3.0的组成部分之一,微软视其为下一代用户界面,总之很高档就是了(在Vista和Windows7中看到的大量与XP不同的界面,就是WPF的身影);Silverlight可以看做是WPF在浏览器里的一个外挂,用于向网络用户展示自己的强大能力,由于受限于网络环境,所以是WPF的一部分核心功能。Siverlight的设计初衷是跨平台,跨浏览器的。
关键词:

第一节、为什么要用Silverlight API? 你用上3G手机了吗?你可能会说,我就是喜欢用nokia1100,ABCDEFG跟我都没关系。但你不能否认3G是一种趋势,最终我们每个人都会被包裹在3G网络中。1100也不是一成不变,没准哪天为了打击犯罪,会在你的1100上强制装上GPS。GIS工作既然建立在计算机的基础上,当然也得随着IT行业与时俱进。        看看现在计算机应用的趋势吧。云(计算),这个东西可讲不清楚,因为云嘛,飘忽不定的。不过可以这样来看它,以后计算机网络上就有一坨(或者几坨)万能的云,有什么需求云都可以满足我们,包括各种资源或者计算工作,就不需要在自己的机器上安装任何软件了(甚至操作系统都可以由天边那朵云来提供给你)。更具体点,SaaS(Software as a Service),各种的网页邮件系统,google docs(一个在线的office)都是SaaS。收发邮件登陆一个网页就行,而不需要在自己机器上安装一个软件。这就是计算机应用的一个趋势,把所有东西都做到网上。再来看看网上的趋势:RIA(Rich InternetApplication)。RIA简单来讲就是一个网页(网络应用),在完成基本功能的同时,会让你觉得很漂亮,操作起来很舒服,效果很炫,而不是打开后立刻就想关掉它。其实大受欢迎的开心网(各种插件)和twitter,甚至QQ空间等,都有RIA的身影。        好了,ArcGIS之所以在行业领先,特点之一就是它能紧跟计算机发展的趋势。ArcGIS Online就是那朵天边的浮云;JavaScript API,Flex API,Siverlight API就是ArcGIS自己RIA的三驾马车。        这里还得插一句,我觉得ArcGIS Server的主角本来是ADF,通过它我们可以完成一个无所不能的ServerGIS。但在大多数情况下,GIS都是作为特定的业务嵌入在一些MIS中的,相比购买ADF这辆悍马来说,还是直接驾驭三套车跑的更轻快一些。        现在看看我们的主角。其实是ArcGIS API forSilverlight/WPF(以下专注Siverlight部分),那么Silverlight和WPF的关系如何呢?Silverlight原来叫WPF/E,E就是Everywhere,从命名可以看出它们的关系:Silverlight是WPF的一个子集。WPF是.NETFramework 3.0的组成部分之一,微软视其为下一代用户界面,总之很高档就是了(在Vista和Windows7中看到的大量与XP不同的界面,就是WPF的身影);Silverlight可以看做是WPF在浏览器里的一个外挂,用于向网络用户展示自己的强大能力,由于受限于网络环境,所以是WPF的一部分核心功能。Siverlight的设计初衷是跨平台,跨浏览器的。        如果这些还是比较抽象,那么可以造一个排比句来进一步说明。之前先肯定一点,Flash现在在网络中的的主导地位。开始造句。Adobe有Flash,微软有Silverlight;Adobe有AIR,微软有WPF;Flex有mxml,Silverlight有xaml;Adobe有ActionScript,微软有Code-Behind(C#/VB.NET)或者JavaScript;Adobe有CS(包括Dreamweaver,Flash,Fireworks,Photoshop,Illustrator),微软有ExpressionStudio(包括Blend,Web,Design,Media,Encoder)。现在,能够看来Siverlight到底是何方神圣了吧?        最后再来说说ArcGIS这三驾马车(JavaScript API,Flex API,SiverlightAPI)。国外有人说,随着Siverlight API的推出,与Flex API一起,将会使JavaScriptAPI慢慢退出历史舞台,因为前两者就是为RIA而生的。但其实也不然,随着Google和Mozilla工程师的推进,他们能够使JavaScript的执行速度提高非常多,Chrome就是例子。在这种背景下,一些非常cool的程序员会让古老的JavaScript获得重生。到底哪匹马跑得更快?别回答这种问题,赶紧挑一匹自己的马儿,快马扬鞭吧~~        在今年的ESRI开发用户大会上,一阵鼓声过后,ESRI隆重推出了ArcGIS API for Silverlight/WPF(beta)。接下来我将把自己在学习Silverlight API中的一些经历和大家分享,与大伙共同进步。 第二节、Getting Started 这一节来对Silverlight API(ArcGIS API for Silverlight,下同)的开发有个总体的认识。   欲善其事先利其器。要做开发,第一步就得搭建环境。因为是在Siverlight基础上做开发,所以先得整理好Siverlight的开发环境。Silverlight并没有内建在VS2008中,而是作为add-on的形式附加的。在这里可以找到详细的安装步骤: 说明一下,步骤1安装了Silverlight add-on(要求有IDE的SP1补丁包);步骤2安装的是ExpressionStudio中的ExpressionBlend,这个工具相当于可视化的xaml编辑器,可以用来轻松的创建Silverlight程序的用户界面;步骤3中安装的是Silverlight一种非常华丽的图片处理效果,可以参看这里的实例;步骤4包括一些可用的Silverlight控件和例子。接下来再去看看Silverlight API的要求。可以看出对于开发ArcGIS Silverlight程序来说,只有步骤1是必须的,其他都是可选的。之后需要从ESRI网站下载Silverlight API(需要免费注册一个ESRI Global账户),以备后用。 总结一下最常见的安装步骤:1、安装VS2008;2、安装VS2008 SP1;3、安装Silverlight Tools for Visual Studio 2008 SP1。到此,就可进行Silverlight程序的开发了。关于开发环境的搭建,还可以参考yyilyzbc版主的帖子。(做Silverlight API的开发不需要在自己的机器上安装ArcGIS Server,可直接使用ArcGIS Online上的数据;但如果要添加自己的数据,当然还是需要ArcGIS Server了) 下面就来一个Hello World吧,对于GIS来说,理所当然就是展示一张漂亮的世界地图了。具体步骤如下: 1、VS2008中,新建project,选择Silverlight Application; 2、在出现的提示框中选择Add a new ASP.NET Web project to the solution to hostSilverlight;(Silverlight程序与flash一样,相当于网页中的一个插件。第一个选项是将Silverlight嵌入到一个ASP.NET网站中,第二个选项是将Silverlight嵌入到一个临时的html页面中) 3、添加Silverlight API的引用:与.NET程序开发一样,add reference(注意是在Silverlight工程上而不是ASP.NET工程上),找到从ESRI下载的API,选择添加ESRI.ArcGIS.dll; 4、打开Page.xaml,在UserControl标签中添加一句引用,在Grid标签之间添加一些代码,完成后看起来像这样: · · · ·     ·        ·     · · · 5、按F5,运行程序,就完成了我们的hello world in GIS可以在浏览器中看到下面的画面:  看到效果之后,再来对它进行理解吧。          先说下Silverlight的程序的基本背景。page.xaml实际上是一个控件,相当于asp.net中的default.aspx,大部分的工作都在这里面完成(app.xaml相当于global.asax);上面的是xaml(读:[ig`zeml])代码,是微软针对wpf/silverlight的标记语言,与flex中的mxml类似。Silverlight程序中所有的布局工作都是由xaml来完成的;Silverlight2中,VS2008可以实时对xaml的效果做预览,但是这个预览效果是只读的,对于预览中的控件也不可选;为了弥补这个缺陷,可以用前面提到的ExpressionBlend来可视化地设计程序界面,会自动生成对应的xaml代码,使用于复杂的布局和美化工作(可参考Silverlight中的clock例子);再看page.xaml。usercontrol标签(页面的根元素)证明了page.xaml实际上是一个控件类;下面的几句相当于引入了xml的特定命名空间,里面包括了我们的ESRI.ArcGIS;width和height指明了Silverlight控件本身的尺寸,一般我们将这里的width和height属性去掉,已达到全屏的效果(你也可以试试哦);Grid标签是布局控件,相当于html中的表格,可以进行灵活的页面布局,xaml中常用的布局控件还有Canvas和StackPanel;每一个xaml的Control都可以有一个x:Name属性,以便在code-behind页面中对其引用。          之后是我们的主角了。Map标签(继承自xaml的Control)相当于一个Map控件,可以在其中加入图层;这里我们添加了一个ArcGISTiledMapServiceLayer图层(在后面的文章中会专门讲到SilverlightAPI中的图层类型),对应使用的是ArcGIS Server发布的经过cache的服务,作为客户端的API,同JavaScript与FlexAPI一样,都是通过REST方式对资源和操作进行引用的;对这个图层,赋予了一个ID属性,因为SilverlightAPI中的图层是从xaml中的DependencyObject继承而来,所以没有x:Name的属性,为了方便在code-behind(与asp.net类似的托管代码)的代码中找到这个图层,便使用了ID属性;URL的内容便是ArcGIS Online发布好的一个世界地图资源。          到此,应该对这个例子理解的差不多了。如果还想再添加一个图层该怎么办呢?没错,就是在Map标签中再添加一个layer,不过要注意的是,第一个加入的图层会显示在最下面,并且决定了整个Map控件的空间参考信息。大家自然会想到叠加一个自己的数据图层来看看效果,于是对Map标签内容做了修改(china是本机发布的一个中国地图): · 运行后却还是只有世界地图一个图层(已经确保拼写、大小写正确),怎么回事呢?来用事件帮助查找错误吧。 Silverlight能够利用.net的一些核心库内容,包括事件。来对刚才的那个图层添加一个事件:InitializationFailed,当图层添加失败的时候会出发这个事件。添加这个事件的处理也非常简单:在上面的图层中加入InitializationFailed属性,会提示你生成新的eventhandler,默认回车,看上去像这样: · 在事件上面右键单击,Navigate to Event Handler,就会进入前面所说的code-behind页面(本例为C#),添加以下代码: private void ArcGISDynamicMapServiceLayer_InitializationFailed(object sender, EventArgs e) { ESRI.ArcGIS.Layer layer = sender as ESRI.ArcGIS.Layer; MessageBox.Show(layer.InitializationFailure.Message); } 然后运行程序,会得到初始化图层失败的原因: 原来,为了安全原因考虑,同flash一样,Silverlight对跨域访问也做了严格的限制。 要解决这个问题,可以参考帮助中的说明,将两个xml文件保存在网站根目录,比如C:\Inetpub\wwwroot中即可(其实保存其中一个就可以了,ArcGISOnline已经将两个xml文件都放在了网站根目录中,所以我们可以引用上面的服务)。 看下最后的效果吧。 为了更好的理解xaml和Silverlight,建议首先独立完成Silverlight帮助中的两个workthrough:hello world和clock。 下一节将会在事件和Silverlight特性的基础上带给大家一个比较完整的地图实例。 第三节、一个基础地图实例 这节在一个地图实例的基础上,来对Silverlight API中的一些基本概念做一个总体了解,顺便熟悉一下Silverlight的开发知识。点击这里,直接看效果。 根据上一节的知识,可以知道这个Silverlight程序里包含了一个Map控件,并且里面至少有一个WorldImagery的图层。那么Page.xaml里的关键代码看起来应该是这样的: 1. 2. 3. 4. 6. 7. 8. 所有的布局工作都在一个Grid中进行,给它起个名字叫LayoutRoot。Grid里面放了一个esri:Map元素(Map控件),它继承自Silverlight的Control,所以拥有Width和Height属性,默认是Auto,自动填充整个Grid。Map.Layers是一个集合,可以往里面添加layer,这里的layer指的是ArcGIS Server或其他软件发布的地图服务,目前SilverlightAPI中支持的能够直接使用的有ArcGISDynamicMapServiceLayer,ArcGISTiledMapServiceLayer,ArcGISImageServiceLayer,分别对应ArcGIS Server发布的动态地图服务,缓存地图服务(两种Map Service)和ImageService,这三种图层是拿来即用的,如果你想加入别的地图服务,比如WMS服务,则需要自己继承相应类型的的Layer;此外还有GraphicsLayer,ElementLayer,SilverlightAPI特有的FeatureLayer等。这些都会在之后的小节中讲到。强调一下,与ADF开发里MapResourceManager一样,在Map中加入的内容实际上是地图服务,但当做一个layer处理。 下面就对这个例子中的每一部分来做说明(与上图中的序号相对应)。 1、当地图移动时获取地图范围。当地图范围改变后,显示出当前地图范围的边界值。 这部分的页面布局是这样的: 1. 2. 3. 4. 5. 6. 7. 8. 9. 有关xaml中详细的布局知识请大家参照其他例子学习,这里稍作讲解。外面的Gridright这个Grid就是页面右边1、2、3、6的父容器,之所以不用StackPanel是因为6需要贴着页面底部,StackPanel中的元素都会flow贴到一起。三个矩形组合便构成了整体轮廓,由于它们都在一个Canvas中,所以会产生压盖效果。最先加入的rectBottom这个矩形便是最底下的阴影效果,中间的矩形是蓝色框,最上面的矩形是白色的文字显示区域。“{ }”里的内容在xaml中称作markupextention,StaticResource是使用在别处已经定义好的资源(resource)来对本元素的一些属性进行自动赋值,这里用来修饰Rectangle的外观。xaml中除了StaticResource这种markupextention之外还有Binding和TemplateBinding两种markup extention,分别用于数据绑定(databinding)和自定义control的外观。上面的StaticResource是在App.xaml中定义的,这样就可以在本工程的任何页面中使用,当然也可以定义为LayoutRoot这个Grid的Resource。贴出来大家一看就明白了: 1.          2.          11.          21.          31.     它们就相当于网页中的css。如果不使用StaticResource,那么三个矩形看起来应该是这样的: 1. 2. 3. 你猜的没错,在其他矩形框部分也使用到了这些属性。通过实践可以感受到,xaml中的布局在一般使用中比html+css的布局要简单和灵活许多。好了,继续。 Map控件里面已经封装了一些事件来供我们使用,我们可以在需要的时候捕获它们来进行处理。如果做过ArcGIS产品的二次开发,你应该已经想到我们要捕获的就是Map的ExtentChanged事件;而要在地图移动或者缩放的过程中也实时显示地图范围,则还要对ExtentChanging事件做处理。细心的你可能已经发现,在上面的xaml代码中已经对世界地图这个图层的Initialized事件添加了一个hanlder:WorldImageLayer_Initialized。当然可以像这样一样给Map的这两个事件添加handler,但这里并不这么做,而是在世界地图图层的Initialized事件里来绑定它们(移动地图时出发ExtentChanged事件,网速过慢导致图层并未加入到Map中,则会报错)。来看看Page.xaml.cs中的code-behind代码: 1. private void WorldImageLayer_Initialized(object sender, EventArgs e) 2. { 3. Map1.ExtentChanged += new EventHandler(Map1_ExtentChange); 4. Map1.ExtentChanging += new EventHandler(Map1_ExtentChange); 5. } 没错,把两个事件绑定到同一个handler即可。再看看Map1_ExtentChange中的代码: 1. private void Map1_ExtentChange(object sender, ESRI.ArcGIS.ExtentEventArgs e) 2. { 3. TBextent.Text = string.Format(“地图范围:\nMinX:{0}\nMinY:{1}\nMaxX:{2}\nMaxY:{3}”, 4. e.NewExtent.XMin, e.NewExtent.YMin, e.NewExtent.XMax, e.NewExtent.YMax); 5. } 很简单吧?顺便提一下,ExtentEventArgs里既然有NewExtent,当然就有OldExtent了,通过比较这两个变量就可以分析出当前进行的是放大、缩小还是平移操作了。其实还有个更简单的办法,查查看Map的Resolution属性吧。 对于Silverlight API中内容,是不是感觉很容易呢(当然你得做够xaml的功课才行)?那么赶快来看第二部分。 2、当鼠标移动时获取鼠标坐标。  包括屏幕坐标和地图坐标。外观样式方面是这样的: 1. 2. 3. 4. 5. 6. 7. 9. 11. 12. 那么接下来要捕捉那个事件呢?当然就是MouseMove啦。不过如果查看SilverlightAPI中的Map类,发现并没有这个事件。但要记住Map是继承自xaml中的Control,Control继承自FrameworkElement,FrameworkElement继承自UIElement,这里就有一个MouseMove事件了。所以Map控件的MouseMove是xaml中而不是Siverlight API中的事件(当然整个SilverlightAPI都是建立在xaml基础上的)。在esri:Map标签中添加一个MouseMove事件(MouseMove=”Map1_MouseMove”),来看看code-behind代码: 1. private void Map1_MouseMove(object sender, MouseEventArgs e) 2. { 3. if (Map1.Extent != null) 4. { 5. System.Windows.Point screenPnt = e.GetPosition(Map1); 6. TBscreencoords.Text = string.Format(“屏幕坐标:\nX:{0},Y:{1}”, screenPnt.X, screenPnt.Y); 7. 8. ESRI.ArcGIS.Geometry.MapPoint mapPnt = Map1.ScreenToMap(screenPnt); 9. TBmapcoords.Text = string.Format(“地图坐标:\nX:{0}\nY:{1}”, Math.Round(mapPnt.X, 4), Math.Round(mapPnt.Y, 4)); 10. } 11. } 可以看到Map控件提供了屏幕与地图坐标之间转换的方法,好比开发人员的一座桥梁,用来往返于Silverlight特性与地图之间,非常方便。需要说明的是,这里GetPosition(Map1)获得的屏幕坐标是相对于Map控件的,而不是显示器的左上角。ok,继续来看第三部分。 3、Map里的动画效果。 当地图放大和平移时都可以看到平滑的效果,这归功于Silverlight的动画功能。Map在封装完动画效果后,给了我们两个属性来对它们进行设置:PanDuration和ZoomDuration,用于设置这两个动作持续的时间。它们都是TimeSpan类型的变量,合理的设置可以带来良好的用户体验。看看这部分的布局: 1. 2. 3. 4. 5. 6. 7. 8. 9. 11. 12. 13. 15. 16. 主要用到了两个slider控件。再看看拖动滑块时的事件代码,为了省事,这两个事件也用了同一个handler: 1. private void slideranimation_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 2. { 3. Slider s=sender as Slider; 4. if (s.Name == “sliderzoomanimation”) 5. { 6. Map1.ZoomDuration = new TimeSpan(0, 0, Convert.ToInt32(sliderzoomanimation.Value)); 7. TBzoomdurationvalue.Text = string.Format(“当前值:{0}秒”, Convert.ToInt32(sliderzoomanimation.Value)); 8. } 9. else 10. { 11. Map1.PanDuration = new TimeSpan(0, 0, Convert.ToInt32(sliderpananimation.Value)); 12. TBpandurationvalue.Text = string.Format(“当前值:{0}秒”, Convert.ToInt32(sliderpananimation.Value)); 13. } 14. } 对应着地图效果,应该很容易理解。继续第四部分。 4、对地图服务可见性与动态地图服务中图层可见性的控制。 还是要强调一下,WorldImagery和StreetMap两个能看到的地图实际上都是地图服务,当作layer加入到了Map控件中;而动态地图服务USA中的图层Cities,Rivers,States才是与ArcMap中图层相对的概念。对于WorldImagery和StreetMap之间的切换,主要用到了Silverlight API里Layer的 Visible属性;而动态服务中图层可见性的操作,主要是对ArcGISDynamicMapServiceLayer的VisibleLayers数组做了设置。 StreetMap这个服务其实一开始就加入了地图(在esri:Map标签中): 1. 而设置了Visible=”False”。图层不可见时地图不会对它做任何处理,所以不用担心会耗费流量或加重程序负担。         看看布局部分: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 这里使用了ToggleButton,CheckBox和RadioButton都由它派生而来。Silverlight2中的ToggleButton不能设置Group(一个Group中自动限定同时只能有一个控件处于激活状态),不如Flex里的ToggleButton来的方便,所以code-behind中多做了些工作。当然这里使用RadioButton也是可以的。 1. private void TBimagery_Clicked(object sender, RoutedEventArgs e) 2. { 3. if (TBstreetmap.IsChecked==true) 4. { 5. Map1.Layers["WorldImageLayer"].Visible = true; 6. Map1.Layers["WorldImageLayer"].Opacity = 0; 7. TBstreetmap.IsChecked = false; 8. Storyboard sbworldmapshow = makestoryboard(“WorldImageLayer”, 0, 1); 9. Storyboard sbstreetmaphide = makestoryboard(“StreetMapLayer”, 1, 0); 10. sbworldmapshow.Begin(); 11. sbstreetmaphide.Begin(); 12. hidelayername = “StreetMapLayer”; 13. timer.Begin(); 14. } 15. TBimagery.IsChecked = true; 16. } 17. 18. private void TBstreetmap_Clicked(object sender, RoutedEventArgs e) 19. { 20. if (TBimagery.IsChecked==true) 21. { 22. Map1.Layers["StreetMapLayer"].Visible = true; 23. Map1.Layers["StreetMapLayer"].Opacity = 0; 24. TBimagery.IsChecked = false; 25. Storyboard sbstreetmapshow = makestoryboard(“StreetMapLayer”, 0, 1); 26. Storyboard sbworldmaphide = makestoryboard(“WorldImageLayer”, 1, 0); 27. sbstreetmapshow.Begin(); 28. sbworldmaphide.Begin(); 29. hidelayername = “WorldImageLayer”; 30. timer.Begin(); 31. } 32. TBstreetmap.IsChecked = true; 33. } 34. 35. private void timer_Tick(object sender, EventArgs e) 36. { 37. Map1.Layers[hidelayername].Visible = false; 38. } 39. 40. public Storyboard makestoryboard(string layername, double from, double to) 41. { 42. Storyboard sb = new Storyboard(); 43. ESRI.ArcGIS.ArcGISTiledMapServiceLayer layer = Map1.Layers[layername] as ESRI.ArcGIS.ArcGISTiledMapServiceLayer; 44. DoubleAnimation doubleAnim = new DoubleAnimation(); 45. doubleAnim.Duration = new TimeSpan(0, 0, 5); 46. doubleAnim.From = from; 47. doubleAnim.To = to; 48. Storyboard.SetTarget(doubleAnim, layer); 49. Storyboard.SetTargetProperty(doubleAnim, new PropertyPath(“Opacity”)); 50. sb.Children.Add(doubleAnim); 51. 52. return sb; 53. } 当切换两个地图服务时能够看到一个渐变的效果,这里用到了Silverlight中的动画,它们都是在StoryBoard里面进行的,以后的小节中会讲Silverlight中的动画,这里不再废话了,有兴趣的朋友可以自己参考帮助学习。hidelayername是这个一个公用的string变量,用来在切换的动画效果完成后设置不可见的图层Visible属性。timer也是一个StoryBoard: 1. 这里可以看出把StoryBoard也能巧妙的用作计时器。到了特定时间(5秒)后会自动timer_Tick函数,当然也可以使用.net中的各种timer类。         下面是添加动态服务的部分。 1. private void chkboxDynamicLayer_Click(object sender, RoutedEventArgs e) 2. { 3. if (chkboxDynamicLayer.IsChecked == true) 4. { 5. Map1.Layers.Add(california); 6. Map1.ZoomTo(california.FullExtent); 7. 8. if (california.IsInitialized == false) 9. { 10. chkboxDynamicLayer.IsEnabled = false; 11. } 12. chkboxDynamicLayer.Content = “去掉它”; 13. SVlayers.Visibility = Visibility.Visible; 14. } 15. else 16. { 17. Map1.Layers.Remove(california); 18. chkboxDynamicLayer.Content = “添加一个动态图层吧”; 19. SVlayers.Visibility = Visibility.Collapsed; 20. } 21. } 22. 23. private void dynamiclayer_initialized(object s, EventArgs e) 24. { 25. //若图层没有初始化好就移除图层,当然会报错了,所以这样做就不会了 26. chkboxDynamicLayer.IsEnabled = true; 27. Map1.ZoomTo(california.InitialExtent); 28. SVlayers.Visibility = Visibility.Visible; 29. california.ID = “layercalifornia”; 30. 31. ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer = s as ESRI.ArcGIS.ArcGISDynamicMapServiceLayer; 32. if (dynamicServiceLayer.VisibleLayers == null) 33. dynamicServiceLayer.VisibleLayers = GetDefaultVisibleLayers(dynamicServiceLayer); 34. UpdateLayerList(dynamicServiceLayer); 35. } 当添加了动态服务后,会自动弹出一个listbox,当然这些也都是在xaml中定义好的(加在上面的Canvas后面): 1. 2. 3. 4. 5. 8. 9. 10. 11. 这里把ListBox放到了ScrollVierwer中,固定了它的高度,当内容过多时可以自动显示纵向滚动条。这里要提一下,ListBox的内容用到了数据绑定(参考xaml中的DataBinding,有OneTime,OneWay和TwoWay三种模式,这里使用的是默认的OneWay),看起来里面只有一个CheckBox,但它相当于一个模板,在code-behind中设置了ListBox.ItemSource之后,根据该属性的内容自动生成多个CheckBox。代码中自定义了一个LayerListData类,它的几个属性分别与上面的CheckBox属性绑定;将一个List赋给了ListBox.ItemSource,则会自动生成ListBox中的内容。通过一个List类型变量,来控制动态服务的可见图层。代码如下: 1. public class LayerListData 2. { 3. public bool Visible { get; set; } 4. public string ServiceName { get; set; } 5. public string LayerName { get; set; } 6. public int LayerIndex { get; set; } 7. } 8. 9. private int[] GetDefaultVisibleLayers(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicService) 10. { 11. List visibleLayerIDList = new List(); 12. 13. ESRI.ArcGIS.LayerInfo[] layerInfoArray = dynamicService.Layers; 14. 15. for (int index = 0; index < layerInfoArray.Length; index++) 16. { 17. if (layerInfoArray[index].DefaultVisibility) 18. visibleLayerIDList.Add(index); 19. } 20. 21. return visibleLayerIDList.ToArray(); 22. } 23. 24. private void UpdateLayerList(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer) 25. { 26. int[] visibleLayerIDs = dynamicServiceLayer.VisibleLayers; 27. 28. if (visibleLayerIDs == null) 29. visibleLayerIDs = GetDefaultVisibleLayers(dynamicServiceLayer); 30. 31. List visibleLayerList = new List(); 32. 33. ESRI.ArcGIS.LayerInfo[] layerInfoArray = dynamicServiceLayer.Layers; 34. 35. for (int index = 0; index < layerInfoArray.Length; index++) 36. { 37. visibleLayerList.Add(new LayerListData() 38. { 39. Visible = visibleLayerIDs.Contains(index), 40. ServiceName = dynamicServiceLayer.ID, 41. LayerName = layerInfoArray[index].Name, 42. LayerIndex = index 43. }); 44. } 45. 46. LayerVisibilityListBox.ItemsSource = visibleLayerList; 47. } 48. 49. void chkboxToggleVilible_Click(object sender, RoutedEventArgs e) 50. { 51. CheckBox tickedCheckBox = sender as CheckBox; 52. 53. string serviceName = tickedCheckBox.Tag.ToString(); 54. bool visible = (bool)tickedCheckBox.IsChecked; 55. 56. int layerIndex = Int32.Parse(tickedCheckBox.Name); 57. 58. ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer = Map1.Layers[serviceName] as 59. ESRI.ArcGIS.ArcGISDynamicMapServiceLayer; 60. 61. List visibleLayerList = 62. dynamicServiceLayer.VisibleLayers != null 63. ? dynamicServiceLayer.VisibleLayers.ToList() : new List(); 64. 65. if (visible) 66. { 67. if (!visibleLayerList.Contains(layerIndex)) 68. visibleLayerList.Add(layerIndex); 69. } 70. else 71. { 72. if (visibleLayerList.Contains(layerIndex)) 73. visibleLayerList.Remove(layerIndex); 74. } 75. 76. dynamicServiceLayer.VisibleLayers = visibleLayerList.ToArray(); 77. } 5、比例尺。  Silverlight API提供了一个ScaleBar类,可以方便的设置地图比例尺。 1. 2. 3. 4. 需要在初始化的时候设置scalebar的Map属性,顺便来看看整个页面的初始化工作: 1. namespace demo_02_extendedmap 2. { 3. public partial class Page : UserControl 4. { 5. private ESRI.ArcGIS.ArcGISDynamicMapServiceLayer california = new ESRI.ArcGIS.ArcGISDynamicMapServiceLayer(); 6. private string hidelayername; 7. 8. public Page() 9. { 10. InitializeComponent(); 11. 12. scalebar.Map = Map1; 13. scalebarstoryboard.Begin(); 14. TBzoomdurationvalue.Text = string.Format(“当前值:{0}.{1}秒”, Map1.ZoomDuration.Seconds, Map1.ZoomDuration.Milliseconds); 15. TBpandurationvalue.Text = string.Format(“当前值:{0}.{1}秒”, Map1.PanDuration.Seconds, Map1.PanDuration.Milliseconds); 16. california.Url = “http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer”; 17. california.Opacity = 0.5; 18. california.Initialized += new EventHandler(dynamiclayer_initialized); 19. 20. TBimagery.IsChecked = true; 21. makestoryboard(“WorldImageLayer”, 0, 1).Begin(); 22. //切换全屏/窗口 23. Application.Current.Host.Content.FullScreenChanged += new EventHandler(fullscreen_changed); 24. } 25. } 26. } scalebarstoryboard是xaml里自定义的一个动画,效果见比例尺旁的单位。 6、地图相关操作。 Map控件已经内置了一些键盘鼠标事件,但目前不能像JavascriptAPI中那样禁用这些事件。这里还用到了Silverlight程序的一个全屏特性,其实是对Application.Current.Host.Content的一个属性做了设置。直接看代码吧: 1. 2. 3. 4. 5. 6. 8. 9. 放到Gridright Grid中, 1. private void TBfullscreen_Click(object sender, RoutedEventArgs e) 2. { 3. System.Windows.Interop.Content content = Application.Current.Host.Content; 4. content.IsFullScreen=!content.IsFullScreen; 5. } 6. 7. private void fullscreen_changed(object o,EventArgs e) 8. { 9. System.Windows.Interop.Content content=Application.Current.Host.Content; 10. TBfullscreen.IsChecked = content.IsFullScreen; 11. } 7、进度条。 最后还剩下地图中的这个进度条。利用了Map控件内置的一个Progress事件。 1. 2. 3. 4. 5. 在esri:Map标签中加入一个事件:Progress=”Map1_Progress”, 1. private void Map1_Progress(object sender, ESRI.ArcGIS.ProgressEventArgs e) 2. { 3. if (e.Progress < 100) 4. { 5. progressGrid.Visibility = Visibility.Visible; 6. MyProgressBar.Value = e.Progress; 7. ProgressValueTextBlock.Text = String.Format(“正在处理 {0}%”, e.Progress); 8. } 9. else 10. { 11. progressGrid.Visibility = Visibility.Collapsed; 12. } 13. } 好了到此就已经讲完了整个地图功能。尽管想尽可能详细说明每段代码,便于初学的朋友学习,但也不可能面面俱到。没有讲明白的地方大家可以自己思考,查帮助。学习的过程中,不思考,无进步。 下一节我们来看看Silverlight API中提供的小玩具——Widget。 第四节、Widget Widgets翻译过来是小玩具。如果使用过Dojo或者ExtJS等js框架肯定会了解到这个“小玩具”也有大用处,能够在很大程度上减少我们的工作量,快速完成功能需求。能减少多大工作量呢?让我们先来,点击这里,看一个例子。 前两节的地图中,总感觉少点什么……对,就是一个sliderbar,有了它感觉就像汽车有了方向盘一样,能够控制方向了。那么来看看实现上面这个例子中的滑块条需要做什么工作吧。 在silverlight中创建一个UserControl,把上面sliderbar的外观和功能都封装在里面。 来看具体工作。vs中,在silverlight工程上右键单击,add,new item,选择silverlight user control,起名叫mapslider,在mapslider.xaml中填如下代码: 1. 2.          3.             

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享文档获得金币 ] 9 人已下载

下载文档