Compilare un'applicazione WPF

Le applicazioni Windows Presentation Foundation (WPF) possono essere compilate come eseguibili .NET Framework (.exe), librerie (dll) o una combinazione di entrambi i tipi di assembly. Questo argomento illustra come compilare applicazioni WPF e descrive i passaggi principali del processo di compilazione.

Compilazione di un'applicazione WPF

Un'applicazione WPF può essere compilata nei modi seguenti:

Pipeline di compilazione WPF

Quando viene compilato un progetto WPF, viene richiamata la combinazione di destinazioni specifiche del linguaggio e specifiche di WPF. Il processo di esecuzione di queste destinazioni è definito pipeline di compilazione e i passaggi principali sono illustrati nella figura seguente.

WPF build process

Inizializzazioni pre-compilazione

Prima della compilazione, MSBuild determina la posizione di strumenti e librerie importanti, tra cui:

  • The .NET Framework.

  • Directory di Windows SDK.

  • Percorso degli assembly di riferimento WPF.

  • La proprietà per i percorsi di ricerca degli assembly.

Il primo percorso in cui MSBuild cerca gli assembly è la directory dell'assembly di riferimento (%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0\). Durante questo passaggio, il processo di compilazione inizializza anche le varie proprietà e i gruppi di elementi, quindi esegue le operazioni di pulizia necessarie.

Risoluzione dei riferimenti

Il processo di compilazione individua e associa gli assembly necessari per compilare il progetto di applicazione. Questa logica è contenuta nell'attività ResolveAssemblyReference. Tutti gli assembly dichiarati come Reference nel file di progetto sono disponibili per l'attività insieme a informazioni sui percorsi di ricerca e metadati degli assembly già installati nel sistema. L'attività cerca gli assembly e usa i metadati dell'assembly installato per filtrare gli assembly WPF di base che non devono essere visualizzati nei manifesti di output. Questa operazione viene eseguita per evitare informazioni ridondanti nei manifesti ClickOnce. Ad esempio, poiché PresentationFramework.dll può essere considerato rappresentativo di un'applicazione basata su e per WPF e poiché tutti gli assembly WPF esistono nello stesso percorso in ogni computer in cui è installato .NET Framework, non è necessario includere tutte le informazioni su tutti gli assembly di riferimento di .NET Framework nei manifesti.

Compilazione del markup - Passaggio 1

In questo passaggio i file XAML vengono analizzati e compilati in modo che il runtime non passi tempo ad analizzare XML e convalidare i valori delle proprietà. Il file XAML compilato viene pre-tokenizzato in modo che, in fase di esecuzione, il caricamento sia molto più veloce rispetto al caricamento di un file XAML.

Durante questo passaggio vengono eseguite le attività seguenti per ogni file XAML che è un Page elemento di compilazione:

  1. Il file XAML viene analizzato dal compilatore di markup.

  2. Viene creata una rappresentazione compilata per il codice XAML e copiata nella cartella obj\Release.

  3. Una rappresentazione CodeDOM di una nuova classe parziale viene creata e copiata nella cartella obj\Release.

Inoltre, viene generato un file di codice specifico del linguaggio per ogni file XAML. Ad esempio, per una pagina Page1.xaml in un progetto Visual Basic, viene generato un oggetto Page1.g.vb; per una pagina Page1.xaml in un progetto C#, viene generato un file Page1.g.cs. Il componente ".g" nel nome del file indica che il file è costituito da codice generato con una dichiarazione di classe parziale per l'elemento di livello superiore del file di markup (ad esempio Page o Window). La classe viene dichiarata con il partial modificatore in C# (Extends in Visual Basic) per indicare che esiste un'altra dichiarazione per la classe altrove, in genere nel file code-behind Page1.xaml.cs.

La classe parziale si estende dalla classe base appropriata (ad esempio Page per una pagina) e implementa l'interfaccia System.Windows.Markup.IComponentConnector . L'interfaccia IComponentConnector include metodi per inizializzare un componente e connettere nomi ed eventi sugli elementi nel relativo contenuto. Di conseguenza, il file di codice generato ha un'implementazione del metodo simile all'esempio seguente:

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

Per impostazione predefinita, la compilazione di markup viene eseguita nello stesso AppDomain modo del motore MSBuild. In questo modo si ottengono miglioramenti significativi delle prestazioni. Questo comportamento può essere attivato o disattivato tramite la proprietà AlwaysCompileMarkupFilesInSeparateDomain. Questo ha il vantaggio di scaricare tutti gli assembly di riferimento scaricando l'oggetto separato AppDomain.

Compilazione del markup - Passaggio 2

Non tutte le pagine XAML vengono compilate durante il passaggio 1 della compilazione del markup. I file XAML con riferimenti di tipo definiti localmente (riferimenti ai tipi definiti nel codice altrove nello stesso progetto) sono esentati dalla compilazione in questo momento. poiché tali tipi definiti in locale esistono solo nel codice sorgente e non sono ancora stati compilati. Per determinare questo aspetto, il parser utilizza un'euristica che implica la ricerca di elementi, ad esempio x:Name, nel file di markup. Se si individua un'istanza di questo tipo, la compilazione di tale file di markup viene posticipata fino a quando non vengono compilati i file di codice. Dopodiché, nel secondo passaggio di compilazione del markup, si elaborano questi file.

Classificazione file

