MSDN Magazine - December 2007 - (Page 48) Figure 3 Polygon Style Utilizing Data Triggers defines a style that, when applied to a Polygon control, will cause the polygon to be rendered with a thick orange border when its associated MapRegion has its IsSelected property set to true, and with a thin black border otherwise. This enables the code driving the application to simply mark a region as selected and then have the UI respond accordingly. Application Object Model WPF data binding is awesome, so I set out to enable it in my application wherever possible. My goals were to be able to write clear, concise, and domain-specific code to drive the app, and to enable the use of Expression Blend to design its interface. To accomplish this, I created an object model for representing map information that heavily utilizes the WPF dependency property system. It consists of three main classes—Maps, MapRegion, and MapPolygon—all derived from DependencyObject. The Map class is the root of the object model and is used to represent a map containing a list of regions. The MapRegion class repDependency properties resents a region or geoallow many tasks to be graphic entity on a map. It implemented directly rather contains a list of polygons, with each polygon describthan requiring controls to ing the boundaries of one be explicitly manipulated by of the contiguous geothe programmer. graphic areas defining the region. The MapPolygon class represents a polygon within a region and contains a collection of Point objects describing its vertices. The Map class has five dependency properties: BoundingBox, ScaleX, ScaleY, TranslateX, and TranslateY. The BoundingBox property is an instance of the Rect class and stores the smallest rectangular area that contains all the regions on the map. The ScaleX, ScaleY, TranslateX, and TranslateY properties are defined to support zooming and panning on the map. These properties are important because the coordinates of the points on the map represent offsets, in kilometers, from the intersection of the equator and the prime meridian. WPF, on the other hand, uses a different coordinate system, one that is defined in terms of offsets, in logical pixels (one logical pixel is equal to 1/96th of an inch), from the upper-left corner of a window. 48 msdnmagazine Map LINQ Because of this, the ScaleX, ScaleY, TranslateX, and TranslateY properties are used to do the following: 1. Scale the map so that it can fit within a window. 2. Flip the map vertically so that it is not drawn upside down. (In the world view, Y coordinates increase as you travel north; in the WPF view, Y coordinates increase as you move toward the bottom of a window.) 3. Move the map so that its upper-left corner corresponds to the upper-left corner of the control that displays the map. You can also use those properties to implement zoom and pan functionality within the UI. For example, multiplying the ScaleX and ScaleY values by 1.5 causes the map to be zoomed in by 150 percent, and adding 50 to its TranslateX property causes the view of the map to be moved 50 kilometers to the west. It’s worth pointing out that changing these values does not mutate the coordinates in the map. Instead, the values are used to inform the user interface of what it needs to do in order to display the map. The Map class exposes its region collection via the Regions property. Unlike the other properties on the class, however, Regions is not a dependency property. Instead, it is a read-only property typed as an ObservableCollection(of MapRegion). It’s not declared as a dependency property because I did not need the ability to assign an entire collection at a time. The collection itself is, however, mutable, and regions can be added and removed from the map using the Add and Remove methods of the ObservableCollection class. Also, because the collection is exposed as an ObservableCollection, it can still participate in the WPF data-binding system. For example, if a ListBox is data-bound to an ObservableCollection, then simply adding a new item to that collection will cause a new item to appear in the ListBox. Similarly, removing an item from the collection will cause it to be removed from the ListBox. Map Classes The MapRegion class defines two dependency properties: BoundingBox and RegionName. The BoundingBox property defines the rectangular area containing the region and RegionName stores the name of the region. The class also defines two collection properties: Polygons and FipsCodes. The Polygons collection stores the polygons that define the region and FipsCodes stores the set of numeric FIPS (Federal Information Processing Standard) codes associated with the region. The class uses FIPS codes because the polygon data I use in the application was downloaded from the United States Census Bureau, which uses FIPS codes to uniquely identify geographical entities within the United States. More information on the Federal Information Processing Standard can be downloaded from itl.nist.gov/fipspubs/by-num.htm. Also, technical information about the U.S. Census Bureau’s public domain TIGER Geographic Database can be accessed at census.gov/geo/www/cob. The MapPolygon class defines two properties. The first is a readonly dependency property named Region that stores a reference to the region containing the polygon. The second is a collection property named Points that stores the set of Point structures that define the vertices of the polygon. You can see the full source for the class in Figure 4. http://itl.nist.gov/fipspubs/by-num.htm http://www.census.gov/geo/www/cob
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.