本文内容:Silverlight的自定义UserControl,NavigationService(页面导航),WP的Application Bar,ArcGIS API的ElementLayer。
上一节中,已经完成了程序的准备工作,利用FeatureLayer来显示超市位置,接下来的几篇文章中我们就来依次实现程序的四个功能点:
- 点击查看某个超市的详细信息;
- 按空间范围查看某几个店面的销售总额;
- 按时间的方式动态查看每个店面的营业情况;
- 对某个店面的近期营业情况做出具体分析。
第一个功能在GIS中叫做Identify。这个功能的作用是,当使用者激活该功能后,点击地图上的某个超市图标,会弹出一个类似气泡的小窗口(InfoWindow)显示超市名称,点击这个气泡后,程序可导航到另一个页面来显示该超市的详细信息。
由于很多功能都是利用单击地图的方式来实现,为了不让这些功能混淆,我将程序中的四个功能分别做成不同的工具(封装成不同的类),点击某个工具后就激活它,此时地图或其中的控件就响应该工具的功能。
我们在工程中新建一个文件夹Tools,放置与功能点有关的类;并在其中新建一个Identify.cs的文件,用来实现我们的第一个功能点。
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类)显示超市店名。
这个类是我们自定义的一个UserControl,可以很方便的给其中加入额外的功能,比如点击了这个InfoWindow后,将程序导航到超市详细信息的页面。关于这个InfoWindow类详细代码以及在线例子,可以在这里查看。此外2.1以后版本的API中也提供了InfoWindow的ToolKit,有兴趣的同学可以在这里查看。
Windows Phone程序中,由于屏幕尺寸原因,如果需要另外显示比较多的内容,则需要将程序导航到一个新的页面中,比如这里我们需要显示超市的详细信息。
所以在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
没有评论:
发表评论