Ámbitos de nombres en 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.

Note

C# y C++/WinRT son compatibles con las aplicaciones winUI 3/SDK de Aplicaciones para Windows y UWP. C++/CX solo se aplica a UWP y no se admite para WinUI 3.

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 habitual de un nombre en un namescope de XAML es como referencia directa a una instancia de un objeto, que se habilita mediante la fase de compilación del marcado como acción de compilación del proyecto, junto con un método InitializeComponent generado en las plantillas de clase parcial.

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 pasa por una pasada del compilador de marcado al mismo tiempo que el XAML y la clase parcial que define para el code-behind se compilan conjuntamente. 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 usa reflexión sobre ellos o examina de otro modo su contenido en lenguaje intermedio.

Note

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 el código subyacente en C++/CX, use otras API o recorra el á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 espacio 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 sobre el que se llama a FindName determina el ámbito, que es el ámbito de nombres XAML en el que se encuentra el objeto invocador. 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 se encuentra en la clase parcial correspondiente a la "página" raíz y, por tanto, el ámbito de nombres XAML es el ámbito de nombres XAML de la 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 en un objeto obtenido fuera del ámbito de nombres XAML discreto, el método no encontrará objetos con nombre definido 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:

  • Recorra todo el árbol en pasos discretos con Parent y/o propiedades de colección que se sabe que existen en su estructura de árbol de objetos (como 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 del contenido) de la ventana actual de la aplicación con una sola línea de código mediante la llamada Window.Current.Content. A continuación, puede convertirlo en FrameworkElement y llamar a FindName en 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 prescindir por completo de los nombres y de las consideraciones sobre el ámbito de nombres de XAML 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 puede usarse 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. Cada instancia de la plantilla tiene su propio ámbito de nombres XAML, por lo que en este ejemplo, cada ámbito de nombres XAML de la plantilla con instancias contendrá exactamente un nombre. Sin embargo, el ámbito de nombres XAML principal no contiene el nombre de cualquiera de las dos plantillas.

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 los componentes importantes sean identificables para los usuarios del control que deseen aplicar una plantilla diferente, la cual tendría que reemplazar los componentes con nombre para mantener la funcionalidad del control.