Начинаем цикл публикаций, призванных помочь начинающим программистам в создании контекстно-зависимых приложений для Windows 7. Об этом много говорили на PDC да и мы неоднократно писали о этом новом классе приложений. Пользователи Windows 7 build 6801 могли заметить появление в гаджете погоды опции автоматического определения местоположения компьютера. И сегодня у вас появилась уникальная возможность попробовать себя в создании подобных приложений. Я решил немного поиграть с Windows Location Platform, входящей в состав Windows 7 build 6801, которая была роздана посетителям конференции PDC 2008, и решил попробовать создать свое WPF-приложение, которое показывало бы карту Virtual Earth и мое местоположение на ней. В сборке 6801 всем СОМ-классы Location API обозначены как ThreadingModel = Free (MTA). Поэтому, для того чтобы вызвать их из другой потоковой модели (STA), придется прибегнуть к промежуточным классам. Похоже, что сборка, генерируемая VS (LocationDispLib.dll), не включает в себя эти самые промежуточные классы для COM-распределения. Таким образом, для получения доступа к Location API нам необходимо использовать другой поток, который позволит использовать эти данные в нашем WPF-приложении. Что нужно для создания приложения
Нажимаем на кнопку Show Me on Map, Windows Location Platform получает координаты, приложение отмечает текущее местоположение на карте.
Приложение отслеживает перемещения и ежесекундно отмечает их:
Как это реализовано Windows Location Platform API представлены COM- и Win32-объектами. Для упрощения программирования мы воспользуемся COM-версией API, которая внедрена в файл LocationDisp.dll в папке %Windir%\System32\ (в x86-версии). Для использования данного API нам необходимо добавить в проект ссылку на эту библиотеку. 1. Так как API запускаются в режиме MTA, а основное WPF-приложение в STA, нам нужно использовать отдельный поток для того, чтобы наше приложение могло получить информацию от API.
Code:
BackgroundWorker m_worker = new BackgroundWorker(); BackgroundWorker m_workerStatus = new BackgroundWorker(); private LatLongReport m_report = new LatLongReport(); private LocationPlatformStatus m_status = new LocationPlatformStatus(); DispatcherTimer m_timerAutoTracking = new DispatcherTimer(); DispatcherTimer m_timerLocPlatformStatus = new DispatcherTimer(); bool m_useAutoTracking = false;
Таймеры будут использоваться для обработки изменения местоположения (m_timerAutoTracking). LatLongReport - .NET упаковщик для класса DispLatLongReport из Interop.LocationDisp.dll, которая была создана из LocationDisp.dll силами Visual Studio. Она необходима для того, чтобы несколько упростить COM-функцию, вызываемую в .NET-приложении.
public Window1() { InitializeComponent(); m_worker.DoWork += new DoWorkEventHandler(worker_DoWork); m_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); m_workerStatus.DoWork += new DoWorkEventHandler(m_workerStatus_DoWork); m_workerStatus.RunWorkerCompleted += new RunWorkerCompletedEventHandler (m_workerStatus_RunWorkerCompleted); m_timerAutoTracking.Interval = new TimeSpan(0, 0, 0, 0, 1000); m_timerAutoTracking.Tick += new EventHandler(m_timer_Tick); m_timerLocPlatformStatus.Interval = new TimeSpan(0, 0, 0, 0, 1000); m_timerLocPlatformStatus.Tick += new EventHandler(m_timerLocPlatformStatus_Tick); // enabling Status Timer m_timerLocPlatformStatus.Start(); }
Здесь мы просто инициализировали асинхронные обработчики событий DoWork и RunWorkerCompleted для обоих исполнителей Background Worker, настров интервал в 1 секунду для таймеров и обработчиков их событий.
public void ShowMeOnMapCommand_Executed(object sender, ExecutedRoutedEventArgs e) { try { m_worker.RunWorkerAsync(); } catch { // some times it can't do the job at 1 sec } }
Тут, однако, есть маленькая проблема. Так как мы не подписываемся на события через Location API, но при этом вызываем их, используя таймеры, исполнители Background Worker могут столкнуться с проблемой, при которой они не успели завершить свою работу. Чтобы не усложнять приложение, я решил добавить здесь блок. RunWorkerAsync() включает событие DoWork() из фонового исполнителя m_worker:
void worker_DoWork(object sender, DoWorkEventArgs e) { LatLongReportFactoryClass factory = new LatLongReportFactoryClass(); m_report = new LatLongReport(factory.LatLongReport); }
Именно здесь происходит реальное обращение к Windows Location API. То есть нам необходимо сослаться на LatLongReportFactoryClass и получить от него LatLongReport, который и будет использоваться в приложении. В действительности, первое, что нам необходимо сделать - проверить поле LatLongFactoryClass.Status, чтобы убедится, что сенсоры доступны и работают.
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { //Cancelled } else if (e.Error != null) { //Exception Thrown } else { //Completed PushPin pushPin = new PushPin(); pushPin.Latitude = m_report.Latitude; pushPin.Longitude = m_report.Longitude; t his.VirtualEarth.PushPins.Add(pushPin); pushPin.CenterInMap(); } }
В данном методе мы центрируем карту, чтобы созданная отметка показывалась по центру Virtual Earth. В случае если сенсоры присутствуют, включены и доступны, нажатие кнопки Show Me on Map покажет в центре карты Virtual Earth нашу позицию.