Compartir a través de


Capítulo 2: Creación de una aplicación "Longhorn"

 

Introducción
Capítulo 1: Modelo de aplicación "Longhorn"

Capítulo 2: Creación de una aplicación "Longhorn"

Brent Rector
Wise Owl Consulting

Noviembre de 2003

Contenido

El motor de compilación de Microsoft .NET: MSBuild.exe
Compilación de Hola mundo mediante MSBuild
Terminología de MSBuild
Compilación de una aplicación ejecutable de Longhorn
Creación de un ensamblado de biblioteca longhorn
Creación de un documento longhorn
Un archivo XAML como una declaración de clase
Manifiesto de aplicación
Manifiesto de implementación
Ejecutar la aplicación
¿Por qué crear otro sistema de compilación?
Resumen

Para compilar una aplicación longhorn, necesita el Kit de desarrollo de software (SDK) de Longhorn instalado. Como alternativa, puede instalar una versión de Microsoft® Visual Studio® que admita Longhorn. En este libro, no hablo del uso de Visual Studio porque sus asistentes, características de generación de código elegante y funcionalidades de compilación de proyectos ocultan lo que realmente sucede en segundo plano. Creo que usted debe entender lo que hace una herramienta para usted antes de confiar en la herramienta.

Al instalar el SDK de Longhorn, crea un conjunto de elementos de menú Inicio que puede usar para crear una sesión de símbolo del sistema en la que puede compilar aplicaciones de Longhorn. Para compilar versiones de depuración de la aplicación en un sistema de Microsoft Windows® XP de 32 bits, desplácese por los siguientes elementos de menú para crear la sesión de símbolo del sistema adecuada:

  • Inicio
  • Programas
  • Microsoft Longhorn SDK
  • Abrir la ventana Entorno de compilación
  • Entorno de compilación de Windows XP de 32 bits
  • Establecer entorno de compilación de Windows XP de 32 bits (depuración)

El motor de compilación de Microsoft .NET: MSBuild.exe

MSBuild es la herramienta principal que se usa para compilar una aplicación longhorn. Puede ejecutar MSBuild con la opción de línea de comandos de ayuda para obtener información detallada sobre su uso:

MSBuild /?

Cuando ejecuta MSBuild sin ningún argumento de línea de comandos, como se muestra aquí, busca en el directorio de trabajo actual un nombre de archivo que termina con "proj", por ejemplo, .proj, .csproj, etc. Cuando encuentra uno, compila el proyecto según las directivas de ese archivo.

MSBuild

Cuando tenga más de un archivo de proyecto en el directorio, puede especificar el archivo de proyecto adecuado en la línea de comandos:

MSBuild <ProjectName>.proj

Normalmente, MSBuild compila el destino predeterminado en el archivo de proyecto. Puede invalidar esto y especificar el destino que desea compilar. Por ejemplo, para compilar el destino denominado CleanBuild, se invoca MSBuild de la siguiente manera:

MSBuild /t:Cleanbuild

Compilación de Hola mundo mediante MSBuild

Echemos un vistazo a los archivos necesarios para crear una aplicación de Hola mundo basada en navegación sencilla. Más adelante describiré el propósito y el uso de cada archivo en detalle.

En primer lugar, debe definir el objeto Application . Esto se hace en un archivo normalmente denominado archivo de definición de aplicación. Este archivo HelloWorldApplication.xaml define mi objeto Application .

HelloWorldApplication.xaml

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

Esta definición dice: "Para mi objeto Application , quiero usar una instancia de la clase MSAvalon.Windows.Navigation.NavigationApplication . Al iniciarse, la aplicación debe navegar y mostrar la interfaz de usuario (UI) definida en el archivo HelloWorld.xaml".

Este es el contenido del archivo HelloWorld.xaml. Es una versión ligeramente más interesante del ejemplo de Hola mundo anterior en el capítulo 1.

HelloWorld.xaml

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

Ahora que tengo todo el "código" para mi sencilla aplicación de Hola mundo, necesito un archivo de proyecto que defina cómo compilar mi aplicación. Este es el archivo 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>

