Click here to Skip to main content
15,886,578 members
Articles / Hosted Services / Azure

Exploring Opportunities for the Cloud: Part 2

Rate me:
Please Sign up or sign in to vote.
4.91/5 (7 votes)
20 Jan 2011CPOL18 min read 23K   210   8  
A series of articles exploring Azure development featuring Bing Maps and phone.
using GalaSoft.MvvmLight;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using System.ServiceModel;
using System.Collections.ObjectModel;
using System;
using System.Collections.Generic;
using Microsoft.Maps.MapControl;
using GalaSoft.MvvmLight.Messaging;
using SLMapTest.Messages;
using SLMapTest.Model;


namespace SLMapTest.ViewModel
{
    //To define a segment: user clicks on streets until the desired street segment is located-the selection is
    //displayed in lower left. Selecting 'New' button will start the segment definition sequence and 
    //transfer the current street selection to the 'Segment Street' text block. The user then selects the
    //desired start intersection street and depresses 'SetXStart' button. The start intersection will be
    //identified with an 'X' at the defined intersection. The process continuous by selecting the 
    //street that deliniates the end intersection for the segment.
    //When both intersections have been defined the system will display the segment on the screen and
    //the user has the option to 'Save' it or 'Cancel' if there was an error.
    //If the segment is saved it will be added as a route segment and all route segments are displayed as
    //a route. The user can select a route segment from list and the system will highlight the
    //selected segment. The only editable fields of the segment are the start and end address #s. But 
    //a segment can be deleted and re-defined. And the segment definition can be canceled to clear
    //out the current definition.
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        enum UserEditingState
        {
            NotEditing,
            DefiningSegment,
            EditingSegment
        }
        enum SegmentDefinitionStates
        {
            NotDefining,
            StartXStreet,
            EndXStreet,
            DefinitionComplete
        }

        RelayCommand buttonNewSegment;
        RelayCommand buttonDeleteSegment;
        RelayCommand buttonCancelSegment;

        RelayCommand buttonSetStartXStreet;
        RelayCommand buttonSetEndXStreet;

        RouteSegment currentSegment = null;
        ReverseGeocodeResultMsg currentSelection;

        UserEditingState editingState = UserEditingState.NotEditing;
        SegmentDefinitionStates definitionState = SegmentDefinitionStates.NotDefining;
        ObservableCollection<RouteSegment> routeSegments;
        RouteSegment selectedSegment = null;

        private BingRouteService.RouteServiceClient routeClient;
        bool showBusy;

        bool enableSegmentSelection = true;
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            routeSegments = new ObservableCollection<RouteSegment>();

            buttonNewSegment = new RelayCommand(NewSegmentClick, CanNewSegment);
            buttonDeleteSegment = new RelayCommand(DeleteSegmentClick, CanDeleteSegment);
            buttonCancelSegment = new RelayCommand(CancelSegmentClick, CanCancelSegment);

