Поделиться через


Расширьте своё настольное приложение с помощью современных компонентов UWP

Некоторые возможности Windows (например, страница пользовательского интерфейса с поддержкой сенсорного ввода) должна выполняться внутри AppContainer. Если вы хотите добавить такие возможности, расширьте настольное приложение с помощью проектов UWP и компонентов среды выполнения Windows.

Во многих случаях можно вызывать API среды выполнения Windows непосредственно из классического приложения, поэтому перед прочтением этого руководства просмотрите статью Улучшение для Windows.

Примечание.

Функции, описанные в этом разделе, требуют, чтобы ваше приложение было упаковано (то есть имело идентификатор пакета во время выполнения). Это включает упакованные приложения (см. статью "Создание нового проекта для упакованного настольного приложения WinUI 3") и упакованные приложения с внешним расположением (см. раздел "Предоставление удостоверения пакета путем упаковки с внешним расположением"). Также см. функции, требующие идентификации пакета.

Сначала следует настроить решение

Добавьте в решение один или несколько проектов UWP и компонентов среды выполнения.

Начните с решения, которое содержит проект упаковки приложений Windows со ссылкой на ваше настольное приложение.

На этом рисунке показан пример решения.

Расширение начального проекта

Если ваше решение не содержит проект упаковки, см. упакуйте ваше настольное приложение с помощью Visual Studio.

Настройка настольного приложения

Убедитесь, что ваше настольное приложение имеет ссылки на файлы, необходимые для вызова Windows Runtime API.

Соответствующую процедуру см. в статье Вызов API для среды выполнения Windows в классических приложениях.

Добавление проекта UWP

Добавьте в решение проект Пустое приложение (универсальная платформа Windows).

Там вы сможете создать современный пользовательский интерфейс XAML или использовать API, которые выполняются только в процессе UWP.

Добавление нового проекта

В проекте упаковки щелкните правой кнопкой узел Приложения и выберите команду Добавить ссылку.

Добавление ссылки

Затем добавьте ссылку на проект UWP.

Выбор проекта UWP

Решение будет выглядеть следующим образом:

Решение с проектом UWP

(Необязательно) Создайте компонент среды выполнения Windows

Для некоторых сценариев потребуется добавить код в компонент среды выполнения Windows.

Служба приложений компонента среды выполнения

Затем в проекте UWP добавьте ссылку на компонент среды выполнения. Решение будет выглядеть следующим образом:

Ссылка на компонент среды выполнения

Создание решения

Убедитесь, что в вашем решении не появляется ошибок. При возникновении ошибок откройте Диспетчер конфигурации и убедитесь, что проекты предназначены для той же платформы.

Диспетчер конфигурации

Рассмотрим некоторые действия, которые можно выполнять с проектами UWP и компонентами среды выполнения.

Отображение современного пользовательского интерфейса XAML

В рамках вашего приложения можно включать современные интерфейсы пользователя на основе XAML в ваше настольное приложение. Эти интерфейсы пользователя естественным образом адаптируются под различные размеры и разрешение экрана и поддерживают современные интерактивные модели, в том числе сенсорный и рукописный ввод.

Например, с помощью небольшого количества разметки XAML вы можете предоставить пользователям мощные функции визуализации, связанные с картой.

На рисунке показано приложение Windows Forms, которое открывает современный пользовательский интерфейс на основе XAML, содержащий элементы управления картой.

Адаптивный макет

Примечание.

В этом примере показан пользовательский интерфейс XAML, полученный добавлением проекта UWP в решение. Это стабильный поддерживаемый подход к отображению пользовательских интерфейсов XAML в классическом приложении. Альтернативный вариант — добавление элементов управления XAML UWP непосредственно в классическое приложение с помощью островков XAML. XAML Islands сейчас предоставляется в предварительной версии для разработчиков. Несмотря на то что вы можете опробовать их в своем собственном прототипном коде сейчас, мы не рекомендуем использовать их в производственном коде в настоящее время. В будущих выпусках Windows эти API и элементы управления будут дорабатываться и совершенствоваться. См. сведения о XAML Islands в руководстве по элементам управления UWP в классических приложениях.

Шаблон проектирования

Для отображения пользовательского интерфейса на основе XAML выполните следующие действия:

1️⃣ Настройка решения

2️⃣ Создание пользовательского интерфейса XAML

3️⃣ Добавление расширения протокола в проект UWP

4️⃣ Запуск приложения UWP из классического приложения

5️⃣ Отображение нужной страницы в проекте UWP

Настройте ваше решение

Общие рекомендации по настройке вашего решения см. в разделе "Сначала настройте своё решение" в начале этого руководства.

Сейчас ваше решение выглядит примерно так:

Решение пользовательского интерфейса XAML

В этом примере проект Windows Forms называется Landmarks, а проект UWP, который содержит пользовательский интерфейс XAML, называется MapUI.

Создание пользовательского интерфейса XAML

Добавьте пользовательский интерфейс XAML в проект UWP Вот код XAML для простой карты.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="12,20,12,14">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <maps:MapControl x:Name="myMap" Grid.Column="0" Width="500" Height="500"
                     ZoomLevel="{Binding ElementName=zoomSlider,Path=Value, Mode=TwoWay}"
                     Heading="{Binding ElementName=headingSlider,Path=Value, Mode=TwoWay}"
                     DesiredPitch="{Binding ElementName=desiredPitchSlider,Path=Value, Mode=TwoWay}"
                     HorizontalAlignment="Left"
                     MapServiceToken="<Your Key Goes Here" />
    <Grid Grid.Column="1" Margin="12">
        <StackPanel>
            <Slider Minimum="1" Maximum="20" Header="ZoomLevel" Name="zoomSlider" Value="17.5"/>
            <Slider Minimum="0" Maximum="360" Header="Heading" Name="headingSlider" Value="0"/>
            <Slider Minimum="0" Maximum="64" Header=" DesiredPitch" Name="desiredPitchSlider" Value="32"/>
        </StackPanel>
    </Grid>
</Grid>

Добавление расширения протокола

В обозревателе решений откройте файл package.appxmanifest проекта упаковки из нужного решения и добавьте это расширение.

<Extensions>
  <uap:Extension Category="windows.protocol" Executable="MapUI.exe" EntryPoint="MapUI.App">
    <uap:Protocol Name="xamluidemo" />
  </uap:Extension>
</Extensions>

Задайте имя протокола, введите имя исполняемого файла, созданного проектом UWP, и имя класса для точки входа.

Также можно открыть package.appxmanifest в конструкторе, выбрать вкладку Объявления и затем добавить сюда расширение.

Вкладка объявлений

Примечание.

Элементы управления картой скачивают данные из Интернета, следовательно, если вы используете такой элемент, необходимо также добавить в манифест возможность internet client.

Запуск приложения UWP

Сначала в настольном приложении создайте URI, включающий имя протокола и все передаваемые параметры в приложение UWP. Затем вызовите метод LaunchUriAsync.


private void Statue_Of_Liberty_Click(object sender, EventArgs e)
{
    ShowMap(40.689247, -74.044502);
}

private async void ShowMap(double lat, double lon)
{
    string str = "xamluidemo://";

    Uri uri = new Uri(str + "location?lat=" +
        lat.ToString() + "&?lon=" + lon.ToString());

    var success = await Windows.System.Launcher.LaunchUriAsync(uri);

}

Анализ параметров и отображение страницы

В классе App проекта UWP переопределите обработчик событий OnActivated. Если приложение активируется вашим протоколом, выполните анализ параметров и откройте требуемую страницу.

protected override void OnActivated(Windows.ApplicationModel.Activation.IActivatedEventArgs e)
{
    if (e.Kind == ActivationKind.Protocol)
    {
        ProtocolActivatedEventArgs protocolArgs = (ProtocolActivatedEventArgs)e;
        Uri uri = protocolArgs.Uri;
        if (uri.Scheme == "xamluidemo")
        {
            Frame rootFrame = new Frame();
            Window.Current.Content = rootFrame;
            rootFrame.Navigate(typeof(MainPage), uri.Query);
            Window.Current.Activate();
        }
    }
}

В коде за вашей страницей XAML переопределите метод OnNavigatedTo, чтобы использовать параметры, переданные в страницу. В нашем примере мы будем использовать данные широты и долготы, переданные на страницу, чтобы показать расположение на карте.

protected override void OnNavigatedTo(NavigationEventArgs e)
 {
     if (e.Parameter != null)
     {
         WwwFormUrlDecoder decoder = new WwwFormUrlDecoder(e.Parameter.ToString());

         double lat = Convert.ToDouble(decoder[0].Value);
         double lon = Convert.ToDouble(decoder[1].Value);

         BasicGeoposition pos = new BasicGeoposition();

         pos.Latitude = lat;
         pos.Longitude = lon;

         myMap.Center = new Geopoint(pos);

         myMap.Style = MapStyle.Aerial3D;

     }

     base.OnNavigatedTo(e);
 }

Сделайте ваше настольное приложение получателем данных для обмена

Вы можете сделать ваше настольное приложение целевым объектом для обмена, чтобы пользователи могли легко передавать ему данные, такие как изображения, из других приложений с поддержкой обмена.

Пользователи смогут выбрать ваше приложение, чтобы передать изображения из Microsoft Edge или приложения "Фотографии". Вот пример приложения WPF, которое поддерживает такую возможность.

Целевая платформа для обмена .

Полный пример см. здесь.

Шаблон проектирования

Чтобы сделать ваше приложение целью для обмена, выполните следующие действия:

1️⃣ Добавить расширение цели для общего доступа

2️⃣ Переопределите обработчик событий OnShareTargetActivated

3️⃣ Добавьте расширения рабочего стола в проект UWP

4️⃣ Добавьте расширение процесса полного доверия

5️⃣ Измените настольное приложение для получения общего файла

Дальнейшие шаги

Добавление расширения цели общего доступа

В Проводнике решений откройте файл package.appxmanifest проекта Packaging из вашего решения и добавьте расширение целевого объекта обмена.

<Extensions>
      <uap:Extension
          Category="windows.shareTarget"
          Executable="ShareTarget.exe"
          EntryPoint="App">
        <uap:ShareTarget>
          <uap:SupportedFileTypes>
            <uap:SupportsAnyFileType />
          </uap:SupportedFileTypes>
          <uap:DataFormat>Bitmap</uap:DataFormat>
        </uap:ShareTarget>
      </uap:Extension>
</Extensions>  

Укажите имя исполняемого файла, созданного проектом UWP, и имя класса точки входа. В этой разметке предполагается, что исполняемый файл для приложения UWP называется ShareTarget.exe.

Также необходимо указать типы файлов, которые могут быть переданы приложению. В этом примере мы делаем настольное приложение WPF PhotoStoreDemo целью для обмена растровыми изображениями, указав Bitmap как поддерживаемый тип файла.

Переопределите обработчик события OnShareTargetActivated

Переопределите обработчик событий OnShareTargetActivated в классе App проекта UWP.

Этот обработчик событий вызывается, когда пользователи выбирают ваше приложение для обмена своими файлами.


protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    shareWithDesktopApplication(args.ShareOperation);
}

private async void shareWithDesktopApplication(ShareOperation shareOperation)
{
    if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))
    {
        var items = await shareOperation.Data.GetStorageItemsAsync();
        StorageFile file = items[0] as StorageFile;
        IRandomAccessStreamWithContentType stream = await file.OpenReadAsync();

        await file.CopyAsync(ApplicationData.Current.LocalFolder);
            shareOperation.ReportCompleted();

        await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
    }
}

В этом коде мы сохраняем переданное пользователем изображение в папку локального хранилища приложений. Позже мы изменим настольное приложение, чтобы оно извлекало изображения из этой папки. Настольное приложение может это сделать, так как включено в тот же пакет, что и UWP-приложение.

Добавление расширений настольных приложений в проект UWP

Добавьте в проект приложения UWP расширения рабочего стола Windows для UWP. Вы увидите несколько версий расширения (например, 10.0.18362.0 и 10.0.19041.0). Дополнительные сведения о том, как выбрать версию, см. в SDK расширения и как ссылаться на них.

Расширение для рабочего стола

Добавить расширение процесса полного доверия

В Обозревателе решений откройте файл package.appxmanifest проекта упаковки вашего решения и добавьте расширение процесса полного доверия рядом с ранее добавленным расширением получателя данных в этот файл.

<Extensions>
  ...
      <desktop:Extension Category="windows.fullTrustProcess" Executable="PhotoStoreDemo\PhotoStoreDemo.exe" />
  ...
</Extensions>  

Это расширение позволит приложению UWP запускать классическое приложение, с которым вы хотите поделиться файлом. В примере мы ссылаемся на исполняемый файл настольного приложения WPF PhotoStoreDemo.