Il processo di compilazione inserisce i file di output in diversi gruppi di risorse sulla base dell'assembly dell'applicazione in cui verranno inseriti. In una tipica applicazione non localizzata tutti i file di dati contrassegnati come Resource vengono inseriti nell'assembly principale (eseguibile o libreria). Quando UICulture viene impostato nel progetto, tutti i file XAML compilati e le risorse contrassegnate specificamente come specifiche del linguaggio vengono inserite nell'assembly di risorse satellite. Anche le risorse indipendenti dal linguaggio vengono inserite nell'assembly principale. In questo passaggio del processo di compilazione viene determinato tale aspetto.

Le azioni di compilazione ApplicationDefinition, Page e Resource nel file di progetto possono essere estese con i metadati Localizable (i valori accettabili sono true e false), che determinano se il file è specifico del linguaggio o indipendente da esso.

Compilazione base

Il passaggio base della compilazione include la compilazione dei file di codice. Questa operazione viene gestita dalla logica nei file di destinazioni specifici del linguaggio Microsoft.CSharp.targets e Microsoft.VisualBasic.targets. Se l'euristica ha determinato che è sufficiente un unico passaggio del compilatore di markup, viene generato l'assembly principale. Tuttavia, se uno o più file XAML nel progetto hanno riferimenti a tipi definiti in locale, viene generato un file DLL temporaneo in modo che gli assembly dell'applicazione finali possano essere creati dopo il completamento della seconda fase di compilazione del markup.

Generazione di manifesti

Al termine del processo di compilazione, dopo che tutti gli assembly e i file di contenuto dell'applicazione sono pronti, vengono generati i manifesti ClickOnce per l'applicazione.

Il file manifesto della distribuzione descrive il modello di distribuzione: la versione corrente, il comportamento di aggiornamento e l'identità del server di pubblicazione insieme alla firma digitale. Questo manifesto deve essere creato dagli amministratori che gestiscono la distribuzione. L'estensione di file è xbap (per le applicazioni browser XAML (XBAPs) e .application per le applicazioni installate. La prima estensione è dovuta alla proprietà del progetto HostInBrowser e, di conseguenza, il manifesto identifica l'applicazione come ospitata dal browser.

Il manifesto dell'applicazione (un file con estensione exe.manifest) descrive l'assembly dell'applicazione e le librerie dipendenti, oltre a elencare le autorizzazioni richieste dall'applicazione. Questo file deve essere creato dallo sviluppatore dell'applicazione. Per avviare un'applicazione ClickOnce, un utente apre il file manifesto della distribuzione dell'applicazione.

Questi file manifesto vengono sempre creati per gli XBAP. Per le applicazioni installate, i file manifesto non vengono creati, a meno che la proprietà GenerateManifests venga specificata nel file di progetto con il valore true.

Gli XBAP ottengono due autorizzazioni aggiuntive oltre a quelle assegnate alle applicazioni tipiche dell'area Internet: WebBrowserPermission e MediaPermission. Il sistema di compilazione WPF dichiara tali autorizzazioni nel manifesto dell'applicazione.

Supporto della compilazione incrementale

Il sistema di compilazione WPF fornisce il supporto per le compilazioni incrementali. in quanto è in grado di rilevare le modifiche apportate al codice o al markup e compila solo gli elementi interessati dalla modifica. Il meccanismo di compilazione incrementale utilizza i file seguenti:

  • Un file $(NomeAssembly)_MarkupCompiler.Cache per gestire lo stato corrente del compilatore.

  • Un file $(AssemblyName)_MarkupCompiler.lref per memorizzare nella cache i file XAML con riferimenti ai tipi definiti localmente.

Di seguito è riportato un set di regole che controlla la compilazione incrementale:

  • Il file è la più piccola unità di rilevazione delle modifiche per il sistema di compilazione. Pertanto, per un file di codice, il sistema di compilazione non è in grado di stabilire se è stato modificato un tipo o se è stato aggiunto codice. Lo stesso vale per i file di progetto.

  • Il meccanismo di compilazione incrementale deve essere consapevole del fatto che una pagina XAML definisce una classe o usa altre classi.

  • Se le voci Reference vengono modificate, è necessario ricompilare tutte le pagine.

  • Se un file di codice viene modificato, ricompilare tutte le pagine contenenti riferimenti a tipi definiti in locale.

  • Se un file XAML cambia:

    • Se XAML viene dichiarato come Page nel progetto: se XAML non dispone di riferimenti di tipo definiti localmente, ricompilare il codice XAML più tutte le pagine XAML con riferimenti locali; se xaml include riferimenti locali, ricompilare tutte le pagine XAML con riferimenti locali.

    • Se XAML viene dichiarato come ApplicationDefinition nel progetto: ricompilare tutte le pagine XAML (motivo: ogni XAML ha riferimento a un Application tipo che potrebbe essere stato modificato).

  • Se il file di progetto dichiara un file di codice come definizione dell'applicazione anziché un file XAML:

    • Controllare se il valore ApplicationClassName nel file di progetto è stato modificato (se è presente un nuovo tipo di applicazione). In questo caso, è necessario ricompilare l'intera applicazione.

    • In caso contrario, ricompilare tutte le pagine XAML con riferimenti locali.

  • Quando viene modificato un file di progetto: applicare tutte le regole precedenti e stabilire cosa deve essere ricompilato. Le modifiche alle seguenti proprietà determinano una ricompilazione completa: AssemblyName, IntermediateOutputPath, RootNamespace e HostInBrowser.

Sono possibili gli scenari di ricompilazione seguenti:

  • Viene ricompilata l'intera applicazione.

  • Vengono ricompilati solo i file XAML con riferimenti di tipo definiti localmente.

  • Non viene ricompilato nulla (se non è stato modificato nulla nel progetto).

Vedi anche