Condividi tramite


Capitolo 2: Creazione di un'applicazione "Longhorn"

 

Introduzione
Capitolo 1: Modello di applicazione "Longhorn"

Capitolo 2: Creazione di un'applicazione "Longhorn"

Rettore di Brent
Wise Owl Consulting

Novembre 2003

Contenuto

Motore di compilazione Microsoft .NET: MSBuild.exe
Compilazione di Hello World con MSBuild
Terminologia di MSBuild
Compilazione di un'applicazione eseguibile Longhorn
Creazione di un assembly della libreria Longhorn
Compilazione di un documento longhorn
Un file XAML come dichiarazione di classe
Manifesto dell'applicazione
Manifesto della distribuzione
Esecuzione dell'applicazione
Perché creare un altro sistema di compilazione?
Sommario

Per compilare un'applicazione Longhorn, è necessario installare Longhorn Software Development Kit (SDK). In alternativa, è possibile installare una versione di Microsoft® Visual Studio® che supporta Longhorn. In questo libro non viene illustrato l'uso di Visual Studio perché le procedure guidate, le funzionalità di generazione di codice fantasia e le funzionalità di compilazione del progetto oscurano ciò che accade effettivamente sotto le quinte. Credo che dovresti capire cosa fa uno strumento per te prima di affidarti allo strumento.

Quando si installa Longhorn SDK, viene creato un set di voci di menu Start che è possibile usare per creare una sessione del prompt dei comandi in cui è possibile compilare applicazioni Longhorn. Per compilare versioni di debug dell'applicazione in un sistema a 32 bit di Microsoft Windows® XP, spostarsi tra le voci di menu seguenti per creare la sessione del prompt dei comandi appropriata:

  • Inizio
  • Programmi
  • Microsoft Longhorn SDK
  • Aprire la finestra Ambiente di compilazione
  • Ambiente di compilazione a 32 bit di Windows XP
  • Impostare l'ambiente di compilazione a 32 bit di Windows XP (debug)

Motore di compilazione Microsoft .NET: MSBuild.exe

MSBuild è lo strumento principale usato per compilare un'applicazione Longhorn. È possibile eseguire MSBuild con l'opzione della riga di comando della Guida per ottenere informazioni dettagliate sull'utilizzo:

MSBuild /?

Quando si esegue MSBuild senza argomenti della riga di comando, come illustrato di seguito, cerca nella directory di lavoro corrente un nome di file che termina con "proj", ad esempio .proj, .csproj e così via. Quando ne trova uno, compila il progetto in base alle direttive in tale file.

MSBuild

Quando nella directory sono presenti più file di progetto, è possibile specificare il file di progetto appropriato nella riga di comando:

MSBuild <ProjectName>.proj

In genere, MSBuild compila la destinazione predefinita nel file di progetto. È possibile eseguire l'override di questo e specificare la destinazione che si vuole compilare. Ad esempio, per compilare la destinazione denominata CleanBuild, richiamare MSBuild come indicato di seguito:

MSBuild /t:Cleanbuild

Compilazione di Hello World con MSBuild

Verranno ora esaminati i file necessari per creare una semplice applicazione Hello World basata sulla navigazione. Più avanti descriverò lo scopo e l'uso di ogni file in dettaglio.

Prima di tutto, è necessario definire l'oggetto application . Questa operazione viene eseguita in un file denominato in genere file di definizione dell'applicazione. Questo file HelloWorldApplication.xaml definisce l'oggetto application .

HelloWorldApplication.xaml

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml" 
                       StartupUri="HelloWorld.xaml" />

Questa definizione indica: "Per l'oggetto application di , si vuole usare un'istanza della classe MSAvalon.Windows.Navigation.NavigationApplication. All'avvio, l'applicazione deve passare a e visualizzare l'interfaccia utente definita nel file HelloWorld.xaml."

Ecco il contenuto del file HelloWorld.xaml. È una versione leggermente più interessante dell'esempio Hello World precedente nel capitolo 1.

HelloWorld.xaml

<Border xmlns="https://schemas.microsoft.com/2003/xaml">
  <FlowPanel>
    <SimpleText Foreground="DarkRed" FontSize="14">Hello World!</SimpleText>   </FlowPanel>
</Border>

Ora che ho tutto il "codice" per la mia semplice applicazione Hello World, ho bisogno di un file di progetto che definisce come compilare l'applicazione. Ecco il mio file HelloWorld.proj.

HelloWorld.proj

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property Language="C#" />   
    <Property DefaultClrNameSpace="IntroLonghorn" />
    <Property TargetName="HelloWorld" />
  </PropertyGroup>

  <!--Imports the target which contains all the common targets-->
  <Import Project="$(LAPI)\WindowsApplication.target" />

  <ItemGroup>
    <!-- Application markup -->
    <Item Type="ApplicationDefinition" Include="HelloWorldApplication.xaml" />
   
    <!-- Compiled Xaml Files list -->
    <Item Type="Pages" Include="HelloWorld.xaml"/>      
  </ItemGroup>
</Project>

Inserire questi tre file in una directory. Aprire un prompt dei comandi di Longhorn SDK, passare alla directory contenente i file ed eseguire MSBuild. Compilerà il programma in un eseguibile.

Il contenuto del file di definizione dell'applicazione verrà esaminato più avanti in questo capitolo. Nel capitolo 3, descrivo in dettaglio molti degli elementi XAML (Extensible Application Markup Language) che puoi usare per definire un'interfaccia utente. Prima di esaminare il file di progetto in modo più approfondito, si esaminerà la terminologia di MSBuild.