Coloque estos tres archivos en un directorio. Abra un símbolo del sistema del SDK de Longhorn, vaya al directorio que contiene los archivos y ejecute MSBuild. Compilará el programa en un archivo ejecutable.

Examinaremos el contenido del archivo de definición de aplicación más adelante en este capítulo. En el capítulo 3, describo con detalle muchos de los elementos de Lenguaje de marcado de aplicaciones extensibles (XAML) que puedes usar para definir una interfaz de usuario. Antes de examinar el archivo del proyecto con más detalle, vamos a revisar algunos términos de MSBuild.

Terminología de MSBuild

Vamos a establecer definiciones para algunos elementos de MSBuild. Una propiedad es un par clave-valor. El valor de una propiedad puede originarse en una variable de entorno, desde un modificador de línea de comandos o desde una definición de propiedad en un archivo de proyecto, como se muestra aquí:

<Property OutputDir="bin\" />

Puede considerar un elemento como una matriz de archivos. Un elemento puede contener caracteres comodín y puede excluir archivos específicos. MSBuild usa el atributo Type de un elemento para clasificar los elementos, como se muestra aquí:

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

Una tarea es una unidad atómica en el proceso de compilación. Una tarea puede aceptar parámetros de entrada de elementos Property , elementos Item o cadenas sin formato. El nombre de una tarea identifica el tipo de datos de .NET de compilación necesario para realizar la tarea. Una tarea puede emitir elementosque consumen otras tareas. MSBuild incluye muchas tareas, todas las cuales se pueden clasificar ampliamente como

  • Tareas de herramientas de .NET
  • Tareas de implementación
  • Tareas de Shell

Por ejemplo, the Task con un nombre de Csc invoca el compilador de C# como herramienta de compilación, que compila todos los elementos Item especificados en el atributo Sources (que especifica elementos Item con un Tipo de compilación) en un ensamblado y genera el ensamblado como un elemento de salida .

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

Un destino es un único paso lógico en el proceso de compilación. Un destino puede realizar análisis de marca de tiempo. Esto significa que un destino no se ejecutará si no es necesario. Un destino ejecuta una o varias tareaspara realizar las operaciones deseadas, como se muestra aquí:

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

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

Un atributo Condition es aproximadamente equivalente a una instrucción if simple. Una condición puede comparar dos cadenas o comprobar la existencia de un archivo o directorio. Puede aplicar una condición a cualquier elemento de un archivo de proyecto. Por ejemplo, este es un grupo de propiedades que se definen solo cuando la propiedad Configuration tiene el valor Depurar:

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

Una importación es aproximadamente equivalente a una instrucción #include de C/C++, como se muestra en el ejemplo siguiente. Al importar un proyecto, el contenido del proyecto importado se convierte lógicamente en parte del proyecto de importación.

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

Ahora que la terminología está fuera del camino, vamos a examinar un archivo de proyecto típico.

Compilación de una aplicación ejecutable de Longhorn

Este es un archivo de proyecto sencillo, pero relativamente completo, que compila una aplicación Longhorn ejecutable:

<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

Todos los archivos de proyecto comienzan con una definición de elemento raíz denominada Project. Su atributo DefaultTargets especifica los nombres de los destinos que el sistema debe compilar cuando no se especifica un destino. En este ejemplo, se especifica que, de forma predeterminada, el sistema debe compilar el destino denominado Build.

Los elementos PropertyGroup y Property

Las reglas de compilación se pueden ejecutar condicionalmente en función de los valores de propiedad. Como se mencionó, el valor de una propiedad puede originarse desde una variable de entorno, desde un modificador de línea de comandos de MSBuild o desde una definición de propiedad en un archivo de proyecto.

Un proyecto de una aplicación debe especificar, como mínimo, un valor para las propiedades Language y TargetName . En este ejemplo, se especifica que el lenguaje es C# y que el nombre de la aplicación resultante debe ser MyApp. También he asignado un valor a la propiedad denominada DefaultClrNameSpace.

