-
Notifications
You must be signed in to change notification settings - Fork 70
/
MainPage.xaml.cs
659 lines (582 loc) · 28.7 KB
/
MainPage.xaml.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
// ---------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ---------------------------------------------------------------------------------
using Location;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using TrafficApp.Common;
using Windows.ApplicationModel.Core;
using Windows.Devices.Geolocation;
using Windows.Networking.Connectivity;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Maps;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Navigation;
namespace TrafficApp
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>finput
public sealed partial class MainPage : Page
{
private LocationData locationInEdit;
private BasicGeoposition locationInEditOriginalPosition;
private bool isNewLocationInEdit;
private bool isMapSelectionEnabled;
private bool isExistingLocationBeingRepositioned;
#region Location data
/// <summary>
/// Gets or sets the saved locations.
/// </summary>
public ObservableCollection<LocationData> Locations { get; private set; }
/// <summary>
/// Gets or sets the locations represented on the map; this is a superset of Locations, and
/// includes the current location and any locations being added but not yet saved.
/// </summary>
public ObservableCollection<LocationData> MappedLocations { get; set; }
private object selectedLocation;
/// <summary>
/// Gets or sets the LocationData object corresponding to the current selection in the locations list.
/// </summary>
public object SelectedLocation
{
get { return this.selectedLocation; }
set
{
if (this.selectedLocation != value)
{
var oldValue = this.selectedLocation as LocationData;
var newValue = value as LocationData;
if (oldValue != null)
{
oldValue.IsSelected = false;
this.InputMap.Routes.Clear();
}
if (newValue != null)
{
newValue.IsSelected = true;
if (newValue.FastestRoute != null) this.InputMap.Routes.Add(new MapRouteView(newValue.FastestRoute));
}
this.selectedLocation = newValue;
}
}
}
#endregion Location data
#region Initialization and navigation code
/// <summary>
/// Initializes a new instance of the class and sets up the association
/// between the Locations and MappedLocations collections.
/// </summary>
public MainPage()
{
this.InitializeComponent();
this.Locations = new ObservableCollection<LocationData>();
this.MappedLocations = new ObservableCollection<LocationData>(this.Locations);
// MappedLocations is a superset of Locations, so any changes in Locations
// need to be reflected in MappedLocations.
this.Locations.CollectionChanged += (s, e) =>
{
if (e.NewItems != null) foreach (LocationData item in e.NewItems) this.MappedLocations.Add(item);
if (e.OldItems != null) foreach (LocationData item in e.OldItems) this.MappedLocations.Remove(item);
};
// Update the travel times every 5 minutes.
Helpers.StartTimer(5, async () => await this.UpdateLocationsTravelInfoAsync());
// Update the freshness timestamp every minute;
Helpers.StartTimer(1, () => { foreach (var location in this.Locations) location.RefreshFormattedTimestamp(); });
LocationHelper.RegisterTrafficMonitor();
}
/// <summary>
/// Loads the saved location data on first navigation, and
/// attaches a Geolocator.StatusChanged event handler.
/// </summary>
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode == NavigationMode.New)
{
// Load location data from storage if it exists;
// otherwise, load sample location data.
var locations = await LocationDataStore.GetLocationDataAsync();
if (locations.Count == 0) locations = await LocationDataStore.GetSampleLocationDataAsync();
foreach (var location in locations) this.Locations.Add(location);
// Start handling Geolocator and network status changes after loading the data
// so that the view doesn't get refreshed before there is something to show.
LocationHelper.Geolocator.StatusChanged += Geolocator_StatusChanged;
NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;
}
}
/// <summary>
/// Cancels any in-flight request to the Geolocator, and
/// disconnects the Geolocator.StatusChanged event handler.
/// </summary>
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
LocationHelper.CancelGetCurrentLocation();
LocationHelper.Geolocator.StatusChanged -= Geolocator_StatusChanged;
NetworkInformation.NetworkStatusChanged -= NetworkInformation_NetworkStatusChanged;
}
#endregion Initialization and navigation code
#region Geolocator and network status and map refresh code
/// <summary>
/// Handles the Geolocator.StatusChanged event to refresh the map and locations list
/// if the Geolocator is available, and to display an error message otherwise.
/// </summary>
private async void Geolocator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
{
await Helpers.CallOnUiThreadAsync(async () =>
{
switch (args.Status)
{
case PositionStatus.Ready:
this.UpdateLocationStatus(true);
await this.ResetViewAsync();
break;
case PositionStatus.Initializing:
break;
case PositionStatus.NoData:
case PositionStatus.Disabled:
case PositionStatus.NotInitialized:
case PositionStatus.NotAvailable:
default:
this.UpdateLocationStatus(false);
await this.ResetViewAsync(false);
break;
}
});
}
/// <summary>
/// Handles the NetworkInformation.NetworkStatusChanged event to refresh the locations
/// list if the internet is available, and to display an error message otherwise.
/// </summary>
/// <param name="sender"></param>
private async void NetworkInformation_NetworkStatusChanged(object sender)
{
await Helpers.CallOnUiThreadAsync(async () =>
{
var profile = NetworkInformation.GetInternetConnectionProfile();
bool isNetworkAvailable = profile != null;
this.UpdateNetworkStatus(isNetworkAvailable);
if (isNetworkAvailable) await this.ResetViewAsync();
});
}
/// <summary>
/// Updates the UI to account for the user's current position, if available,
/// resetting the MapControl bounds and refreshing the travel info.
/// </summary>
/// <param name="isGeolocatorReady">false if the Geolocator is known to be unavailable; otherwise, true.</param>
/// <returns></returns>
private async Task ResetViewAsync(bool isGeolocatorReady = true)
{
LocationData currentLocation = null;
if (isGeolocatorReady) currentLocation = await this.GetCurrentLocationAsync();
if (currentLocation != null)
{
if (this.MappedLocations.Count > 0 && this.MappedLocations[0].IsCurrentLocation)
{
this.MappedLocations.RemoveAt(0);
}
this.MappedLocations.Insert(0, new LocationData { Position = currentLocation.Position, IsCurrentLocation = true });
}
// Set the current view of the map control.
var positions = this.Locations.Select(loc => loc.Position).ToList();
if (currentLocation != null) positions.Insert(0, currentLocation.Position);
var bounds = GeoboundingBox.TryCompute(positions);
double viewWidth = ApplicationView.GetForCurrentView().VisibleBounds.Width;
var margin = new Thickness((viewWidth >= 500 ? 300 : 10), 10, 10, 10);
bool isSuccessful = await this.InputMap.TrySetViewBoundsAsync(bounds, margin, MapAnimationKind.Default);
if (isSuccessful && positions.Count < 2) this.InputMap.ZoomLevel = 12;
else if (!isSuccessful && positions.Count > 0)
{
this.InputMap.Center = new Geopoint(positions[0]);
this.InputMap.ZoomLevel = 12;
}
if (currentLocation != null) await this.TryUpdateLocationsTravelInfoAsync(this.Locations, currentLocation);
}
/// <summary>
/// Updates the travel time and distance info for all locations in the Locations collection,
/// based on the user's current position (if available).
/// </summary>
private async Task UpdateLocationsTravelInfoAsync()
{
var currentLocation = await this.GetCurrentLocationAsync();
if (currentLocation != null) await this.TryUpdateLocationsTravelInfoAsync(this.Locations, currentLocation);
}
/// <summary>
/// Shows or hides the error message relating to network status, depending on the specified value.
/// </summary>
/// <param name="isNetworkAvailable">true if network resources are available; otherwise, false.</param>
private void UpdateNetworkStatus(bool isNetworkAvailable)
{
this.MapServicesDisabledMessage.Visibility =
isNetworkAvailable ? Visibility.Collapsed : Visibility.Visible;
}
/// <summary>
/// Shows or hides the error message relating to the Geolocator status, depending on the specified value.
/// </summary>
/// <param name="isLocationAvailable">true if the Geolocator is available; otherwise, false.</param>
private void UpdateLocationStatus(bool isLocationAvailable)
{
this.LocationDisabledMessage.Visibility =
isLocationAvailable ? Visibility.Collapsed : Visibility.Visible;
}
/// <summary>
/// Attempts to update the travel distance and time info for the specified locations,
/// relative to the current location, and raises an alert for each flagged location
/// if traffic is currently increasing the travel time by 10 minutes or more; also
/// updates the network status message depending on the results.
/// </summary>
private async Task<bool> TryUpdateLocationsTravelInfoAsync(IEnumerable<LocationData> locations, LocationData currentLocation)
{
bool isNetworkAvailable = await LocationHelper.TryUpdateLocationsTravelInfoAsync(this.Locations, currentLocation);
this.UpdateNetworkStatus(isNetworkAvailable);
return isNetworkAvailable;
}
/// <summary>
/// Gets the current location if the geolocator is available,
/// and updates the Geolocator status message depending on the results.
/// </summary>
/// <returns>The current location.</returns>
private async Task<LocationData> GetCurrentLocationAsync()
{
var currentLocation = await LocationHelper.GetCurrentLocationAsync();
this.UpdateLocationStatus(currentLocation != null);
return currentLocation;
}
#endregion Geolocator status and map refresh code
#region Primary commands: app-bar buttons, map holding gesture
/// <summary>
/// Handles the button click event to hide or show the locations list, enabling
/// greater access to the map control with small windows.
/// </summary>
private void ListDisplayModeButton_Click(object sender, RoutedEventArgs e)
{
this.ToggleLocationsPaneVisibility();
}
/// <summary>
/// Changes the visibility of the locations list, and updates
/// the app bar button to reflect the new state.
/// </summary>
private void ToggleLocationsPaneVisibility()
{
if (this.LocationsView.Visibility == Visibility.Visible)
{
this.LocationsView.Visibility = Visibility.Collapsed;
this.ListDisplayModeButton.Icon = new SymbolIcon { Symbol = Symbol.OpenPane };
ToolTipService.SetToolTip(this.ListDisplayModeButton, "Show locations list");
}
else
{
this.LocationsView.Visibility = Visibility.Visible;
this.ListDisplayModeButton.Icon = new SymbolIcon { Symbol = Symbol.ClosePane };
ToolTipService.SetToolTip(this.ListDisplayModeButton, "Hide locations list");
}
}
/// <summary>
/// Handles clicks to the Add Current button by adding a new location
/// to the Locations list with the user's current position.
/// </summary>
private async void AddCurrentLocation_Click(object sender, RoutedEventArgs e)
{
(sender as Button).IsEnabled = false;
var currentLocation = await this.GetCurrentLocationAsync();
if (currentLocation != null)
{
// Resolve the address given the geocoordinates.
await LocationHelper.TryUpdateMissingLocationInfoAsync(currentLocation, currentLocation);
this.EditNewLocation(currentLocation);
}
(sender as Button).IsEnabled = true;
}
/// <summary>
/// Handles clicks to the Add New button by creating a new, empty location
/// in the Locations list and displaying the editor flyout.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddNewLocation_Click(object sender, RoutedEventArgs e)
{
this.EditNewLocation(new LocationData());
}
/// <summary>
/// Handles the Holding event of the MapControl to add a new location
/// to the Locations list, using the position indicated by the gesture.
/// </summary>
private async void InputMap_MapHolding(MapControl sender, MapInputEventArgs args)
{
var location = new LocationData { Position = args.Location.Position };
// Resolve the address given the geocoordinates. In this case, because the
// location is unambiguous, there is no need to pass in the current location.
await LocationHelper.TryUpdateMissingLocationInfoAsync(location, null);
this.EditNewLocation(location);
}
#endregion Primary commands: app-bar buttons, map holding gesture
#region Location commands: per-location buttons
/// <summary>
/// Handles edit button clicks to enter edit mode for the selected location.
/// </summary>
private void EditLocation_Click(object sender, RoutedEventArgs e)
{
this.EditLocation(this.GetLocation(sender as Button));
}
/// <summary>
/// Handles delete button clicks to remove the selected
/// location from the Locations collection.
/// </summary>
private async void DeleteLocation_Click(object sender, RoutedEventArgs e)
{
var location = this.GetLocation(sender as Button);
int index = this.Locations.IndexOf(location);
this.Locations.Remove(location);
await LocationDataStore.SaveLocationDataAsync(this.Locations);
}
/// <summary>
/// Handles clicks to the Show Route button to launch the Maps app and display
/// the route to the selected location from the user's current position.
/// </summary>
private async void ShowRouteButton_Click(object sender, RoutedEventArgs e)
{
var currentLocation = await this.GetCurrentLocationAsync();
if (currentLocation != null)
{
var location = this.GetLocation(sender as Button);
await LocationHelper.ShowRouteToLocationInMapsAppAsync(location, currentLocation);
}
}
/// <summary>
/// Handles clicks to the Track button to flag the selected location for monitoring
/// by a background task that periodically checks traffic and sends a notification
/// whenever traffic adds 10 minutes or more to the travel time.
/// </summary>
private async void TrackButton_Click(object sender, RoutedEventArgs e)
{
var button = sender as ToggleButton;
var location = this.GetLocation(button);
location.IsMonitored = button.IsChecked.Value;
this.UpdateTrafficMonitor(button.IsChecked.Value);
await LocationDataStore.SaveLocationDataAsync(this.Locations);
}
/// <summary>
/// Gets the data context of the specified element as a LocationData instance.
/// </summary>
/// <param name="element">The element bound to the location.</param>
/// <returns>The location bound to the element.</returns>
private LocationData GetLocation(FrameworkElement element) =>
(element.FindName("Presenter") as FrameworkElement).DataContext as LocationData;
/// <summary>
/// Registers or unregisters the traffic monitoring background task depending
/// on whether the number of tracked locations changes from 1 to 0 or from 0 to 1.
/// </summary>
/// <param name="isIncrement">true if a location was just flagged;
/// false if a location was just unflagged.</param>
private void UpdateTrafficMonitor(bool isIncrement)
{
var monitoredLocationCount = this.Locations.Count(location => location.IsMonitored);
if (isIncrement && monitoredLocationCount == 1) LocationHelper.RegisterTrafficMonitor();
else if (monitoredLocationCount == 0) LocationHelper.UnregisterTrafficMonitor();
}
#endregion Location commands: per-location buttons
#region Editor functionality
/// <summary>
/// Handles clicks to the Save button by saving location edits.
/// </summary>
private async void FlyoutSave_Click(object sender, RoutedEventArgs e)
{
await this.SaveAsync((sender as FrameworkElement).DataContext as LocationData);
}
/// <summary>
/// Handles presses to the Enter key by saving location edits.
/// </summary>
private async void TextBox_KeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter)
{
await this.SaveAsync((sender as FrameworkElement).DataContext as LocationData);
}
}
/// <summary>
/// Adds the specified location to the Locations list and shows the editor flyout.
/// </summary>
public void EditNewLocation(LocationData location)
{
if (this.LocationsView.Visibility == Visibility.Collapsed) this.ToggleLocationsPaneVisibility();
this.Locations.Add(location);
this.LocationsView.UpdateLayout();
this.isNewLocationInEdit = true;
this.EditLocation(location);
}
/// <summary>
/// Opens the editor, binding it to a temporary copy of the current location
/// that can be saved, or discarded if the user dismisses the editor.
/// </summary>
/// <param name="location"></param>
private void EditLocation(LocationData location)
{
this.locationInEdit = location;
var element = this.GetTemplateRootForLocation(location);
var flyout = Flyout.GetAttachedFlyout(element) as Flyout;
(flyout.Content as FrameworkElement).DataContext = location.Clone();
flyout.ShowAt(element);
}
/// <summary>
/// Applies the changes represented by the specified LocationData to
/// the cached location in edit, saves the changes to the file system,
/// and updates the user interface to account for the changes.
/// </summary>
private async Task SaveAsync(LocationData workingCopy)
{
Flyout.GetAttachedFlyout(this.GetTemplateRootForLocation(this.locationInEdit)).Hide();
this.isNewLocationInEdit = false;
this.isExistingLocationBeingRepositioned = false;
bool isAddressNew = workingCopy.Address != this.locationInEdit.Address;
bool areCoordinatesNew = !workingCopy.Position.Equals(this.locationInEdit.Position);
// If just the address OR just the coordinates are new,
// clear the other value so that it can be updated.
if (isAddressNew ^ areCoordinatesNew)
{
if (isAddressNew) workingCopy.Position = new BasicGeoposition();
if (areCoordinatesNew) workingCopy.Address = string.Empty;
}
// If the address, the coordinates, or both have changed, clear the travel
// info and the route so that it doesn't reflect the old position.
if (isAddressNew || areCoordinatesNew)
{
workingCopy.ClearTravelInfo();
this.InputMap.Routes.Clear();
}
this.locationInEdit.Copy(workingCopy);
var currentLocation = await this.GetCurrentLocationAsync();
if (currentLocation != null)
{
if (isAddressNew ^ areCoordinatesNew)
{
await LocationHelper.TryUpdateMissingLocationInfoAsync(this.locationInEdit, currentLocation);
}
}
await LocationDataStore.SaveLocationDataAsync(this.Locations);
if (currentLocation != null)
{
bool isNetworkAvailable = await this.TryUpdateLocationsTravelInfoAsync(this.Locations, currentLocation);
if (isNetworkAvailable) this.InputMap.Routes.Add(new MapRouteView(this.locationInEdit.FastestRoute));
}
}
/// <summary>
/// Handles the light-dismiss of the editor flyout to cancel edit mode.
/// </summary>
private void Flyout_Closed(object sender, object e)
{
// Do nothing if the flyout is closing in order to enter map selection mode.
if (this.isMapSelectionEnabled) return;
// If a new location is still in edit, then the user has light-dismissed
// the editor without saving. In this case, delete the new location.
else if (this.isNewLocationInEdit)
{
this.isNewLocationInEdit = false;
this.Locations.RemoveAt(this.Locations.Count - 1);
}
// If the user has repositioned an existing location but has not yet
// saved the changes, revert the position to the original one.
else if (this.isExistingLocationBeingRepositioned)
{
this.isExistingLocationBeingRepositioned = false;
this.locationInEdit.Position = this.locationInEditOriginalPosition;
}
}
/// <summary>
/// Gets the UI element that represents the specified location;
/// used to access the attached editor flyout.
/// <param name="location">The location to edit.</param>
/// <returns>The element that represents the location.</returns>
private FrameworkElement GetTemplateRootForLocation(LocationData location)
{
var item = this.LocationsView.ContainerFromItem(location) as ListViewItem;
return item.ContentTemplateRoot as FrameworkElement;
}
#endregion Editor functionality
#region Map selection mode for repositioning a location
/// <summary>
/// Handles the Tapped event of the map-selection-mode display message to leave selection mode.
/// </summary>
private void TextBlock_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
this.DisableMapSelection();
}
/// <summary>
/// Handles the Tapped event of the map control to reposition a location
/// when map selection mode is enabled.
/// </summary>
private async void InputMap_MapTapped(MapControl sender, MapInputEventArgs args)
{
this.InputMap.Routes.Clear();
this.isExistingLocationBeingRepositioned = true;
this.locationInEditOriginalPosition = this.locationInEdit.Position;
this.locationInEdit.Position = args.Location.Position;
var element = this.GetTemplateRootForLocation(this.locationInEdit);
var flyout = Flyout.GetAttachedFlyout(element) as Flyout;
var location = (flyout.Content as FrameworkElement).DataContext as LocationData;
location.Position = args.Location.Position;
location.Address = String.Empty;
await LocationHelper.TryUpdateMissingLocationInfoAsync(location, null);
this.DisableMapSelection();
flyout.ShowAt(element);
}
/// <summary>
/// Enters map selection mode, where the user can reposition the selected
/// location by tapping a new location on the map control.
/// </summary>
private void EnterMapSelectionMode(object sender, RoutedEventArgs e)
{
this.isMapSelectionEnabled = true;
this.InputMap.MapTapped += InputMap_MapTapped;
this.InputMap.MapHolding -= InputMap_MapHolding;
this.LocationsView.Visibility = Visibility.Collapsed;
this.MapSelectionModeMessage.Visibility = Visibility.Visible;
this.ListDisplayModeButton.IsEnabled = false;
this.AddCurrentLocationButton.IsEnabled = false;
this.AddNewLocationButton.IsEnabled = false;
Flyout.GetAttachedFlyout(this.GetTemplateRootForLocation(this.locationInEdit)).Hide();
}
/// <summary>
/// Leaves map selection mode.
/// </summary>
private void DisableMapSelection()
{
this.isMapSelectionEnabled = false;
this.InputMap.MapTapped -= InputMap_MapTapped;
this.InputMap.MapHolding += InputMap_MapHolding;
this.LocationsView.Visibility = Visibility.Visible;
this.MapSelectionModeMessage.Visibility = Visibility.Collapsed;
this.ListDisplayModeButton.IsEnabled = true;
this.AddCurrentLocationButton.IsEnabled = true;
this.AddNewLocationButton.IsEnabled = true;
}
#endregion Map selection mode for repositioning a location
}
}