Terminologia di MSBuild

Verranno ora stabilite definizioni per alcuni elementi di MSBuild. Un Property è una coppia chiave-valore. Un Proprietàvalore può provenire da una variabile di ambiente, da un'opzione della riga di comando o da una definizione Property in un file di progetto, come illustrato di seguito:

<Property OutputDir="bin\" />

Si può pensare a un Item come una matrice di file. Un elemento può contenere caratteri jolly e può escludere file specifici. MSBuild usa l'attributo type di un Item per classificare gli elementi, come illustrato di seguito:

<Item Type="Compile" Include="*.cs" Exclude="DebugStuff.cs" />

Un Task è un'unità atomica nel processo di compilazione. Un Task di può accettare parametri di input da elementi Property , elementi Item o stringhe semplici. Il nome di un'attività identifica il tipo di dati .NET di compilazione necessario per eseguire l'attività . Un attività di può generare itemche altri taskutilizzano. MSBuild include molte attività, che possono essere classificate in modo ampio come

  • Attività degli strumenti .NET
  • Attività di distribuzione
  • Attività della shell

Ad esempio, tTask con un Name di Csc richiama il compilatore C# come strumento di compilazione, che compila tutti gli elementi Item specificati nell'attributo Sources (che specifica gli elementi Item con un type di Compile) in un assembly, e produce l'assembly come output Item.

<Task Name="Csc" AssemblyName="$(OutputDir)\HelloWorld.exe"
                 Sources="@(Compile)" />

Un target è un singolo passaggio logico nel processo di compilazione. Un target può eseguire l'analisi del timestamp. Ciò significa che un di destinazione non verrà eseguito se non è necessario. Un destinazione esegue una o più attivitàper eseguire le operazioni desiderate, come illustrato di seguito:

<Target Name="CopyToServer"
        Inputs="$(OutputDir)\HelloWorld.exe"
        Outputs="\\DeployServer\$(BuildNum)\HelloWorld.exe"
        DependsOnTargets="Compile">

  <Task Name="Copy" ... />
</Target>

Un attributo Condition è approssimativamente equivalente a una semplice se istruzione. Un Condizione può confrontare due stringhe o verificare l'esistenza di un file o di una directory. È possibile applicare un Condizione a qualsiasi elemento di un file di progetto. Di seguito, ad esempio, è riportato un gruppo di proprietà definite solo quando la proprietà configuration ha il valore Debug:

<PropertyGroup Condition=" '$(Configuration)'=='Debug' " >
    <Property ... />
    <Property ... />
</PropertyGroup>

Un Import equivale approssimativamente a un'istruzione #include C/C++, come illustrato nell'esempio seguente. Quando si importa un progetto, il contenuto del progetto importato diventa logicamente parte del progetto di importazione.

<Import Project="$(LAPI)\WindowsApplication.target" />

Ora che la terminologia non è corretta, esaminiamo un file di progetto tipico.

Compilazione di un'applicazione eseguibile Longhorn

Ecco un file di progetto semplice, ma relativamente completo, che compila un'applicazione Longhorn eseguibile:

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property Language="C#" />
    <Property DefaultClrNameSpace="IntroLonghorn" />
    <Property TargetName="MyApp" />
  </PropertyGroup>

  <Import Project="$(LAPI)\WindowsApplication.target" />

  <ItemGroup>
    <Item Type="ApplicationDefinition" Include="MyApp.xaml" />

    <Item Type="Pages" Include="HomePage.xaml" />
    <Item Type="Pages" Include="DetailPage.xaml" />
    <Item Type="Code" Include="DetailPage.xaml.cs"/>

    <Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

    <Item Type="Components" Include="SomeThirdParty.dll" />

    <Item Type="Resources" Include="Picture1.jpg"
          FileStorage="embedded" Localizable="False"/>
    <Item Type="Resources" Include="Picture2.jpg"
          FileStorage="embedded" Localizable="True"/>
  </ItemGroup>
</Project>

Elemento Project

Tutti i file di progetto iniziano con una definizione di elemento radice denominata Project. L'attributo DefaultTargets specifica i nomi delle destinazioni che il sistema deve compilare quando non si specifica diversamente una destinazione. In questo esempio si specifica che, per impostazione predefinita, il sistema deve compilare la destinazione denominata Build.

Elementi propertygroup e

Le regole di compilazione possono essere eseguite in modo condizionale in base ai valori delle proprietà. Come accennato, il valore di una proprietà può provenire da una variabile di ambiente, da un'opzione della riga di comando di MSBuild o da una definizione di proprietà in un file di progetto.

Un progetto per un'applicazione deve specificare almeno un valore per le proprietà Language e TargetName. In questo esempio si specifica che il linguaggio è C# e che il nome dell'applicazione risultante deve essere MyApp. È stato assegnato anche un valore alla proprietà denominata DefaultClrNameSpace.

Il sistema di compilazione compila ogni file XAML in una definizione di classe gestita. Per impostazione predefinita, la classe gestita avrà lo stesso nome del nome file di base del file di origine XAML. Ad esempio, il file Markup.xaml viene compilato in una definizione di una classe denominata Markup. Impostando la proprietà defaultClrNameSpace su IntroLonghorn, viene chiesto al sistema di compilazione di anteporre i nomi delle classi generate con lo spazio dei nomi introlonghorn . Per questo motivo, il sistema di compilazione produce una classe denominata IntroLonghorn.Markup per la definizione Markup.xaml.

