Ámbitos de nombres XAML
Un ámbito de nombres XAML almacena relaciones entre los nombres definidos por XAML de objetos y sus equivalentes de instancia. Este concepto es similar al significado más amplio del término ámbito de nombres en otros lenguajes de programación y tecnologías.
Cómo se definen los ámbitos de nombres XAML
Los nombres en ámbitos de nombres XAML permiten al código de usuario hacer referencia a los objetos que se declararon inicialmente en XAML. El resultado interno del análisis de XAML es que el tiempo de ejecución crea un conjunto de objetos que conservan algunas o todas las relaciones que estos objetos tenían en las declaraciones XAML. Estas relaciones se mantienen como propiedades de objeto específicas de los objetos creados o se exponen a métodos de utilidad en las API del modelo de programación.
El uso más típico de un nombre en un ámbito de nombres XAML es como referencia directa a una instancia de objeto, que está habilitada por el paso de compilación de marcado como una acción de compilación de proyecto, combinada con un método InitializeComponent generado en las plantillas de clase parciales.
También puedes usar el método de utilidad FindName en tiempo de ejecución para devolver una referencia a objetos definidos con un nombre en el marcado XAML.
Más información sobre las acciones de compilación y XAML
Lo que sucede técnicamente es que el propio XAML se somete a un paso del compilador de marcado al mismo tiempo que el XAML y la clase parcial que define para el código subyacente se compilan juntos. Cada elemento de objeto con un atributo Name o x:Name definido en el marcado genera un campo interno con un nombre que coincide con el nombre XAML. Este campo está inicialmente vacío. A continuación, la clase genera un método InitializeComponent al que se llama solo después de cargar todo el CÓDIGO XAML. Dentro de la lógica InitializeComponent, cada campo interno se rellena con el valor devuelto FindName para la cadena de nombre equivalente. Puedes observar esta infraestructura por ti mismo examinando los archivos ".g" (generados) que se crean para cada página XAML en la subcarpeta /obj de un proyecto de aplicación de Windows Runtime después de la compilación. También puede ver los campos y el método InitializeComponent como miembros de los ensamblados resultantes si los refleja o examina su contenido del lenguaje de interfaz.
Nota Específicamente para las aplicaciones de extensiones de componentes de Visual C++ (C++/CX), no se crea un campo de respaldo para una referencia x:Name para el elemento raíz de un archivo XAML. Si necesita hacer referencia al objeto raíz desde código subyacente de C++/CX, use otras API o recorrido de árbol. Por ejemplo, puede llamar a FindName para un elemento secundario con nombre conocido y, a continuación, llamar a Parent.
Creación de objetos en tiempo de ejecución con XamlReader.Load
XAML también se puede usar como entrada de cadena para el método XamlReader.Load , que actúa de forma análoga a la operación de análisis de origen XAML inicial. XamlReader.Load crea un nuevo árbol desconectado de objetos en tiempo de ejecución. A continuación, el árbol desconectado se puede adjuntar a algún punto en el árbol de objetos principal. Debe conectar explícitamente el árbol de objetos creado, ya sea agregándolo a una colección de propiedades de contenido como Children, o estableciendo alguna otra propiedad que tome un valor de objeto (por ejemplo, cargando un nuevo ImageBrush para un valor de propiedad Fill).
Implicaciones del ámbito de nombres XAML de XamlReader.Load
El ámbito de nombres XAML preliminar definido por el nuevo árbol de objetos creado por XamlReader.Load evalúa los nombres definidos en el XAML proporcionado para la unicidad. Si los nombres del XAML proporcionado no son únicos internamente en este momento, XamlReader.Load produce una excepción. El árbol de objetos desconectado no intenta combinar su ámbito de nombres XAML con el ámbito de nombres XAML de la aplicación principal, si está conectado al árbol de objetos de aplicación principal. Después de conectar los árboles, la aplicación tiene un árbol de objetos unificado, pero ese árbol tiene ámbitos de nombres XAML discretos dentro de él. Las divisiones se producen en los puntos de conexión entre objetos, donde se establece alguna propiedad para que sea el valor devuelto desde una llamada XamlReader.Load .
La complicación de tener ámbitos de nombres XAML discretos y desconectados es que las llamadas al método FindName , así como las referencias directas de objetos administrados ya no funcionan con un ámbito de nombres XAML unificado. En su lugar, el objeto concreto en el que se llama a FindName implica el ámbito, con el ámbito que es el ámbito de nombres XAML dentro del que se encuentra el objeto que realiza la llamada. En el caso de referencia de objeto administrado directo, el ámbito está implícito en la clase donde existe el código. Normalmente, el código subyacente para la interacción en tiempo de ejecución de una "página" del contenido de la aplicación existe en la clase parcial que respalda la raíz "page" y, por tanto, el ámbito de nombres XAML es el ámbito de nombres XAML raíz.
Si llamas a FindName para obtener un objeto con nombre en el ámbito de nombres XAML raíz, el método no encontrará los objetos de un ámbito de nombres XAML discreto creado por XamlReader.Load. Por el contrario, si llamas a FindName desde un objeto obtenido del ámbito de nombres XAML discreto, el método no encontrará objetos con nombre en el ámbito de nombres XAML raíz.
Este problema discreto del ámbito de nombres XAML solo afecta a la búsqueda de objetos por nombre en ámbitos de nombres XAML cuando se usa la llamada FindName .
Para obtener referencias a objetos definidos en un ámbito de nombres XAML diferente, puedes usar varias técnicas:
- Recorre todo el árbol en pasos discretos con las propiedades primarias o de colección que se sabe que existen en la estructura del árbol de objetos (por ejemplo, la colección devuelta por Panel.Children).
- Si llamas desde un ámbito de nombres XAML discreto y quieres el ámbito de nombres XAML raíz, siempre es fácil obtener una referencia a la ventana principal que se muestra actualmente. Puedes obtener la raíz visual (el elemento XAML raíz, también conocido como origen de contenido) desde la ventana de aplicación actual en una línea de código con la llamada
Window.Current.Content
a . A continuación, puede convertir a FrameworkElement y llamar a FindName desde este ámbito. - Si llamas desde el ámbito de nombres XAML raíz y quieres un objeto dentro de un ámbito de nombres XAML discreto, lo mejor que debes hacer es planear con antelación el código y conservar una referencia al objeto devuelto por XamlReader.Load y, a continuación, agregarlo al árbol de objetos principal. Este objeto es ahora un objeto válido para llamar a FindName dentro del ámbito de nombres XAML discreto. Puede mantener este objeto disponible como una variable global o pasarlo de otro modo mediante parámetros de método.
- Puedes evitar los nombres y las consideraciones sobre el ámbito de nombres XAML completamente examinando el árbol visual. La API visualTreeHelper permite recorrer el árbol visual en términos de objetos primarios y colecciones secundarias, basadas exclusivamente en la posición y el índice.
Ámbitos de nombres XAML en plantillas
Las plantillas en XAML proporcionan la capacidad de reutilizar y volver a aplicar contenido de forma sencilla, pero las plantillas también pueden incluir elementos con nombres definidos en el nivel de plantilla. Esa misma plantilla se puede usar varias veces en una página. Por este motivo, las plantillas definen sus propios ámbitos de nombres XAML, independientemente de la página contenedora en la que se aplica el estilo o la plantilla. Considere este ejemplo:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Page.Resources>
<ControlTemplate x:Key="MyTemplate">
....
<TextBlock x:Name="MyTextBlock" />
</ControlTemplate>
</Page.Resources>
<StackPanel>
<SomeControl Template="{StaticResource MyTemplate}" />
<SomeControl Template="{StaticResource MyTemplate}" />
</StackPanel>
</Page>
Aquí, la misma plantilla se aplica a dos controles diferentes. Si las plantillas no tenían ámbitos de nombres XAML discretos, el nombre "MyTextBlock" usado en la plantilla provocaría una colisión de nombres. Todas las instancias de la plantilla tienen su propio ámbito de nombres XAML, por lo que, en este ejemplo, el ámbito de nombres XAML de todas las plantillas de instancia contendrían exactamente un nombre. Sin embargo, el ámbito de nombres XAML raíz no contiene el nombre de ninguna plantilla.
Debido a los ámbitos de nombres XAML independientes, la búsqueda de elementos con nombre dentro de una plantilla desde el ámbito de la página donde se aplica la plantilla requiere una técnica diferente. En lugar de llamar a FindName en algún objeto del árbol de objetos, primero se obtiene el objeto que tiene aplicada la plantilla y, a continuación, se llama a GetTemplateChild. Si es un autor de control y está generando una convención en la que un elemento con nombre determinado en una plantilla aplicada es el destino de un comportamiento definido por el propio control, puede usar el método GetTemplateChild desde el código de implementación del control. El método GetTemplateChild está protegido, por lo que solo el autor del control tiene acceso a él. Además, existen convenciones que los autores de controles deben seguir para asignar nombres a elementos y elementos de plantilla y notificarlos como valores de atributo aplicados a la clase de control. Esta técnica hace que los nombres de las partes importantes sean reconocibles para controlar a los usuarios que podrían querer aplicar una plantilla diferente, lo que tendría que reemplazar las partes con nombre para mantener la funcionalidad de control.