Cross Browser Mobile Application Solutions

The future is all about mobile. We hear this more and more. Anybody who followed the recent Adobe Max conference in LA will have seen Adobes vision of the future; cloud computing and mobile. But in 2011, the mobile space remains confusing; smartphones, tablets, native apps, hybrid apps, mobile web apps. Where do we begin?

Let’s try to demystify things a little.

Mobile Hardware and Software

There are two basic types of mobile hardware: smartphones and tablets. There are big variations in each category, one of the most critical is size. Size as it relates to portability, and more importantly screen. Smartphone screens vary from 2.1″ to just over 4″; in contrast tablets have a range of 7″ to 10.1″. Why is screen size so critical? Try interacting with an ArcGIS map on a 2.1″ screen!

Users can interact with mobile applications in one of two ways. They can use their Web browser to access a Web site, or they can download and install an application from one of the Mobile App stores. Most Web pages have been built for the PC. This means mouse interaction. Increasingly, Web sites are being optimized for mobile viewing and interaction. This means built for use on smaller screens, and allowing for finger interaction. This usually requires multiple Web sites; one for PC’s and one or more for mobiles. Now suppose your company has taken advantage of the excellent Flex Viewer for ArcGIS to give access to your spatial data. You share with your users the news of a new widget you have added, one perfect for use on mobiles. The deluge of angry iPhone and iPad users, complaining they cannot access the Web site, makes you regret your announcement. It’s an unfortunate reality that both Flash and Silverlight will not run on the browser of any Apple device.

Visiting Apple’s App store or Google’s Android Market, users can access an array of mobile apps. These are installed mobile apps; both free and fee based. A simple search on GIS or maps in an App Store produces a nice list of mobile apps. Let’s imagine you have a unique idea for a mobile application; one you’d like to push into one of these stores. Where do you begin? In the old days, we are talking here about months not years, you would first choose the platform to target for the app. That was usually Apples iOS, for running on the iPad or iPhone. Next hire an Objective C programmer. And build a so-called native mobile application. Once done, launch it into the Apple marketplace and watch it jump off the shelves, figuratively. Android from Google has seen increasing popularity. Suppose you wanted to take your app and push it into the Android market. Stop. Rewind. Hire a Java programmer. Have him rebuild the application. Push it to the Android Market. You get the idea; a single mobile app running on different platforms requires multiple rewrites. When you add in bug fixing and updates, this has the potential to be expensive.

Cross Platform Mobile Apps

In a perfect world we should be able to write an application once and run it across all mobile devices. Whether it be a Web or installed application respectively. Thanks to HTML5 and Adobe Air this is now possible. HTML5 is an augmentation of the current HTML 4.01 specification, which adds new features that designers and developers will be able to use. Important new additions include multimedia elements and dynamic graphics, allowing for improvements in data visualization, image manipulation, and video. In combination with Javascript, HTML5 presents some new and exciting possibilities for Web development. Apple iOS, Android, BlackBerry 6, and HP/Palm WebOS devices all support HTML5 features. HTML5 can be a compelling Web solution for multi-device and multi-screen applications.

Adobe AIR is a sister technology to Adobe Flex. AIR does not run in a browser like Flex; it is used to create an installed application, which has access to more local resources than are available to Flex. Mobile AIR is a new flavor of AIR, designed specifically for mobile devices. Adobe has made some important changes to their existing development tools. Now a project built in mobile Adobe AIR can be cross compiled to run on Apple iOS, Android and Blackberry. That is one code base for multiple platforms!

So now an application built in HTML5 will run on ALL mobile web browsers. An application built in mobile AIR, and distributed in the App stores, can be installed on all major mobile devices. That is huge. One more thing. If you build your HTML5 app for the mobile Web and at some time in the future decide you want this to be an installed app. Step forward PhoneGap; an open source tool to convert an HTML5 app to a cross platform installable app. So there is one code base for both Web and installed apps!

We live in exciting times. It could well be that we are seeing the end of the PC. Applications which are location-focused are a huge part of the new mobile revolution. The cross platform development tools, described in this article, available to designers and developers, will simplify the building of state of the art mobile solutions.

Flex Paths and Button Skins

So you’re building a Flex application. And you want to skin your buttons. But, alas, you’re tired of rectangular buttons. Never fear, Flex paths are here.

First you need to define the coordinates of the vector shape you want to create.

<fx:Declarations>
    <fx:String id="arrowPath">
        M 0 0
        L 20 0
        L 100 0
        L 120 20
        L 100 40
        L 0 40
        L 20 20
        L 0 0
    </fx:String>
</fx:Declarations>

Then, you’ll need to create a Graphic object. You can use a scale9Grid to fit the width of the label text without distortion.