Sono stati definiti le proprietà prima di importare altri progetti, quindi le regole nei progetti importati useranno i valori delle proprietà specificate, ad esempio si otterranno le regole di compilazione appropriate per le applicazioni C# perché si definisce la proprietà linguaggio di come C#.

Elemento Import

Le regole nella destinazione compilazione producono il file eseguibile dell'applicazione Longhorn. Specificare tali regole di compilazione in ogni file di progetto sarebbe noioso e ripetitivo. Quindi un po ' più avanti nel file di progetto, uso la definizione seguente per importare un file di progetto predefinito denominato WindowsApplication.target:

  <Import Project="$(LAPI)\WindowsApplication.target" />

Questo file importato contiene le regole di compilazione standard per la compilazione di un'applicazione Windows e definisce (indirettamente) la destinazione denominata Build.

Elementi itemgroup e item

L'elemento ItemGroup e gli elementi figlio Item definiscono tutte le parti necessarie per compilare l'applicazione.

È necessario disporre di un elemento con un tipo di ApplicationDefinition, come illustrato di seguito. Questo Item specifica il file che descrive l'oggetto application da usare per l'applicazione. L'oggetto Application è in genere un'istanza della classe MSAvalon.Windows.Application o della classe MSAvalon.Windows.Navigation.NavigationApplication, entrambi descritti più avanti in questo capitolo.

<Item Type="ApplicationDefinition" Include="MyApp.xaml" />

Ogni Item con un type di Pages definisce un set di file XAML, come illustrato di seguito. Il sistema di compilazione compila queste definizioni XAML in classi incluse nell'assembly risultante.

<Item Type="Pages" Include="HomePage.xaml" />
<Item Type="Pages" Include="DetailPage.xaml" />

Ogni elemento con un di tipo di Code rappresenta un file di origine, come illustrato di seguito. Il sistema di compilazione compila questi file di origine usando il compilatore appropriato selezionato dalla proprietà Language del progetto.

<Item Type="Code" Include="DetailPage.xaml.cs"/>

Questo progetto potrebbe dipendere da altri progetti. Il sistema di compilazione deve compilare questi progetti dipendenti prima di poter compilare questo progetto. Ogni progetto dipendente viene elencato usando un elemento con tipo di DependentProjects:

<Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

Il codice in questo progetto può usare tipi in un assembly predefinito, noto anche come assembly del componente . Per compilare il codice usando tali assembly di componenti, il compilatore deve avere un riferimento a ogni assembly. Inoltre, quando si distribuisce l'applicazione, sarà necessario distribuire anche questi assembly di componenti. Ogni assembly di componenti viene elencato usando un elemento con tipo di Components:

<Item Type="Components" Include="SomeThirdParty.dll" />

Un assembly a cui si fa riferimento è leggermente diverso da un assembly del componente. In entrambi i casi, il codice usa tipi in un assembly predefinito. Tuttavia, non viene fornito un assembly a cui si fa riferimento come parte dell'applicazione, mentre si esegue la spedizione di un assembly di componenti come parte dell'applicazione. Il sistema di compilazione deve conoscere questa distinzione.

È necessario specificare un item con un Type di riferimenti per indicare che il compilatore deve fare riferimento all'assembly specificato in fase di compilazione, come illustrato di seguito, ma l'assembly non farà parte della distribuzione dell'applicazione. Il sistema di compilazione include automaticamente riferimenti agli assembly di sistema standard, ad esempio mscorlib.dll, System.dll, PresentationFramework.dll. e altro ancora, ma è necessario aggiungere qualsiasi assembly non standard a cui l'applicazione deve fare riferimento.

<Item Type="References" Include="SharedThirdParty.dll" />

L'applicazione potrebbe anche usare risorse. Un elemento con un di tipo di Risorse descrive una risorsa usata dall'applicazione, come illustrato di seguito. Il sistema di compilazione può incorporare la risorsa nell'assembly risultante o includerla come file autonomo. Il sistema di compilazione può anche inserire risorse localizzabili in assembly satellite.

<Item Type="Resources" Include="Picture1.jpg"
      FileStorage="embedded" Localizable="False"/>
<Item Type="Resources" Include="Picture2.jpg"
      FileStorage="embedded" Localizable="True"/>

Creazione di un assembly della libreria Longhorn

È anche possibile creare librerie oltre alle applicazioni eseguibili. Le differenze principali tra un progetto di applicazione e un progetto di libreria sono le seguenti:

  • Un progetto di libreria imposta il valore della proprietà TargetType su Library.
  • Un progetto di libreria in genere non include un elemento di definizione dell'applicazione.

Ecco un esempio di file di progetto che crea una libreria:

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property Language="C#" />
    <Property DefaultClrNameSpace="IntroLonghorn" />
    <Property TargetName="MyLibrary" />
    <Property TargetType="Library" />
  </PropertyGroup>

  <Import Project="$(LAPI)\WindowsApplication.target" />

  <ItemGroup>
    <Item Type="Pages" Include="ErrorPage.xaml" />
    <Item Type="Code" Include="ErrorPage.xaml.cs"/>
    <Item Type="Code" Include="Utilities.cs"/>

    <Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

    <Item Type="Components" Include="SomeThirdParty.dll" />

    <Item Type="Resources" Include="Picture1.jpg"
          FileStorage="embedded" Localizable="False"/>
    <Item Type="Resources" Include="Picture2.jpg"
          FileStorage="embedded" Localizable="True"/>
  </ItemGroup>
