設定地理柵欄
在您的應用程式中設定地理柵欄,並了解如何在前景和背景中處理通知。
啟用位置功能
- 在 [方案總管] 中,按兩下 [package.appxmanifest],然後選取 [功能] 索引標籤。
- 在 [功能] 清單中,檢查 [位置]。 這會將
Location
裝置功能新增至套件資訊清單檔案。
<Capabilities>
<!-- DeviceCapability elements must follow Capability elements (if present) -->
<DeviceCapability Name="location"/>
</Capabilities>
設定地理柵欄
步驟 1:要求存取使用者的位置
重要
您必須先使用 RequestAccessAsync 方法來要求使用者位置的存取權,才能嘗試存取使用者的位置。 必須從 UI 執行緒呼叫 RequestAccessAsync 方法,並且應用必須位於前景。 在使用者授與應用程式權限之前,您的應用程式將無法存取使用者的位置資訊。
using Windows.Devices.Geolocation;
...
var accessStatus = await Geolocator.RequestAccessAsync();
RequestAccessAsync 方法提示使用者提供存取其位置的權限。 只會提示使用者一次 (每個應用程式)。 第一次授與或拒絕權限之後,這個方法就不會再提示使用者提供權限。 若要協助使用者在提示之後變更位置權限,建議您提供位置設定的連結,如本主題稍後所示。
步驟 2:註冊地理柵欄狀態和位置權限的變更
在此範例中,switch 陳述式在這裡搭配 accessStatus (來自前一個範例) 一起使用,只有在允許存取使用者位置時才會執行。 如果允許存取使用者的位置,程式碼會存取目前的地理柵欄、註冊地理柵欄狀態變更,以及註冊位置權限的變更。
提示 使用地理柵欄時,使用 GeofenceMonitor 類別的 StatusChanged 事件來監視位置權限的變更,而不是 Geolocator 類別中的 StatusChanged 事件。 Disabled 的 GeofenceMonitorStatus 相當於已停用的 PositionStatus - 兩者都表示應用程式沒有存取使用者位置的權限。
switch (accessStatus)
{
case GeolocationAccessStatus.Allowed:
geofences = GeofenceMonitor.Current.Geofences;
FillRegisteredGeofenceListBoxWithExistingGeofences();
FillEventListBoxWithExistingEvents();
// Register for state change events.
GeofenceMonitor.Current.GeofenceStateChanged += OnGeofenceStateChanged;
GeofenceMonitor.Current.StatusChanged += OnGeofenceStatusChanged;
break;
case GeolocationAccessStatus.Denied:
_rootPage.NotifyUser("Access denied.", NotifyType.ErrorMessage);
break;
case GeolocationAccessStatus.Unspecified:
_rootPage.NotifyUser("Unspecified error.", NotifyType.ErrorMessage);
break;
}
然後,在離開前景應用程式時,取消註冊事件接聽程式。
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
GeofenceMonitor.Current.GeofenceStateChanged -= OnGeofenceStateChanged;
GeofenceMonitor.Current.StatusChanged -= OnGeofenceStatusChanged;
base.OnNavigatingFrom(e);
}
步驟 3:建立地理柵欄
現在,您已準備好定義及設定 Geofence 物件。 視您的需求而定,有幾個不同的建構函式多載可供選擇。 在最基本的地理柵欄建構函式中,只指定 Id 和 Geoshape,如下所示。
// Set the fence ID.
string fenceId = "fence1";
// Define the fence location and radius.
BasicGeoposition position;
position.Latitude = 47.6510;
position.Longitude = -122.3473;
position.Altitude = 0.0;
double radius = 10; // in meters
// Set a circular region for the geofence.
Geocircle geocircle = new Geocircle(position, radius);
// Create the geofence.
Geofence geofence = new Geofence(fenceId, geocircle);
您可以使用其中一個其他建構函式進一步微調地理柵欄。 在下一個範例中,地理柵欄建構函式會指定下列其他參數:
- MonitoredStates - 指出您想要接收哪些地理柵欄事件,包括進入定義區域、離開定義區域或移除地理柵欄。
- SingleUse - 移除地理柵欄,一旦已符合所監視的所有狀態,就會移除地理柵欄。
- DwellTime - 指出使用者必須進入或離開定義區域多久時間,才能觸發進入/離開事件。
- StartTime - 指出何時開始監視地理柵欄。
- Duration - 指出要監視地理柵欄的期間。
// Set the fence ID.
string fenceId = "fence2";
// Define the fence location and radius.
BasicGeoposition position;
position.Latitude = 47.6510;
position.Longitude = -122.3473;
position.Altitude = 0.0;
double radius = 10; // in meters
// Set the circular region for geofence.
Geocircle geocircle = new Geocircle(position, radius);
// Remove the geofence after the first trigger.
bool singleUse = true;
// Set the monitored states.
MonitoredGeofenceStates monitoredStates =
MonitoredGeofenceStates.Entered |
MonitoredGeofenceStates.Exited |
MonitoredGeofenceStates.Removed;
// Set how long you need to be in geofence for the enter event to fire.
TimeSpan dwellTime = TimeSpan.FromMinutes(5);
// Set how long the geofence should be active.
TimeSpan duration = TimeSpan.FromDays(1);
// Set up the start time of the geofence.
DateTimeOffset startTime = DateTime.Now;
// Create the geofence.
Geofence geofence = new Geofence(fenceId, geocircle, monitoredStates, singleUse, dwellTime, startTime, duration);
建立之後,請記得向監視器註冊新的 Geofence。
// Register the geofence
try {
GeofenceMonitor.Current.Geofences.Add(geofence);
} catch {
// Handle failure to add geofence
}
步驟 4:處理位置權限的變更
Geolocator 物件會觸發 StatusChanged 事件,指出使用者的位置設定已變更。 該事件透過引數的 sender.Status 屬性 (類型為 GeofenceMonitorStatus) 傳遞相應的狀態。 在此範例中,不會從 UI 執行緒呼叫方法,而 Dispatcher 物件會叫用 UI 變更。
using Windows.UI.Core;
...
public async void OnGeofenceStatusChanged(GeofenceMonitor sender, object e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Show the location setting message only if the status is disabled.
LocationDisabledMessage.Visibility = Visibility.Collapsed;
switch (sender.Status)
{
case GeofenceMonitorStatus.Ready:
_rootPage.NotifyUser("The monitor is ready and active.", NotifyType.StatusMessage);
break;
case GeofenceMonitorStatus.Initializing:
_rootPage.NotifyUser("The monitor is in the process of initializing.", NotifyType.StatusMessage);
break;
case GeofenceMonitorStatus.NoData:
_rootPage.NotifyUser("There is no data on the status of the monitor.", NotifyType.ErrorMessage);
break;
case GeofenceMonitorStatus.Disabled:
_rootPage.NotifyUser("Access to location is denied.", NotifyType.ErrorMessage);
// Show the message to the user to go to the location settings.
LocationDisabledMessage.Visibility = Visibility.Visible;
break;
case GeofenceMonitorStatus.NotInitialized:
_rootPage.NotifyUser("The geofence monitor has not been initialized.", NotifyType.StatusMessage);
break;
case GeofenceMonitorStatus.NotAvailable:
_rootPage.NotifyUser("The geofence monitor is not available.", NotifyType.ErrorMessage);
break;
default:
ScenarioOutput_Status.Text = "Unknown";
_rootPage.NotifyUser(string.Empty, NotifyType.StatusMessage);
break;
}
});
}
設定前景通知
在您建立地理圍欄後,必須新增處理地理圍欄事件發生時的邏輯。 視您已設定的 MonitoredStates 而定,您可能會在下列情況下收到事件:
- 使用者進入關注區域。
- 使用者離開關注區域。
- 地理柵欄已過期或已移除。 請注意,移除事件不會啟用背景應用程式。
您可以在應用程式執行時直接接聽事件,或註冊背景工作,以便在事件發生時收到背景通知。
步驟 1:註冊地理柵欄狀態變更事件
若要讓應用程式收到地理柵欄狀態變更的前景通知,您必須註冊事件處理常式。 這通常會在您建立地理柵欄時設定。
private void Initialize()
{
// Other initialization logic
GeofenceMonitor.Current.GeofenceStateChanged += OnGeofenceStateChanged;
}
步驟 2:實作地理柵欄事件處理常式
下一個步驟是實作事件處理常式。 此處所採取的動作取決於您的應用程式使用地理柵欄的用途。
public async void OnGeofenceStateChanged(GeofenceMonitor sender, object e)
{
var reports = sender.ReadReports();
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
foreach (GeofenceStateChangeReport report in reports)
{
GeofenceState state = report.NewState;
Geofence geofence = report.Geofence;
if (state == GeofenceState.Removed)
{
// Remove the geofence from the geofences collection.
GeofenceMonitor.Current.Geofences.Remove(geofence);
}
else if (state == GeofenceState.Entered)
{
// Your app takes action based on the entered event.
// NOTE: You might want to write your app to take a particular
// action based on whether the app has internet connectivity.
}
else if (state == GeofenceState.Exited)
{
// Your app takes action based on the exited event.
// NOTE: You might want to write your app to take a particular
// action based on whether the app has internet connectivity.
}
}
});
}
設定背景通知
在您建立地理圍欄後,必須新增處理地理圍欄事件發生時的邏輯。 視您已設定的 MonitoredStates 而定,您可能會在下列情況下收到事件:
- 使用者進入關注區域。
- 使用者離開關注區域。
- 地理柵欄已過期或已移除。 請注意,移除事件不會啟用背景應用程式。
接聽背景中的地理柵欄事件
- 應用程式資訊清單中宣告背景工作。
- 在應用程式中註冊背景工作。 如果您的應用程式需要網路存取權,例如存取雲端服務,您可以在事件觸發時設定旗標。 您也可以設定旗標,以確保在事件觸發時使用者在場,這樣您就可以確保使用者收到通知。
- 當您的應用程式在前景執行時,提示使用者授與您的應用程式位置權限。
步驟 1:註冊地理柵欄狀態變更
在應用程式的資訊清單中,於 [宣告] 索引標籤底下,新增位置背景工作的宣告。 若要這樣做:
- 新增背景工作類型的宣告。
- 設定 [位置] 的屬性工作類型。
- 設定應用程式的進入點,以在事件觸發時呼叫。
步驟 2:註冊背景工作
此步驟中的程式碼會註冊地理柵欄背景工作。 回想一下,建立地理柵欄時,我們已檢查過位置權限。
async private void RegisterBackgroundTask(object sender, RoutedEventArgs e)
{
// Get permission for a background task from the user. If the user has already answered once,
// this does nothing and the user must manually update their preference via PC Settings.
BackgroundAccessStatus backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();
// Regardless of the answer, register the background task. Note that the user can use
// the Settings app to prevent your app from running background tasks.
// Create a new background task builder.
BackgroundTaskBuilder geofenceTaskBuilder = new BackgroundTaskBuilder();
geofenceTaskBuilder.Name = SampleBackgroundTaskName;
geofenceTaskBuilder.TaskEntryPoint = SampleBackgroundTaskEntryPoint;
// Create a new location trigger.
var trigger = new LocationTrigger(LocationTriggerType.Geofence);
// Associate the location trigger with the background task builder.
geofenceTaskBuilder.SetTrigger(trigger);
// If it is important that there is user presence and/or
// internet connection when OnCompleted is called
// the following could be called before calling Register().
// SystemCondition condition = new SystemCondition(SystemConditionType.UserPresent | SystemConditionType.InternetAvailable);
// geofenceTaskBuilder.AddCondition(condition);
// Register the background task.
geofenceTask = geofenceTaskBuilder.Register();
// Associate an event handler with the new background task.
geofenceTask.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
BackgroundTaskState.RegisterBackgroundTask(BackgroundTaskState.LocationTriggerBackgroundTaskName);
switch (backgroundAccessStatus)
{
case BackgroundAccessStatus.Unspecified:
case BackgroundAccessStatus.Denied:
rootPage.NotifyUser("This app is not allowed to run in the background.", NotifyType.ErrorMessage);
break;
}
}
步驟 3:處理背景通知
您用來通知使用者的動作取決於應用程式用途,但您可以顯示快顯通知、播放音訊音效或更新動態磚。 此步驟中的程式碼會處理通知。
async private void OnCompleted(IBackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs e)
{
if (sender != null)
{
// Update the UI with progress reported by the background task.
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
try
{
// If the background task threw an exception, display the exception in
// the error text box.
e.CheckResult();
// Update the UI with the completion status of the background task.
// The Run method of the background task sets the LocalSettings.
var settings = ApplicationData.Current.LocalSettings;
// Get the status.
if (settings.Values.ContainsKey("Status"))
{
rootPage.NotifyUser(settings.Values["Status"].ToString(), NotifyType.StatusMessage);
}
// Do your app work here.
}
catch (Exception ex)
{
// The background task had an error.
rootPage.NotifyUser(ex.ToString(), NotifyType.ErrorMessage);
}
});
}
}
變更隱私權設定
如果位置隱私權設定不允許您的應用程式存取使用者的位置,建議您提供 [設定] 應用程式中 [位置隱私權設定] 的便利連結。 在此範例中,會使用 Hyperlink 控制項瀏覽至 ms-settings:privacy-location
URI。
<!--Set Visibility to Visible when access to the user's location is denied. -->
<TextBlock x:Name="LocationDisabledMessage" FontStyle="Italic"
Visibility="Collapsed" Margin="0,15,0,0" TextWrapping="Wrap" >
<Run Text="This app is not able to access Location. Go to " />
<Hyperlink NavigateUri="ms-settings:privacy-location">
<Run Text="Settings" />
</Hyperlink>
<Run Text=" to check the location privacy settings."/>
</TextBlock>
或者,您的應用程式可以呼叫 LaunchUriAsync 方法來從程式碼啟動 [設定] 應用程式。 如需詳細資訊,請參閱啟動 Windows 設定應用程式。
using Windows.System;
...
bool result = await Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-location"));
測試和偵錯應用程式
測試和偵錯地理柵欄應用程式可能是一項挑戰,因為它們取決於裝置的位置。 在這裡,我們將概述數種方法來測試前景和背景地理柵欄。
偵錯地理柵欄應用程式
- 將裝置實際移至新位置。
- 透過建立包含您目前實際位置的地理柵欄區域,來進行進入地理柵欄的測試,這樣您就已經位於地理柵欄內,並且立即觸發「進入地理柵欄」事件。
- 使用 Microsoft Visual Studio 模擬器來模擬裝置的位置。
測試及偵錯在前景執行的地理柵欄應用程式
測試在前景中執行的地理柵欄應用程式
- 在 Visual Studio 中建置您的應用程式。
- 在 Visual Studio 模擬器中啟動您的應用程式。
- 使用這些工具來模擬地理柵欄區域內外的各種位置。 請務必等候足夠的時間超過 DwellTime 屬性所指定的時間,以觸發事件。 請注意,您必須接受提示,才能啟用應用程式的位置權限。 如需模擬位置的詳細資訊,請參閱設定裝置的模擬地理位置。
- 您也可以使用模擬器來估算在不同速度下需要被偵測到的柵欄大小和停留時間。
測試及偵錯在背景執行的地理柵欄應用程式
測試在背景執行的地理柵欄應用程式
- 在 Visual Studio 中建置您的應用程式。 請注意,您的應用程式應該設定 [位置] 背景工作類型。
- 先在本機部署應用程式。
- 關閉本機執行的應用程式。
- 在 Visual Studio 模擬器中啟動您的應用程式。 請注意,模擬器內一次只支援模擬一個應用程式的背景地理柵欄。 請勿在模擬器內啟動多個地理柵欄應用程式。
- 從模擬器中模擬地理柵欄區域內外的各種位置。 請務必等候足夠的時間超過 DwellTime 以觸發事件。 請注意,您必須接受提示,才能啟用應用程式的位置權限。
- 使用 Visual Studio 來觸發位置背景工作。 如需在Visual Studio中觸發背景工作的詳細資訊,請參閱如何觸發背景工作。
疑難排解您的應用程式
您的應用程式必須先在裝置上啟用 [位置],才能存取使用者的位置。 在 [設定] 應用程式中,檢查是否已開啟下列 [位置隱私權設定]:
- 此裝置的位置...已開啟 (不適用於 Windows 10 行動裝置版)
- 位置服務設定 [位置] 已開啟
- 在 [選擇可使用您位置的應用程式] 下,您的應用程式設定為 [開啟]