El sistema de compilación compila cada archivo XAML en una definición de clase administrada. De forma predeterminada, la clase administrada tendrá el mismo nombre que el nombre de archivo base del archivo de origen XAML. Por ejemplo, el archivo Markup.xaml se compila en una definición de una clase denominada Markup. Al establecer la propiedad DefaultClrNameSpaceen IntroLonghorn, le pido al sistema de compilación que asigne un prefijo a los nombres de clase generados con el espacio de nombres IntroLonghorn . Por este motivo, el sistema de compilación genera una clase denominada IntroLonghorn.Markup para la definición de Markup.xaml.

He definido mis propiedades antes de importar otros proyectos, por lo que las reglas de los proyectos importados usarán mis valores de propiedad especificados; por ejemplo, obtendré las reglas de compilación adecuadas para las aplicaciones de C# porque defina la propiedad Language como C#.

Elemento Import

Las reglas del destino build generan el archivo ejecutable de la aplicación Longhorn. Especificar esas reglas de compilación en cada archivo de proyecto sería tediosa y repetitiva. Por lo tanto, un poco más adelante en el archivo del proyecto, uso la siguiente definición para importar un archivo de proyecto predefinido denominado WindowsApplication.target:

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

Este archivo importado contiene las reglas de compilación estándar para compilar una aplicación de Windows y define (indirectamente) el destino denominado Build.

Elementos ItemGroup y Item

El elemento ItemGroup y sus elementos Item secundarios definen todos los elementos necesarios para compilar la aplicación.

Debe tener un elemento con un tipoapplicationDefinition, como se muestra aquí. Este elemento especifica el archivo que describe el objeto Application que se va a usar para la aplicación. El objeto Application suele ser una instancia de la clase MSAvalon.Windows.Application o de la clase MSAvalon.Windows.Navigation.NavigationApplication , que describo más adelante en este capítulo.

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

Cada elemento con un tipo de página define un conjunto de archivos XAML, como se muestra aquí. El sistema de compilación compila estas definiciones XAML en clases que incluye en el ensamblado resultante.

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

Cada elemento con un tipo de código representa un archivo de código fuente, como se muestra aquí. El sistema de compilación compila estos archivos de origen con el compilador adecuado seleccionado por la propiedad Language del proyecto.

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

Este proyecto puede depender de otros proyectos. El sistema de compilación debe compilar estos proyectos dependientes para poder compilar este proyecto. Puede enumerar cada uno de estos proyectos dependientes mediante un elemento con el tipo de dependentProjects:

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

El código de este proyecto podría usar tipos en un ensamblado precompilado, también conocido como ensamblado de componentes. Para compilar código mediante estos ensamblados de componentes, el compilador necesita una referencia a cada ensamblado. Además, al implementar la aplicación, también tendrá que implementar estos ensamblados de componentes. Puede enumerar cada ensamblado de componentes mediante un elemento con tipo de componentes:

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

Un ensamblado al que se hace referencia es algo diferente de un ensamblado de componentes. En ambos casos, el código usa tipos en un ensamblado precompilado. Sin embargo, no se envía un ensamblado al que se hace referencia como parte de la aplicación, mientras que se envía un ensamblado de componentes como parte de la aplicación. El sistema de compilación debe conocer esta distinción.

Especifique un elemento con un tipo de referencias para indicar que el compilador debe hacer referencia al ensamblado especificado en tiempo de compilación, como se muestra aquí, pero el ensamblado no formará parte de la implementación de la aplicación. El sistema de compilación incluye automáticamente referencias a ensamblados del sistema estándar, por ejemplo, mscorlib.dll, System.dll, PresentationFramework.dll. y mucho más, pero tendrá que agregar cualquier ensamblado no estándar al que debe hacer referencia la aplicación.

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

La aplicación también puede usar recursos. Un elemento con un tipo de recursos describe un recurso usado por la aplicación, como se muestra aquí. El sistema de compilación puede insertar el recurso en el ensamblado resultante o incluirlo como un archivo independiente. El sistema de compilación también puede colocar recursos localizables en ensamblados satélite.

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