</Project>

Compilazione di un documento longhorn

Non è possibile creare applicazioni con XAML. Puoi anche usare i file XAML per creare un documento altamente interattivo, intelligentemente sottoposto a rendering, adattivo per consentire a un utente di leggere. In questo caso, i file XAML rappresentano collettivamente le pagine di un documento. È possibile usare il motore MSBuild per compilare tali documenti.

Le modifiche apportate al file di progetto per compilare un documento anziché un'applicazione sono minori:

  • Impostare il valore della proprietà TargetType su Document.
  • Importare il progetto WindowsDocument.target per le regole di compilazione appropriate.
  • Includere tutti gli altri file di progetto come di consueto.

È importante comprendere cosa produce realmente un TargetType di Document. Quando si compila un Document, l'output di compilazione è un file con estensione container e il sistema di compilazione ottimizza il contenuto del contenitore per il download anziché per la velocità. Un file contenitore è un'estensione di Archiviazione strutturata di Windows, nota anche come DocFile, formato. La gestione dei contenitori Longhorn offre funzionalità che consentono il rendering di file parzialmente scaricati. Pertanto, non è necessario scaricare l'intero contenitore prima che l'applicazione inizi l'esecuzione.

Inoltre, quando si chiede a MSBuild di creare un file contenitore, compila ogni file XAML in una rappresentazione binaria del codice XML, denominato CODICE XAML binario (BAML). BAML è molto più compatto del file di testo originale o di un assemblyto-IL compilato. I file BAML vengono scaricati più rapidamente, ottimizzati per il download, ma un interprete deve analizzarli in fase di esecuzione per creare istanze delle classi descritte nel file. Pertanto, tali file non sono ottimizzati per la velocità. Fino ad ora, ho generato file compilatito-IL (noti anche come file CAML, in breve per XAML compilato).

Ecco un esempio di file di progetto che crea un documento elettronico:

<Project DefaultTargets="Build">
  <PropertyGroup>
    <Property TargetType="Document" />
      <Property Language="C#" />
      <Property DefaultClrNameSpace="IntroLonghorn" />
      <Property TargetName="MyDocument" />
  </PropertyGroup>
    
  <Import Project="$(LAPI)\WindowsDocument.target" />

  <ItemGroup>
    <Item Type="ApplicationDefinition" Include="MyApp.xaml" />

    <Item Type="Pages" Include="Markup.xaml" />
    <Item Type="Pages" Include="Navigate.xaml" />
    <Item Type="Code" Include="Navigate.xaml.cs"/>

    <Item Type="Resources" Include="Picture1.jpg"
          FileStorage="embedded" Localizable="False"/>
    <Item Type="Resources" Include="Picture2.jpg"
          FileStorage="embedded" Localizable="True"/>
  </ItemGroup>
</Project>

Ora che hai appreso come creare i vari tipi di applicazioni e componenti longhorn, esaminiamo in modo più dettagliato i file XAML. In particolare, esaminiamo cosa fa il sistema di compilazione quando trasforma un file XAML in una classe .NET.

Un file XAML come dichiarazione di classe

Il file di definizione dell'applicazione è il file XAML che definisce la classe dell'oggetto 'applicazione dell'applicazione. Tuttavia, in generale, un documento XAML è semplicemente un file che definisce una classe. La classe prodotta dalla definizione XAML deriva dalla classe associata al nome dell'elemento radice del documento XML. Per impostazione predefinita, il sistema di compilazione usa il nome del file di base XAML come nome della classe generata.

Creazione di un file di definizione dell'applicazione per un'applicazione di spostamento

Tenere presente che l'elemento item con Type di ApplicationDefinition specifica il nome del file XAML che definisce l'oggetto Application . In altre parole, questo elemento specifica il file XAML che contiene il punto di ingresso per l'applicazione. La piattaforma Longhorn creerà un'istanza del MSAvalon.Windows.Applicationtipo derivato dall'utente definito in questo file e consente di gestire l'avvio, l'arresto e lo spostamento dell'applicazione.

Nel capitolo 1 è stato illustrato come creare e usare un'istanza dell'applicazione a livello di codice. Il file XAML seguente usa il markup per definire l'oggetto application per un progetto:

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml" 
                       StartupUri="HelloWorld.xaml" />

Si prevede che la maggior parte delle applicazioni Longhorn sarà basata sulla navigazione e, pertanto, spesso riutilicherà semplicemente l'oggetto NavigationApplication standard. È possibile riutilizzare questo file di definizione dell'applicazione per la maggior parte delle applicazioni basate sulla navigazione modificando solo il valore dell'attributo StartupUri .

Ad esempio, se la definizione dell'applicazione precedente si trova nel file HelloWorldApplication.xaml e uso il file di progetto HelloWorld.proj elencato in precedenza, il sistema di compilazione produce la dichiarazione di classe seguente:

namespace IntroLonghorn {
  class HelloWorldApplication :
           MSAvalon.Windows.Navigation.NavigationApplication {
.
.
.
   }
 }

Lo spazio dei nomi risultante dalla dichiarazione DefaultClrNameSpace nel file di progetto, il nome della classe dichiarato corrisponde al nome del file di base e la classe dichiarata estende la classe rappresentata dall'elemento radice nel file XAML.

