.net wpf xaml

.net - ¿Cómo se interpreta y ejecuta XAML en tiempo de ejecución?



wpf (1)

¿Qué sucede internamente entre la carga de XAML (o BAML) y la recepción del objeto raíz (Ventana, por ejemplo)?

Lo primero que surge en mi mente es que Reflecton se utiliza para crear objetos, establecer sus propiedades, etc. Pero tal vez estoy equivocado?

¿Tal vez alguien pueda explicar cómo se analiza y ejecuta XAML / BAML en tiempo de ejecución o puede proporcionar un enlace a un buen artículo con la explicación?

Para que mi pregunta sea un poco más clara, analicemos el breve ejemplo:

<Button Margin="10">OK</Button>

Por lo tanto, el analizador ve que se debe crear un objeto Button, que su propiedad Margin se debe establecer en 10 y su contenido se debe configurar en "OK". ¿Cómo se hace eso? ¿Usando Reflection (más TypeConverters, etc.)?


Puede observar en sus archivos .xaml.cs que las clases que respaldan sus archivos XAML compilados están marcadas como clases partial . Una tarea de compilación XAML genera un segundo archivo .cs con otra sección de clase parcial que contiene una implementación del método IComponentConnector.InitializeComponent() , que el constructor predeterminado llama en el código que está detrás. Básicamente, este método se ejecuta a través del XAML (que en realidad está en forma BAML en este punto) y lo utiliza para "arreglar" el objeto recién creado, en lugar de crear un nuevo objeto desde la fuente XAML, que es lo que sucedería si usted debían utilizar un XamlReader para cargar o analizar un objeto.

Entonces, cuando crea una instancia de un nuevo objeto XAML compilado (por ejemplo, un UserControl ), se ejecutará cualquier código que preceda a la llamada a InitializeComponent() en el constructor. Luego, todas las propiedades y los controladores de eventos establecidos en el archivo XAML se procesarán durante la llamada a InitializeComponent() , después de lo cual se reanudará el constructor. Puede ser útil saber esto, ya que es posible que desee asegurarse de que ciertas propiedades se configuren antes o después de que se procese el archivo XAML.

En cuanto a cómo se analiza el XAML, se lee esencialmente como un flujo de nodos XAML que representan asignaciones de propiedades, declaraciones de objetos, etc., que se realizan en orden por los servicios en System.Xaml . Este flujo de nodo se basa en un modelo de objetos común, que puede haber sido construido a partir de un flujo BAML, un documento XML (por ejemplo, un archivo .xaml suelto), otra instancia de objeto, etc. BAML, siendo más compacto que el basado en XML El formato, generalmente es más rápido de analizar.

Addendum: en el ejemplo que agregó, usted pregunta cómo el analizador ve que se debe crear un objeto Button y el conjunto de Margin . La respuesta corta es: depende. Específicamente, depende del contexto del esquema utilizado para leer el flujo XAML.

El analizador XAML utiliza su propio sistema de tipos, de los cuales hay al menos dos implementaciones:

  1. Un sistema de tipo CLR estándar basado en la reflexión y System.ComponentModel ;
  2. Un sistema de tipo WPF que se extiende # 1 al incluir soporte especial para propiedades de dependencia y eventos enrutados.

Esto es, basado en mi recuerdo de la especificación del lenguaje XAML, aproximadamente lo que sucede:

  1. El analizador XAML encuentra un nodo StartObject para el tipo de Button , que (para las asignaciones de espacio de nombres estándar de WPF) se resuelve en System.Windows.Controls.Button . Esto le indica al analizador que necesita crear una instancia de un objeto Button , lo que hace invocando a su constructor predeterminado a través de la reflexión.
  2. El siguiente nodo en la StartMember es un nodo StartMember , con un nombre de miembro de Margin . El modelo de tipo para el contexto del esquema de WPF resolverá esto en la propiedad de dependencia Margin .
  3. Un nodo de Value viene a continuación, que le dice al analizador que establezca un valor de "10" (una cadena). El analizador ve que el tipo de propiedad Thickness es incompatible con el valor de cadena. Consulta su sistema de tipos para ver si existe un atributo [ValueSerializer] en la propiedad Margin , que podría usarse para convertir la cadena; No existe tal atributo. Comprueba la propiedad de un atributo [TypeConverter] ; de nuevo, no encuentra ninguno. Busca un atributo [TypeConverter] en el propio tipo de Thickness y encuentra uno, que le indica que use un ThicknessConverter para convertir el valor de la cadena en un Thickness . Lo hace Como Margin es una propiedad de dependencia, usa la API SetValue() para establecer el valor de la propiedad; si fuera una propiedad CLR, usaría la reflexión o un PropertyDescriptor .
  4. Un nodo EndMember le dice al analizador que termine la asignación de propiedad.
  5. El analizador encuentra un nodo de Value con el contenido "OK" . El analizador sabe que está construyendo un objeto complejo, por lo que el contenido no puede representar todo el objeto. Busca un atributo [ContentProperty] en Button y sus supertipos; encuentra uno en ContentControl , que indica que el valor debe usarse para establecer la propiedad de Content (que se resuelve en la propiedad de dependencia correspondiente). Content es un object , por lo que asigna el valor de la string directamente (nuevamente, usando SetValue() ).
  6. El siguiente nodo es EndObject , que le dice al analizador que ha terminado de procesar el objeto Button .

Tenga en cuenta que utilicé el término "analizador" para simplificar las cosas. Sinceramente, nada de esto sucede en la etapa de análisis (si existe una etapa de "análisis"). Lo que puede pensar como la etapa de "análisis" es simplemente la construcción de un flujo de nodos XAML. La creación y / o la población de los objetos declarados realmente sucede al alimentar esa secuencia en un XamlObjectWriter , que es simplemente la implementación de XamlWriter que escribe los nodos XAML en un objeto (a diferencia de un documento XML o una secuencia BAML). En el nivel alto, solo están sucediendo dos cosas:

  1. XamlReader transforma algo en un flujo de nodos XAML.
  2. XamlWriter transforma una secuencia de nodos XAML en algo.

En el caso de un recurso XAML cumplido, una tarea de compilación en tiempo de compilación canaliza la salida de un XamlXmlReader en un BamlWriter para "compilar" el XAML. En tiempo de ejecución, la entrada de un BamlReader se canaliza a un XamlObjectWriter para crear o "arreglar" el objeto raíz.

Una vez que comprenda todo esto, puede comenzar a reconocer XAML como un poderoso formato de serialización y persistencia, en lugar de ser simplemente un lenguaje para crear interfaces de usuario.