Creación de un ensamblado de biblioteca de Longhorn

También querrá compilar bibliotecas además de aplicaciones ejecutables. Las principales diferencias entre un proyecto de aplicación y un proyecto de biblioteca son las siguientes:

  • Un proyecto de biblioteca establece el valor de la propiedad TargetType en Library.
  • Normalmente, un proyecto de biblioteca no incluye un elemento de definición de aplicación.

Este es un ejemplo de un archivo de proyecto que crea una biblioteca:

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

Creación de un documento longhorn

No está restringido a compilar aplicaciones con XAML. También puedes usar archivos XAML para crear un documento adaptable altamente interactivo e inteligentemente representado para que un usuario lo lea. En este caso, los archivos XAML representan colectivamente páginas de un documento. Puede usar el motor de MSBuild para compilar estos documentos.

Los cambios realizados en el archivo de proyecto para compilar un documento en lugar de una aplicación son menores:

  • Establezca el valor de la propiedad TargetType en Document.
  • Importe el proyecto WindowsDocument.target para las reglas de compilación adecuadas.
  • Incluya todos los demás archivos de proyecto como de costumbre.

Es importante comprender lo que realmente genera un TargetType de Document . Al compilar un documento, la salida de compilación es un archivo .container y el sistema de compilación optimiza el contenido del contenedor para su descarga en lugar de velocidad. Un archivo de contenedor es una extensión del almacenamiento estructurado de Windows, también conocido como DocFile, formato. El control de contenedores longhorn proporciona características que permiten la representación de archivos parcialmente descargados. Por lo tanto, no necesita el contenedor completo descargado antes de que la aplicación empiece a ejecutarse.

Además, al pedir a MSBuild que cree un archivo de contenedor, compila cada archivo XAML en una representación binaria del XML, denominada XAML binario (BAML). BAML es mucho más compacto que el archivo de texto original o un ensamblado compilado a IL. Los archivos BAML se descargan más rápidamente, están optimizados para su descarga, pero un intérprete debe analizarlos en tiempo de ejecución para crear instancias de las clases descritas en el archivo. Por lo tanto, estos archivos no están optimizados para la velocidad. Hasta ahora, he estado generando archivos compilados a IL (también conocidos como archivos CAML, short for compiled XAML).

Este es un ejemplo de un archivo de proyecto que crea un documento electrónico:

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

Ahora que has aprendido a compilar los distintos tipos de aplicaciones y componentes de Longhorn, echemos un vistazo a los archivos XAML con más detalle. En concreto, echemos un vistazo a lo que hace el sistema de compilación cuando convierte un archivo XAML en una clase .NET.

Un archivo XAML como declaración de clase

El archivo de definición de aplicación es el archivo XAML que define la clase del objeto Application de la aplicación. Sin embargo, en general, un documento XAML es simplemente un archivo que define una clase. La clase generada por la definición XAML deriva de la clase asociada al nombre del elemento raíz del documento XML. De forma predeterminada, el sistema de compilación usa el nombre de archivo base XAML como nombre de clase generado.

Crear un archivo de definición de aplicación para una aplicación de navegación

Recuerde que el elemento Item con Type of ApplicationDefinition especifica el nombre del archivo XAML que define el objeto Application . En otras palabras, este elemento especifica el archivo XAML que contiene el punto de entrada de la aplicación. La plataforma Longhorn creará una instancia del tipo derivado de MSAvalon.Windows.Application que defina en este archivo y le permitirá administrar el inicio, el apagado y la navegación de la aplicación.

En el capítulo 1, ha visto cómo crear y usar una instancia de aplicación mediante programación. El siguiente archivo XAML usa marcado para definir el objeto Application para un proyecto:

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

Espero que la mayoría de las aplicaciones de Longhorn sean aplicaciones basadas en navegación y, por lo tanto, a menudo simplemente reutilizará el objeto NavigationApplication estándar. Puede reutilizar este archivo de definición de aplicación para la mayoría de las aplicaciones basadas en navegación cambiando solo el valor del atributo StartupUri .