Personalizzazione del codice generato tramite attributi

Quando dichiari un elemento radice in un file XAML, puoi usare attributi sull'elemento radice per controllare il nome della dichiarazione di classe generata. È possibile usare uno degli attributi facoltativi seguenti:

  • Definizione del prefisso dello spazio dei nomi che associa un prefisso a uno spazio dei nomi denominato Definizione. È necessario definire un prefisso per questo spazio dei nomi per usare gli attributi Language e Class. Tradizionalmente, viene usato il prefisso def.
  • Attributo language (definito nello spazio dei nomi definizione ) che specifica il linguaggio di programmazione usato da qualsiasi codice inline nel file XAML.
  • Attributo classe (definito nello spazio dei nomi definizione ) che specifica il nome della classe generata. Quando si specifica un nome contenente uno o più punti, il sistema di compilazione non antepone il nome al valore DefaultClrNameSpace.

Ad esempio, si modificherà il contenuto del file HelloWorldApplication.xaml nel modo seguente:

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml"
                       xmlns:def="Definition"
                       def:Class="Special.MyApp"
                       def:CodeBehind="HelloWorldApplication.xaml.cs" 
                       StartupUri="HelloWorld.xaml" />

La classe generata sarà quindi la seguente:

namespace Special {
  class MyApp :
           MSAvalon.Windows.Navigation.NavigationApplication {
.
.
.
  }
}

Uso di codice e markup nella stessa classe

Quasi tutte le applicazioni richiederanno di scrivere codice, ad esempio un gestore eventi click per un pulsante o un override del metodo virtuale, oltre al markup che specifica l'interfaccia utente. Ricordare dal capitolo 1 che l'applicazione non basata su navigazione esegue l'overrode del metodo OnStartingUp per creare la finestra e i controlli. Questo esempio verrà riscritto per illustrare come combinare il codice dell'applicazione e il markup.

Anche se questo prossimo esempio crea un'applicazione non di navigazione, voglio sottolineare che non c'è davvero motivo interessante per creare un'applicazione di questo tipo. È sempre possibile creare un'applicazione basata sulla navigazione che non passa mai effettivamente a una pagina diversa. Tuttavia, la scrittura di un'applicazione di questo tipo richiede di combinare codice e markup nella stessa classe, pertanto fornisce un buon esempio.

Tenere presente che la creazione di un'applicazione non di spostamento richiede di definire una classe personalizzata che eredita da MSAvalon.Windows.Application e che esegue l'override del metodo OnStartingUp. Il file di definizione dell'applicazione dichiara la classe oggetto applicazione usata dal programma. Pertanto, un'applicazione non di navigazione deve definirne l'override metodo OnStartingUp nella stessa classe.

Ad eccezione delle modifiche seguenti, un file di configurazione dell'applicazione per un'applicazione non di navigazione contiene gli stessi elementi di un file di definizione per un'applicazione di navigazione:

  • L'elemento radice è Application anziché NavigationApplication.
  • Il file deve contenere o fare riferimento all'implementazione del metodo OnStartingUp per la classe dell'applicazione.

Poiché devo usare markup e codice per implementare una singola classe, devo mostrare una tecnica per associare un file di codice sorgente a un file XAML.

Associazione di un file Source-Behind a un file XAML

Spesso si vogliono sviluppare parti dell'applicazione usando markup e per sviluppare altre parti usando un linguaggio di programmazione più tradizionale. È consigliabile separare l'interfaccia utente e la logica in singoli file di origine usando la tecnica seguente.

Puoi aggiungere un elemento CodeBehind XAML (definito nello spazio dei nomi definizione ) all'elemento radice di qualsiasi file XAML e specificare il nome di un file di codice sorgente (noto anche come file code-behind ). Il motore di compilazione compilerà le dichiarazioni XAML in una classe gestita. Il sistema di compilazione compila anche il file code-behind in una dichiarazione di classe gestita. L'aspetto difficile è che entrambe queste dichiarazioni di classe rappresentano dichiarazioni parziali di una singola classe.

Ecco una definizione XAML che produce una classe di applicazione non di navigazione equivalente al primo esempio del capitolo 1:

<Application xmlns="https://schemas.microsoft.com/2003/xaml"
             xmlns:def="Definition"
             def:Language="C#"
             def:Class="IntroLonghorn.CodeBehindSample"
             def:CodeBehind="CodeBehind.xaml.cs" />

Esistono due aspetti importanti per questo file di definizione dell'applicazione:

  • L'attributo linguaggio specifica che il file code-behind contiene codice sorgente C#.
  • L'attributo codeBehind specifica che il nome del file di origine è CodeBehindMySample2.xaml.cs.

Ecco il file source-behind corrispondente:

namespace IntroLonghorn {
  using System;
  using MSAvalon.Windows;
  using MSAvalon.Windows.Controls;
  using MSAvalon.Windows.Media;

  public partial class CodeBehindSample {
    MSAvalon.Windows.Controls.SimpleText txtElement;
    MSAvalon.Windows.Window              mainWindow;

    protected override
    void OnStartingUp (StartingUpCancelEventArgs e) {
      base.OnStartingUp (e);
      CreateAndShowMainWindow ();
    }

    private void CreateAndShowMainWindow () {
      // Create the application's main window
      mainWindow = new MSAvalon.Windows.Window ();

      // Add a dark red, 14 point, "Hello World!" text element
      txtElement = new MSAvalon.Windows.Controls.SimpleText ();
      txtElement.Text = "Hello World!";
      txtElement.Foreground = new
       MSAvalon.Windows.Media.SolidColorBrush (Colors.DarkRed);
      txtElement.FontSize = new FontSize (14,
                                          FontSizeType.Point);
      mainWindow.Children.Add (txtElement);
      mainWindow.Show ();
    }
  }
}

Si noti la parola chiave parziale nella dichiarazione di classe nel file code-behind. Questa parola chiave indica che il compilatore deve unire questa definizione di classe con altre definizioni della stessa classe. In questo modo è possibile fornire più definizioni parziali di una classe, ognuna in un file di origine separato, che il compilatore combina in una singola definizione di classe nell'assembly risultante.

Combinazione di codice sorgente e markup in un singolo file XAML

Penso che sia sbagliato combinare codice sorgente e markup nello stesso file. Ho anche pensato di non mostrarti come farlo. Tuttavia, un po ' maledoer scriverà un programma di esempio usando questa tecnica, quindi potrebbe essere necessario capire cosa ha fatto. Inoltre, è possibile usare l'approccio code-behind descritto in precedenza per eliminare il mondo di una piccola quantità di male e separare l'interfaccia utente dalla logica.

Ecco un file di definizione dell'applicazione con il codice sorgente inserito direttamente inline con il markup :

<Application xmlns="https://schemas.microsoft.com/2003/xaml"
    xmlns:def="Definition"
    def:Language="C#"
    def:Class="IntroLonghorn.MySample2" >

  <def:Code>
  <![CDATA[
    protected override void OnStartingUp (StartingUpCancelEventArgs e) {
      base.OnStartingUp (e);
      CreateAndShowMainWindow ();
    }
    . . . Remaining methods elided for clarity
  ]]>
  </def:Code>
</Application>

In questo esempio, l'attributo language specifica che il codice sorgente inline è C#. Si noti che l'elemento Code è un blocco CDATA contenente il codice sorgente inline. A volte è tecnicamente necessario racchiudere il codice sorgente inline in un blocco CDATA XML per assicurarsi che il documento sia ben formato. Infatti, il parser XAML richiede sempre di racchiudere il codice sorgente inline in un blocco CDATA, anche se omettendolo produce un documento ben formato.

Mi scuso ancora una volta per mostrarti una tale travestitura.

Manifesto dell'applicazione

Quando si compila un'applicazione, MSBuild produce il file .exe più due file manifesto: il manifesto dell'applicazione, *.manifest e un manifesto di distribuzione, *.deploy. Questi manifesti vengono usati quando si distribuisce un'applicazione o un documento da un server. Copiare prima di tutto l'applicazione, tutte le relative dipendenze e i due file manifesto nel percorso appropriato nel server. In secondo luogo, modificare il manifesto della distribuzione in modo che punti al percorso del manifesto dell'applicazione.

Per completezza, esaminiamo gli esempi dei manifesti dell'applicazione e della distribuzione. Il manifesto dell'applicazione, illustrato nell'esempio seguente, non è effettivamente interessante come il manifesto della distribuzione. Il manifesto dell'applicazione definisce semplicemente tutte le parti che costituiscono un'applicazione. MSBuild genera il manifesto dell'applicazione quando compila l'applicazione e in genere si modifica poco o nulla in esso.

HelloWorld.manifest

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
          xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd">

  <assemblyIdentity name="HelloWorld" version="1.0.0.0"
                    processorArchitecture="x86" asmv2:culture="en-us"
                    publicKeyToken="0000000000000000" />

  <entryPoint name="main" xmlns="urn:schemas-microsoft-com:asm.v2"
              dependencyName="HelloWorld">

    <commandLine file="HelloWorld.exe" parameters="" />
  </entryPoint>

  <TrustInfo xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:temp="temporary">
    <Security>
      <ApplicationRequestMinimum>
        <PermissionSet class="System.Security.PermissionSet" version="1" 
                       ID="SeeDefinition">
          <IPermission 
            class="System.Security.Permissions.FileDialogPermission"
            version="1" Unrestricted="true" />
          <IPermission 
            class="System.Security.Permissions.IsolatedStorageFilePermission" 
            version="1" Allowed="DomainIsolationByUser" UserQuota="5242880" />
          <IPermission
            class="System.Security.Permissions.SecurityPermission"
            version="1" Flags="Execution" />
          <IPermission
            class="System.Security.Permissions.UIPermission" version="1"
            Window="SafeTopLevelWindows" Clipboard="OwnClipboard" />
          <IPermission
            class="System.Security.Permissions.PrintingPermission"
            version="1" Level="SafePrinting" />
          <IPermission
            class="MSAvalon.Windows.AVTempUIPermission, PresentationFramework,
                   Version=6.0.4030.0, Culture=neutral,
                   PublicKeyToken=a29c01bbd4e39ac5" version="1"
                   NewWindow="LaunchNewWindows" FullScreen="SafeFullScreen" />
        </PermissionSet>

        <AssemblyRequest name="HelloWorld"
                         PermissionSetReference="SeeDefinition" />
      </ApplicationRequestMinimum>
    </Security>
  </TrustInfo>

  <dependency asmv2:name="HelloWorld">
    <dependentAssembly>
      <assemblyIdentity name="HelloWorld" version="0.0.0.0"
                        processorArchitecture="x86" />
    </dependentAssembly>

    <asmv2:installFrom codebase="HelloWorld.exe"
                       hash="5c58153494c16296d9cab877136c3f106785bfab" 
                       hashalg="SHA1" size="5632" />
  </dependency>