            //We want to guide the user...
            buttonSetStartXStreet = new RelayCommand(SetStartXStreetClick, CanSetStartXStreet);
            buttonSetEndXStreet = new RelayCommand(SetEndXStreetClick, CanSetEndXStreet);
            //Subscribe to the message, we know what to do
            Messenger.Default.Register<ReverseGeocodeResultMsg>(this, ReverseGeocodeMessageHandler);
            //and the result of intersection request
            Messenger.Default.Register<StreetIntersectionResultMsg>(this, GetIntersectionMessageHandler);

        }
        #region message handlers
        /// <summary>
        /// The location that user clicked...
        /// </summary>
        /// <param name="msgData"></param>
        void ReverseGeocodeMessageHandler(ReverseGeocodeResultMsg msgData)
        {
            currentSelection = msgData;
            buttonNewSegment.RaiseCanExecuteChanged();
            this.RaisePropertyChanged("SelectedLocation");
        }
        /// <summary>
        /// The result of geocoding an intersection
        /// </summary>
        void GetIntersectionMessageHandler(StreetIntersectionResultMsg msgData)
        {
            //Start or end intersection?
            if (definitionState == SegmentDefinitionStates.StartXStreet)
            {
                if (msgData.StreetsIntersect)
                {
                    currentSegment.StartXStreet = msgData.IntersectionLocation;
                    //Plot an 'X' at the intersection
                    Messenger.Default.Send<PlotIntersectionMsg>(new PlotIntersectionMsg(){ IntersectionLocation = msgData.IntersectionLocation});
                    definitionState = SegmentDefinitionStates.EndXStreet;
                }
                else
                {
                    //Tell user that the selected street does not intersect segment street
                    Messenger.Default.Send<UserActionAlertMsg>(new UserActionAlertMsg() { AlertText = "Streets do not intersect!" });
                }
            }
            else
            {
                if (msgData.StreetsIntersect)
                {
                    currentSegment.EndXStreet = msgData.IntersectionLocation;
                    //Plot an 'X' at the intersection
                    Messenger.Default.Send<PlotIntersectionMsg>(new PlotIntersectionMsg(){ IntersectionLocation = msgData.IntersectionLocation});
                    //And plot the segment...when we get the results
                    GetRouteSegmentPlot(currentSegment.StartXStreet, currentSegment.EndXStreet, currentSegment.ID);
                    definitionState = SegmentDefinitionStates.DefinitionComplete;
                    UpdateSegmentButtons();
                }
                else
                {
                    //Tell user that the selected street does not intersect segment street
                    Messenger.Default.Send<UserActionAlertMsg>(new UserActionAlertMsg() { AlertText = "Streets do not intersect!" });
                }
            }

            UpdateStateButtons();
        }
        #endregion
        #region commanding
        public ICommand ButtonNewSegment
        {
            get { return buttonNewSegment; }
        }
        /// <summary>
        /// New or save
        /// </summary>
        void NewSegmentClick()
        {
            switch (editingState)
            {
                case UserEditingState.EditingSegment:
                    //Save update, start/end numbers
                    selectedSegment.StartNumber = currentSegment.StartNumber;
                    selectedSegment.EndNumber = currentSegment.EndNumber;
                    editingState = UserEditingState.NotEditing;
                    currentSegment = null;
                    selectedSegment = null;
                    break;
                case UserEditingState.DefiningSegment:
                    //Add segment to route layer...
                    RouteSegment newSegment = new RouteSegment(0);
                    newSegment.EndNumber = currentSegment.EndNumber;
                    newSegment.StartNumber = currentSegment.StartNumber;
                    newSegment.StartXStreet = currentSegment.StartXStreet;
                    newSegment.EndXStreet = currentSegment.EndXStreet;
                    newSegment.StreetName = currentSegment.StreetName;
                    newSegment.PlotPoints.AddRange(currentSegment.PlotPoints);
                    routeSegments.Add(newSegment);
                    
                    //Plot the route
                    UpdateMapRouteDisplay();

                    editingState = UserEditingState.NotEditing;
                    currentSelection = new ReverseGeocodeResultMsg();
                    currentSegment = null;
                    selectedSegment = null;
                    break;
                case UserEditingState.NotEditing:
                    //New segment definition, use current street selection
                    currentSegment = new RouteSegment(0);
                    currentSegment.StreetName = GetCurrentSelectionStreet();
                    definitionState = SegmentDefinitionStates.StartXStreet;
                    editingState = UserEditingState.DefiningSegment;
                    break;
            }

            buttonDeleteSegment.RaiseCanExecuteChanged();
            buttonNewSegment.RaiseCanExecuteChanged();
            buttonCancelSegment.RaiseCanExecuteChanged();

            BulkPropertyChangedHelper();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        bool CanNewSegment()
        {
            bool canEnable = false;

            if ((editingState == UserEditingState.NotEditing && currentSelection.StreetAddress != null) || selectedSegment != null)
                canEnable = true;
            else
            {
                if (editingState == UserEditingState.DefiningSegment && definitionState == SegmentDefinitionStates.DefinitionComplete)
                    canEnable = true;
            }
            return canEnable;
        }
        /// <summary>
        /// 
        /// </summary>
        public ICommand ButtonDeleteSegment
        {
            get { return buttonDeleteSegment; }
        }
        /// <summary>
        /// 
        /// </summary>
        void DeleteSegmentClick()
        {
            //Delete selected segment...
            if (selectedSegment != null)
                routeSegments.Remove(selectedSegment);

            editingState = UserEditingState.NotEditing;

            definitionState = SegmentDefinitionStates.NotDefining;

            //Re-display the route
            UpdateMapRouteDisplay();

            BulkPropertyChangedHelper();
        }
        /// <summary>
        /// Delete or cancel 
        /// </summary>
        /// <returns></returns>
        bool CanDeleteSegment()
        {
            bool canEnable = true;
            
            canEnable = selectedSegment != null;

            return canEnable;
        }
        public ICommand ButtonCancelSegment
        {
            get { return buttonCancelSegment; }
        }
        void CancelSegmentClick()
        {
            selectedSegment = null;
            currentSegment = new RouteSegment(0);
            currentSelection = new ReverseGeocodeResultMsg();
            editingState = UserEditingState.NotEditing;

            definitionState = SegmentDefinitionStates.NotDefining;
            //Re-display the route
            UpdateMapRouteDisplay();

            BulkPropertyChangedHelper();
        }
        bool CanCancelSegment()
        {
            bool canEnable = true;
            if (editingState == UserEditingState.NotEditing)
                canEnable = false;
            return canEnable;
        }
        public ICommand ButtonSetStartXStreet
        {
            get { return buttonSetStartXStreet; }
        }
        void SetStartXStreetClick()
        {
            //Cross street name can't be same as segment street
            string streetName = GetCurrentSelectionStreet();
            if (streetName != currentSegment.StreetName)
            {
                //Get intersection, if exists
                Messenger.Default.Send<GetStreetIntersectionMsg>(new GetStreetIntersectionMsg()
                {
                    SegmentStreet = currentSegment.StreetName,
                    XStreetName = streetName,
                    XCity = currentSelection.City,
                    XState = currentSelection.State
                });
            }
            else
            {
                Messenger.Default.Send<UserActionAlertMsg>(new UserActionAlertMsg() { AlertText="Streets are same!"});
            }
        }
        bool CanSetStartXStreet()
        {
            return editingState == UserEditingState.DefiningSegment && definitionState == SegmentDefinitionStates.StartXStreet && currentSelection.StreetAddress != null;
        }
        public ICommand ButtonSetEndXStreet
        {
            get { return buttonSetEndXStreet; }
        }
        void SetEndXStreetClick()
        {

            //Cross street name can't be same as segment street...and we should check that they intersect
            string streetName = GetCurrentSelectionStreet();

            if (streetName != currentSegment.StreetName)
            {
                //Get intersection, if exists
                Messenger.Default.Send<GetStreetIntersectionMsg>(new GetStreetIntersectionMsg()
                {
                    SegmentStreet = currentSegment.StreetName,
                    XStreetName = streetName,
                    XCity = currentSelection.City,
                    XState = currentSelection.State
                });
            }
            else
            {
                //Let user know with a message box...that cross street needs to 'cross' segment street
     
            }

        }
        bool CanSetEndXStreet()
        {
            return editingState == UserEditingState.DefiningSegment && definitionState == SegmentDefinitionStates.EndXStreet && currentSelection.StreetAddress != null;
        }
        #endregion
        #region data context
        public bool IsBusy
        {
            get { return showBusy; }
        }
        public bool EnableSegmentSelection
        {
            get { return enableSegmentSelection; }
        }
        public string SegmentStreet
        {
            get {
                if (currentSegment != null)
                    return currentSegment.StreetName;
                else
                    return null;
            }
        }
        public string EndNumber
        {
            get
            {
                if (currentSegment != null)
                    return currentSegment.EndNumber.ToString();
                else
                    return null;
            }
            set
            {
                if (currentSegment != null)
                    currentSegment.EndNumber = Convert.ToInt32(value);
            }
        }
        public string StartNumber
        {
            get {
                if (currentSegment != null)
                    return currentSegment.StartNumber.ToString();
                else
                    return null;
            }
            set { 
                if (currentSegment != null)
                    currentSegment.StartNumber = Convert.ToInt32(value);
            }
        }
        public string SelectedLocation
        {
            get { return currentSelection.StreetAddress; }
        }
        public ObservableCollection<RouteSegment> RouteSegments
        {
            get { return routeSegments; }
        }
        public RouteSegment SelectedSegment
        {
            get { return selectedSegment; }
            set
            {
                selectedSegment = value;
                if (value != null)
                {
                    currentSegment = selectedSegment;
                    editingState = UserEditingState.EditingSegment;
                    //Highlight segment on map
                    if (selectedSegment.PlotPoints.Count > 0)
                    {
                        PlotRouteSegmentMsg msg = new PlotRouteSegmentMsg();
                        msg.segmentPoints = new List<Location>(selectedSegment.PlotPoints);

                        Messenger.Default.Send<PlotRouteSegmentMsg>(msg);
                    }
                }
                else
                {
                    editingState = UserEditingState.NotEditing;
                }
                BulkPropertyChangedHelper();
            }
        }
        /// <summary>
        /// Not very international.
        /// </summary>
        public string NewSaveSegment
        {
            get
            {
                if (editingState == UserEditingState.NotEditing)
                    return "New";
                else
                {
                    if (editingState == UserEditingState.DefiningSegment)
                        return "Save";
                    else
                        return "Update";
                }
            }
        }
        #endregion
        #region private methods
        void UpdateMapRouteDisplay()
        {
            PlotRouteMsg msg = new PlotRouteMsg();
            msg.routeSegments = new List<List<Location>>();
            foreach (RouteSegment rs in routeSegments)
            {
                msg.routeSegments.Add(rs.PlotPoints);
            }
            Messenger.Default.Send<PlotRouteMsg>(msg);
        }
        //UI updating...CommandManager where art thou?
        void BulkPropertyChangedHelper()
        {
            UpdateStateButtons();

            UpdateSegmentButtons();

            UpdateSegmentFields();

            RaisePropertyChanged("SelectedLocation");
            RaisePropertyChanged("SelectedSegment");

        }
        void UpdateSegmentFields()
        {
            RaisePropertyChanged("SegmentStreet");
            RaisePropertyChanged("StartNumber");
            RaisePropertyChanged("EndNumber");
        }
         void UpdateSegmentButtons()
        {
            buttonNewSegment.RaiseCanExecuteChanged();
            buttonDeleteSegment.RaiseCanExecuteChanged();
            buttonCancelSegment.RaiseCanExecuteChanged();

            RaisePropertyChanged("NewSaveSegment");
        }
        void UpdateStateButtons()
        {
            buttonSetStartXStreet.RaiseCanExecuteChanged();
            buttonSetEndXStreet.RaiseCanExecuteChanged();
        }
        /// <summary>
        /// Route service 
        /// </summary>
        /// <param name="startLocation"></param>
        /// <param name="endLocation"></param>
        private void GetRouteSegmentPlot(Location startLocation, Location endLocation, int segmentID)
        {
            BingRouteService.RouteRequest request = new BingRouteService.RouteRequest();

            request.Waypoints = new ObservableCollection<BingRouteService.Waypoint>();
            BingRouteService.Waypoint start = new BingRouteService.Waypoint();
            start.Location = new Location();
            start.Location.Latitude = startLocation.Latitude;
            start.Location.Longitude = startLocation.Longitude;
            start.Location.Altitude = startLocation.Altitude;
            request.Waypoints.Add(start);
            BingRouteService.Waypoint end = new BingRouteService.Waypoint();
            end.Location = new Location();
            end.Location.Altitude = endLocation.Altitude;
            end.Location.Latitude = endLocation.Latitude;
            end.Location.Longitude = endLocation.Longitude;
            request.Waypoints.Add(end);

            request.Options = new BingRouteService.RouteOptions();
            request.Options.RoutePathType = BingRouteService.RoutePathType.Points;
            request.Options.TrafficUsage = BingRouteService.TrafficUsage.None;
            request.Options.Optimization = BingRouteService.RouteOptimization.MinimizeDistance;
            request.Options.Mode = BingRouteService.TravelMode.Walking;

            // Don't raise exceptions.
            request.ExecutionOptions = new BingRouteService.ExecutionOptions();
            request.ExecutionOptions.SuppressFaults = true;

            request.Credentials = new Credentials();
            request.Credentials.ApplicationId = (string)App.Current.Resources["BingCredentialsKey"];

            // Make asynchronous call to fetch the data. Set busy indicator...
            showBusy = true;
            this.RaisePropertyChanged("IsBusy");
            RouteClient.CalculateRouteAsync(request, segmentID);
        }
        string GetCurrentSelectionStreet()
        {
            string[] addressParts = currentSelection.StreetAddress.Split(new char[] { ' ' });
            string streetName = currentSelection.StreetAddress.Remove(0, addressParts[0].Length + 1);
            return streetName;
        }
        #endregion
        #region private properties
        private BingRouteService.RouteServiceClient RouteClient
        {
            get
            {
                if (null == routeClient)
                {
                    BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
                    UriBuilder serviceUri = new UriBuilder("http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc");

                    //Create the Service Client
                    routeClient = new BingRouteService.RouteServiceClient(binding, new EndpointAddress(serviceUri.Uri));
                    routeClient.CalculateRouteCompleted += new EventHandler<BingRouteService.CalculateRouteCompletedEventArgs>(routeClient_CalculateRouteCompleted);
                }
                return routeClient;
            }
        }
        #endregion
        #region service message handlers
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void routeClient_CalculateRouteCompleted(object sender, BingRouteService.CalculateRouteCompletedEventArgs e)
        {
            showBusy = false;
            this.RaisePropertyChanged("IsBusy");
            try
            {
                if (e.Result.ResponseSummary.StatusCode != BingRouteService.ResponseStatusCode.Success)
                {
                    Messenger.Default.Send<UserActionAlertMsg>(new UserActionAlertMsg() { AlertText = "Could not determine route segment." });
                }
                else
                {
                    BingRouteService.RoutePath routePath = e.Result.Result.RoutePath;
                    currentSegment.PlotPoints.AddRange(routePath.Points);

                    //Plot the segment
                    PlotRouteSegmentMsg msg = new PlotRouteSegmentMsg();
                    msg.segmentPoints = new List<Location>(routePath.Points);

                    Messenger.Default.Send<PlotRouteSegmentMsg>(msg);
                }
            }
            catch
            {
                Messenger.Default.Send<UserActionAlertMsg>(new UserActionAlertMsg(){ AlertText="Got an exception calling route service."});
            }
        }
        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions