Erweitern deiner Desktop-App mit modernen UWP-Komponenten

Einige Windows-Funktionalitäten (z. B. eine Benutzeroberflächenseite mit Touchunterstützung) müssen sich in einem modernen App-Container befinden. Wenn Sie derartige Umgebungen hinzufügen möchten, müssen Sie Ihre Desktop-App mit UWP-Projekten und mit Komponenten für Windows-Runtime erweitern.

In vielen Fällen können Sie die Windows-Runtime-APIs direkt in Ihrer Desktopanwendung aufrufen. Bevor Sie diese Anleitung lesen, ziehen Sie Verbessern für Windows zurate.

Hinweis

Für die in diesem Thema beschriebenen Features muss Ihre App gepackt sein (also zur Laufzeit über eine Paketidentität verfügen). Hierzu zählen sowohl gepackte Apps (siehe Gepackt: Erstellen eines neuen Projekts für eine gepackte C#- oder C++-WinUI 3-Desktop-App) als auch gepackte Apps mit externem Speicherort (siehe Gewähren der Paketidentität durch Verpackung mit externem Speicherort). Weitere Informationen finden Sie auch unter Features, für die Paketidentität benötigt wird.

Einrichten der Lösung

Füge deiner Projektmappe ein oder mehrere UWP-Projekte und Laufzeitkomponenten hinzu.

Beginne mit einer Projektmappe, die ein Paketerstellungsprojekt für Windows-Anwendungen mit einem Verweis auf deine Desktopanwendung enthält.

Diese Abbildung zeigt ein Beispiel einer Projektmappe.

Extend start project

Wenn deine Projektmappe kein Paketerstellungsprojekt enthält, siehe Packen deiner Desktopanwendung mit Visual Studio.

Konfigurieren der Desktopanwendung

Stelle sicher, dass deine Desktopanwendung Verweise auf die Dateien aufweist, die du für den Aufruf der Windows-Runtime-APIs benötigst.

Weitere Informationen hierzu finden Sie unter Aufrufen von Windows-Runtime-APIs in Desktop-Apps.

Hinzufügen eines UWP-Projekts

Füge deiner Projektmappe eine Leere App (Universelle Windows-App) hinzu.

Hier erstellst du eine moderne XAML-Benutzeroberfläche oder verwendest APIs, die nur innerhalb eines UWP-Prozesses ausgeführt werden.

Add new project

Klicke in deinem Paketerstellungsprojekt mit der rechten Maustaste auf den Knoten Anwendungen, und klicke dann auf Verweis hinzufügen.

Add reference

Füge dann einen Verweis auf das UWP-Projekt hinzu.

Select UWP project

Deine Projektmappe sieht etwa wie folgt aus:

Solution with UWP project

(Optional) Hinzufügen einer Windows-Runtime-Komponente

Um einige Szenarien zu realisieren, musst du einer Windows-Runtime-Komponente Code hinzufügen.

runtime component app service

Füge dann in deinem UWP-Projekt einen Verweis auf die Laufzeitkomponente hinzu. Deine Projektmappe sieht etwa wie folgt aus:

Runtime Component Reference

Kompilieren der Projektmappe

Kompiliere deine Projektmappe, um sicherzustellen, dass keine Fehler angezeigt werden. Wenn Fehler auftreten, öffne den Konfigurationsmanager und stelle sicher, dass deine Projekte auf dieselbe Plattform ausgerichtet sind.

Config manager

Wirf einen Blick auf einige Dinge, die du mit deinen UWP-Projekten und Laufzeitkomponenten tun kannst.

Anzeigen einer modernen XAML-Benutzeroberfläche

Als Teil deines Anwendungsflusses kannst du moderne XAML-basierte Benutzeroberflächen in deine Desktopanwendung integrieren. Diese Benutzeroberflächen passen sich natürlich an unterschiedliche Bildschirmgrößen und Auflösungen an und unterstützen moderne, interaktive Modelle, z. B. Touch- und Freihandeingaben.

Mit etwas XAML-Markup kannst du z. B. Benutzern leistungsstarke Kartenvisualisierungsfunktionen bereitstellen.

Diese Abbildung zeigt eine Windows Forms-Anwendung, die eine XAML-basierte, moderne Benutzeroberfläche mit einem Kartensteuerelement öffnet.

adaptive-design

Hinweis

Dieses Beispiel zeigt eine XAML-Benutzeroberfläche durch Hinzufügen eines UWP-Projekts zur Projektmappe. Dies ist der zuverlässige unterstützte Ansatz, um XAML-Benutzeroberflächen in einer Desktopanwendung anzuzeigen. Die Alternative zu diesem Ansatz besteht darin, der Desktopanwendung UWP-XAML-Steuerelemente mithilfe einer XAML Island direkt hinzuzufügen. XAML Islands sind derzeit als Entwicklervorschau verfügbar. Obwohl wir Sie ermutigen möchten, diese jetzt in Ihrem eigenen Prototypencode zu testen, empfehlen wir Ihnen nicht, sie zu diesem Zeitpunkt im Produktionscode zu verwenden. Diese APIs und Steuerelemente werden in künftigen Windows-Releases weiter ausreifen und stabilisiert. Weitere Informationen zu XAML Islands findest du unter UWP-Steuerelemente in Desktopanwendungen.

Das Entwurfsmuster

Um eine XAML-basierte Benutzeroberfläche anzuzeigen, gehe so vor:

1️⃣ Einrichten Ihrer Lösung

2️⃣ Erstellen einer XAML-Benutzeroberfläche

3️⃣ Hinzufügen einer Protokollerweiterung zum UWP-Projekt

4️⃣ Starten der UWP-App aus Ihrer Desktop-App

5️⃣ Anzeigen der gewünschten Seite im UWP-Projekt

Einrichten der Lösung

Allgemeine Anleitungen zum Einrichten Ihrer Projektmappe finden Sie im Abschnitt Einrichten der Lösung am Anfang dieser Anleitung.

Deine Projektmappe sieht in etwa wie folgt aus:

XAML UI Solution

In diesem Beispiel heißt das Windows Forms-Projekt Landmarks. Das UWP-Projekt, das die XAML-Benutzeroberfläche enthält, heißt MapUI.

Erstellen einer XAML-Benutzeroberfläche

Füge deinem UWP-Projekt eine XAML-Benutzeroberfläche hinzu. Hier ist der XAML-Code für eine einfache Karte.

<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>

Hinzufügen einer Protokollerweiterung

Öffne im Projektmappen-Explorer die Datei package.appxmanifest des Paketerstellungsprojekts in deiner Projektmappe, und füge diese Erweiterung hinzu.

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

Versieh das Protokoll mit einem Namen. Gib den Namen der ausführbaren Datei des UWP-Projekts und den der Einstiegspunktklasse an.

Du kannst auch Package.appxmanifest im Designer öffnen, die Registerkarte Deklarationen wählen und dann dort die Erweiterung hinzufügen.

declarations-tab

Hinweis

Kartensteuerelemente laden Daten aus dem Internet herunter. Wenn du eines verwendest, musst du deinem Manifest auch die Funktion „Internetclient“ hinzufügen.

Starten der UWP-App

Erstelle zunächst in deiner Desktopanwendung einen Uri, der den Protokollnamen und alle Parameter enthält, die an die UWP-App übergeben werden sollen. Rufe dann die LaunchUriAsync-Methode auf.


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);

}

