MSDN Magazine > Inicio > Todos los números > 2008 > Octubre >  Tecnología de vanguardia: Reutilización de códi...
Tecnología de vanguardia
Reutilización de código en WPF y Silverlight 2.
Dino Esposito

Descarga de código disponible en: MSDN Code Gallery (604 KB)
Examinar el código en línea

Esta columna se basa en una versión preliminar de Silverlight 2. Por tanto, toda la información de este documento está sujeta a cambios.

En Silverlight 2 se utiliza un lenguaje de marcado de aplicaciones extensible (XAML) para diseñar y representar la interfaz de usuario. Al mismo tiempo, se emplea el CLR principal integrado para procesar código gestionado dentro del explorador. Como resultado, hay una similitud considerable entre las aplicaciones Silverlight 2 basadas en web y las aplicaciones de escritorio de Windows Presentation Foundation (WPF). Uno de los objetivos de tener un modelo de programación similar es facilitar la reutilización de código entre ambos. En esta columna trataré algunas pautas para facilitar al máximo el intercambio de código y marcado XAML entre Silverlight 2 y WPF.

Compatibilidad entre WPF y Silverlight 2
Con la introducción de Silverlight 2, XAML está llamado a convertirse en la API para una nueva generación de módulos de IU. Silverlight 2 da soporte a un subconjunto del marco completo de WPF que incluye características para la gestión de diseño enriquecido, enlace de datos, estilos, medios, animación, gráficos y plantillas.
Sin embargo, la compatibilidad total de XAML en Silverlight 2 se ve limitada por el tamaño del complemento descargable. En sólo unos pocos megabytes (menos de 5 MB en Beta 2), el complemento Silverlight 2 debe suministrar el CLR principal, una versión de Microsoft .NET Framework 3.5 que incluye WPF y un subconjunto de la plataforma cliente de Windows Communication Foundation (WCF), un analizador de XAML y varios controles específicos de Silverlight.
En Silverlight 2, no se dispone de compatibilidad para las capacidades gráficas 3D de WPF y puede ser que algunos atributos y elementos se hayan eliminado o reducido. A pesar de todo, se trata de un subconjunto compatible, de modo que puede generar una versión Silverlight de una interfaz de usuario WPF moderadamente compleja. En breve, volveré a tratar áreas críticas de compatibilidad.
Tenga en cuenta, sin embargo, que Microsoft también agrega algunas API nuevas en Silverlight que a día de hoy pueden no disponer de un equivalente en la versión de escritorio de WPF. Las características más relevantes incluyen controles, como DataGrid y clases, como WebClient para llamadas de red sencillas GET-style. Estas características también se añadirán a WPF.

Introducción a Visual State Manager
Otra característica que se suministra con Silverlight 2 y que se añadirá a WPF es el atractivo Visual State Manager (VSM) para los controles. VSM simplifica el desarrollo de plantillas de control interactivas introduciendo estados visuales y transiciones de estado. Desde la introducción de WPF, los desarrolladores pueden personalizar el aspecto, la disposición y el comportamiento de los controles WPF mediante plantillas y estilos. Por ejemplo, podría no sólo cambiarse la forma y el aspecto de un control, sino también definirse una nueva acción o animación para cuando se hace clic sobre el control o cuando éste obtiene o pierde el foco, etcétera.
Gracias a VSM, los estados visuales y las transiciones de estado hacen parte de ese trabajo por usted. Los estados visuales típicos de VSM son normal, pasando el mouse, deshabilitado y enfocado. Para cada uno de estos estados puede definir un estilo o una forma. Las transiciones de estado definen cómo pasa el control de un estado a otro. Normalmente las transiciones se definen a través de animaciones. En el tiempo de ejecución, Silverlight reproducirá la animación apropiada y aplicará el estilo especificado para desplazar el control con facilidad y elegancia de un estado a otro.
Para definir un estado visual, inserte un fragmento de marcado en la plantilla del control, tal y como se muestra aquí:
<vsm:VisualStateManager.VisualStateGroups>
  <vsm:VisualStateGroup x:Name="CommonStates">
    <vsm:VisualState x:Name="MouseOver">
      <Storyboard>
        ...
      </Storyboard>
    </vsm:VisualState>
  </vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
Cada control define uno o más grupos de estados como CommonStates y FocusStates. Cada grupo, a su vez, define estados visuales específicos como MouseOver, Pressed y Checked. Para cada estado visual y transición entre estados puede definir un guión gráfico que Silverlight reproducirá automáticamente cuando sea apropiado.
En pocas palabras, hay características en WPF con las que Silverlight 2 no es compatible y características de Silverlight 2 de las que WPF carece. Estas diferencias afectan en su mayor parte a la compatibilidad en el nivel de XAML. Para una compatibilidad completa, puede utilizar el subconjunto común, que, por suerte, es lo suficientemente grande como para permitirle hacer casi cualquier cosa.

Uso compartido de código
Cuando VSM esté disponible para la versión de escritorio de WPF, podrá utilizar el mismo enfoque para las aplicaciones de escritorio de Silverlight y de Windows y compartir plantillas de control entre proyectos de WPF y Silverlight. Hasta entonces, veamos cómo puede reutilizar el código entre proyectos de escritorio de WPF y web hoy en día.
Una aplicación de WPF está formada por XAML y código administrado. El código administrado está dirigido a las clases de la versión compatible de .NET. Es su responsabilidad utilizar un subconjunto común de XAML que tanto la versión de escritorio de WPF como Silverlight 2 puedan comprender. Asimismo, es su responsabilidad organizar sus clases de código subyacente de modo que las diferencias en el marco de back-end se gestionen de manera adecuada.
Hay básicamente dos situaciones en las que querrá reutilizar el código WPF. Una de ellas es en la que se dispone de una aplicación de escritorio de WPF existente que desea suministrar a través de la web para simplificar el mantenimiento y la implementación. La otra se presenta cuando se quiere desarrollar un front-end para un sistema existente y ponerlo a disposición tanto para clientes Windows como clientes web.
Enfocaré la cuestión de reutilización de código desde la perspectiva de WPF a Silverlight. En cuanto a las pautas para refactorizar código y marcado, hay muy poca diferencia.

Reflexión sobre las aplicaciones de WPF
Una aplicación de WPF típica se crea a partir de un árbol de objetos donde Window es la raíz del árbol. A su vez, el elemento Window contiene varios elementos secundarios dispuestos o superpuestos de toda una serie de maneras. Los elementos se refieren a formas básicas, administradores de diseño, guiones gráficos y controles (incluyendo controles de usuario y controles personalizados de terceros). He aquí un ejemplo básico:
<Window x:Class="Samples.MyTestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test App" Height="300" Width="350">
    <StackPanel Margin="10">
        ...
    </StackPanel>
</Window>
Este código no puede integrarse en una aplicación Silverlight 2 tal cual está. En primer lugar, el elemento Window no es compatible con Silverlight. En los ensamblados Silverlight, simplemente no existe una clase de espacio de nombres System.Windows. La etiqueta raíz de cualquier aplicación Silverlight es UserControl. El elemento está asignado a la clase UserControl definida en el espacio de nombres System.Windows.
Aquí está el encabezado modificado de una aplicación Silverlight:
<UserControl x:Class="Samples.MyTestWindow"
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test App" Height="300" Width="350">
    <StackPanel Margin="10">
        ...
    </StackPanel>
</UserControl>
Todo el código de marcado de que dispone dentro de los límites de los elementos <Window> y <UserControl> debería seguir siendo el mismo. Depende de usted modificar el escritorio XAML original para hacerlo coincidir con los requisitos del analizador de XAML de Silverlight. Esta tarea se beneficia en gran medida del alto nivel de compatibilidad entre las dos sintaxis. Casi siempre, ajustar el XAML consiste en corregir algunos elementos y atributos. Permítanme ofrecer un par de ejemplos.
El escritorio de WPF presenta un elemento <label> que Silverlight no reconoce. Al migrar este marcado a Silverlight, deberá encontrar una solución alternativa para la etiqueta. Un rectángulo con un bloque de texto dentro puede ser una solución posible.
En WPF puede asociar información sobre herramientas a controles mediante el atributo Información sobre herramientas, como se ve a continuación:
<Button Tooltip="Click me" ... />
En Silverlight 2 no se admite el atributo Información sobre herramientas. Deberá recurrir al ToolTipService, como en el siguiente fragmento de código:
<Button ToolTipService.ToolTip="Click me" ... />
Debe tenerse en cuenta que ambas soluciones funcionan en la versión de escritorio de WPF. Además, ToolTipService ofrece una serie adicional de propiedades adicionales de información sobre herramientas en la versión de escritorio de WPF como ubicación, desplazamiento, demora inicial y duración. Sin embargo, Silverlight no es compatible con ninguna de estas propiedades adicionales.
¿Son estos todos los problemas de compatibilidad entre WPF y Silverlight? Depende de cómo utilice WPF. En general, migrar una aplicación de WPF importante a Silverlight es difícil y ni siquiera sería un objetivo principal. Para empezar, en Silverlight 2 no dispone de desencadenadores en todos los lugares en los que los podría necesitar. Por ejemplo, hay una recopilación de desencadenadores en los elementos de interfaz de usuario, descendientes de FrameworkElement, pero no en estilos, datos y plantillas de control.
Del mismo modo, el enlace de datos es compatible con Silverlight pero no del mismo modo que lo es en WPF. Por ejemplo, dispone del elemento Binding, tiene contexto de datos, plantillas de datos y colecciones observables. Como se ha mencionado, no dispone de desencadenadores y un marcado XAML simplificado que requiere que escriba código más a menudo que en WPF. Además, la implementación interna es bastante diferente. El objeto Binding en Silverlight tiene muchas menos propiedades que en WPF.
La globalización es otra área que quizás le cause dolores de cabeza. Por motivos de rendimiento, el CLR principal no incluye sus propios datos de globalización para todas las culturas admitidas. En vez de esto, la clase CultureInfo en Silverlight depende de la funcionalidad de globalización proporcionada por el sistema operativo subyacente. Esto significa que no hay manera de dar a las aplicaciones la misma configuración de globalización para diferentes sistemas operativos.
Por último, WPF dispone de un conjunto de controles más rico que no está disponible en Silverlight. Un buen ejemplo es el control RichTextBox.
En resumen, migrar una aplicación de Silverlight a WPF es relativamente sencillo, aunque como desarrollador debería preocuparse por los posibles problemas de rendimiento que podría ocasionar el modelo de objetos más enriquecido. El dato clave que conviene recordar es que Silverlight admite un subconjunto de posibilidades ofrecidas por la versión de escritorio de WPF; depende de usted idear una solución para múltiples plataformas eligiendo las características apropiadas.

Escritura de código WPF para varias plataformas
La manera más fácil de migrar código de proyectos de WPF a proyectos Silverlight es a través de controles de usuario. Aparte de un espacio de nombres XML distinto y diferencias de marcado, un control de usuario es la única manera de compartir marcado y código entre WPF de escritorio y WPF basado en web.
Si su aplicación de WPF está organizada de forma nativa o puede refactorizarse para hacer un uso extensivo de controles de usuario, migrar código a Silverlight será mucho más fácil que la opción, menos atractiva, de cortar y pegar.
¿Entonces es posible compartir ensamblados entre proyectos de WPF y Silverlight? En Silverlight 2, ciertamente puede crear bibliotecas de clase personalizadas empaquetadas en ensamblados. Sin embargo, debería ser consciente de que estas bibliotecas están destinadas a la versión Silverlight de .NET Framework y utilizan un modelo de seguridad diferente (para obtener más información, consulte el número de este mes de Todo sobre CLR).
Cualquier ensamblado que utilice en una aplicación de escritorio de WPF debe recompilarse como biblioteca de clase Silverlight para asegurarse de que se refiere a los ensamblados correctos y de que hace llamadas legales a clases. No hace falta mencionar que este proceso de recompilación puede generar trabajo adicional de corrección de llamadas a clases no compatibles.
En resumen, con un poco de trabajo puede tomar una aplicación de WPF y exponerla a través de la web mediante Silverlight. Y al hacerlo, en realidad está exponiendo su código a través de una serie de plataformas no basadas en Windows, sin obligar a los clientes a tener .NET Framework completo instalado. La figura 1 muestra una simple aplicación autónoma de escritorio de WPF. La figura 2 muestra la misma aplicación alojada en Internet Explorer a través de Silverlight.
Figura 1 Una aplicación de ejemplo de WPF
Figura 2 Una aplicación de WPF adaptada para Silverlight (haga clic en la imagen para ampliarla)