<s:Graphic includeIn="up"
           height="100%" width="100%"
           scaleGridTop="1" scaleGridBottom="39"
           scaleGridLeft="20" scaleGridRight="100">
    <s:Path data="{arrowPath}">
        <s:fill>
            <s:LinearGradient id="gradientUp" rotation="90">
                <s:GradientEntry color="0x4e77b3"/>
                <s:GradientEntry color="0x345c98"/>
            </s:LinearGradient>
        </s:fill>
        <s:stroke>
            <s:LinearGradientStroke rotation="90">
                <s:GradientEntry color="0x7399CF"/>
                <s:GradientEntry color="0xd3d3d3"/>
                <s:GradientEntry color="0x23467B"/>
            </s:LinearGradientStroke>
        </s:stroke>
    </s:Path>
</s:Graphic>

You’re on a roll now, so you toss a few buttons into a button bar, back ‘em up so they saddle up next to each other, and you’ve got something like this:

Bada bing.  Bada boom.

Five Reasons You Should Be Excited About the 10.1 ArcGIS Runtime SDK

With ArcGIS version 10.1, Esri will introduce the ArcGIS Runtime and associated SDKs. There’s already a lot of buzz about the Runtime in the developer community, and for good reason. The runtime was been architected from the ground up with an eye on addressing some familiar challenges for GIS developers: exceedingly complex, fine-grained object models; complicated deployment; poor performance; large and memory intensive applications; lack of native 64-bit support … the list goes on. Esri hopes to eliminate many of these “pain points” with the new Runtime SDK, and I think most of us are also ready to let the healing begin. With its powerful yet coarse-grained architecture, it might be tempting to view the new ArcGIS Runtime as MapObjects and ArcGIS Engines love child, but there are (at least) five reasons why it’s more than that.

1)      Simplified deployment

You may have already seen the now almost legendary demo where an Esri dev drags his Runtime project on to a thumb drive, walks it to another machine and fires it up without an install. It never fails to gets gasps from the crowd, and for good reason. How many times have you had to sheepishly tell your client “hmm, it works on my machine”? The ArcGIS Runtime eliminates complicated deployments by packaging everything required by the application into a (relatively) small deployment package. A simple tracking application I built with a pre-beta version of the runtime weighed in at about 175 megs. Since the Runtime is split into several “functionality sets”, you can keep your deployment lean by eliminating functionality your application doesn’t require. As a bonus, a Runtime application doesn’t care about other versions of ArcGIS you might have installed, and will happily run side-by-side with its progenitors.

2)      Performance

The ArcGIS Runtime has been architected to take advantage of available CPU resources, including multiple processors and cores. It provides true multithreading and native 32 and 64-bit support. It also supports an asynchronous programming pattern (made possible by its multithreaded architecture) that contributes to an improved user experience. The ArcGIS Runtime display architecture has also been optimized for speed.

3)     Connected and disconnected modes

Both local and Web data sources for the Runtime use the same programming model, which means it’s easy to build connected and disconnected modes into your application. Since your application’s data source can be based on map (or tile, locator, geoprocessing, etc.) packages, these data can be downloaded from ArcGIS.com on the initial load and then used locally thereafter. A common and effective approach is to use online data for your base map, while deploying a map package with your application to contain operational data. Editing can also be performed in a disconnected manner, using the geodatabase check-in/check-out replication model.

4)     Intuitive object model

While functionally more powerful than MapObjects, the ArcGIS Runtime SDK is considerably more coarse grained than ArcGIS Engine. In other words, it delivers all the functionality that most GIS applications need without an overly complex object model. Under the hood, the Runtime uses REST for communication with both Web and local data sources. Developers who are familiar with any of the ArcGIS Web APIs should find the Runtime API very intuitive to work with right out of the gate.

5)     Multi-platform support

The ArcGIS Runtime supports applications for 32-bit Windows, 64-bit Windows, and for 64-bit Linux. Windows applications can be built using the WPF, Java, or QT SDKs. Linux applications can be built using Java or QT.

While your applications still won’t be writing themselves anytime soon, the ArcGIS Runtime SDK should make GIS development a little more enjoyable. The time you may have had to spend smoothing out your deployment or pouring over a couple dozen object model diagrams can now be spent doing something fun (or at least productive), like fine-tuning your cartography or making tweaks to your application’s UI.

Applying 50% More D-Factor to Your GIS Projects

If you’re a flash buff, it’s hard to escape the direction Adobe is taking with their newest flash player introducing the Stage 3D capabilities (previously codenamed “Molehill”).  Molehill is a new platform used for low-level GPU-accelerated APIs which will enable support across multiple screens and devices.  For those Flash 3D aficionados out there, what does this mean?  It means our world is about to change… Cube textures, z-buffering, fragmented vertex shaders… And for the layman, here’s some numbers that just make you say wow:

  • Previous flash players supported 4-8 thousand polygons
  • Molehill has been stress-testing supporting millions of polygons