Analysieren von Parametern und Anzeigen einer Seite

Überschreibe in der App-Klasse des UWP-Projekts den OnActivated-Ereignishandler. Wenn die App durch dein Protokoll aktiviert wird, analysiere die Parameter und öffne die Seite, die du benötigst.

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();
        }
    }
}

Überschreibe im Code hinter deiner XAML-Seite die Methode OnNavigatedTo, um die an die Seite übergebenen Parameter zu verwenden. In diesem Fall verwenden wir den Breiten- und Längengrad, die an diese Seite übergeben wurden, um einen Standort auf einer Karte anzuzeigen.

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);
 }

Festlegen deiner Desktopanwendung als Freigabeziel

Du kannst deine Desktopanwendung als Freigabeziel einrichten, damit Benutzer einfach Daten wie Bilder aus anderen Apps freigeben können, die Freigaben unterstützen.

Beispielsweise können Benutzer deine App zum Teilen von Bildern in Microsoft Edge in der App „Fotos“ auswählen. Hier ist eine WPF-Beispielanwendung, die diese Funktion nutzt.

share target.

Das vollständige Beispiel findest du hier.

Das Entwurfsmuster

Um deine Anwendung zu einem Freigabeziel zu machen, gehe wie folgt vor:

1️⃣ Hinzufügen der Freigabezielerweiterung

2️⃣ Außerkraftsetzen des Ereignishandlers „OnShareTargetActivated“

3️⃣ Hinzufügen von Desktoperweiterungen zum UWP-Projekt

4️⃣ Hinzufügen der voll vertrauenswürdigen Prozesserweiterung

5️⃣ Ändern der Desktopanwendung zum Abrufen der freigegebenen Datei

Die folgenden Schritte

Hinzufügen der Freigabezielerweiterung

Öffne im Projektmappen-Explorer die Datei package.appxmanifest des Paketerstellungsprojekts in deiner Projektmappe, und füge diese Freigabezielerweiterung hinzu.

<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>  

Gib den Namen der ausführbaren Datei des UWP-Projekts und der Einstiegspunktklasse an. Dieses Markup geht davon aus, dass der Name der ausführbaren Datei für die UWP-App ShareTarget.exe ist.

Du musst außerdem angeben, welche Arten von Dateien mit deiner App freigegeben werden können. In diesem Beispiel machen wir die Desktopanwendung WPF PhotoStoreDemo zu einem Freigabeziel für Bitmapbilder. Daher geben wir Bitmap als unterstützten Dateityp an.

Überschreiben des Ereignishandlers OnShareTargetActivated