Análisis de código gestionado
Por algún motivo, la reutilización de código entre WPF y Silverlight se percibe como un problema de XAML. Como se ha mencionado, sí que se dan algunos problemas de XAML al adaptar su código, pero el mayor desafío es la clase de código subyacente.
Tanto en Windows como en Silverlight, se empareja un archivo de XAML con una clase de código subyacente escrita en C# u otro lenguaje administrado. Por desgracia, estas clases de código subyacente están destinadas a diferentes versiones de .NET Framework. La versión de escritorio de WPF depende de la biblioteca de clases base (BCL) completa de .NET Framework 3.5, mientras que Silverlight 2 utiliza una versión ligera de la BCL.
La versión de Silverlight de BCL es apreciablemente más pequeña, pero todavía admite capacidades fundamentales como colecciones, reflejo, secuencia de comandos, expresiones regulares, manipulación de cadena, subprocesamiento y temporizadores. También dispone de herramientas para llamar a una serie de servicios como servicios web XML, servicios WCF y ADO.NET Data Services. Además, dispone de un soporte de red enriquecida para comunicarse a través de HTTP con servicios de XML antigua (POX) y Transferencia de estado de representación (REST), y, en general, alcanzar cualquier extremo HTTP público. La compatibilidad con redes también incluye sockets (entre dominios) y comunicación dúplex.
Por último, la BCL de Silverlight ofrece gran compatibilidad para trabajar con datos XML, incluyendo versiones ad hoc de las clases XmlReader y XmlWriter. Estas clases son bastante similares a las clases análogas en la versión de escritorio de .NET Framework.
Tomando como base estas capacidades fundamentales, en Silverlight 2 dispondrá de compatibilidad completa para LINQ to Objects, LINQ to XML y árboles de expresión. Desde Beta 2, Microsoft también incorpora un proveedor completamente nuevo LINQ to JavaScript Object Notation (JSON) para ejecutar consultas de LINQ directamente en datos JSON.
Otro punto a mencionar es que las funciones de red en Silverlight sólo suceden asincrónicamente. Para hacer una llamada sincrónica, deberá recurrir a un truco que consiste básicamente en invocar la capa de interoperabilidad del explorador y alcanzar la implementación del explorador de XMLHttpRequest, siempre que esto sea posible (para obtener más información, consulte go.microsoft.com/fwlink/?LinkId=124048).
La conclusión es que las clases de código subyacente de Silverlight y WPF aprovechan bibliotecas de clase completamente diferentes. Este hecho dificulta la reutilización mucho más que cualquier diferencia de XAML. Tratemos este problema a continuación.

Decisión de una estrategia de código crítico
Al escribir código que debe ser compartido por tiempos de ejecución Silverlight y Windows, debe saber muy bien lo que admite cada plataforma. Idealmente, se dispondría de una lista de las tareas solicitadas que requieran un tratamiento especial en cada plataforma. A continuación, se aislarían estas partes de código en un objeto y de ellas se extraería una interfaz.
Como resultado, tendría un bloque de código completamente reutilizable que invocará componentes separados para funcionalidades críticas. Convertir este tipo de aplicación a Silverlight (o WPF) sería tan fácil como reemplazar los componentes críticos por otros específicos de plataforma. Como todos estos componentes siguen exponiendo una interfaz común, su implementación es transparente para el bloque de código principal.
La pauta detrás de este enfoque es la pauta de "estrategia". Para leer una definición formal de esto, consulte go.microsoft.com/fwlink/?LinkId=124047. En resumen, la pauta de estrategia es útil siempre que necesite cambiar dinámicamente el algoritmo utilizado para realizar una tarea concreta en una aplicación.
Una vez que ha identificado un área de su código que tiene el potencial de variar dependiendo de las condiciones de tiempo de ejecución, defina una interfaz que especifique el comportamiento abstracto de su código. A continuación, cree una o más clases de estrategia que implementen la interfaz, con lo que representa diferentes maneras de poder lograr el comportamiento abstracto. Al cambiar luego la clase de estrategia, simplemente se cambia su enfoque para resolver el algoritmo definido por la interfaz. Al hacerlo, se desacopla la implementación real del comportamiento (la estrategia) del código que se está utilizando.
En ASP.NET, por ejemplo, la pauta de estrategia se utiliza en la implementación del modelo de proveedor de pertenencia, perfiles de usuario, etcétera. El tiempo de ejecución de ASP.NET sabe que existe una interfaz particular para tratar con, digamos, la pertenencia y los usuarios. También sabe cómo encontrar y crear una instancia de las clases concretas para estos tipos de interfaz. Sin embargo, los componentes del tiempo de ejecución dependen sólo de la interfaz, haciendo que los detalles de la clase concreta sean irrelevantes para ASP.NET.

