Freigeben über


Kompilieren einer WPF-Anwendung

Windows Presentation Foundation (WPF)-Anwendungen können als ausführbare .NET Framework-Dateien (.exe), Bibliotheken (.dll) oder eine Kombination aus beiden Assemblytypen erstellt werden. In diesem Artikel wird beschrieben, wie WPF-Anwendungen erstellt werden, und die wichtigsten Schritte im Buildprozess werden erläutert.

Erstellen einer WPF-Anwendung

Eine WPF-Anwendung kann wie folgt kompiliert werden:

WPF-Buildpipeline:

Beim Erstellen eines WPF-Projekts wird die Kombination aus sprachspezifischen und WPF-spezifischen Zielen aufgerufen. Das Ausführen dieser Ziele wird als Buildpipeline bezeichnet. Die wichtigsten Schritte werden in der folgenden Abbildung dargestellt.

WPF-Buildprozess

Präbuildinitialisierungen

Vor Beginn der Erstellung bestimmt MSBuild den Speicherort wichtiger Tools und Bibliotheken (einschließlich der folgenden):

  • The .NET Framework.

  • Die Windows SDK-Verzeichnisse.

  • Speicherort von WPF-Verweisassemblys

  • Eigenschaft für die Assemblysuchpfade

Der erste Speicherort, an dem MSBuild nach Assemblys sucht, ist das Referenzassemblysverzeichnis (%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0\). In diesem Schritt initialisiert der Buildprozess auch die verschiedenen Eigenschaften und Elementgruppen und führt die erforderlichen Bereinigungen durch.

Auflösen von Verweisen

Der Buildprozess lokalisiert und bindet die Assemblies, die zum Erstellen des Anwendungsprojekts erforderlich sind. Diese Logik ist in Aufgabe ResolveAssemblyReference enthalten. Alle in der Projektdatei als Reference deklarierten Assemblys werden der Aufgabe mit Informationen zu Suchpfaden und Metadaten für Assemblys zur Verfügung gestellt, die bereits im System installiert sind. Die Aufgabe sucht Assemblys auf und verwendet die Metadaten der installierten Assembly, um die WPF-Kernassemblys herauszufiltern, die nicht in den Ausgabemanifesten angezeigt werden müssen. Dies geschieht, um redundante Informationen in den ClickOnce-Manifesten zu vermeiden. Da PresentationFramework.dll beispielsweise als repräsentativ für eine Anwendung gelten kann, die auf und für WPF basiert, und da alle WPF-Assemblys an demselben Speicherort auf jedem Computer vorhanden sind, auf dem .NET Framework installiert ist, müssen nicht alle Informationen zu allen .NET Framework-Referenzassemblys in die Manifeste eingeschlossen werden.

Markupkompilierungsdurchlauf 1

In diesem Schritt werden XAML-Dateien analysiert und kompiliert, sodass die Laufzeit keine Zeit für die Analyse von XML und das Validieren von Eigenschaftswerten aufwendet. Die kompilierte XAML-Datei ist vorab tokenisiert, sodass das Laden zur Laufzeit wesentlich schneller sein sollte als das Laden einer XAML-Datei.

Während dieses Schritts werden die folgenden Aktivitäten für jede XAML-Datei ausgeführt, die ein Page Buildelement ist:

  1. Die XAML-Datei wird vom Markupcompiler analysiert.

  2. Für diesen XAML-Code wird eine kompilierte Darstellung erstellt und in den Ordner "obj\Release" kopiert.

  3. Eine CodeDOM-Darstellung einer neuen partiellen Klasse wird erstellt und in den Ordner "obj\Release" kopiert.

Darüber hinaus wird für jede XAML-Datei eine sprachspezifische Codedatei generiert. Für eine Page1.xaml-Seite in einem Visual Basic-Projekt wird beispielsweise die Datei „Page1.g.vb“ generiert und für eine Page1.xaml-Seite in einem C#-Projekt die Datei „Page1.g.cs“. Das „.g“ im Dateinamen gibt an, dass die Datei generierten Code darstellt, der über eine Deklaration der partiellen Klasse für das Element der obersten Ebene der Markupdatei verfügt (z. B. Page oder Window). Die Klasse wird mit dem partial Modifizierer in C# (Extends in Visual Basic) deklariert, um anzugeben, dass an anderer Stelle, in der Regel in der CodeBehind-Datei Page1.xaml.cs, eine weitere Deklaration für die Klasse vorhanden ist.