Talk about holy poly-count Batman!

But diving into the guts of the API isn’t for the faint hearted.  Coming from the development world, we’re all familiar with the ‘Hello World’ examples… so how about a ‘Hello Triangle’ example?  The following code snippet was authored by Ryan Speets.  This is an incomplete example of a simple triangle and square on the screen.

public function myContext3DHandler ( event : Event ) : void {

var stage3D:Stage3D;

var vertexShaderAssembler:AGALMiniAssembler;

var fragmentShaderAssembler:AGALMiniAssembler;

stage3D = event.target as Stage3D;

context = stage3D.context3D;

context.configureBackBuffer(640, 480, 4, true);

// Set up triangle's buffers

trianglevertexbuffer = context.createVertexBuffer(3, 6);

trianglevertexbuffer.uploadFromVector ( Vector.<Number>([

0, 1, 0,  1,0,0,

-1,-1, 0,  0,1,0,

1,-1, 0,  0,0,1

]),0, 3 );

triangleindexbuffer = context.createIndexBuffer(3);

triangleindexbuffer.uploadFromVector(Vector.<uint>([0, 1, 2]), 0, 3);

// Set up square's buffers

squarevertexbuffer = context.createVertexBuffer(4, 6);

squarevertexbuffer.uploadFromVector ( Vector.<Number>([

-1, 1, 0,  0.5,0.5,1.0,

1, 1, 0,  0.5,0.5,1.0,

1,-1, 0,  0.5,0.5,1.0,

-1,-1, 0,  0.5,0.5,1.0

]),0, 4 );

squareindexbuffer = context.createIndexBuffer(6);

squareindexbuffer.uploadFromVector(Vector.<uint>([0, 1, 2, 0, 2, 3]), 0, 6);

// Assemble shaders

vertexShaderAssembler = new AGALMiniAssembler();

vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,

"m44 vt0, va0, vc0 \n" +

"m44 op, vt0, vc4 \n" +

"mov v0, va1"

);

fragmentShaderAssembler = new AGALMiniAssembler();

fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,

"mov oc, v0\n"

);

// Upload and set active the shaders

program = context.createProgram();

program.upload(vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);

context.setProgram(program);

this.addEventListener(Event.ENTER_FRAME, enterFrame)

}

Whatever happened to var triangle:Triangle = new Triangle()?  As mentioned earlier, Molehill is a low-level API (and they mean really low).  Fortunately there are a number of robust frameworks freely available that are compatible with Molehill and abstract much of the complexity of the Stage3D away from us so we can focus on doing “funner” stuff.  What are some of these frameworks?