Exposición de un ejemplo
Tratemos la aplicación de ejemplo mostrada en las figuras 1 y 2. Tanto en Silverlight como en Windows, la aplicación presenta un formulario en el que los usuarios pueden escribir un símbolo de acción para obtener una oferta actual. La figura 3 muestra el diagrama de la aplicación cuando el usuario hace clic en el botón. La interfaz de usuario emplea un Modelo-Vista-Presentación (MVP) para transmitir en una sola clase de moderador toda la lógica que se encuentra detrás de la IU. El moderador, a su vez, invoca una clase QuoteService interna que responde proporcionando un objeto de StockInfo con cualquier información que deba incorporarse a la interfaz de usuario (consulte la figura 4).
Figura 3 Diagrama del comportamiento de la aplicación (haga clic en la imagen para ampliarla)
namespace Samples
{
    class SymbolFinderPresenter
    {
        // Internal reference to the view to update
        private ISymbolFinderView _view;

        public SymbolFinderPresenter(ISymbolFinderView view) {
            this._view = view;
        }

        public void Initialize() { }

        // Triggered by the user's clicking on button Find
        public void FindSymbol() {
            // Clear the view before operations start
            ClearView();

            // Get the symbol to retrieve 
            string symbol = this._view.SymbolName;
            if (String.IsNullOrEmpty(symbol)) {
                _view.QuickInfoErrorMessage = "Symbol not found.";
                return;
            }

            QuoteService service = new QuoteService();
            StockInfo stock = service.GetQuote(symbol);

            // Update the view
            UpdateView(stock);
        }

         private void ClearView() {
            _view.SymbolDisplayName = String.Empty;
            _view.SymbolValue = String.Empty;
            _view.SymbolChange = String.Empty;
            _view.ServiceProviderName = String.Empty;
        }

        private void UpdateView(StockInfo stock) {
             // Update the view
            _view.QuickInfoErrorMessage = String.Empty;
            _view.SymbolDisplayName = stock.Symbol;
            _view.SymbolValue = stock.Quote;
            _view.SymbolChange = stock.Change;
            _view.ServiceProviderName = stock.ProviderName;
        }
     }
}
La clase QuoteService no intenta recuperar las ofertas por sí misma. Crea primero un componente de proveedor y se somete a él. La clase QuoteService implementa un algoritmo muy sencillo. Si hay una conexión a Internet, usa una clase de proveedor que utiliza un servicio web público para obtener datos financieros; si no la hay, pasa a un proveedor falso que sólo devuelve números al azar. Así que en algún momento, la clase QuoteService necesita buscar una conexión a Internet, tal y como se muestra en la figura 5.
public StockInfo GetQuote(string symbol)
{
    // Get the provider of the service
    IQuoteServiceProvider provider = ResolveProvider();
    return provider.GetQuote(symbol);
}
private IQuoteServiceProvider ResolveProvider()
{
    bool isOnline = IsConnectedToInternet();
    if (isOnline)
        return new FinanceInfoProvider();

    return new RandomProvider();
}
Hasta aquí no hay diferencia entre Silverlight y WPF, y todo el código es completamente reutilizable. Para buscar una conexión de Internet en .NET, puede utilizar alguno de los métodos estáticos sobre el objeto NetworkInterface. El objeto está definido en el espacio de nombres System.Net.NetworkInformation. En particular, el método GetIsNetworkAvailable devuelve un valor booleano que denota si hay disponible alguna conexión de red. Desafortunadamente, esto no da demasiada información acerca de la conectividad a Internet. Para asegurarse de que tiene acceso a Internet, el único método seguro es intentar hacer ping a un host (consulte la figura 6).
private bool IsConnectedToInternet()
{
    string host = "...";
    bool result = false;
    Ping p = new Ping();
    try
    {
        PingReply reply = p.Send(host, 3000);
        if (reply.Status == IPStatus.Success)
            return true;
    }
    catch { }

    return result;
}
El único problema en la figura 6 es que el código no es compatible con Silverlight 2. (Y, del mismo modo, no es admitido por el objeto NetworkInterface). Debe aislar este código (y cualquier otro código que detecte con potenciales problemas de compatibilidad) en una clase reemplazable. (Para obtener más información sobre esta restricción, consulte la barra lateral CoreCLR en Silverlight que acompaña a esta columna). En el código fuente complemento, creo una interfaz de la utilidad para estos tipos de métodos potencialmente problemáticos y, a continuación, creo clases de estrategia que implementan la interfaz para cada plataforma, tal y como se muestra en la figura 7.
public partial class SilverCompatLayer : ICompatLib
{
    public bool IsConnectedToInternet()
    {
        string host = "...";
        bool result = false;
        Ping p = new Ping();
        try
        {
            PingReply reply = p.Send(host, 3000);
            if (reply.Status == IPStatus.Success)
                return true;
        }
        catch { }

        return result;
    }

    public string GetRawQuoteInfo(string symbol)
    {
        string output = String.Empty;
        StreamReader reader = null;

        // Set the URL to invoke
        string url = String.Format(UrlBase, symbol);

        // Connect and get response
        WebRequest request = WebRequest.Create(url);
        WebResponse response = request.GetResponse();

        // Read the response
        using (reader = new StreamReader(response.GetResponseStream()))
        {
           output = reader.ReadToEnd();
           reader.Close();
        }

        return output;
    }

    // A few other methods that require a different implementation 
    // (See source code)
    ...
}
La interfaz aquí desacopla la clase estratégica específica de plataforma de la aplicación host. Ocultando el código que crea una instancia de la clase de estrategia concreta detrás de un método de fábrica, el siguiente código puede utilizarse sin riesgos tanto en WPF como Silverlight:
private bool IsConnectedToInternet()
{
    ICompatLib layer = ServiceResolver.ResolveCompatLayer();
    return layer.IsConnectedToInternet();
}
La clase SilverCompatLayer se encuentra en un ensamblado independiente. Este ensamblado es todo lo que tiene que cambiar al migrar su código WPF a Silverlight, o viceversa.
Después que crear las clases de estrategia específicas de plataforma necesarias, todo lo que queda es crear una versión Silverlight de la aplicación. El proyecto Silverlight que se deriva de la aplicación de WPF original contiene copias exactas de todos los archivos excepto el ensamblado de compatibilidad.
Advertirá en la descarga de código asociada con este artículo que he determinado qué estrategia de implementación se debe utilizar creando una instancia de clase concreta explícitamente en un método de fábrica. Puede crear una instancia de clases directamente de este modo o leer sus nombres desde un archivo de configuración y utilizar la reflexión para obtener instancias. Éstos son detalles de implementación que dependen en su mayor parte del tipo de sistema que esté creando. Por lo que concierne a la compatibilidad WPF-Silverlight, las estrategias y la disposición en capas son los conceptos clave que hay que comprender.

Consideraciones finales
En la aplicación de ejemplo, hago una llamada de red. En la aplicación original de WPF, la llamada es sincrónica. En Silverlight 2, sin embargo, no hay compatibilidad para llamadas de red sincrónicas. La única manera de hacer llamadas sincrónicas es utilizando el truco mencionado arriba basado en llamar a la implementación de explorador de XMLHttpRequest. (Consulte el código fuente para obtener más información).
En el código de ejemplo he convertido con éxito mi aplicación original de WPF a la web. Al migrar su código, quizás también desee considerar las características nativas del entorno Silverlight y modificar la estructura de su aplicación cuando corresponda. Tenga en cuenta que en mi sencillo ejemplo, reescribir la aplicación utilizando el modelo de programación Silverlight habría requerido menos código y menos esfuerzo que adaptarlo a una aplicación de WPF.
WPF y Silverlight tienen mucho en común cuando se trata de elementos puramente visuales como XAML. El modelo de programación subyacente, sin embargo, es un poco diferente y no siempre pueden aplicarse soluciones que funcionan en WPF a Silverlight tal cual y viceversa. Dicho esto, es perfectamente posible compartir XAML y código entre aplicaciones de WPF y Silverlight.

Envíe sus preguntas y comentarios para Dino a cutting@microsoft.com.

Dino Esposito es actualmente arquitecto en IDesign y el autor de Programming ASP.NET 3.5 Core References. Con residencia en Italia, Dino participa habitualmente en conferencias y eventos del sector en todo el mundo. Puede participar en su blog en weblogs.asp.net/despos.

Page view tracker