Die partielle Klasse erstreckt sich von der entsprechenden Basisklasse (z. B. Page für eine Seite) und implementiert die System.Windows.Markup.IComponentConnector Schnittstelle. Die IComponentConnector-Schnittstelle verfügt über Methoden zum Initialisieren einer Komponente und um Namen und Ereignisse mit Elementen in ihrem Inhalt zu verknüpfen. Folglich verfügt die generierte Codedatei über eine Methodenimplementierung wie die folgenden:

public void InitializeComponent() {
    if (_contentLoaded) {
        return;
    }
    _contentLoaded = true;
    System.Uri resourceLocater =
        new System.Uri(
            "window1.xaml",
            System.UriKind.RelativeOrAbsolute);
    System.Windows.Application.LoadComponent(this, resourceLocater);
}
Public Sub InitializeComponent() _

    If _contentLoaded Then
        Return
    End If

    _contentLoaded = True
    Dim resourceLocater As System.Uri = _
        New System.Uri("mainwindow.xaml", System.UriKind.Relative)

    System.Windows.Application.LoadComponent(Me, resourceLocater)

End Sub

Standardmäßig wird die Markupkompilierung in derselben AppDomain ausgeführt wie die MSBuild-Engine. Dies bietet erhebliche Leistungssteigerungen. Dieses Verhalten kann mit der AlwaysCompileMarkupFilesInSeparateDomain-Eigenschaft geändert werden. Dies hat den Vorteil, dass alle Verweisassemblys durch Entladen der separaten AppDomain entladen werden.

Markupkompilierungsdurchlauf 2

Während des Durchlaufs 1 der Markupkompilierung werden nicht alle XAML-Seiten kompiliert. XAML-Dateien, die lokal definierte Typverweise enthalten (Verweise auf Typen, die an anderer Stelle im selben Projekt im Code definiert sind), sind momentan von der Kompilierung ausgenommen. Dies liegt daran, dass diese lokal definierten Typen nur in der Quelle vorhanden sind und noch nicht kompiliert wurden. Um dies zu bestimmen, verwendet der Parser heuristische Verfahren, die das Suchen nach Elementen umfassen (z. B. x:Name in der Markupdatei). Wird eine solche Instanz gefunden, wird die Kompilierung der Markupdatei bis zur Kompilierung der Codedateien zurückgestellt. Anschließend werden diese Dateien im zweiten Durchlauf der Markupkompilierung verarbeitet.

Dateiklassifizierung

Der Buildprozess ordnet Ausgabedateien verschiedenen Ressourcengruppen zu, je nachdem, in welche Anwendungszusammenstellung sie aufgenommen werden. In einer normalen, nicht lokalisierten Anwendung werden alle als Resource markierten Datendateien in der Hauptassembly platziert (ausführbare Datei oder Bibliothek). Wenn UICulture im Projekt festgelegt wird, werden alle kompilierten XAML-Dateien sowie die Ressourcen, die speziell als sprachspezifisch gekennzeichnet sind, in der Satellitenressourcen-Assemblierung platziert. Außerdem werden alle sprachunabhängigen Ressourcen in die Hauptassembly eingefügt. In diesem Schritt des Buildprozesses wird diese Bestimmung vorgenommen.

Die Build-Aktionen ApplicationDefinition, Pageund Resource können in der Projektdatei mit den Metadaten Localizable erweitert werden (zulässige Werte sind true und false), wodurch festgelegt wird, ob die Datei sprachspezifisch oder -neutral ist.

Kernkompilierung

Der Hauptkompilierungsschritt umfasst die Kompilierung von Codedateien. Dies wird durch Logik in den sprachspezifischen Zieldateien "Microsoft.CSharp.targets" und "Microsoft.VisualBasic.targets" koordiniert. Die Hauptassembly wird generiert, wenn die Heuristik einen einzelnen Durchlauf des Markupcompilers als ausreichend einschätzt. Wenn jedoch eine oder mehrere XAML-Dateien im Projekt Verweise auf lokal definierte Typen haben, wird eine temporäre .dll Datei generiert, sodass die endgültigen Anwendungsassemblys nach Abschluss des zweiten Markupkompilierungsvorgangs erstellt werden können.

Manifestgenerierung

Nachdem alle Anwendungsassemblys und Inhaltsdateien fertiggestellt wurden, werden die ClickOnce-Manifeste am Ende des Buildprozesses für die Anwendung generiert.