</assembly>

La maggior parte del contenuto del manifesto dell'applicazione dovrebbe essere relativamente ovvia. L'elemento entryPoint specifica il nome del metodo del punto di ingresso, principalee fa riferimento alla dipendenza , denominata HelloWorld, che contiene il punto di ingresso. L'elemento entryPoint contiene anche il nome del programma e l'argomento della riga di comando che la shell dovrà eseguire l'applicazione.

L'elemento HelloWorlddependency contiene le informazioni (l'elemento dependentAssembly ) che specifica l'assembly dipendente e un elemento installFrom che indica al caricatore dove trovare il file dell'assembly e l'hash originale del file. Il caricatore può usare l'hash per rilevare le modifiche apportate all'assembly dopo la compilazione.

Longhorn Trust Manager usa l'elemento TrustInfo per determinare le autorizzazioni di sicurezza richieste dall'applicazione. Nell'esempio precedente, l'applicazione HelloWorld definisce un set di autorizzazioni che denomina il set di autorizzazioni SeeDefinition. Subito dopo aver definito il set di autorizzazioni, l'elemento AssemblyRequest richiede che l'assembly denominato HelloWorld riceva almeno il set di autorizzazioni nel set denominato SeeDefinition. Le autorizzazioni in questo esempio sono le autorizzazioni normalmente concesse alle applicazioni in esecuzione in SEE, quindi l'applicazione Hello World viene eseguita senza visualizzare all'utente avvisi di sicurezza di Trust Manager.

Manifesto della distribuzione

Come accennato, il manifesto della distribuzione è più interessante. Il manifesto della distribuzione contiene, ovviamente, impostazioni sufficienti che controllano la distribuzione dell'applicazione.

HelloWorld.deploy

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" 
          xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"  
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd">
  
  <assemblyIdentity name="HelloWorld.deploy" version="1.0.0.0" 
                    processorArchitecture="x86" asmv2:culture="en-us" 
                    publicKeyToken="0000000000000000" />

  <description asmv2:publisher="Wise Owl, Inc." 
               asmv2:product="Brent's HelloWorld Application"            
    asmv2:supportUrl="http://www.wiseowl.com/AppServer/HelloWorld/support.asp" 
  />
  
  <deployment xmlns="urn:schemas-microsoft-com:asm.v2" 
              isRequiredUpdate="false">
    <install shellVisible="true" />
    <subscription>
      <update>
        <beforeApplicationStartup />
        <periodic>
          <minElapsedTimeAllowed time="6" unit="hours" />
          <maxElapsedTimeAllowed time="1" unit="weeks" />
        </periodic>
      </update>
    </subscription>
  </deployment>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity name="HelloWorld" version="1.0.0.0" 
                        processorArchitecture="x86" asmv2:culture="en-us" 
                        publicKeyToken="0000000000000000" />
    </dependentAssembly>
    <asmv2:installFrom codebase="HelloWorld.manifest" />
  </dependency>
</assembly>

Il manifesto della distribuzione contiene informazioni richieste da Longhorn per installare e aggiornare un'applicazione. Si noti che l'elemento assemblyIdentity del manifesto della distribuzione fa riferimento al manifesto dell'applicazione. Dopo tutto, il manifesto dell'applicazione descrive già tutti i componenti di un'applicazione. Per installare un'applicazione, il manifesto della distribuzione indica, in effetti, "Ecco la descrizione dei file necessari per installare l'applicazione".

Naturalmente, quando si installa un'applicazione, sono necessarie anche altre informazioni rispetto ai file da copiare in un sistema. L'elemento description elenca gli attributi publisher, producte supportUrl; il sistema visualizza il relativo contenuto nella finestra di dialogo Installazione applicazioni.

L'elemento distribuzione specifica come distribuire e aggiornare l'applicazione dopo la distribuzione. In questo esempio, l'applicazione è visibile alla shell e il sistema del client verificherà e, se necessario, scaricare una nuova versione dell'applicazione ogni volta che l'utente avvia l'applicazione. Inoltre, il sistema periodicamente, non più di ogni sei ore e non meno di una volta alla settimana, verifica la presenza di una nuova versione. Quando il controllo periodico individua una nuova versione, Longhorn scaricherà la nuova versione in background e la installerà; sarà quindi pronto per l'esecuzione alla successiva esecuzione dell'applicazione da parte dell'utente.

Esecuzione dell'applicazione

In genere, un utente "eseguirà" il manifesto dell'applicazione per eseguire l'applicazione dal server direttamente senza installare l'applicazione nel computer locale. Longhorn scarica i componenti dell'applicazione in base alle esigenze. In questo caso, il server deve essere disponibile per eseguire l'applicazione.

Quando un utente "esegue" il manifesto della distribuzione, Longhorn scarica e installa l'applicazione nel computer locale. L'applicazione può installare icone sul desktop, aggiungere voci di menu Start e in genere diventare un'applicazione installata tradizionale. Naturalmente, si ottengono anche gli aggiornamenti in background automatici, il rollback della versione e il supporto per la disinstallazione.

