ArcGIS API for Windows Phone开发实例(4):点击查看超市信息

  本文内容:Silverlight的自定义UserControl,NavigationService(页面导航),WP的Application Bar,ArcGIS API的ElementLayer。
  上一节中,已经完成了程序的准备工作,利用FeatureLayer来显示超市位置,接下来的几篇文章中我们就来依次实现程序的四个功能点:

  • 点击查看某个超市的详细信息;
  • 按空间范围查看某几个店面的销售总额;
  • 按时间的方式动态查看每个店面的营业情况;
  • 对某个店面的近期营业情况做出具体分析。

  第一个功能在GIS中叫做Identify。这个功能的作用是,当使用者激活该功能后,点击地图上的某个超市图标,会弹出一个类似气泡的小窗口(InfoWindow)显示超市名称,点击这个气泡后,程序可导航到另一个页面来显示该超市的详细信息。
  由于很多功能都是利用单击地图的方式来实现,为了不让这些功能混淆,我将程序中的四个功能分别做成不同的工具(封装成不同的类),点击某个工具后就激活它,此时地图或其中的控件就响应该工具的功能。
  我们在工程中新建一个文件夹Tools,放置与功能点有关的类;并在其中新建一个Identify.cs的文件,用来实现我们的第一个功能点。

clip_image002

   1: public class Identify

   2:     {

   3:         private bool _isActivated;

   4:         private ElementLayer _elementLayer; //InfoWindow layer 

   5:         public Map _map1 { get; set; }

   6:         public GraphicsLayer GLayer { get; set; } //supermarket layer

   7:         public bool IsActivated

   8:         {

   9:             get { return _isActivated; }

  10:             set

  11:             {

  12:                 if (_isActivated != value)

  13:                 {

  14:                     _isActivated = value;

  15:                     if (value)

  16:                     {

  17:                         if(_elementLayer ==null)

  18:                             _ elementLayer = new ElementLayer();

  19:                         if (!_map1.Layers.Contains(_elementLayer))

  20:                             _map1.Layers.Add(_elementLayer);

  21:                         GLayer.MouseLeftButtonDown += GLayer_MouseLeftButtonDown;

  22:                     }

  23:                     else

  24:                     {

  25:                         if (_map1.Layers.Contains(_elementLayer))

  26:                             _map1.Layers.Remove(_elementLayer);

  27:                         GLayer.MouseLeftButtonDown -= GLayer_MouseLeftButtonDown;

  28:                         if (GLayer.SelectedGraphics.Count() > 0)

  29:                             GLayer.SelectedGraphics.ToList()[0].UnSelect();

  30:                         _ elementLayer.Children.Clear();

  31:                     }

  32:                 }

  33:             }

  34:         }

  35:         

  36:         public Identify(Map map,GraphicsLayer glayer)

  37:         {

  38:             _map1 = map;

  39:             GLayer = glayer;

  40:         }

  41:     }

在这个类(Identify工具)的构造函数中,有两个参数:一个Map控件和一个GraphicsLayer,分别是我们程序中的Map控件和超市图层FeatureLayer(继承自GraphicsLayer),因此我们可以在Identify工具中控制它们的行为。
  IsActived属性控制着这个工具的状态,如果IsActived=ture,则该工具处于激活状态,此时地图和超市图层响应Identify工具规定的行为;如果IsActived=false,则代表程序不使用该工具,做一些清理工作。其他工具也是如此,可以用代码来控制某一时刻只能有一个工具处于激活状态。
  另外我们还可以看到,Identify这个类中还有一个ElementLayer类型的_Elayer属性。ElementLayer是ArcGIS API for Windows Phone/Silverlight/WPF中的一种图层类型,主要用来承载Silverlight中的原生对象(UIElement),比较关键的一点是,ElementLayer中的元素会随着地图范围的变化而变化(缩放/平移),而不用自己去处理这些UIElement的地理坐标。所以在这里我们就用ElementLayer来放置我们的气泡(InfoWindow)。
  来看一下Identify的主要功能:

   1: void GLayer_MouseLeftButtonDown(object sender, GraphicMouseButtonEventArgs e)

   2:         {

   3:             if (GLayer.SelectedGraphics.Count() > 0)

   4:                 GLayer.SelectedGraphics.ToList()[0].UnSelect();

   5:  

   6:             Graphic g = e.Graphic;

   7:             _ELayer.Children.Clear();//remove other infowindow

   8:             InfoWindow infoWindow = new InfoWindow(_ELayer, e.Graphic);

   9:             ESRI.ArcGIS.Client.ElementLayer.SetEnvelope(infoWindow, new Envelope((e.Graphic.Geometry as MapPoint), (e.Graphic.Geometry as MapPoint)));

  10:             _ELayer.Children.Add(infoWindow);

  11:  

  12:             e.Graphic.Select();

  13:         }

  GraphicsLayer(超市图层)的MouseButtonDown事件可以保证,只有当点击到某个Graphic(超市)后,才会触发此事件,而点击空白地方是不会触发此事件的。我们在这里并需要做任何查询操作,就可以通过被点击Graphic的Attributes属性获得该超市的所有信息,因为所有超市的属性信息(还记得OutFields属性的“*”吗)在FeatureLayer初始化的时候,已经被传送到了客户端(手机上)。点击某个超市后,屏幕上会出现一个气泡(InfoWindow类)显示超市店名。