For the following demonstrations, I leveraged Away3D.  Away3D is an open-sourced framework created by the same folks who put together (the now dead?) Papervision.  Away3D features a great developer community, significant samples, responsive contributors, and open-source accessibility.  Seemed like a good choice.  Alternativa3D was a close second and was the shop responsible for putting together the MAX3D racer demo available from Adobe’s website (http://alternativaplatform.com/en/demos/maxracer/).

But we’re a GIS Technology company and so we started asking ourselves how these new advances in web-based 3D visualization can help the geospatial community.  Really, the possibilities are endless, but here are some of the low hanging fruit:

  • Terrain visualization
  • Flight simulation
  • 3D based COP
  • Utility/Pipelines
  • Physics simulations
  • Development pre-visualization
  • Resource geolocation/tracking
  • Etc.

The demonstrations range from simple primitive test, elevation and ESRI basemaps, to global geolocation and AVL tracking.

Caption:  Who would have thought that simple polygons would get me so excited?

Once the polygons were in place I thought it might be beneficial if we could interact with the 3D objects.  A simple property change (mouseEnabled = true) and an extra EventListener and voila, we now have interactive 3D objects.

Caption:  Imagine, if you will, something cool!

Then I asked myself what would a 3D scene be without a little mood lighting and textures?  Away3D supports a number of light and material options including DirectionalLight, Point Light, BitmapMaterial, ColorMaterial, VideoMaterial, etc.  Notice the Poly count listed on the statistics in the upper left corner… 19802 polygons.  That sphere has waaay too many divisions, but take my word for it, manipulating the 3D scene is a smooth as butter.

Caption:  Adding a little mood makes everything seem more dramatic.

Next up was the manipulation of 3D objects using embedded audio assets.  We’re all familiar with audio equalizers, but what about representing audio frequencies in real-time 3D?  The flash.media.SoundMixer class offers useful utilities for introspecting audio playback and tying it to 3D objects makes an interesting demonstration.  Not much to see by way of screenshot, but visualize each bar bumping up and down to the rhythm.

Caption:  3D equalizer gives a new meaning to audio visualizations

At this point I was ready to begin delving into more geospatial experimentation.  What’s the first thing that many of us GIS professionals think of when we think of 3D… TINS, DEMS, elevation.  Right?  Unfortunately, the following demonstrations don’t represent true elevevations (rather pixel values are converted into relative extrusion heights), but from a visualization perspective the results are impressive (if not accurate).  Notice the POLY count on this screenshot… 80,000 polygons without a single hiccup in framerate as I spin the map.

Caption:  Pixel colors are converted into relative heights to create the 3D extrusion.

Caption:  A closer look

As developers, we’re always looking for ways to create value for our stakeholders.  Those familiar with the ESRI development APIs are probably familiar with the ESRI basemaps and the manipulation of graphics via GraphicLayers… now if only this could be extruded into 3D…

Caption:  Starting sample application.  User may begin sketching their waypoints.

Caption:  Ready for 3D visualization


Caption:  Elevation and basemap data mashed together to create 3D flight simulation.  Red box could easily be swapped out for camera.

In the above concepts, I use three different map services published with ESRI’s ArcGIS server.  The first map service uses the ESRI street basemap as simple reference while sketching the waypoints.  Once the waypoints are drawn, the application analyzes the extent of the points and exports images from a map service rendering DEM data, as well as ESRI’s topographic basemap.  These are mashed together to create the final scene.  The red box represents an object that could just as well have been a camera to create a “pilots perspective” flight simulation.  Again, the terrain is only a simulation/visualization, and should not be considered representative as true elevation.

Now that I had location on a local level, I started thinking international/global.  Can I use the concepts learned while generating local 3D scenes on a much larger scope?  You bet!  These examples not only demonstrate the ability to load data from external web services dynamically into a 3D scene, but also mapping geographic coordinates to pixel coordinates.  We can also attach event listeners to the markers to provide click or tooltip information.

Caption:  There’s something about don’t click me buttons that drives people crazy

Caption:  Data loaded and converted to pixel-space from external web-service, with tooltip interactivity

The mapping of lat/lon data in real-time 3D reminded me of AVL situations on COP applications.  What if you were managing a wildfire in rough terrain and wanted to visualize on-the-ground resources in context of the terrain?  In the next experiment, I created a simulated web-service that the 3D scene can poll every XX seconds to update the locations of resources on the ground.  The blue resources move over time as their coordinates are updated via a web-service call.  All the while I’m zooming in and out of canyons, panning around, etc within the 3D scene.   How about real-time FAA flight visualizations?

Caption:  Select area to analyze

Caption:  See on the ground resource locations in near realtime (simulated from web-service).

Finally, (and just for the fun of it) I wanted to start testing 3D physics.  Adobe has another great project underway that allows users to compile raw C and C++ code into SWF or SWC files that can be embedded in your applications (codename “Alchemy”).  Someone in the community was kind enough to compile the Bullet physics engine (http://bulletphysics.org) into a consumable SWC file used in the following demonstration.  Not much use for it yet, but it certainly opens the door of possibilities.

Caption:  Shooting balls at a wall of cubes could be packaged into a game

Caption:  Resulting chaos

Hopefully we can post a demo video up soon, but in the meantime enjoy the provided screenshots/discussion to get you thinking about adding 50% more D to your world.

Esri Mobile 10 SDK, WPF and MVVM

We are going to be building off the project we created from my previous blog on ‘Esri Mobile 10 SDK and WPF Beginner’s Tutorial’. In that tutorial we created a map, added basic navigation and set up syncing. In this blog we are going to add another common GIS tool, selecting features and displaying results but following the MVVM design pattern.

MVVM stands for the Model-View-ViewModel design pattern commonly used in WPF/Silverlight applications. Like MVP and MVC, it strives to separate the user interface (UI) or View from the code behind doing the heavy lifting (querying, updating data, so on). UI design is becoming its own discipline and often the View will be designed by someone else working in Expression Blend (EB). For more information on MVVM, check out http://en.wikipedia.org/wiki/Model_View_ViewModel.

In this blog I am going to dive in and give a brief introduction and examples using the Esri Mobile 10 SDK with the following concepts:

  • Model
  • View
  • ViewModel
  • Bindings
  • Commands
  • Storyboards
  • Event Triggers

In your project add a Model, View and ViewModel folder. There are, like everything, multiple ways to implement MVVM as well as debates on where to put certain things. The implementation of MVVM design pattern I will use is a slightly easier variation of this one by Josh Smith- http://joshsmithonwpf.wordpress.com/advanced-mvvm/.  At the end of this tutorial, you should have a class diagram that resembles the following:

First let’s set up which layer we are going to select. In my example here, I am going to use the Schools layer that you can pull off the ESRI Data & Maps DVD. I clipped the schools by the State of Texas and named this layer tx_schools. (You can use whatever layer you have in your cache or pull this layer out as well. If you use your own layer, you will have to also create your own Model and modify the ViewModels covered here accordingly.)

In the Settings.xml file, I add the tag <selectLayer>tx_schools</selectLayer>. We will come back later in this blog to parse this out. Let’s now switch to setting up the View. Add a UserControl to the View folder and name it ResultsControlView . Size as you would like. Add a DataGrid control to your UserControl and name it dataGridResults. For my example, I want three of those fields to be displayed in my datagrid so I add three columns like so:

<DataGrid AutoGenerateColumns="False" Height="225" HorizontalAlignment="Left" Margin="10" Name="dataGridResults" VerticalAlignment="Top" Width="475" ItemsSource="{Binding SchoolList}" SelectedItem="{Binding SelectedItem}"><DataGrid.Columns><DataGridTextColumn Binding="{Binding SchoolName}" Header="School Name" FontWeight="Bold"/><DataGridTextColumn Binding="{Binding LabelFlag}" Header="Label Flag"/><DataGridTextColumn Binding="{Binding StCtyFips}" Header="FIPS"/></DataGrid.Columns></DataGrid>

A couple thinks to point out in this snippet from our xaml is the {Binding } term. Like I mentioned early we are going to use xaml’s ability to bind values from our ViewModel and Model to our View. All the view needs to know is what these properties will be named. My DataGrid source will be bound to a collection called SchoolList. The DataGrid SelectedItem is bound to my property SelectedItem. Each of the columns are bound to three properties coming from my school Model. When we create our ViewModel and Model things will start to make more sense.

Switch back to MainWindow.xaml and let’s add our new control. First add reference to Window header tag to our View namespace xmlns:view=”clr-namespace:WpfMobileTuturialMVVM.View”.  Now we can add our control in the xaml like so with some minor styling (you can change as you feel fit):

<view:ResultsControlView x:Name="resultsView" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10" Visibility="Hidden"/>

Next let’s create our Model. Before we get started I want to introduce INotifyPropertyChanged. This interface is very important for bound properties. This is the mechanism that lets the View bound properties know if changes have occurred back in Model or ViewModel automatically. Pretty sweet right? Before we create the Model for our schools Layer we will create a base class that sets up the INotifyPropertyChanged. Add a new class to Model folder and call it EntityBase. And insert following code:

public class EntityBase:INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        protected void FirePropertyChange(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    }

Now right click Model folder and add new class called SchoolModel that implements our EntityBase. The reason for the separation is because more than likely in a real world solution you will have multiple models. And there is no point in re-implementing the same code across all of them. Add the following code snippet to SchoolModel:

public class SchoolModel:EntityBase
    {
        public SchoolModel() { }
        #region members
        private string _schoolName;
        private string _stCtyFips;
        private int _labelFlag;
        private Geometry _schoolGeometry;
        #endregion
        #region properties
        public string SchoolName
        {
            get { return _schoolName; }
            set
            {
                _schoolName = value;
                base.FirePropertyChange("SchoolName");
            }
        }
        public string StCtyFips
        {
            get { return _stCtyFips; }
            set
            {
                _stCtyFips = value;
                base.FirePropertyChange("StCtyFips");
            }
        }
        public int LabelFlag
        {
            get { return _labelFlag; }
            set
            {
                _labelFlag = value;
                base.FirePropertyChange("LabelFlag");
            }
        }
        public Geometry SchoolGeometry
        {
            get { return _schoolGeometry; }
            set
            {
                _schoolGeometry = value;
                base.FirePropertyChange("SchoolGeometry");
            }
        }
        #endregion
    }

Let’s walk through this now. If you have worked in other design patterns you will notice this looks very similar to what are termed Domains but with utilization of the INotifyPropertyChanged method we defined in EntityBase (If you are a stickler you can separate this class into Domain and Model classes). If you look at the layer in ArcCatalog you will notice that I did not include all the fields. Typically I have the Model reflect every field in the actual layer, but for this tutorial I am cutting some corners. But you see where those property names we used in View are now defined here in the Model.  But we need the middle piece to bind them together so time to move onto the ViewModel.

First off let’s set up a base class for all the ViewModels that will implement INotifyPropertyChanged. Add a new class to ViewModel folder and name it ViewModelBase. Add the following code snippet:

#region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        #region Protected Methods
        /// <summary>
        /// Fires the PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The name of the changed property.</param>
        protected void RaisePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                PropertyChanged(this, e);
            }
        }
        #endregion

