/// <summary>
        /// Refreshes the list of items obtained from the API and populates the view model
        /// </summary>
        public async Task Refresh()
        {
            // get bounding box of current map
            var topLeft = TheMap.ConvertViewportPointToGeoCoordinate(new Point(0, 0));
            var bottomRight = TheMap.ConvertViewportPointToGeoCoordinate(new Point(TheMap.ActualWidth, TheMap.ActualHeight));
            var box = new BoundingBox(topLeft.Latitude, bottomRight.Latitude, topLeft.Longitude, bottomRight.Longitude);

            //
            // TODO: invoke API that returns a list of point-of-interest items now in view on the map. This will populate
            //       an observable collection of IMappable items on the ApiViewModel class (with the name Results). As that 
            //       collection is populated, the Results_CollectionChanged callback will fire to create the appropriate
            //       point-of-interest pins and associate them with the map.
            //
            this.DefaultViewModel.ApiStatus = await _tomTomApi.GetCameras(box, (Int32)App.Current.Resources["MaxResults"]);

            // if there's a problem in the request, show a message box
            if (!DefaultViewModel.ApiStatus.IsSuccessStatusCode)
            {
                MessageBox.Show(
                    String.Format("There was an error in handling the last request.\n\nCode: {0}\n\nMessage: {1}", 
                                               (Int32) DefaultViewModel.ApiStatus.StatusCode, 
                                               String.IsNullOrEmpty(DefaultViewModel.ApiStatus.Message) ?  DefaultViewModel.ApiStatus.StatusCode.ToString() :  DefaultViewModel.ApiStatus.Message),
                    "Request Error",
                    MessageBoxButton.OK);
            }
        }
        /// <summary>
        /// Refreshes the list of items obtained from the API and populates the view model
        /// </summary>
        /// <param name="box">Bounding box of current map view</param>
        /// <param name="id">Id of IMappable item that should be selected</param>
        public async Task Refresh(BoundingBox box, String id = null) 
        {
            //
            // TODO: refresh the items in the panel to reflect points of interest in the current map view. You
            //       will invoke your target API that populates the view model's ObservableCollection and returns
            //       a status object.  The "NoResults" entry in the view model is used to drive the visibility
            //       of text that appears when the query returns no elements (versus providing no feedback).
            //
            //
            this.DefaultViewModel["ApiStatus"] =
                await _tomTomApi.GetCameras(box, this.MaxResults);
            this.DefaultViewModel["NoResults"] = _tomTomApi.TomTomViewModel.Results.Count == 0;


            // if there's an IMappable ID provided, select that item automatically
            if (id != null)
                MappableListView.SelectedItem = MappableListView.Items.Where((c) => (c as IMappable).Id == id).FirstOrDefault();

            // signal that panel has been refreshed
            OnRefreshed();
        }   