Por ejemplo, si la definición de aplicación anterior reside en el archivo HelloWorldApplication.xaml y uso el archivo de proyecto HelloWorld.proj enumerado anteriormente, el sistema de compilación genera la siguiente declaración de clase:

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

El espacio de nombres resulta de la declaración DefaultClrNameSpace en el archivo de proyecto, el nombre de clase declarado es el mismo que el nombre de archivo base y la clase declarada extiende la clase representada por el elemento raíz en el archivo XAML.

Personalización del código generado mediante atributos

Cuando declaras un elemento raíz en un archivo XAML, puedes usar atributos en el elemento raíz para controlar el nombre de la declaración de clase generada. Puede usar cualquiera de los siguientes atributos opcionales:

  • Definición de prefijo de espacio de nombres que asocia un prefijo a un espacio de nombres denominado Definition. Debe definir un prefijo para este espacio de nombres para usar los atributos Language y Class . Tradicionalmente, se usa el prefijo def .
  • Atributo Language (definido en el espacio de nombres Definition ) que especifica el lenguaje de programación utilizado por cualquier código insertado en el archivo XAML.
  • Atributo Class (definido en el espacio de nombres Definition ) que especifica el nombre de la clase generada. Cuando se especifica un nombre que contiene uno o varios puntos, el sistema de compilación no prefijo el nombre con el valor DefaultClrNameSpace .

Por ejemplo, vamos a cambiar el contenido del archivo HelloWorldApplication.xaml por lo siguiente:

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

La clase generada sería la siguiente:

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

Usar código y marcado en la misma clase

Casi todas las aplicaciones requerirán que escriba código (por ejemplo, un controlador de eventos de clic para un botón o una invalidación de método virtual), además del marcado que especifica la interfaz de usuario. Recuerde del capítulo 1 que mi aplicación no basada en navegación sobrepone el método OnStartingUp para crear su ventana y controles. Volveré a escribir ese ejemplo para ilustrar cómo combinaría el código y el marcado de la aplicación.

Aunque en este siguiente ejemplo se crea una aplicación que no es de navegación, quiero destacar que realmente no hay ninguna razón atractiva para crear dicha aplicación. Siempre puede crear una aplicación basada en navegación que nunca navegue a una página diferente. Sin embargo, escribir una aplicación de este tipo requiere que me combine código y marcado en la misma clase, por lo tanto, se proporciona un buen ejemplo.

Recuerde que la creación de una aplicación que no sea de navegación requiere definir una clase personalizada que herede de MSAvalon.Windows.Application y que invalide el método OnStartingUp . El archivo de definición de aplicación declara la clase de objeto de aplicación que usa el programa. Por lo tanto, una aplicación que no sea de navegación debe definir su método invalidado OnStartingUp en la misma clase.

Excepto para los siguientes cambios, un archivo de configuración de aplicación para una aplicación que no es de navegación contiene los mismos elementos que un archivo de definición para una aplicación de navegación:

  • El elemento raíz es Application en lugar de NavigationApplication.
  • El archivo debe contener o hacer referencia a la implementación del método OnStartingUp para la clase de aplicación.

Dado que necesito usar marcado y código para implementar una sola clase, necesito mostrar una técnica para asociar un archivo de código fuente con un archivo XAML.

Asociación de un archivo Source-Behind con un archivo XAML

Con frecuencia, querrá desarrollar partes de la aplicación mediante el marcado y desarrollar otras partes mediante un lenguaje de programación más tradicional. Recomiendo encarecidamente separar la interfaz de usuario y la lógica en archivos de origen individuales mediante la siguiente técnica.

Puedes agregar un elemento CodeBehind XAML (definido en el espacio de nombres Definition ) al elemento raíz de cualquier archivo XAML y especificar el nombre de un archivo de código fuente (también conocido como archivo de código subyacente). El motor de compilación compilará las declaraciones XAML en una clase administrada. El sistema de compilación también compila el archivo de código subyacente en una declaración de clase administrada. El aspecto complicado es que ambas declaraciones de clase representan declaraciones parciales de una sola clase.