Die Bereitstellungsmanifestdatei beschreibt das Bereitstellungsmodell: die aktuelle Version, das Updateverhalten und die Herausgeberidentität sowie die digitale Signatur. Dieses Manifest soll von Administratoren erstellt werden, die für die Bereitstellung zuständig sind. Die Dateierweiterung ist .xbap (für XAML-Browseranwendungen (XBAPs)) und .application für installierte Anwendungen. Erstere wird durch die HostInBrowser-Projekteigenschaft vorgeschrieben. Als Ergebnis identifiziert das Manifest die Anwendung als vom Browser gehostet.

Das Anwendungsmanifest (eine .exe.manifest-Datei) beschreibt die Anwendungsassemblys und abhängigen Bibliotheken und listet Berechtigungen auf, die von der Anwendung benötigt werden. Diese Datei soll vom Anwendungsentwickler erstellt werden. Um eine ClickOnce-Anwendung zu starten, öffnet ein Benutzer die Bereitstellungsmanifestdatei der Anwendung.

Diese Manifestdateien werden immer für XBAPs erstellt. Für installierte Anwendungen werden sie nicht erstellt, sofern die GenerateManifests-Eigenschaft in der Projektdatei nicht mit dem Wert true angegeben wird.

XBAPs erhalten zwei zusätzliche Berechtigungen neben denjenigen, die typischen Internetzonenanwendungen zugewiesen werden: WebBrowserPermission und MediaPermission. Das WPF-Buildsystem deklariert diese Berechtigungen im Anwendungsmanifest.

Unterstützung für inkrementelle Builds

Das WPF-Buildsystem bietet Unterstützung für inkrementelle Builds. Es erkennt recht intelligent Änderungen am Markup oder Code und kompiliert nur die Artefakte, die von der Änderung betroffen sind. Der inkrementelle Buildmechanismus verwendet die folgenden Dateien:

  • Eine $(AssemblyName)_MarkupCompiler.Cache-Datei, um den aktuellen Compilerzustand beizubehalten.

  • Eine $(AssemblyName)_MarkupCompiler.lref-Datei, um die XAML-Dateien mit Verweisen auf lokal definierte Typen zwischenzuspeichern.

Im Folgenden finden Sie Regeln für inkrementelle Builds:

  • Die Datei ist die kleinste Einheit, bei der das Buildsystem Änderungen erkennt. Für eine Codedatei kann das Buildsystem also nicht feststellen, ob ein Typ geändert wurde oder ob Code hinzugefügt wurde. Gleiches gilt für Projektdateien.

  • Der inkrementelle Buildmechanismus muss erkennen, dass eine XAML-Seite entweder eine Klasse definiert oder andere Klassen verwendet.

  • Wenn Reference Einträge geändert werden, kompilieren Sie alle Seiten erneut.

  • Wenn sich eine Codedatei ändert, kompilieren Sie alle Seiten mit lokal definierten Typverweisen neu.

  • Wenn sich eine XAML-Datei ändert:

    • Wenn XAML im Projekt als Page deklariert ist: Wenn das XAML keine lokal definierten Typverweise hat, kompilieren Sie dieses XAML zusammen mit allen XAML-Seiten mit lokalen Verweisen neu; Wenn das XAML lokale Verweise enthält, kompilieren Sie alle XAML-Seiten mit lokalen Verweisen neu.

    • Wenn XAML im Projekt als ApplicationDefinition deklariert wird: Kompilieren Sie alle XAML-Seiten neu (Grund: Jede XAML-Seite hat einen Verweis auf einen Application-Typ, der sich möglicherweise geändert hat).

  • Wenn die Projektdatei eine Codedatei anstelle einer XAML-Datei als Anwendungsdefinition deklariert:

    • Überprüfen Sie, ob sich der ApplicationClassName Wert in der Projektdatei geändert hat (gibt es einen neuen Anwendungstyp?). Wenn ja, kompilieren Sie die gesamte Anwendung erneut.

    • Kompilieren Sie andernfalls alle XAML-Seiten mit lokalen Referenzen neu.

  • Wenn sich eine Projektdatei ändert: Wenden Sie alle vorherigen Regeln an, und sehen Sie, was neu kompiliert werden muss. Änderungen an den folgenden Eigenschaften lösen eine vollständige Neukompilierung aus: AssemblyName, IntermediateOutputPath, RootNamespaceund HostInBrowser.

Die folgenden Neukompilierungsszenarien sind möglich:

  • Die gesamte Anwendung wird neu kompiliert.

  • Nur die XAML-Dateien, die lokal definierte Typverweise enthalten, werden neu kompiliert.

  • Nichts wird neu kompiliert (wenn sich nichts im Projekt geändert hat).

Siehe auch