clip_image002[5]

  这个类是我们自定义的一个UserControl,可以很方便的给其中加入额外的功能,比如点击了这个InfoWindow后,将程序导航到超市详细信息的页面。关于这个InfoWindow类详细代码以及在线例子,可以在这里查看。此外2.1以后版本的API中也提供了InfoWindow的ToolKit,有兴趣的同学可以在这里查看
  Windows Phone程序中,由于屏幕尺寸原因,如果需要另外显示比较多的内容,则需要将程序导航到一个新的页面中,比如这里我们需要显示超市的详细信息。

clip_image004

  所以在InfoWindow本身的单击事件中,用下面的代码将程序导航到显示超市详细信息的页面(Attributes.xaml)。

   1: private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)

   2:         {

   3:             //add clicked graphic to app level, so attributes.xaml could access it

   4:             App app = Application.Current as App;

   5:             if (app.AppParameters.ContainsKey("IdentifyGraphic"))

   6:                 app.AppParameters["IdentifyGraphic"] = Graphic;

   7:             else

   8:                 app.AppParameters.Add("IdentifyGraphic", Graphic);

   9:             (app.AppParameters["MainPage"] as PhoneApplicationPage).NavigationService.Navigate(new Uri("/Tools/Attributes.xaml", UriKind.Relative));

  10:         }

  其中重要的类是NavigationService,每个Windows Phone的页面(PhoneApplicationPage)都有这样一个属性。在多页面的Silverlight程序中也使用此类来导航。虽然Navigate方法可以在页面之间传递参数(类似asp.net),但仅限于string类型,因此我们的超市属性信息集合不好处理。我在App.xaml.cs文件里的Application类中,定义一个全局的Dictionary,用于存储我们需要用到的全局变量。

   1: //used to store variables which need to pass from one page to another

   2:         public Dictionary<string, object> AppParameters;

  先将被点击的Graphic存入程序的Silverlight的全局变量AppParameters中,然后在Attributes.xaml页面就能取到。Attributes.xaml中用ListBox控件来显示超市信息,由于Windows Phone基于Silverlight 3,而Graphic的属性信息Attributes是Dictionary集合,dictionary binding在Silverlight 4中才支持,所以这里我们依然需要使用到DictionaryConverter来在绑定过程中进行转换。Attributes.xaml页面:

   1: <!--ContentPanel - place additional content here-->

   2:         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

   3:             <ListBox x:Name="listbox" Margin="0,0,-12,0" ItemsSource="{Binding}">

   4:                 <ListBox.ItemTemplate>

   5:                     <DataTemplate>

   6:                         <StackPanel Margin="0,0,0,17" Width="432">

   7:                             <Canvas>

   8:                                 <Rectangle Fill="#FF83919D" Canvas.Left="0" Canvas.Top="0" Height="31" Width="800"/>

   9:                                 <TextBlock Text="店名"

  10:                         TextWrapping="Wrap" Margin="12,-4,4,5" FontSize="26" Foreground="White"/>

  11:                             </Canvas>

  12:                             <TextBlock Text="{Binding Path=Attributes, Converter={StaticResource MyDictionaryConverter}, ConverterParameter=Name}" 

  13:                             TextWrapping="Wrap" FontSize="30" Margin="12,28,12,3" Foreground="#FF188DC6"/>

  14:                             <Canvas>

  15:                                 <Rectangle Fill="#FF83919D" Canvas.Left="0" Canvas.Top="0" Height="31" Width="800"/>

  16:                                 <TextBlock Text="地址"

  17:                         TextWrapping="Wrap" Margin="12,-4,4,5" FontSize="26" Foreground="White"/>

  18:                             </Canvas>

  19:                             <TextBlock Text="{Binding Path=Attributes, Converter={StaticResource MyDictionaryConverter}, ConverterParameter=Address}" 

  20:                             TextWrapping="Wrap" FontSize="30" Margin="12,28,12,3" Foreground="#FF188DC6"/>

  21:                             。。。。。。。。。。。。

  22:                         </StackPanel>

  23:                     </DataTemplate>

  24:                 </ListBox.ItemTemplate>

  25:             </ListBox>

  26:         </Grid>

  Attributes.xaml.cs文件:

   1: public partial class Attributes : PhoneApplicationPage

   2:     {

   3:         private Graphic _graphic;

   4:         public Attributes()

   5:         {

   6:             InitializeComponent();

   7:  

   8:             _graphic = (Application.Current as App).AppParameters["IdentifyGraphic"] as Graphic;

   9:             GraphicCollection gc = new GraphicCollection();

  10:             gc.Add(_graphic);

  11:             listbox.ItemsSource = gc;

  12:         }

  13:     }

  完成了整个Identify功能后,在MainPage.xaml页面中,我们在Application Bar中添加四个按钮,点击某个按钮后,就激活相应的工具。比如在点击了Identify功能按钮后,我们需要将Identify工具的IsActived属性设为true,将其他工具的IsActived属性设为false。最后别忘了,在超市图层FeatureLayer初始化时(Update事件),实例化我们的四个工具:

   1: public partial class MainPage : PhoneApplicationPage

   2:     {

   3:         private Identify _OpIdentify;

   4:         private SpatialQuery _OpSpatialQuery;

   5:         private TimeQuery _OpTimeQuery;

   6:         private Analysis _OpAnalysis;

   7:         private FeatureLayer _FLayer;

   8:  

   9:         // Constructor

  10:         public MainPage()

  11:         {

  12:             InitializeComponent();

  13:             _FLayer = map1.Layers["BusinessLayer"] as FeatureLayer;

  14:             _FLayer.UpdateCompleted += (s, a) =>

  15:             {

  16:                 //Add MainPage to app level, so other pages can navigates from it.

  17:                 App app = Application.Current as App;

  18:                 if (!app.AppParameters.ContainsKey("MainPage"))

  19:                     app.AppParameters.Add("MainPage", this);             

  20:                 _OpIdentify = new Identify(map1, _FLayer);

  21:                 _OpSpatialQuery = new SpatialQuery(map1, BusyIndicator);

  22:                 _OpTimeQuery = new TimeQuery(map1);

  23:                 _OpAnalysis = new Analysis(map1,this,BusyIndicator);

  24:             };

  25:         }

  26:     }

  下一节中,我们来实现按空间范围查看某几个店面的销售总额的功能,也就是SpatialQuery这个类。