Esta es una definición XAML que genera una clase de aplicación que no es de navegación equivalente al primer ejemplo del capítulo 1:

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

Hay dos aspectos destacados para este archivo de definición de aplicación:

  • El atributo Language especifica que el archivo de código subyacente contiene código fuente de C#.
  • El atributo CodeBehind especifica que el nombre del archivo de origen es CodeBehindMySample2.xaml.cs.

Este es el archivo de origen subyacente coincidente:

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

Observe la palabra clave parcial en la declaración de clase en el archivo de código subyacente. Esta palabra clave indica que el compilador debe combinar esta definición de clase con otras definiciones de la misma clase. Esto le permite proporcionar varias definiciones parciales de una clase, cada una en un archivo de origen independiente, que el compilador combina en una única definición de clase en el ensamblado resultante.

Combinación de código fuente y marcado en un único archivo XAML

Creo que es incorrecto mezclar código fuente y marcado en el mismo archivo. Incluso he considerado que no te mostré cómo hacerlo. Sin embargo, algún malvado en algún lugar escribirá un programa de ejemplo con esta técnica, por lo que es posible que tenga que entender lo que ha hecho. Además, puede usar el enfoque de archivo de código subyacente descrito anteriormente para deshacer el mundo de una pequeña cantidad de maldad y separar la interfaz de usuario de la lógica.

Este es un archivo de definición de aplicación con el código fuente insertado directamente en línea con el marcado:

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

En este ejemplo, el atributo Language especifica que el código fuente insertado es C#. Observe que el elemento Code es un bloque CDATA que contiene el código fuente insertado. A veces, técnicamente es necesario incluir código fuente insertado en un bloque CDATA XML para asegurarse de que el documento tiene un formato correcto. De hecho, el analizador XAML siempre requiere que incluyas el código fuente insertado en un bloque CDATA, incluso al omitirlo, genera un documento bien formado.

Me disculpe una vez más por mostrarte tal travesti.

Manifiesto de aplicación

Al compilar una aplicación, MSBuild genera el archivo .exe más dos archivos de manifiesto: el manifiesto de aplicación, *.manifest y un manifiesto de implementación, *.deploy. Estos manifiestos se usan al implementar una aplicación o un documento desde un servidor. En primer lugar, copie la aplicación, todas sus dependencias y los dos archivos de manifiesto en la ubicación adecuada en el servidor. En segundo lugar, edite el manifiesto de implementación para que apunte a la ubicación del manifiesto de aplicación.

Para obtener información completa, echemos un vistazo a ejemplos de los manifiestos de aplicación e implementación. El manifiesto de aplicación, que se muestra en el ejemplo siguiente, no es tan interesante como el manifiesto de implementación. El manifiesto de aplicación simplemente define todas las partes que componen una aplicación. MSBuild genera el manifiesto de aplicación cuando compila la aplicación y normalmente modifica poco o nada en ella.

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 mayoría del contenido del manifiesto de aplicación debe ser relativamente obvio. El elemento entryPoint especifica el nombre del método de punto de entrada, main y hace referencia a la dependencia, denominada HelloWorld, que contiene el punto de entrada. El elemento entryPoint también contiene el nombre del programa y el argumento de línea de comandos que el shell necesitará para ejecutar la aplicación.

El elemento de dependenciaHelloWorld contiene la información (el elemento dependentAssembly) que especifica el ensamblado dependiente y un elemento installFrom que indica al cargador dónde encontrar el archivo del ensamblado y el hash original del archivo. El cargador puede usar el hash para detectar los cambios realizados en el ensamblado posterior a la compilación.

Longhorn Trust Manager usa el elemento TrustInfo para determinar los permisos de seguridad que requiere la aplicación. En el ejemplo anterior, mi aplicación HelloWorld define un conjunto de permisos que asigna al conjunto de permisos SeeDefinition . Inmediatamente después de definir el conjunto de permisos, el elemento AssemblyRequest solicita que el ensamblado denominado HelloWorld reciba al menos el conjunto de permisos en el conjunto denominado SeeDefinition. Los permisos de este ejemplo son los permisos concedidos normalmente a las aplicaciones que se ejecutan en SEE, por lo que la aplicación Hola mundo se ejecuta sin mostrar al usuario advertencias de seguridad de Trust Manager.

