|
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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.