For each View, we will have its own ViewModel. The master View is MainWindow and contains the ResultControlView. We are going to follow this pattern and create a MainWindowViewModel class and ResultsViewModel class in the ViewModel folder. Both this classes implement ViewModelBase. MainWindowViewModel will contain the reference to the ResultsViewModel and will pass calls that require changes to ResultsControllView bound properties to ResultsViewModel. Let us start setting up MainWindowViewModel:

public MainWindowViewModel() 
        {
            _resultsViewModel = new ResultsViewModel();
        }
        #region members
        ESRI.ArcGIS.Mobile.WPF.NavigationMode m_lastNavigatioMode;
        private ESRI.ArcGIS.Mobile.WPF.Map _map;
        private MobileCache _mobileCache;
        GraphicLayer selectionLayer;
        EnvelopeSketchTool envelopeSketchTool;
        private ResultsViewModel _resultsViewModel;
        #endregion
        #region properties
        public string FeatureLayerName { get; set; }
        public ESRI.ArcGIS.Mobile.WPF.Map Map
        {
            get { return _map; }
            set
            {
                _map = value;            
            }
        }
        public MobileCache MobileCache
        {
            get { return _mobileCache; }
            set
            {
                _mobileCache = value;
            }
        }
        public ResultsViewModel MyResultsViewModel
        {
            get { return _resultsViewModel; }
            set
            {
                _resultsViewModel = value;
                base.RaisePropertyChangedEvent("MyResultsViewModel");
            }
        }
        #endregion

Before we set the binding between the Views and ViewModels let us set up ResultsViewModel with following code snippet:

public ResultsViewModel() { }

        #region members
        private ObservableCollection<SchoolModel> _schoolList;
        private SchoolModel _selectedItem;
        private MobileCache _mobileCache;
        private ESRI.ArcGIS.Mobile.WPF.Map _map;
        private string _featureLayerName;
        private GraphicLayer glSelected;
        private FeatureDataTable selectedDataTable;
        private FeatureLayer featureLayer = null;
        #endregion
        #region properties
        public ObservableCollection<SchoolModel> SchoolList
        {
            get { return _schoolList; }
            set
            {
                _schoolList = value;
                base.RaisePropertyChangedEvent("SchoolList");
            }
        }
        public SchoolModel SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                base.RaisePropertyChangedEvent("SelectedItem");                
            }
        }
        public string FeatureLayerName
        {
            get { return _featureLayerName; }
            set { _featureLayerName = value; }
        }
        public MobileCache MobileCache
        {
            get { return _mobileCache; }
            set { _mobileCache = value; }
        }
        public ESRI.ArcGIS.Mobile.WPF.Map Map
        {
            get { return _map; }
            set { _map = value; }
        }
        #endregion

For now, we are going to focus on SchoolList and SelectedItem. Notice how SelectedItem is an instance of our ShcoolModel and SchoolList is a collection of those Models. This collection will represent the selection set. And this collection, ObservableCollection, is especially cool because it has built in notification about items getting added, removed, or updated. So, we don’t have to do anything special to handle any of that. Pretty cool.

Let’s connect up the Views with the ViewModels by switching to MainWindow.xaml.cs and update with following code snippet:

public MainWindow()
        {
            InitializeComponent();
            _mainWindowVM = new MainWindowViewModel();
            this.DataContext = _mainWindowVM;
        }
        private MainWindowViewModel _mainWindowVM;

In the method, Windows_Loaded add the following to the very bottom of the method:

_mainWindowVM.Map = map1;
_mainWindowVM.MobileCache = mobileCache;

Switch to MainWindow.xaml and add DataContext=”{Binding MyResultsViewModel}” to our ResultsControlView control.

What this did was bind instance of MainWindowViewModel to MainWindow.xaml by setting the DataContect. Since MainWindowViewModel exposes instance of ResultsViewModel, we then bound that to our ResultsControlView by also setting the DataContext. If we dive back into our ResultsControlView, we have that DataGrid that we set the source to SchoolList. This is exposed now through the bound instance of ResultsViewModel. If you are getting lost that is OK. Binding and how it tickles down to the children controls was new to me when I started as well.

So, let’s switch back to ResultsViewModel and add the methods for performing the spatial selection and populating our SchoolList so it shows up in our DataGrid on ResultsControlView.

#region methods
        public void SpatialQuerySchools(Envelope queryEnvelope)
        {
            //reset eveything
            SchoolList = new ObservableCollection<SchoolModel>();
            SelectedItem = new SchoolModel();
            //set up spatial queryfilter
            QueryFilter queryFilter = new QueryFilter();
            queryFilter.Geometry = queryEnvelope;
            queryFilter.GeometricRelationship = GeometricRelationshipType.Intersect;
            LoadSchoolResult(queryFilter);           
        }
        private void LoadSchoolResult(QueryFilter queryFilter)
        {
            //get feature layer
            if (featureLayer == null) featureLayer = getFeatureLayer();
            //if still null
            if (featureLayer == null)
            {
                MessageBox.Show("Could not find feature layer " + FeatureLayerName + " in map cache", "Search Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            selectedDataTable = featureLayer.GetDataTable(queryFilter);
            FeatureDataReader selectDataReader = featureLayer.GetDataReader(queryFilter);
            //set up graphic layer
            _map.MapGraphicLayers.Remove(glSelected);
            //create selected graphic layer
            glSelected = new GraphicLayer();
            //create brush for fill
            System.Windows.Media.SolidColorBrush myBrush = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Aqua);
            // Create a Pen to add to the GeometryDrawing created above.
            System.Windows.Media.Pen myPen = new System.Windows.Media.Pen();
            myPen.Thickness = 3;
            myPen.Brush = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Aqua);
            glSelected.Fill = myBrush;
            glSelected.DrawingPen = myPen;
            glSelected.SelectionFill = myBrush;
            glSelected.SelectionPen = myPen;
            glSelected.DrawVertexes = false;
            _map.MapGraphicLayers.Add(glSelected);
            ObservableCollection<SchoolModel> schools = new ObservableCollection<SchoolModel>();
            try
            {
                if (selectedDataTable != null && selectedDataTable.Rows.Count > 0)
                {
                    FeatureDataRow featureRow;
                    for (int i = 0; i < selectedDataTable.Rows.Count; i++)
                    {

                        featureRow = selectedDataTable[i];
                        // work with retrieved records
                        SchoolModel school = new SchoolModel();
                        //set values
                        school.SchoolName = featureRow["NAME"].ToString().Trim();
                        school.StCtyFips = featureRow["STCTYFIPS"].ToString().Trim();
                        if (featureRow["LABEL_FLAG"] != DBNull.Value)
                            school.LabelFlag = Convert.ToInt32(featureRow["LABEL_FLAG"]);
                        //set spatial geometry
                        Geometry m_geometry = featureRow.Geometry;
                        ESRI.ArcGIS.Mobile.Geometries.Multipoint mapPoint = m_geometry as ESRI.ArcGIS.Mobile.Geometries.Multipoint;
                        school.SchoolGeometry = m_geometry;
                        //add to graphic
                        glSelected.GeometryBag.Add(m_geometry);
                        //add to list
                        schools.Add(school);
                    }
                    SchoolList = schools;
                }
                selectedDataTable.Dispose();
            }
            catch (Exception e)
            {
                System.Windows.MessageBox.Show(e.Message, "Error in SignSupport Query", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
                //throw;
            }
        }
        private FeatureLayer getFeatureLayer()
        {
            //get access to map layer 
            FeatureLayer flayer = MobileCache.Layers[FeatureLayerName] as FeatureLayer;
            return flayer;
        }
        #endregion

The SpatialQuerySchools method will accept the selection envelope. The LoadSchoolResults gets a reference to our selected layer and sets up the graphic layer that will show the highlight features on map. It then runs the query and loops through results to first add them to a new instance of a SchoolModel which are then added to the ObservableCollection SchoolList as well as to the graphic layer.

Switching back to MainWindow.xaml, add two buttons like so for starting and clearing map selection:

<Button Content="Select Schools" Height="24" HorizontalAlignment="Left" Margin="10,52,0,0" Name="btnSelect" Opacity="0.6" VerticalAlignment="Top" Width="100" Command="{Binding SelectingSchoolsCommand}"/>
        <Button Content="Clear Selection" Height="24" HorizontalAlignment="Left" Margin="10,82,0,0" Name="btnClear" Opacity="0.6" VerticalAlignment="Top" Width="100" />

Notice in btnSelect instead of using an event (like Click or MouseDown) we are using a bound Command. This is another cool feature of WPF that sets the MainWindowViewModel to handle the event. Now let’s switch to MainWindowViewModel and set up our SelectingShoolsCommand with the following private class create inside the MainWindowViewModel:

private class SelectSchoolsCommand : ICommand
        {
            #region ICommand Members
            readonly MainWindowViewModel _mv;
            public SelectSchoolsCommand(MainWindowViewModel mv)
            {
                _mv = mv;
            }
            public bool CanExecute(object paramenter)
            {
                return true;
            }
            public event EventHandler CanExecuteChanged;
            public void Execute(object paramenter)
            {
                _mv.setupSelectTool();
            }
            #endregion
        }

Notice how this class is initiated with a reference to the instance of MainWindowViewModel. Next, add in this code snippet to create instance of this command that is exposed and hence bindable:

#region commands
        private ICommand _selectingSchoolsCommand;
        public ICommand SelectingSchoolsCommand
        {
            get
            {
                if (_selectingSchoolsCommand == null)
                    _selectingSchoolsCommand = new SelectSchoolsCommand(this);
                return _selectingSchoolsCommand;
            }
        }
        #endregion

Before I show the code for the setupSelectTool, let’s move IsDesignMode and getAppPath from MainWindow.xaml.cs to MainWindowViewModel because we will be making calls to it from our ViewModel. You will have to update any references to getAppPath in MainWindow.xaml.cs to _mainWindowVM.getAppPath() or you will get compile errors. Now we can move on by adding following code snippet to MainWindowViewModel:

private void setupSelectTool()
        {                        
            if (selectionLayer != null)
                Map.MapGraphicLayers.Remove(selectionLayer);
            //setup selection layer
            selectionLayer = new GraphicLayer();
            selectionLayer.DrawVertexes = false;
            Map.MapGraphicLayers.Add(selectionLayer);
            selectionLayer.GeometryBag.Add(new ESRI.ArcGIS.Mobile.Geometries.Polygon());
            selectionLayer.SelectedGeometry = selectionLayer.GeometryBag[0];
            m_lastNavigatioMode = Map.CurrentNavigationMode;
            Map.CurrentNavigationMode = ESRI.ArcGIS.Mobile.WPF.NavigationMode.None;
            envelopeSketchTool = new EnvelopeSketchTool();
            envelopeSketchTool.AttachToSketchGraphicLayer(selectionLayer);
            envelopeSketchTool.SketchCompleted += doneSketch;
        }
        private void doneSketch(object sender, EventArgs e)
        {
            Envelope env = envelopeSketchTool.Envelope;
            if (env.XMax == env.XMin && env.YMax == env.YMax)
            {
                env.XMax += 25;
                env.XMin -= 25;
                env.YMax += 25;
                env.YMin -= 25;
            }
            //leave on until collapse or pan turn on
            if (envelopeSketchTool != null)
            {
                Map.CurrentNavigationMode = ESRI.ArcGIS.Mobile.WPF.NavigationMode.Pan;
                if (envelopeSketchTool != null)
                    envelopeSketchTool.DetachFromSketchGraphicLayer();
            }
            //check if need to setup ResultsViewModel
            if (MyResultsViewModel.Map == null)
                MyResultsViewModel.Map = _map;
            if (MyResultsViewModel.MobileCache == null)
                MyResultsViewModel.MobileCache = MobileCache;
            if (MyResultsViewModel.FeatureLayerName == null || MyResultsViewModel.FeatureLayerName.Length < 1)
            {
                string xmlPath = getAppPath() + "Settings.xml";
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(xmlPath);
                XmlNodeList elemList;
                elemList = xmlDoc.GetElementsByTagName("selectLayer");
                MyResultsViewModel.FeatureLayerName = elemList[0].InnerXml;
            }
            MyResultsViewModel.SpatialQuerySchools(env);
        }

In MainWindow.xaml.cs Windows_Loaded, we set the Map and MobileCache properties so in setupSelectTool and doneSketch you see we are making changes to the map that will be reflected when you interact with the application. setupSelectTool sets up the graphic that will handle the select draw rectangle affect on the map. It also sets the SketchCompleted event to doneSketch. doneSketch first checks if this a single click selection and makes a default envelope. It then checks if ResultsViewModel map, mobilecache and FeatureLayerName have been set- if not- sets them. FeatureLayerName is the value we set in the selectedLayer tag in Settings.xml. The final thing is it calls the SpatialQuerySchools method from the instance of ResultsViewModel.

Ok, I know your head must be spinning with all this code. Go ahead and compile, but do not run- you just want to see if you have any errors. The last thing we need to add in is how the ResultsViewControl opens and closes.  And I am going to show you through using Storyboards and Event Triggers in MainWindow.xaml. Add the following between the Window header tag and your Grid tag in xaml:

<Window.Resources>
        <Storyboard x:Key="OpenResults">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="resultsView">
                <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="CloseResults">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="resultsView">
                <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnSelect">
            <BeginStoryboard x:Name="OpenResults_BeginStoryboard1" Storyboard="{StaticResource OpenResults}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnClear">
            <BeginStoryboard x:Name="CloseResults_BeginStoryboard" Storyboard="{StaticResource CloseResults}"/>
        </EventTrigger>
    </Window.Triggers>

If we look at the Storyboards, OpenResults and CloseResults just set the visibility value on our instance of ResultsControlView we named resultsView. The triggers wait for a click event and depending on from which button (btnSelect or btnClear) it will tell which Storyboard to run. This demonstrates how you can control simple UI/View behavior right in xaml and not in xaml.cs. And this is a very simple example. You can have very elaborate Storyboards or use style triggers with multi data triggers.

Now go ahead and compile and run. Play with the select tools and notice how bindings trickle down to populating the datagrid with your selection.

Congratulations- you survived your first MVVM project. I throw a lot of new terms/concepts out there. Googling on any one of them will return a wealth of information for you to explore further. If you are really feeling ambitious, go back and refractor the rest of the code into MVVM. Happy coding.

Follow

Get every new post delivered to your Inbox.