Manifiesto de implementación

Como se mencionó, el manifiesto de implementación es más interesante. El manifiesto de implementación contiene, obviamente, la configuración suficiente que controla la implementación de la aplicación.

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>

El manifiesto de implementación contiene información que Longhorn requiere para instalar y actualizar una aplicación. Observe que el elemento assemblyIdentity del manifiesto de implementación hace referencia al manifiesto de aplicación. Después de todo, el manifiesto de aplicación ya describe todos los componentes de una aplicación. Para instalar una aplicación, el manifiesto de implementación indica, en efecto, "Esta es la descripción de los archivos que necesita para instalar esta aplicación".

Por supuesto, al instalar una aplicación, también necesita más información que los archivos para copiar en un sistema. El elemento description enumera los atributos publisher, product y supportUrl ; el sistema muestra su contenido en el cuadro de diálogo Agregar o quitar programas.

El elemento de implementación especifica cómo implementar y actualizar la aplicación después de la implementación. En este ejemplo, la aplicación es visible para el shell y el sistema del cliente comprobará y, si es necesario, descargará una nueva versión de la aplicación cada vez que el usuario inicie la aplicación. Además, el sistema periódicamente ( no más de cada seis horas y no menos de una vez a la semana) comprueba si hay una nueva versión. Cuando la comprobación periódica localiza una nueva versión, Longhorn descargará la nueva versión en segundo plano e la instalará; A continuación, estará listo para ejecutarse la próxima vez que el usuario ejecute la aplicación.

Ejecutar la aplicación

Normalmente, un usuario "ejecutará" el manifiesto de aplicación para ejecutar la aplicación desde el servidor directamente sin instalar la aplicación en el equipo local. Longhorn descarga los componentes de la aplicación según sea necesario. En este caso, el servidor debe estar disponible para ejecutar la aplicación.

Cuando un usuario "ejecuta" el manifiesto de implementación, Longhorn descarga e instala la aplicación en el equipo local. La aplicación puede instalar iconos en el escritorio, agregar elementos de menú Inicio y, por lo general, convertirse en una aplicación instalada tradicional. Por supuesto, también obtendrá las actualizaciones automáticas en segundo plano, la reversión de versiones y la compatibilidad con desinstalación.

Al iniciar por primera vez un manifiesto de implementación, Longhorn instala la aplicación en la memoria caché de la aplicación y agrega una entrada a la lista Agregar o quitar programas de la Panel de control. Posteriormente, cada vez que ejecute el manifiesto de implementación, la aplicación se carga directamente desde la memoria caché de la aplicación. Normalmente no se descarga de nuevo.

Sin embargo, al desinstalar la aplicación de la memoria caché mediante el applet Add/Remove Programs de la Panel de control, la ejecución posterior del manifiesto de implementación descarga e instala la aplicación una vez más.

Como alternativa, puede cambiar el número de versión de la aplicación en el servidor. Después, al ejecutar el manifiesto de implementación, Longhorn descargará e instalará la nueva versión en paralelo con la versión actual. Ambas versiones de la aplicación aparecerán en la lista Agregar o quitar programas.

¿Por qué crear otro sistema de compilación?

Realmente me gusta MSBuild, aunque, en el momento de redactar este artículo, he tenido solo unas semanas de experiencia con ella. Por supuesto, años de experiencia con makefiles hace que cualquier sistema de compilación más elegante sea atractivo. En la actualidad, hay dos sistemas de compilación alternativos en uso común: Make y Ant. Parece natural comparar MSBuild con estas alternativas.

¿Por qué no usar Make?

¿Por qué desarrollar un nuevo sistema de compilación cuando muchos desarrolladores están familiarizados con uno existente denominado Make? Make tiene una integración deficiente de las herramientas en el sistema de compilación. Simplemente ejecuta comandos de shell. Por este motivo, no hay ninguna capacidad inherente para que una herramienta se comunique con otra durante el proceso de compilación. MSBuild crea instancias de las clases Task y las tareas se pueden comunicar entre sí pasando tipos normales de .NET.