Quando si avvia per la prima volta un manifesto di distribuzione, Longhorn installa l'applicazione nella cache dell'applicazione e aggiunge una voce all'elenco Installazione applicazioni del Pannello di controllo. Successivamente, ogni volta che si esegue il manifesto della distribuzione, l'applicazione viene caricata direttamente dalla cache dell'applicazione. In genere non viene scaricato di nuovo.

Tuttavia, quando si disinstalla l'applicazione dalla cache usando l'applet Installazione applicazioni del Pannello di controllo, successivamente l'esecuzione del manifesto di distribuzione scarica e installa nuovamente l'applicazione.

In alternativa, è possibile modificare il numero di versione dell'applicazione nel server. Quindi, quando si esegue il manifesto della distribuzione, Longhorn scaricherà e installerà la nuova versione side-by-side con la versione corrente. Entrambe le versioni dell'applicazione verranno visualizzate nell'elenco Installazione applicazioni.

Perché creare un altro sistema di compilazione?

Mi piace molto MSBuild, anche se, al momento della stesura di questo articolo, ho avuto solo poche settimane di esperienza con esso. Naturalmente, anni di esperienza con makefiles rende il sistema di compilazione più elegante attraente. Attualmente, esistono due sistemi di compilazione alternativi in uso comune: Make e Ant. Sembra naturale confrontare MSBuild con tali alternative.

Perché non usare make?

Perché sviluppare un nuovo sistema di compilazione quando molti sviluppatori hanno familiarità con uno esistente denominato Make? Make ha una scarsa integrazione degli strumenti nel sistema di compilazione. Eseguire semplicemente i comandi della shell. Per questo motivo, non esiste alcuna capacità intrinseca per uno strumento di comunicare con un altro strumento durante il processo di compilazione. MSBuild crea istanze delle classi di Task e le attività possono comunicare tra loro passando tipi .NET normali.

I makefile hanno una sintassi insolita, sono difficili da scrivere e non sono scalabili correttamente, perché diventano complessi per progetti di grandi dimensioni. Inoltre, gli strumenti diversi da Make non possono elaborare facilmente un makefile. Gli strumenti diversi da MSBuild possono generare e analizzare facilmente la sintassi basata su XML di un progetto MSBuild.

Infine, Make non ha davvero supporto per i progetti. Non esiste alcuna astrazione del file system e nessun supporto per le proprietà a catena. Inoltre, non esiste alcun supporto in fase di progettazione per la generazione di un makefile.

Perché non usare Ant?

Una domanda frequente simile è il motivo per cui sviluppare un nuovo sistema di compilazione basato su XML quando esiste un sistema di successo e ricco esistente chiamato Ant? Ant è un sistema di compilazione open source Java di Apache.org che ha introdotto i file e le attività di progetto basati su XML come operazione di compilazione atomica. È disponibile anche una grande porta .NET di Ant denominata NAnt disponibile da nant.sourceforge.net. Sulla superficie, MSBuild e Ant/NAnt sono simili. Entrambi gli strumenti usano XML come formato di serializzazione del progetto e entrambi gli strumenti usano attività come unità atomica dell'operazione di compilazione. Entrambi gli strumenti hanno i loro punti di forza, ma quando si esaminano più da vicino hanno obiettivi di progettazione diversi.

Ant ha deciso di impostare molte funzionalità in un ampio set di attività. MSBuild ha un obiettivo di progettazione diverso, in cui la funzionalità simile viene incapsulata dal motore ,ad esempio l'analisi del timestamp, la comunicazione intertask tramite elementi, invio in batch di attività, trasformazioni di elementi e così via. Entrambi gli approcci hanno i loro punti di forza e debolezza.

Il modello di Ant consente agli sviluppatori di estendere e controllare ogni dettaglio della compilazione e quindi è molto flessibile. Tuttavia, pone anche una maggiore responsabilità sui writer di attività perché le attività devono essere molto più sofisticate per fornire funzionalità coerenti. Il modello di MSBuild riduce la quantità di funzionalità che ogni attività deve implementare. Gli autori di progetti possono quindi basarsi su funzionalità coerenti in progetti, destinazioni e attività diversi. Inoltre, gli ambienti di sviluppo integrati come Visual Studio possono anche basarsi su tali servizi per offrire risultati coerenti e un'esperienza utente avanzata, senza dover conoscere gli elementi interni delle attività chiamate durante il processo di compilazione.

Analogamente, mentre Ant ha il concetto di script di compilazione, non ha il concetto di manifesto del progetto che MSBuild ha. Uno script di compilazione indica come creare un set di file, ma non fornisce semantiche aggiuntive che descrivono come vengono usati i file. Un manifesto descrive inoltre la semantica dei file, che consente strumenti aggiuntivi, ad esempio un IDE, di integrarsi più profondamente con il sistema di compilazione. Al contrario, la mancanza di un manifesto del progetto significa che uno sviluppatore può più facilmente personalizzare Ant per creare nuovi tipi di "roba" perché non esiste uno schema con vincolo per lo script di compilazione.

Sommario

Ora hai appreso le nozioni di base. È possibile scrivere XAML e compilare, distribuire ed eseguire l'applicazione risultante. Sfortunatamente, le applicazioni che hai imparato a scrivere finora sono abbastanza noiose. capitolo 3 immergersi in modo approfondito in XAML e illustra come usare un'ampia gamma di oggetti dell'interfaccia utente forniti dalla piattaforma Longhorn. I capitoli successivi mostrano una serie di altre nuove tecnologie che è possibile usare anche nelle applicazioni.

Continuare con il capitolo 3: Controlli eXAML.