Измените настольное приложение для получения общего файла

Измените настольное приложение так, чтобы оно находило и обрабатывало общий файл. В нашем примере приложение UWP сохранило переданный файл в локальной папке с данными приложения. Поэтому мы настроим в приложении WPF PhotoStoreDemo получение фотографий из этой папки.

Photos.Path = Windows.Storage.ApplicationData.Current.LocalFolder.Path;

Для экземпляров настольного приложения, которые уже открыты пользователем, мы также можем обработать событие FileSystemWatcher и передать путь к расположению файла. Любой открытый экземпляр настольного приложения отобразит переданную фотографию.

...

   FileSystemWatcher watcher = new FileSystemWatcher(Photos.Path);

...

private void Watcher_Created(object sender, FileSystemEventArgs e)
{
    // new file got created, adding it to the list
    Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() =>
    {
        if (File.Exists(e.FullPath))
        {
            ImageFile item = new ImageFile(e.FullPath);
            Photos.Insert(0, item);
            PhotoListBox.SelectedIndex = 0;
            CurrentPhoto.Source = (BitmapSource)item.Image;
        }
    }));
}

Создание фоновой задачи

Вы можете добавить фоновую задачу для выполнения кода, даже если приложение приостановлено. Фоновые задачи отлично подходят для небольших задач, не требующих участия пользователя. Например, ваша задача может загружать почту, показывать уведомление о входящем сообщении в чате или реагировать на изменение состояния системы.

Вот пример приложения WPF, которое регистрирует фоновую задачу.

Фоновая задача

Эта задача создает запрос HTTP и измеряет время получения ответа на этот запрос. Ваши задачи наверняка будут более интересными, но это отличный пример для изучения базового механизма фоновой задачи.

Полный пример см. здесь.

Шаблон проектирования

Чтобы создать фоновую службу, выполните следующие действия:

1️⃣ Реализация фоновой задачи

2️⃣ Настройка фоновой задачи

3️⃣ Регистрация фоновой задачи

Реализация фоновой задачи

Реализуйте фоновую задачу, добавив код в проект компонентов среды выполнения Windows.

public sealed class SiteVerifier : IBackgroundTask
{
    public async void Run(IBackgroundTaskInstance taskInstance)
    {

        taskInstance.Canceled += TaskInstance_Canceled;
        BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
        var msg = await MeasureRequestTime();
        ShowToast(msg);
        deferral.Complete();
    }

    private async Task<string> MeasureRequestTime()
    {
        string msg;
        try
        {
            var url = ApplicationData.Current.LocalSettings.Values["UrlToVerify"] as string;
            var http = new HttpClient();
            Stopwatch clock = Stopwatch.StartNew();
            var response = await http.GetAsync(new Uri(url));
            response.EnsureSuccessStatusCode();
            var elapsed = clock.ElapsedMilliseconds;
            clock.Stop();
            msg = $"{url} took {elapsed.ToString()} ms";
        }
        catch (Exception ex)
        {
            msg = ex.Message;
        }
        return msg;
    }

Настройка фоновой задачи

В конструкторе манифестов откройте файл package.appxmanifest проекта упаковки в своем решении.

На вкладке Объявления добавьте объявление Фоновые задачи.

Вариант фоновой задачи

Затем выберите требуемые свойства. В нашем примере используется свойство Timer.

Свойство Timer

Укажите полное имя класса в компоненте среды выполнения Windows, который реализует фоновую задачу.

Указание точки входа

Регистрация фоновой задачи

Добавьте код в проект настольного приложения, который регистрирует фоновую задачу.

public void RegisterBackgroundTask(String triggerName)
{
    var current = BackgroundTaskRegistration.AllTasks
        .Where(b => b.Value.Name == triggerName).FirstOrDefault().Value;

    if (current is null)
    {
        BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
        builder.Name = triggerName;
        builder.SetTrigger(new MaintenanceTrigger(15, false));
        builder.TaskEntryPoint = "HttpPing.SiteVerifier";
        builder.Register();
        System.Diagnostics.Debug.WriteLine("BGTask registered:" + triggerName);
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("Task already:" + triggerName);
    }
}

Получение ответов на вопросы

Есть вопросы? Спросите нас на Stack Overflow. Наша команда следит за этими тегами. Вы также можете задать нам вопросы здесь.