Example #3
0
        public MainPage()
        {
            this.InitializeComponent();

            // view model
            this.DefaultViewModel["PendingRefresh"] = false;

            // check to see if this is the first time application is being executed by checking for data in local settings.
            // After checking add some notional data as marker for next time app is run. This will be used to determine whether
            // to prompt the user (or not) that location services are not turned on for the app/system. Without this check, the
            // first time the app is run, it will provide a system prompt, and if that prompt is dismissed without granting 
            // access the propmpt displayed by the application would also appear unnecessarily.
            _firstRun = ApplicationData.Current.LocalSettings.Values.Count == 0;
            if (_firstRun)
                ApplicationData.Current.LocalSettings.Values.Add(
                    new System.Collections.Generic.KeyValuePair<string, object>("InitialRunDate", DateTime.UtcNow.ToString()));

            this.SizeChanged += (s, e) =>
            {
                // determine if there's been a change in orientation that doesn't require notice that the map view has changed (e.g., app open, snapped mode transitions)              
                if (_priorOrientation == ApplicationView.Value) return;

                _noRefreshRequiredViewChange = (_priorOrientation == ApplicationViewState.Snapped || ApplicationView.Value == ApplicationViewState.Snapped);
                _priorOrientation = ApplicationView.Value;

                VisualStateManager.GoToState(LeftPanel, ApplicationView.Value.ToString(), true);
            };

            // whenever map view changes track center point and zoom level in page state
            TheMap.ViewChangeEnded += (s, e) =>
                {
                    // save new center/zoom for page state
                    _pageState.MapCenter = new LatLong(TheMap.TargetCenter.Latitude, TheMap.TargetCenter.Longitude);
                    _pageState.Zoom = TheMap.TargetZoomLevel;

                    // ViewChangeEnded fires a bit too often, so retain bounding box to determine if the view truly has changed
                    BoundingBox thisBox = new BoundingBox(TheMap.TargetBounds.North, TheMap.TargetBounds.South,
                                      TheMap.TargetBounds.West, TheMap.TargetBounds.East);

                    // determine if view change should notify user of pending refresh requirement
                    if (_retainRefreshRequiredViewChange)
                        this.DefaultViewModel["PendingRefresh"] = _pageState.PendingRefresh;
                    else if (_noRefreshRequiredViewChange)
                        this.DefaultViewModel["PendingRefresh"] = false;
                    else if (App.InitialMapResizeHasOccurred)
                        this.DefaultViewModel["PendingRefresh"] = (thisBox != _lastBox);

                    // update state variables
                    _lastBox = thisBox;
                    if (App.InitialMapResizeHasOccurred) _noRefreshRequiredViewChange = false;
                    _retainRefreshRequiredViewChange = false;
                    App.InitialMapResizeHasOccurred = true;
                };

            // if refresh prompt is tapped, refresh the map
            RefreshPrompt.Tapped += async (s, e) =>
                {
                    await LeftPanel.Refresh(_lastBox);
                    this.DefaultViewModel["PendingRefresh"] = false;
                };

            // a tap on map will cause refresh is one is predicated
            TheMap.Tapped += async (s, e) =>
                {
                    if ((Boolean)this.DefaultViewModel["PendingRefresh"])
                    {
                        await LeftPanel.Refresh(_lastBox);
                        this.DefaultViewModel["PendingRefresh"] = false;
                    }
                };

            // set the reference to the current map for the LeftPanel (note: using Element binding will not handle all of the page navigation scenarios)
            LeftPanel.Map = TheMap;

            // whenever the contents of left panel are refreshed, save the map coordinates that were in play as part of the page state
            LeftPanel.Refreshed += (s, e) =>
                {
                    _pageState.MapBox = new BoundingBox(TheMap.TargetBounds.North, TheMap.TargetBounds.South, TheMap.TargetBounds.West, TheMap.TargetBounds.East);
                };

            // whenver a new item is selected in the left panel, update the map pins and save the item selected as part of the page state
            LeftPanel.ItemSelected += (s, e) =>
                {
                    TheMap.HighlightPointOfInterestPin(e.NewItem, true);
                    TheMap.HighlightPointOfInterestPin(e.OldItem, false);

                    this._pageState.SelectedItemId = e.NewItem == null ? null : e.NewItem.Id;
                };

            // whenever a new location is selected from the SearchFlyout (this is NOT the Search charm) update the position accordingly
            SearchFlyout.LocationChanged += (s, e) =>
            {
                GotoLocation(e.Position);
                SearchFlyout.Hide();
            };

            // manage SearchFlyout visibility/interaction
            this.Tapped += (s, e) =>
            {
                if (SearchFlyout.IsOpen)
                {
                    SearchFlyout.Hide();
                    e.Handled = true;
                }
            };
            BottomAppBar.Opened += (s, e) => { SearchFlyout.Hide(); };
            SearchFlyout.Tapped += (s, e) => { e.Handled = true; };

            // allow type-to-search for Search charm
            SearchPane.GetForCurrentView().ShowOnKeyboardInput = true;

            // The BingMaps API allows use of a "session key" if the application leverages the Bing Maps control. By using the session
            // key instead of the API key, only one transaction is logged agains the key versus one transaction for every API call! This 
            // code sets the key asynchronously and stored it as a resource so it's available when the REST API's are invoked.
            TheMap.Loaded += async (s, e) =>
            {
                if (!Application.Current.Resources.ContainsKey("BingMapsSessionKey"))
                    Application.Current.Resources.Add("BingMapsSessionKey", await TheMap.GetSessionIdAsync());
            };
        }