- 浏览: 38784 次
- 性别:
- 来自: 长春
最新评论
-
yh_private:
amonlei 写道单元测试: 看代码覆盖率
集成测试:看业务 ...
二. 测试的粒度,我们到底应该把粒度控制到多细? -
amonlei:
单元测试: 看代码覆盖率
集成测试:看业务操作覆盖率
不能一 ...
二. 测试的粒度,我们到底应该把粒度控制到多细? -
gigix:
daquan198163 写道另外每次都让hibernate重 ...
二. 测试的粒度,我们到底应该把粒度控制到多细? -
daquan198163:
抛出异常的爱 写道
勇气之类的是个虚词。。。。。
代码评审要时 ...
二. 测试的粒度,我们到底应该把粒度控制到多细? -
daquan198163:
我就是像你说的方法一那样做的,在setUp里new 出很多VO ...
二. 测试的粒度,我们到底应该把粒度控制到多细?
这节在一个地图实例的基础上,来对Silverlight API中的一些基本概念做一个总体了解,顺便熟悉一下Silverlight的开发知识。
点击这里,直接看效果。
根据上一节的知识,可以知道这个Silverlight程序里包含了一个Map控件,并且里面至少有一个WorldImagery的图层。那么Page.xaml里的关键代码开起来应该是这样的:
复制代码
所有的布局工作都在一个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、当地图移动时获取地图范围。
当地图范围改变后,显示出当前地图范围的边界值。
这部分的页面布局是这样的:
复制代码
有关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。贴出来大家一看就明白了:
复制代码
它们就相当于网页中的css。如果不使用StaticResource,那么三个矩形看起来应该是这样的:
复制代码
你猜的没错,在其他矩形框部分也使用到了这些属性。通过实践可以感受到,xaml中的布局在一般使用中比html+css的布局要简单和灵活许多。好了,继续。
Map控件里面已经封装了一些事件来供我们使用,我们可以在需要的时候捕获它们来进行处理。如果做过ArcGIS产品的二次开发,你应该已经想到我们要捕获的就是Map的ExtentChanged事件;而要在地图移动或者缩放的过程中也实时显示地图范围,则还要对ExtentChanging事件做处理。细心的你可能已经发现,在上面的xaml代码中已经对世界地图这个图层的Initialized事件添加了一个hanlder:WorldImageLayer_Initialized。当然可以像这样一样给Map的这两个事件添加handler,但这里并不这么做,而是在世界地图图层的Initialized事件里来绑定它们(移动地图时出发ExtentChanged事件,网速过慢导致图层并未加入到Map中,则会报错)。来看看Page.xaml.cs中的code-behind代码:
复制代码
没错,把两个事件绑定到同一个handler即可。再看看Map1_ExtentChange中的代码:
复制代码
很简单吧?顺便提一下,ExtentEventArgs里既然有NewExtent,当然就有OldExtent了,通过比较这两个变量就可以分析出当前进行的是放大、缩小还是平移操作了。其实还有个更简单的办法,查查看Map的Resolution属性吧。
对于Silverlight API中内容,是不是感觉很容易呢(当然你得做够xaml的功课才行)?那么赶快来看第二部分。
2、当鼠标移动时获取鼠标坐标。
包括屏幕坐标和地图坐标。外观样式方面是这样的:
复制代码
那么接下来要捕捉那个事件呢?当然就是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代码:
复制代码
可以看到Map控件提供了屏幕与地图坐标之间转换的方法,好比开发人员的一座桥梁,用来往返于Silverlight特性与地图之间,非常方便。需要说明的是,这里GetPosition(Map1)获得的屏幕坐标是相对于Map控件的,而不是显示器的左上角。ok,继续来看第三部分。
3、Map里的动画效果。
当地图放大和平移时都可以看到平滑的效果,这归功于Silverlight的动画功能。Map在封装完动画效果后,给了我们两个属性来对它们进行设置:PanDuration和ZoomDuration,用于设置这两个动作持续的时间。它们都是TimeSpan类型的变量,合理的设置可以带来良好的用户体验。看看这部分的布局:
<!--map animation slider-->
复制代码
主要用到了两个slider控件。再看看拖动滑块时的事件代码,为了省事,这两个事件也用了同一个handler:
复制代码
对应着地图效果,应该很容易理解。继续第四部分。
4、对地图服务可见性与动态地图服务中图层可见性的控制。
还是要强调一下,WorldImagery和StreetMap两个能看到的地图实际上都是地图服务,当作layer加入到了Map控件中;而动态地图服务USA中的图层Cities,Rivers,States才是与ArcMap中图层相对的概念。对于WorldImagery和StreetMap之间的切换,主要用到了Silverlight API里Layer的
Visible属性;而动态服务中图层可见性的操作,主要是对ArcGISDynamicMapServiceLayer的VisibleLayers数组做了设置。
StreetMap这个服务其实一开始就加入了地图(在esri:Map标签中):
复制代码
而设置了Visible="False"。图层不可见时地图不会对它做任何处理,所以不用担心会耗费流量或加重程序负担。
看看布局部分:
复制代码
这里使用了ToggleButton,CheckBox和RadioButton都由它派生而来。Silverlight2中的ToggleButton不能设置Group(一个Group中自动限定同时只能有一个控件处于激活状态),不如Flex里的ToggleButton来的方便,所以code-behind中多做了些工作。当然这里使用RadioButton也是可以的。
复制代码
当切换两个地图服务时能够看到一个渐变的效果,这里用到了Silverlight中的动画,它们都是在StoryBoard里面进行的,以后的小节中会讲Silverlight中的动画,这里不再废话了,有兴趣的朋友可以自己参考帮助学习。hidelayername是这个一个公用的string变量,用来在切换的动画效果完成后设置不可见的图层Visible属性。timer也是一个StoryBoard:
<Storyboard x:Name="timer" Completed="timer_Tick" Duration="0:0:5" />
复制代码
这里可以看出把StoryBoard也能巧妙的用作计时器。到了特定时间(5秒)后会自动timer_Tick函数,当然也可以使用.net中的各种timer类。
下面是添加动态服务的部分。
复制代码
这里把ListBox放到了ScrollVierwer中,固定了它的高度,当内容过多时可以自动显示纵向滚动条。这里要提一下,ListBox的内容用到了数据绑定(参考xaml中的DataBinding,有OneTime,OneWay和TwoWay三种模式,这里使用的是默认的OneWay),看起来里面只有一个CheckBox,但它相当于一个模板,在code-behind中设置了ListBox.ItemSource之后,根据该属性的内容自动生成多个CheckBox。代码中自定义了一个LayerListData类,它的几个属性分别与上面的CheckBox属性绑定;将一个List赋给了ListBox.ItemSource,则会自动生成ListBox中的内容。通过一个List类型变量,来控制动态服务的可见图层。代码如下:
复制代码
5、比例尺。
复制代码
需要在初始化的时候设置scalebar的Map属性,顺便来看看整个页面的初始化工作:
复制代码
scalebarstoryboard是xaml里自定义的一个动画,效果见比例尺旁的单位。
6、地图相关操作。
Map控件已经内置了一些键盘鼠标事件,但目前不能像JavascriptAPI中那样禁用这些事件。这里还用到了Silverlight程序的一个全屏特性,其实是对Application.Current.Host.Content的一个属性做了设置。直接看代码吧:
复制代码
放到Gridright Grid中,
复制代码
7、进度条。
最后还剩下地图中的这个进度条。利用了Map控件内置的一个Progress事件。
<!--progressbar 放在LayoutRoot中-->
复制代码
在esri:Map标签中加入一个事件:Progress="Map1_Progress",
复制代码
好了到此就已经讲完了整个地图功能。尽管想尽可能详细说明每段代码,便于初学的朋友学习,但也不可能面面俱到。没有讲明白的地方大家可以自己思考,查帮助。学习的过程中,不思考,无进步。
转自:http://bbs.esrichina-bj.cn/ESRI/thread-44365-1-3.html
点击这里,直接看效果。
根据上一节的知识,可以知道这个Silverlight程序里包含了一个Map控件,并且里面至少有一个WorldImagery的图层。那么Page.xaml里的关键代码开起来应该是这样的:
<Grid x:Name="LayoutRoot"> <esri:Map x:Name="Map1"> <esri:Map.Layers> <esri:ArcGISTiledMapServiceLayer ID="WorldImageLayer" x:Name="WorldImageLayer" Initialized="WorldImageLayer_Initialized" Url="http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer" /> </esri:Map.Layers> </esri:Map> </Grid>
复制代码
所有的布局工作都在一个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、当地图移动时获取地图范围。
当地图范围改变后,显示出当前地图范围的边界值。
这部分的页面布局是这样的:
<Grid x:Name="Gridright" Margin="0,15,20,0" HorizontalAlignment="Right" VerticalAlignment="Stretch"> <!--extent--> <Canvas Width="215" Height="110" VerticalAlignment="Top"> <Rectangle Style="{StaticResource rectBottom}" /> <Rectangle Style="{StaticResource rectMiddle}" /> <Rectangle Style="{StaticResource rectTop}" /> <TextBlock x:Name="TBextent" Margin="20,15,15,0" Text="范围:" TextWrapping="Wrap" FontWeight="Bold" /> </Canvas> </Grid>
复制代码
有关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。贴出来大家一看就明白了:
<Application.Resources> <Style x:Key="rectBottom" TargetType="Rectangle"> <Setter Property="RadiusX" Value="10" /> <Setter Property="RadiusY" Value="10" /> <Setter Property="Fill" Value="#22000000" /> <Setter Property="Canvas.Left" Value="5" /> <Setter Property="Canvas.Top" Value="5" /> <Setter Property="Width" Value="215" /> <Setter Property="Height" Value="110" /> </Style> <Style x:Key="rectMiddle" TargetType="Rectangle"> <Setter Property="RadiusX" Value="10" /> <Setter Property="RadiusY" Value="10" /> <Setter Property="Fill" Value="#775C90B2" /> <Setter Property="Canvas.Left" Value="0" /> <Setter Property="Canvas.Top" Value="0" /> <Setter Property="Width" Value="215" /> <Setter Property="Height" Value="110" /> <Setter Property="Stroke" Value="Gray" /> </Style> <Style x:Key="rectTop" TargetType="Rectangle"> <Setter Property="RadiusX" Value="5" /> <Setter Property="RadiusY" Value="5" /> <Setter Property="Fill" Value="#FFFFFFFF" /> <Setter Property="Canvas.Left" Value="10" /> <Setter Property="Canvas.Top" Value="10" /> <Setter Property="Width" Value="195" /> <Setter Property="Height" Value="90" /> <Setter Property="Stroke" Value="DarkGreen" /> </Style> </Application.Resources>
复制代码
它们就相当于网页中的css。如果不使用StaticResource,那么三个矩形看起来应该是这样的:
<Rectangle RadiusX="10" RadiusY="10" Fill="#22000000" Canvas.Left="5" Canvas.Top="5" Width="215" Height="110" /> <Rectangle RadiusX="10" RadiusY="10" Fill="#775C90B2" Canvas.Left="0" Canvas.Top="0" Width="215" Height="110" Stroke="Gray" /> <Rectangle RadiusX="5" RadiusY="5" Fill="#FFFFFFFF" Canvas.Left="10" Canvas.Top="10" Width="195" Height="90" Stroke="DarkGreen" />
复制代码
你猜的没错,在其他矩形框部分也使用到了这些属性。通过实践可以感受到,xaml中的布局在一般使用中比html+css的布局要简单和灵活许多。好了,继续。
Map控件里面已经封装了一些事件来供我们使用,我们可以在需要的时候捕获它们来进行处理。如果做过ArcGIS产品的二次开发,你应该已经想到我们要捕获的就是Map的ExtentChanged事件;而要在地图移动或者缩放的过程中也实时显示地图范围,则还要对ExtentChanging事件做处理。细心的你可能已经发现,在上面的xaml代码中已经对世界地图这个图层的Initialized事件添加了一个hanlder:WorldImageLayer_Initialized。当然可以像这样一样给Map的这两个事件添加handler,但这里并不这么做,而是在世界地图图层的Initialized事件里来绑定它们(移动地图时出发ExtentChanged事件,网速过慢导致图层并未加入到Map中,则会报错)。来看看Page.xaml.cs中的code-behind代码:
private void WorldImageLayer_Initialized(object sender, EventArgs e) { Map1.ExtentChanged += new EventHandler<ESRI.ArcGIS.ExtentEventArgs>(Map1_ExtentChange); Map1.ExtentChanging += new EventHandler<ESRI.ArcGIS.ExtentEventArgs>(Map1_ExtentChange); }
复制代码
没错,把两个事件绑定到同一个handler即可。再看看Map1_ExtentChange中的代码:
private void Map1_ExtentChange(object sender, ESRI.ArcGIS.ExtentEventArgs e) { TBextent.Text = string.Format("地图范围:\nMinX:{0}\nMinY:{1}\nMaxX:{2}\nMaxY:{3}", e.NewExtent.XMin, e.NewExtent.YMin, e.NewExtent.XMax, e.NewExtent.YMax); }
复制代码
很简单吧?顺便提一下,ExtentEventArgs里既然有NewExtent,当然就有OldExtent了,通过比较这两个变量就可以分析出当前进行的是放大、缩小还是平移操作了。其实还有个更简单的办法,查查看Map的Resolution属性吧。
对于Silverlight API中内容,是不是感觉很容易呢(当然你得做够xaml的功课才行)?那么赶快来看第二部分。
2、当鼠标移动时获取鼠标坐标。
包括屏幕坐标和地图坐标。外观样式方面是这样的:
<!--mouse coords--> <Canvas Width="215" Height="110" Margin="0,120,0,0" VerticalAlignment="Top"> <Rectangle Style="{StaticResource rectBottom}" /> <Rectangle Style="{StaticResource rectMiddle}" /> <Rectangle Style="{StaticResource rectTop}" /> <StackPanel Orientation="Vertical" Margin="20,15,15,0"> <TextBlock x:Name="TBscreencoords" HorizontalAlignment="Left" VerticalAlignment="Center" Text="屏幕坐标:" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock x:Name="TBmapcoords" HorizontalAlignment="Left" VerticalAlignment="Center" Text="地图坐标:" TextWrapping="Wrap" FontWeight="Bold" /> </StackPanel> </Canvas>
复制代码
那么接下来要捕捉那个事件呢?当然就是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代码:
private void Map1_MouseMove(object sender, MouseEventArgs e) { if (Map1.Extent != null) { System.Windows.Point screenPnt = e.GetPosition(Map1); TBscreencoords.Text = string.Format("屏幕坐标:\nX:{0},Y:{1}", screenPnt.X, screenPnt.Y); ESRI.ArcGIS.Geometry.MapPoint mapPnt = Map1.ScreenToMap(screenPnt); TBmapcoords.Text = string.Format("地图坐标:\nX:{0}\nY:{1}", Math.Round(mapPnt.X, 4), Math.Round(mapPnt.Y, 4)); } }
复制代码
可以看到Map控件提供了屏幕与地图坐标之间转换的方法,好比开发人员的一座桥梁,用来往返于Silverlight特性与地图之间,非常方便。需要说明的是,这里GetPosition(Map1)获得的屏幕坐标是相对于Map控件的,而不是显示器的左上角。ok,继续来看第三部分。
3、Map里的动画效果。
当地图放大和平移时都可以看到平滑的效果,这归功于Silverlight的动画功能。Map在封装完动画效果后,给了我们两个属性来对它们进行设置:PanDuration和ZoomDuration,用于设置这两个动作持续的时间。它们都是TimeSpan类型的变量,合理的设置可以带来良好的用户体验。看看这部分的布局:
<!--map animation slider-->
<Canvas Width="215" Height="130" Margin="0,240,0,0" VerticalAlignment="Top"> <Rectangle Style="{StaticResource rectBottom}" Height="130" /> <Rectangle Style="{StaticResource rectMiddle}" Height="130" /> <Rectangle Style="{StaticResource rectTop}" Height="110" /> <StackPanel Orientation="Vertical" Margin="20,15,15,0"> <TextBlock HorizontalAlignment="Left" Text="设置地图缩放动作持续时间:" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock x:Name="TBzoomdurationvalue" HorizontalAlignment="Left" Text="当前值:" TextWrapping="Wrap" FontWeight="Bold" /> <Slider x:Name="sliderzoomanimation" Orientation="Horizontal" Minimum="0" Maximum="20" SmallChange="1" LargeChange="5" Cursor="Hand" ValueChanged="slideranimation_ValueChanged" Width="180" /> <TextBlock HorizontalAlignment="Left" Text="设置地图平移动作持续时间:" TextWrapping="Wrap" FontWeight="Bold" /> <TextBlock x:Name="TBpandurationvalue" HorizontalAlignment="Left" Text="当前值:" TextWrapping="Wrap" FontWeight="Bold" /> <Slider x:Name="sliderpananimation" Orientation="Horizontal" Minimum="0" Maximum="20" SmallChange="1" LargeChange="5" Cursor="Hand" ValueChanged="slideranimation_ValueChanged" Width="180" /> </StackPanel> </Canvas>
复制代码
主要用到了两个slider控件。再看看拖动滑块时的事件代码,为了省事,这两个事件也用了同一个handler:
private void slideranimation_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { Slider s=sender as Slider; if (s.Name == "sliderzoomanimation") { Map1.ZoomDuration = new TimeSpan(0, 0, Convert.ToInt32(sliderzoomanimation.Value)); TBzoomdurationvalue.Text = string.Format("当前值:{0}秒", Convert.ToInt32(sliderzoomanimation.Value)); } else { Map1.PanDuration = new TimeSpan(0, 0, Convert.ToInt32(sliderpananimation.Value)); TBpandurationvalue.Text = string.Format("当前值:{0}秒", Convert.ToInt32(sliderpananimation.Value)); } }
复制代码
对应着地图效果,应该很容易理解。继续第四部分。
4、对地图服务可见性与动态地图服务中图层可见性的控制。
还是要强调一下,WorldImagery和StreetMap两个能看到的地图实际上都是地图服务,当作layer加入到了Map控件中;而动态地图服务USA中的图层Cities,Rivers,States才是与ArcMap中图层相对的概念。对于WorldImagery和StreetMap之间的切换,主要用到了Silverlight API里Layer的
Visible属性;而动态服务中图层可见性的操作,主要是对ArcGISDynamicMapServiceLayer的VisibleLayers数组做了设置。
StreetMap这个服务其实一开始就加入了地图(在esri:Map标签中):
<esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer" Url="http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer" Visible="False" />
复制代码
而设置了Visible="False"。图层不可见时地图不会对它做任何处理,所以不用担心会耗费流量或加重程序负担。
看看布局部分:
<StackPanel HorizontalAlignment="Left" Margin="20,15,0,0"> <Canvas x:Name="Canvasleft" Width="165" Height="90" HorizontalAlignment="Left" VerticalAlignment="Top"> <Rectangle Style="{StaticResource rectBottom}" Width="165" Height="90" /> <Rectangle Style="{StaticResource rectMiddle}" Fill="#7758FF00" Width="165" Height="90" /> <Rectangle Style="{StaticResource rectTop}" Width="145" Height="70" /> <!--change layer--> <StackPanel Margin="20,15,15,0"> <TextBlock Text="切换图层:" TextWrapping="Wrap" FontWeight="Bold" /> <StackPanel Orientation="Horizontal"> <ToggleButton x:Name="TBimagery" Content="Imagery" Click="TBimagery_Clicked" Cursor="Hand" /> <ToggleButton x:Name="TBstreetmap" Content="StreetMap" Click="TBstreetmap_Clicked" Cursor="Hand" /> </StackPanel> <CheckBox Margin="0,5,0,0" x:Name="chkboxDynamicLayer" Content="添加一个动态图层吧" IsChecked="False" Click="chkboxDynamicLayer_Click" Cursor="Hand" /> </StackPanel> </Canvas> </StackPanel>
复制代码
这里使用了ToggleButton,CheckBox和RadioButton都由它派生而来。Silverlight2中的ToggleButton不能设置Group(一个Group中自动限定同时只能有一个控件处于激活状态),不如Flex里的ToggleButton来的方便,所以code-behind中多做了些工作。当然这里使用RadioButton也是可以的。
private void TBimagery_Clicked(object sender, RoutedEventArgs e) { if (TBstreetmap.IsChecked==true) { Map1.Layers["WorldImageLayer"].Visible = true; Map1.Layers["WorldImageLayer"].Opacity = 0; TBstreetmap.IsChecked = false; Storyboard sbworldmapshow = makestoryboard("WorldImageLayer", 0, 1); Storyboard sbstreetmaphide = makestoryboard("StreetMapLayer", 1, 0); sbworldmapshow.Begin(); sbstreetmaphide.Begin(); hidelayername = "StreetMapLayer"; timer.Begin(); } TBimagery.IsChecked = true; } private void TBstreetmap_Clicked(object sender, RoutedEventArgs e) { if (TBimagery.IsChecked==true) { Map1.Layers["StreetMapLayer"].Visible = true; Map1.Layers["StreetMapLayer"].Opacity = 0; TBimagery.IsChecked = false; Storyboard sbstreetmapshow = makestoryboard("StreetMapLayer", 0, 1); Storyboard sbworldmaphide = makestoryboard("WorldImageLayer", 1, 0); sbstreetmapshow.Begin(); sbworldmaphide.Begin(); hidelayername = "WorldImageLayer"; timer.Begin(); } TBstreetmap.IsChecked = true; } private void timer_Tick(object sender, EventArgs e) { Map1.Layers[hidelayername].Visible = false; } public Storyboard makestoryboard(string layername, double from, double to) { Storyboard sb = new Storyboard(); ESRI.ArcGIS.ArcGISTiledMapServiceLayer layer = Map1.Layers[layername] as ESRI.ArcGIS.ArcGISTiledMapServiceLayer; DoubleAnimation doubleAnim = new DoubleAnimation(); doubleAnim.Duration = new TimeSpan(0, 0, 5); doubleAnim.From = from; doubleAnim.To = to; Storyboard.SetTarget(doubleAnim, layer); Storyboard.SetTargetProperty(doubleAnim, new PropertyPath("Opacity")); sb.Children.Add(doubleAnim); return sb; }
复制代码
当切换两个地图服务时能够看到一个渐变的效果,这里用到了Silverlight中的动画,它们都是在StoryBoard里面进行的,以后的小节中会讲Silverlight中的动画,这里不再废话了,有兴趣的朋友可以自己参考帮助学习。hidelayername是这个一个公用的string变量,用来在切换的动画效果完成后设置不可见的图层Visible属性。timer也是一个StoryBoard:
<Storyboard x:Name="timer" Completed="timer_Tick" Duration="0:0:5" />
复制代码
这里可以看出把StoryBoard也能巧妙的用作计时器。到了特定时间(5秒)后会自动timer_Tick函数,当然也可以使用.net中的各种timer类。
下面是添加动态服务的部分。
private void chkboxDynamicLayer_Click(object sender, RoutedEventArgs e) { if (chkboxDynamicLayer.IsChecked == true) { Map1.Layers.Add(california); Map1.ZoomTo(california.FullExtent); if (california.IsInitialized == false) { chkboxDynamicLayer.IsEnabled = false; } chkboxDynamicLayer.Content = "去掉它"; SVlayers.Visibility = Visibility.Visible; } else { Map1.Layers.Remove(california); chkboxDynamicLayer.Content = "添加一个动态图层吧"; SVlayers.Visibility = Visibility.Collapsed; } } private void dynamiclayer_initialized(object s, EventArgs e) { //若图层没有初始化好就移除图层,当然会报错了,所以这样做就不会了 chkboxDynamicLayer.IsEnabled = true; Map1.ZoomTo(california.InitialExtent); SVlayers.Visibility = Visibility.Visible; california.ID = "layercalifornia"; ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer = s as ESRI.ArcGIS.ArcGISDynamicMapServiceLayer; if (dynamicServiceLayer.VisibleLayers == null) dynamicServiceLayer.VisibleLayers = GetDefaultVisibleLayers(dynamicServiceLayer); UpdateLayerList(dynamicServiceLayer); } 复制代码 当添加了动态服务后,会自动弹出一个listbox,当然这些也都是在xaml中定义好的(加在上面的Canvas后面): <ScrollViewer x:Name="SVlayers" Width="165" Visibility="Collapsed" Height="120"> <ListBox x:Name="LayerVisibilityListBox" > <ListBox.ItemTemplate> <DataTemplate> <CheckBox Margin="2" Name="{Binding LayerIndex}" Content="{Binding LayerName}" Tag="{Binding ServiceName}" IsChecked="{Binding Visible}" ClickMode="Press" Click="chkboxToggleVilible_Click" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </ScrollViewer>
复制代码
这里把ListBox放到了ScrollVierwer中,固定了它的高度,当内容过多时可以自动显示纵向滚动条。这里要提一下,ListBox的内容用到了数据绑定(参考xaml中的DataBinding,有OneTime,OneWay和TwoWay三种模式,这里使用的是默认的OneWay),看起来里面只有一个CheckBox,但它相当于一个模板,在code-behind中设置了ListBox.ItemSource之后,根据该属性的内容自动生成多个CheckBox。代码中自定义了一个LayerListData类,它的几个属性分别与上面的CheckBox属性绑定;将一个List赋给了ListBox.ItemSource,则会自动生成ListBox中的内容。通过一个List类型变量,来控制动态服务的可见图层。代码如下:
public class LayerListData { public bool Visible { get; set; } public string ServiceName { get; set; } public string LayerName { get; set; } public int LayerIndex { get; set; } } private int[] GetDefaultVisibleLayers(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicService) { List<int> visibleLayerIDList = new List<int>(); ESRI.ArcGIS.LayerInfo[] layerInfoArray = dynamicService.Layers; for (int index = 0; index < layerInfoArray.Length; index++) { if (layerInfoArray[index].DefaultVisibility) visibleLayerIDList.Add(index); } return visibleLayerIDList.ToArray(); } private void UpdateLayerList(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer) { int[] visibleLayerIDs = dynamicServiceLayer.VisibleLayers; if (visibleLayerIDs == null) visibleLayerIDs = GetDefaultVisibleLayers(dynamicServiceLayer); List<LayerListData> visibleLayerList = new List<LayerListData>(); ESRI.ArcGIS.LayerInfo[] layerInfoArray = dynamicServiceLayer.Layers; for (int index = 0; index < layerInfoArray.Length; index++) { visibleLayerList.Add(new LayerListData() { Visible = visibleLayerIDs.Contains(index), ServiceName = dynamicServiceLayer.ID, LayerName = layerInfoArray[index].Name, LayerIndex = index }); } LayerVisibilityListBox.ItemsSource = visibleLayerList; } void chkboxToggleVilible_Click(object sender, RoutedEventArgs e) { CheckBox tickedCheckBox = sender as CheckBox; string serviceName = tickedCheckBox.Tag.ToString(); bool visible = (bool)tickedCheckBox.IsChecked; int layerIndex = Int32.Parse(tickedCheckBox.Name); ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer = Map1.Layers[serviceName] as ESRI.ArcGIS.ArcGISDynamicMapServiceLayer; List<int> visibleLayerList = dynamicServiceLayer.VisibleLayers != null ? dynamicServiceLayer.VisibleLayers.ToList() : new List<int>(); if (visible) { if (!visibleLayerList.Contains(layerIndex)) visibleLayerList.Add(layerIndex); } else { if (visibleLayerList.Contains(layerIndex)) visibleLayerList.Remove(layerIndex); } dynamicServiceLayer.VisibleLayers = visibleLayerList.ToArray(); }
复制代码
5、比例尺。
Silverlight API提供了一个ScaleBar类,可以方便的设置地图比例尺。 <!--scale bar 放在LayoutRoot Grid中--> <Canvas HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,0,0,20"> <esri:ScaleBar x:Name="scalebar" MapUnit="DecimalDegrees" DisplayUnit="Kilometers" Foreground="Black" FillColor1="White" FillColor2="Blue" /> </Canvas>
复制代码
需要在初始化的时候设置scalebar的Map属性,顺便来看看整个页面的初始化工作:
namespace demo_02_extendedmap { public partial class Page : UserControl { private ESRI.ArcGIS.ArcGISDynamicMapServiceLayer california = new ESRI.ArcGIS.ArcGISDynamicMapServiceLayer(); private string hidelayername; public Page() { InitializeComponent(); scalebar.Map = Map1; scalebarstoryboard.Begin(); TBzoomdurationvalue.Text = string.Format("当前值:{0}.{1}秒", Map1.ZoomDuration.Seconds, Map1.ZoomDuration.Milliseconds); TBpandurationvalue.Text = string.Format("当前值:{0}.{1}秒", Map1.PanDuration.Seconds, Map1.PanDuration.Milliseconds); california.Url = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer"; california.Opacity = 0.5; california.Initialized += new EventHandler<EventArgs>(dynamiclayer_initialized); TBimagery.IsChecked = true; makestoryboard("WorldImageLayer", 0, 1).Begin(); //切换全屏/窗口 Application.Current.Host.Content.FullScreenChanged += new EventHandler(fullscreen_changed); } } }
复制代码
scalebarstoryboard是xaml里自定义的一个动画,效果见比例尺旁的单位。
6、地图相关操作。
Map控件已经内置了一些键盘鼠标事件,但目前不能像JavascriptAPI中那样禁用这些事件。这里还用到了Silverlight程序的一个全屏特性,其实是对Application.Current.Host.Content的一个属性做了设置。直接看代码吧:
<!--operation info--> <Canvas Width="215" Height="110" Margin="0,0,0,30" VerticalAlignment="Bottom"> <Rectangle Style="{StaticResource rectBottom}" /> <Rectangle Style="{StaticResource rectMiddle}" Fill="#77FF0000" /> <Rectangle Style="{StaticResource rectTop}" /> <TextBlock Margin="20,15,15,0" TextWrapping="Wrap" Text="地图操作提示:双击放大 Shift+拖拽:放大到指定范围 Ctrl+Shift+拖拽:缩小到指定范围" /> <ToggleButton x:Name="TBfullscreen" Content="点击切换地图全屏" HorizontalAlignment="Center" Canvas.Left="100" Canvas.Top="15" Height="30" Click="TBfullscreen_Click" /> </Canvas>
复制代码
放到Gridright Grid中,
private void TBfullscreen_Click(object sender, RoutedEventArgs e) { System.Windows.Interop.Content content = Application.Current.Host.Content; content.IsFullScreen=!content.IsFullScreen; } private void fullscreen_changed(object o,EventArgs e) { System.Windows.Interop.Content content=Application.Current.Host.Content; TBfullscreen.IsChecked = content.IsFullScreen; }
复制代码
7、进度条。
最后还剩下地图中的这个进度条。利用了Map控件内置的一个Progress事件。
<!--progressbar 放在LayoutRoot中-->
<Grid HorizontalAlignment="Center" x:Name="progressGrid" VerticalAlignment="Center" Width="200" Height="20" Margin="5,5,5,5"> <ProgressBar x:Name="MyProgressBar" Minimum="0" Maximum="100" /> <TextBlock x:Name="ProgressValueTextBlock" Text="100%" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid>
复制代码
在esri:Map标签中加入一个事件:Progress="Map1_Progress",
private void Map1_Progress(object sender, ESRI.ArcGIS.ProgressEventArgs e) { if (e.Progress < 100) { progressGrid.Visibility = Visibility.Visible; MyProgressBar.Value = e.Progress; ProgressValueTextBlock.Text = String.Format("正在处理 {0}%", e.Progress); } else { progressGrid.Visibility = Visibility.Collapsed; } }
复制代码
好了到此就已经讲完了整个地图功能。尽管想尽可能详细说明每段代码,便于初学的朋友学习,但也不可能面面俱到。没有讲明白的地方大家可以自己思考,查帮助。学习的过程中,不思考,无进步。
转自:http://bbs.esrichina-bj.cn/ESRI/thread-44365-1-3.html
发表评论
-
ArcGIS API for Silverlight开发入门(8):在程序中使用Virtual Earth的服务
2010-08-24 11:50 1540SilverlightAPI中还包括了 ... -
ArcGIS API for Silverlight开发入门(7):使用非AGS数据源的图层
2010-08-24 11:49 2215通过上一节学习,可以看出在Silverlight API中不仅 ... -
ArcGIS API for Silverlight开发入门(6):图层类型小结
2010-08-24 11:48 3333在用SilverlightAPI开发的 ... -
ArcGIS API for Silverlight开发入门(5):任务外包——Tasks
2010-08-24 11:47 2845通过上一节的学习,我 ... -
ArcGIS API for Silverlight开发入门(4):用户与地理信息之间的桥梁--GraphicsLayer
2010-08-24 11:46 3663我们与地图交互的过程时刻在进行着:一个拉框放大操作,或者对地图 ... -
ArcGIS API for Silverlight开发入门(3):Widgets
2010-08-24 11:42 2487Widgets翻译过来是小玩具。如果使用过Dojo或者ExtJ ... -
ArcGIS API for Silverlight开发入门(1):Getting Started
2010-08-24 11:35 2463请各位朋友注意:文中 ... -
ArcGIS API for Silverlight开发入门(0):为什么要用Silverlight API?
2010-08-24 11:32 1472你用上3G手机了吗?你 ...
相关推荐
### ArcGIS API for Silverlight 开发入门 #### 一、为何选择 Silverlight API? 随着信息技术的发展,GIS领域也需要不断地更新迭代以适应新的技术趋势。Silverlight作为一种新兴的技术平台,为GIS开发提供了新的...
**ArcGIS API for Silverlight 3.0** 是Esri公司推出的一...文件名"**ArcGISAPI30forSilverlight.exe**"可能就是该API的安装程序,包含了开发所需的所有组件和文档,开发者可以通过安装这个文件来开始他们的开发之旅。
### ArcGIS API for Silverlight开发入门 #### 一、为何选择Silverlight API? 随着信息技术的不断进步和发展,GIS(地理信息系统)技术也在不断地进化。在众多GIS开发工具中,ArcGIS API for Silverlight因其独特...
总的来说,ArcGIS API for Silverlight是GIS开发的一个重要工具,它结合了Silverlight的优秀特性,为开发者提供了构建下一代网络GIS应用的可能。无论你是选择JavaScript API、Flex API还是Silverlight API,关键在于...
3.0版本是ArcGIS API for Silverlight的一个重要里程碑,它提供了一系列改进和新特性,旨在提升开发者的效率和应用程序的性能。以下是一些关键知识点: 1. **地图服务**:ArcGIS API允许开发者通过连接Esri的Map ...
此应用实例是一个全功能的展示应用。运行调试通过,共享出来,大家参考、...实例充分利用的Silverlight 3的功能,比如:触发器、元件结合等,程序结构合理,实例开发了一系列的用户控件来构建应用,可复用性较强....
arcgis api for flex 开发入门(三)地图浏览控件的使用
在阅读《ArcGIS API for Silverlight开发入门》这本书时,你将学习到如何设置开发环境、创建第一个地图应用、加载不同类型的图层、执行地理分析以及如何利用API提供的丰富功能进行应用扩展。书中还会涵盖错误处理、...
对于那些希望在Web应用中嵌入地图、地理分析功能的开发者来说,学习ArcGIS API for Silverlight是一个很好的选择。这本书不仅介绍了技术本身,还提供了丰富的实践案例,帮助读者逐步掌握Silverlight和GIS的结合应用...