参考资料:

Windows Phone中的Application Bar和Application Menu:
http://msdn.microsoft.com/en-us/library/ff431801(v=VS.92).aspx

Windows Phone中的页面和导航框架:
http://msdn.microsoft.com/en-us/library/ff402536(v=vs.92).aspx

Silverlight中自定义UserControl:
http://www.cnblogs.com/Terrylee/archive/2008/03/08/Silverlight2-step-by-step-part10-using-user-controls.html

ArcGIS API for Windows Phone/Silverlight/WPF中的ElmentLayer:
http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#ElementLayer

6 thoughts on “ArcGIS API for Windows Phone开发实例(4):点击查看超市信息”

  1. 王振

    您好 我是一个初学者 我按照您的教程 当添加完identity类之后 下面一段代码是看一下identity的主要功能 那段代码是放在mainpage.xaml.cs中吗?可是放在里面后 identity中的GLayer_MouseLeftButtonDown和主要功能代码中的GLayer都不识别 是不是中间我漏掉什么过程了 谢谢了

    1. diligentpig

      identify.cs里面主要是封装了一个可以以isactived控制器是否启用的,可以与地图交互的工具类,所有与地图交互,结果展现的内容都封装在这个文件里。而具体的使用,只需要在mainpage.cs中设置isactived为true或false就可以了。
      具体完整代码可以在这里下载到:http://blog.newnaw.com/?p=761

  2. 疯子离

    您好,我是一个初学者,现在使用的是ArcGIS Runtime for windowsphone3.0,在FeatureLayer的属性里找不到Symbol属性。
    请问在这种情况下如何自定义地图控件上要素的符号呢?

    1. 菩提老王 Post Author

      symbol是针对特定的graphic设置的,也就是说symbol是graphic的一个属性,不是featurelayer的属性。
      当然也可以设置featurelayer的renderer,相当于给所有graphics设置了symbol。

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>