Überschreibe in der App-Klasse deines UWP-Projekts den Ereignishandler OnShareTargetActivated.

Dieser Ereignishandler wird aufgerufen, wenn Benutzer deine App zum Teilen ihrer Dateien auswählen.


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();
    }
}

In diesem Code speichern wir das Bild, das vom Benutzer freigegeben wird, in einem lokalen Speicherordner der Anwendung. Zu einem späteren Zeitpunkt ändern wir die Desktopanwendung so, dass Bilder aus demselben Ordner abgerufen werden. Die Desktopanwendung kann das leisten, da sie im gleichen Paket wie die UWP-Anwendung enthalten ist.

Hinzufügen von Desktoperweiterungen zum UWP-Projekt

Füge die Erweiterung Windows Desktop Extensions for the UWP deinem UWP-App-Projekt hinzu. Sie sehen mehr als eine Version der Erweiterung (z. B. 10.0.18362.0 und 10.0.19041.0). Informationen zum Auswählen einer Version finden Sie unter Erweiterungs-SDKs und Verweisen darauf.

desktop extension

Hinzufügen der voll vertrauenswürdigen Prozesserweiterung

Öffne im Projektmappen-Explorer die Datei package.appxmanifest des Paketerstellungsprojekts in deiner Projektmappe, und füge dann die voll vertrauenswürdige Prozesserweiterung neben der Freigabezielerweiterung hinzu, die du dieser Datei zuvor hinzugefügt hast.

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

Diese Erweiterung ermöglicht es der UWP-Anwendung, die Desktopanwendung zu starten, für die du eine Datei freigeben möchtest. In diesem Beispiel verweisen wir auf die ausführbare Datei der Desktopanwendung WPF PhotoStoreDemo.

Ändern der Desktopanwendung zum Abrufen der freigegebenen Datei

Ändere deine Desktopanwendung so, dass die freigegebene Datei gefunden und verarbeitet wird. In diesem Beispiel hat die UWP-App die freigegebene Datei im lokalen Datenordner der App gespeichert. Daher ändern wir die Desktopanwendung WPF PhotoStoreDemo so, dass Fotos aus diesem Ordner abgerufen werden können.

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

Für Instanzen der Desktopanwendung, die bereits vom Benutzer geöffnet sind, könnten wir auch das FileSystemWatcher-Ereignis behandeln und den Pfad zum Speicherort der Datei übergeben. Auf diese Weise wird in allen geöffneten Instanzen der Desktopanwendung das freigegebene Foto angezeigt.

...

   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;
        }
    }));
}

Erstellen einer Hintergrundaufgabe

Du fügst eine Hintergrundaufgaben hinzu, um selbst dann App-Code auszuführen, wenn die App angehalten wurde. Hintergrundaufgaben sind ideal für kleine Aufgaben, die keine Benutzerinteraktion erfordern. Beispielsweise kann deine Aufgabe E-Mails herunterladen, eine Popupbenachrichtigung über eine eingehende Chatnachricht zeigen oder auf eine Änderung in einer Systembedingung reagieren.

Hier ist ein Beispiel einer WPF-App, die eine Hintergrundaufgabe registriert.

background task

Die Aufgabe stellt eine HTTP-Anforderung und misst die Zeit, die die Anforderung benötigt, um eine Antwort zurückzugeben. Deine Aufgaben werden wahrscheinlich viel interessanter sein, aber dieses Beispiel eignet sich gut, um die grundlegende Funktionsweise einer Hintergrundaufgabe kennenzulernen.

Das vollständige Beispiel findest du hier.

Das Entwurfsmuster

Um einen Hintergrunddienst zu erstellen, gehe folgendermaßen vor:

1️⃣ Implementieren der Hintergrundaufgabe

2️⃣ Konfigurieren der Hintergrundaufgabe

3️⃣ Registrieren der Hintergrundaufgabe

Implementieren der Hintergrundaufgabe

Implementiere die Hintergrundaufgabe, indem du einem Windows-Runtime-Komponentenprojekt Code hinzufügst.

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;
    }

Konfigurieren der Hintergrundaufgabe

Öffne im Manifest-Designer die Datei package.appxmanifest des Paketerstellungsprojekts in deiner Projektmappe.

Füge auf der Registerkarte Deklarationen die Deklaration Hintergrundaufgaben hinzu.

Background task option

Wähle dann die gewünschten Eigenschaften aus. Unser Beispiel verwendet die Timer-Eigenschaft.

Timer property

Gib den vollqualifizierten Namen der Klasse in der Windows-Runtime-Komponente an, die die Hintergrundaufgabe implementiert.

Specify entry point

Registrieren der Hintergrundaufgabe

Füge deinem Desktopanwendungsprojekt Code hinzu, der die Hintergrundaufgabe registriert.

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);
    }
}

Antworten auf deine Fragen

Haben Sie Fragen? Frage uns auf Stack Overflow. Unser Team überwacht diese Tags. Du kannst uns auch hier fragen.