Los archivos Make tienen una sintaxis inusual, son difíciles de escribir y no se escalan bien, ya que son complejos para proyectos de gran tamaño. Además, las herramientas distintas de Make no pueden procesar fácilmente un archivo make. Las herramientas distintas de MSBuild pueden generar y analizar fácilmente la sintaxis basada en XML de un proyecto de MSBuild.

Por último, Make no tiene realmente compatibilidad con proyectos. No hay abstracción del sistema de archivos ni compatibilidad con las propiedades en cascada. Además, no hay compatibilidad en tiempo de diseño para generar un archivo make.

¿Por qué no usar ant?

Una pregunta más frecuente similar es por qué desarrollar un nuevo sistema de compilación basado en XML cuando hay un sistema muy exitoso y enriquecido existente llamado Ant? Ant es un sistema de compilación de Java, código abierto a partir de Apache.org que son pioneros en las tareas y los archivos de proyecto basados en XML como operación de compilación atómica. También hay un gran puerto de .NET de Ant llamado NAnt disponible en nant.sourceforge.net. En la superficie, MSBuild y Ant/NAnt son similares. Ambas herramientas usan XML como formato de serialización del proyecto y ambas herramientas usan tareas como su unidad atómica de operación de compilación. Ambas herramientas tienen sus puntos fuertes, pero cuando se echa un vistazo más de cerca, tienen diferentes objetivos de diseño.

Ant tomó la decisión de diseño de colocar gran funcionalidad en un gran conjunto de tareas. MSBuild tiene un objetivo de diseño diferente, donde el motor encapsula una funcionalidad similar (como el análisis de marca de tiempo, la comunicación entre tareas a través de elementos, procesamiento por lotes de tareas, transformaciones de elementos, etc.). Ambos enfoques tienen sus puntos fuertes y débiles.

El modelo de Ant permite a los desarrolladores ampliar y controlar cada detalle de la compilación y, por lo tanto, es muy flexible. Sin embargo, también pone una mayor responsabilidad en los escritores de tareas porque las tareas deben ser mucho más sofisticadas para proporcionar una funcionalidad coherente. El modelo de MSBuild reduce la cantidad de funcionalidad que cada tarea necesita implementar. Por lo tanto, los autores de proyectos pueden confiar en una funcionalidad coherente en diferentes proyectos, destinos y tareas. Además, los entornos de desarrollo integrados, como Visual Studio, también pueden confiar en esos servicios para proporcionar resultados coherentes y una experiencia de usuario enriquecida, sin tener que saber nada sobre los aspectos internos de las tareas a las que se llama durante el proceso de compilación.

De forma similar, aunque Ant tiene el concepto de un script de compilación, no tiene el concepto de un manifiesto de proyecto que MSBuild tiene. Un script de compilación indica cómo crear un conjunto de archivos, pero no proporciona semántica adicional que describe cómo se usan los archivos. Además, un manifiesto describe la semántica de los archivos, lo que permite que herramientas adicionales, como un IDE, se integren más profundamente con el sistema de compilación. Por el contrario, la falta de un manifiesto de proyecto significa que un desarrollador puede adaptar más fácilmente Ant para crear nuevos tipos de "cosas" porque no hay ningún esquema de restricción para el script de compilación.

Resumen

Ahora ha masterizado los conceptos básicos. Puedes escribir XAML y compilar, implementar y ejecutar la aplicación resultante. Desafortunadamente, las aplicaciones que ha aprendido a escribir hasta ahora son bastante aburridos. En el capítulo 3 se profundiza en XAML y se muestra cómo usar una amplia variedad de objetos de interfaz de usuario proporcionados por la plataforma Longhorn. Los capítulos posteriores muestran una serie de otras tecnologías nuevas que también puede usar en las aplicaciones.

Continúe con el capítulo 3: Controles y XAML.