Ámbitos de nombres XAML de WPF

Los ámbitos de nombres XAML son un concepto que identifica objetos que se definen en XAML. Los nombres de un ámbito de nombres XAML se pueden usar para establecer relaciones entre los nombres de objetos definidos por XAML y sus equivalentes de instancia en un árbol de objetos. Normalmente, los ámbitos de nombres XAML del código administrado de WPF se crean al cargar las distintas raíces de página XAML de una aplicación XAML. Los ámbitos de nombres XAML, como el objeto de programación, se definen mediante la interfaz INameScope y también se implementan mediante la clase práctica NameScope.

Ámbitos de nombres en las aplicaciones XAML cargadas

En un contexto de programación o de informática más amplio, los conceptos de programación suelen incluir el principio de un identificador o nombre único que se puede usar para obtener acceso a un objeto. Para los sistemas que usan identificadores o nombres, el ámbito de nombres define los límites en los que buscará un proceso o técnica si se solicita un objeto con ese nombre, o bien los límites en los que se aplica la unicidad de la identificación de nombres. En los ámbitos de nombres XAML se aplican los siguientes principios generales. En WPF, los ámbitos de nombres XAML se crean en el elemento raíz de una página XAML cuando esta se carga. Todos los nombres especificados dentro de la página XAML a partir de la raíz de la página se agregan a un ámbito de nombres XAML pertinente.

En XAML de WPF, los elementos que son elementos raíz comunes (como Page y Window) siempre controlan un ámbito de nombres XAML. Si un elemento como FrameworkElement o FrameworkContentElement es el elemento raíz de la página en el marcado, un procesador de XAML agrega una raíz Page implícitamente para que la Page pueda proporcionar un ámbito de nombres XAML de trabajo.

Nota:

Las acciones de compilación de WPF crean un ámbito de nombres XAML para la producción de XAML, aunque no haya ningún atributo Name o x:Name definido en ningún elemento del marcado de XAML.

Si intenta usar el mismo nombre dos veces en cualquier ámbito de nombres XAML, se producirá una excepción. Para el XAML de WPF que tiene código subyacente y forma parte de una aplicación compilada, la excepción se produce en tiempo de compilación mediante acciones de compilación de WPF, al crear la clase generada para la página durante la compilación inicial del marcado. Para el XAML que no está compilado por el marcado mediante ninguna acción de compilación, las excepciones relacionadas con algún error de ámbito de nombres XAML podrían producirse al cargar el XAML. Los diseñadores de XAML también pueden prever los errores de ámbito de nombres XAML en tiempo de diseño.

Agregar objetos a los árboles de objetos en tiempo de ejecución

El momento en que se analiza el XAML representa el momento en el que se crea y se define un ámbito de nombres XAML de WPF. Si agrega un objeto a un árbol de objetos después de haber analizado el XAML que generó el árbol, los valores Name o x:Name del objeto nuevo no actualizarán automáticamente la información de un ámbito de nombres XAML. Para agregar un nombre de un objeto en un ámbito de nombres XAML de WPF después de cargar el XAML, debe llamar a la implementación adecuada de RegisterName en el objeto que define el ámbito de nombres XAML, que suele ser la raíz de la página XAML. Si el nombre no está registrado, no se puede hacer referencia al objeto agregado por nombre mediante métodos como FindName. Tampoco puede usar ese nombre a efectos de animación.

El escenario más habitual para los desarrolladores de aplicaciones consiste en usar RegisterNamepara registrar los nombres en el ámbito de nombres XAML de la raíz actual de la página. RegisterName forma parte de un escenario importante para guiones gráficos que tienen como destino objetos para las animaciones. Para obtener más información, consulte Información general sobre objetos Storyboard.

Si llama a RegisterName en un objeto que no es el objeto que define el ámbito de nombres XAML, el nombre aún está registrado en el ámbito de nombres XAML en el que se encuentra el objeto de la llamada, como si hubiera llamado a RegisterName en el ámbito de nombres XAML que define el objeto.

Ámbitos de nombres XAML en el código

Puede crear y usar ámbitos de nombres XAML en el código. Las API y los conceptos implicados en la creación de ámbitos de nombres XAML son los mismos, incluso para un uso de código puro, ya que el procesador XAML de WPF usa estas API y estos conceptos cuando procesa el XAML. Los conceptos y las API existen principalmente para poder buscar objetos por nombre dentro de un árbol de objetos que se suele definir parcial o completamente en XAML.

Para las aplicaciones que se crean mediante programación, y no desde el XAML cargado, el objeto que define un ámbito de nombres XAML debe implementar INameScope, o bien debe ser una clase derivada FrameworkElement o FrameworkContentElement, para poder admitir la creación de un ámbito de nombres XAML en sus instancias.

Además, para cualquier elemento que no se haya cargado y procesado con un procesador XAML, el ámbito de nombres XAML del objeto no se crea ni se inicializa de forma predeterminada. Debe crear explícitamente un ámbito de nombres XAML para cualquier objeto en el que después vaya a registrar nombres. Para crear un ámbito de nombres XAML, debe llamar al método estático SetNameScope. Especifique el objeto que lo contendrá como parámetro dependencyObject y una nueva llamada al constructor NameScope como parámetro value.

Si el objeto proporcionado como dependencyObject para SetNameScope no es una implementación INameScope, FrameworkElement o FrameworkContentElement, las llamadas a RegisterName en cualquier elemento secundario no tendrán ningún efecto. Si no puede crear explícitamente el nuevo ámbito de nombres XAML, las llamadas a RegisterName producirán una excepción.

Para ver un ejemplo de cómo usar las API de ámbito de nombres XAML en el código, consulte Definir un ámbito de nombres.

Ámbitos de nombres XAML en estilos y plantillas

Los estilos y las plantillas de WPF ofrecen la posibilidad de reutilizar y volver a aplicar el contenido de una manera sencilla. aunque también pueden incluir elementos con nombres XAML definidos en el nivel de la plantilla. Esa misma plantilla se puede usar varias veces en una página. Por este motivo, tanto los estilos como las plantillas definen sus propios ámbitos de nombres XAML, independientemente de la ubicación en un árbol de objetos en la que se aplique el estilo o plantilla.

Considere el ejemplo siguiente:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <Page.Resources>
    <ControlTemplate x:Key="MyButtonTemplate" TargetType="{x:Type Button}">
      <Border BorderBrush="Red" Name="TheBorder" BorderThickness="2">
        <ContentPresenter/>
      </Border>      
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <Button Template="{StaticResource MyButtonTemplate}">My first button</Button>
    <Button Template="{StaticResource MyButtonTemplate}">My second button</Button>
  </StackPanel>
</Page>

En este caso, se aplica la misma plantilla a dos botones diferentes. Si las plantillas no tuvieran ámbitos de nombres XAML discretos, el nombre TheBorder usado en la plantilla provocaría un conflicto de nombres en el ámbito de nombres XAML. 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.

Los estilos también definen su propio ámbito de nombres XAML, sobre todo para que las partes de los guiones gráficos puedan tener asignados nombres concretos. Estos nombres permiten que haya comportamientos específicos de controles destinados a elementos con ese nombre, incluso si la plantilla se ha vuelto a definir como parte de la personalización de controles.

Debido a los ámbitos de nombres XAML independientes, resulta más difícil buscar elementos con nombre en una plantilla que buscar un elemento con nombre y sin plantilla en una página. En primer lugar debe determinar la plantilla aplicada obteniendo el valor de la propiedad Template del control donde se aplica la plantilla. Luego, debe llamar a la versión de la plantilla de FindName pasando el control donde se ha aplicado la plantilla como segundo parámetro.

Si es el autor de un control y va a generar una convención en la que un determinado elemento con nombre de 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 este.

Si está trabajando desde dentro de una plantilla y necesita obtener el ámbito de nombres XAML donde se aplica la plantilla, obtenga el valor de TemplatedParent y, luego, llame a FindName desde ahí. Un ejemplo de trabajo dentro de la plantilla sería escribir la implementación del controlador de eventos donde se generará el evento desde un elemento de una plantilla aplicada.

FrameworkElement tiene los métodos FindName, RegisterName y UnregisterName. Si el objeto en el que llama a estos métodos tiene un ámbito de nombres XAML, los métodos llaman a los métodos del ámbito de nombres XAML relevante. De lo contrario, se comprueba el elemento primario para ver si tiene un ámbito de nombres XAML. Este proceso continúa de forma recursiva hasta que se encuentra un ámbito de nombres XAML (debido al comportamiento del procesador XAML, se garantiza que haya un ámbito de nombres XAML en la raíz). FrameworkContentElement tiene comportamientos análogos, con la excepción de que ningún FrameworkContentElement tendrá jamás ningún ámbito de nombres XAML. Los métodos existen en FrameworkContentElement para que las llamadas se puedan reenviar a un elemento primario FrameworkElement.

SetNameScope se usa para asignar un nuevo ámbito de nombres XAML a un objeto existente. Puede llamar a SetNameScope más de una vez con el fin de restablecer o borrar el ámbito de nombres XAML, aunque no es un uso habitual. Además, GetNameScope no se suele usar desde el código.

Implementaciones de ámbito de nombres XAML

Las clases siguientes implementan INameScope directamente:

ResourceDictionary no usa nombres o ámbitos de nombres XAML, sino que usa claves, porque se trata de una implementación de diccionario. La única razón por la que ResourceDictionary implementa INameScope es que puede generar excepciones en el código de usuario que pueden ayudar a aclarar la distinción entre un auténtico ámbito de nombres XAML, cómo administra un ResourceDictionary las claves y a garantizar que los elementos primarios no apliquen los ámbitos de nombres XAML a un ResourceDictionary.

FrameworkTemplate y Style implementan INameScope mediante definiciones de interfaz explícitas. Las implementaciones explícitas permiten que estos ámbitos de nombres XAML se comporten de manera convencional cuando se obtiene acceso a ellos a través de la interfaz INameScope, que es cómo los procesos internos de WPF comunican los ámbitos de nombres XAML. Pero las definiciones de interfaz explícitas no forman parte de la superficie de API convencional de FrameworkTemplate y Style, dado que rara vez necesitará llamar directamente a los métodos INameScope en FrameworkTemplate y Style. En su lugar, usaría otra API, como GetTemplateChild.

Las siguientes clases definen su propio ámbito de nombres XAML usando la clase del asistente System.Windows.NameScope y conectándose a su implementación de ámbito de nombres XAML mediante la propiedad adjunta NameScope.NameScope:

Vea también