2011-06-15 25 views
5

Situación:x: Nombre no funciona si el elemento envuelto en el contenido del control de usuario (Silverlight)

Tengo un "panel de envoltura" UserControl como esto (espacios de nombres y detalles visuales eliminado por razones de brevedad):

<UserControl ...> 
<Grid x:Name="LayoutRoot" Background="White"> 
    <ContentPresenter x:Name="integratedPanelContent" Margin="5" /> 
</Grid> 
</UserControl> 

Luego, en el código subyacente he registrado una propiedad de dependencia

public FrameworkElement PanelContent 
{ 
    get { return (FrameworkElement)GetValue(PanelContentProperty); } 
    set { SetValue(PanelContentProperty, value); } 
} 
public static readonly DependencyProperty PanelContentProperty = 
    DependencyProperty.Register("PanelContent", typeof(FrameworkElement), typeof(MyWrapperPanel), 
     new PropertyMetadata(null, OnPanelContentChanged)); 

private static void OnPanelContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    ((MyWrapperPanel)d).OnSetContentChanged(e); 
} 

protected virtual void OnSetContentChanged(DependencyPropertyChangedEventArgs e) 
{ 
    if (PanelContent != null) 
     integratedPanelContent.Content = PanelContent; 
} 

Ahora puedo envolver cualquier contenido en mi control:

<my:MyWrapperPanel x:Name="myWrap"> 
    <my:MyWrapperPanel.PanelContent> 
     <TextBlock x:Name="tbxNothing" Text="Nothing" /> 
    </my:MyWrapperPanel.PanelContent> 
</my:MyWrapperPanel> 

Descripción del problema:

Siempre que intente utilizar el tbxNothing referencia en código subyacente, el sistema lanza NullReferenceException porque tbxNothing, a pesar de que exista una referencia, no apunta a la TextBlock definido en XAML, pero es null.

posible (pero inconveniente) Solución:

hay una solución donde elimino x:Name del TextBlock, y luego me defino explícitamente TextBlock privada llamada tbxNothing. Luego, en el manejador de eventos OnNavigatedTo asigno el valor de la siguiente manera:

tbxNothing = myWrap.PanelContent as TextBlock; 

Esto funciona, pero no es una forma correcta de hacerlo, porque si un contenido es un StackPanel que contiene controles quería, tendría que Atraviesa el árbol para encontrar lo que necesito, lo cual es extremadamente inconveniente.

Pregunta:

¿Por qué el bloque de texto ya no es visible cuando se envolvió en un control de usuario (la manera descrita), y cómo obtenerla por su x: Nombre de código subyacente?

Respuesta

6

El problema es que el contenido de su panel está cayendo entre dos taburetes. Por un lado, el contenido con el nombre "tbxNothing" se crea en el namescope de la página principal. Sin embargo, no se agrega al árbol de objetos en ese punto. Por otro lado, el MyWrapperPanel que es un UserControl tiene su propio namescope y está en el árbol de objetos debajo de este que el elemento con el nombre "tbxNothing" se agrega. FindName en la página principal no encontrará nada dentro de MyWrapperPanel porque tiene su propio namescope y FindName en MyWrapperPanel no encontrará "tbxNothing" porque no existe en su namescope (se está realmente creado en la página principal).

La respuesta es no usar un UserControl como base para MyWrapperPanel. En su lugar, crea un Control de plantilla de Silverlight. Modifique la clase base de la que hereda a ContentControl y modifique su plantilla predeterminada para incluir un ContentPresenter. Debería verse algo como esto: -

public class MyWrapperPanel : ContentControl 
{ 
    public MyWrapperPanel() 
    { 
     this.DefaultStyleKey = typeof(MyWrapperPanel); 
    } 
} 

luego en temas/genéricos.XAML el estilo puede tener este aspecto: -

<Style TargetType="local:MyWrapperPanel"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="local:MyWrapperPanel"> 
       <Grid> 
        <ContentPresenter /> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Su xaml página principal se vería así: -

<my:MyWrapperPanel x:Name="myWrap"> 
    <TextBlock x:Name="tbxNothing" Text="Nothing" /> 
</my:MyWrapperPanel> 

Tenga en cuenta que se deriva de ContentControl le da una propiedad Content cual los ContentPresenter cables de auto-mágicamente a .

+0

Definitivamente voy a intentar esto, pero no estoy seguro de una cosa. Algunos elementos del MyWrapperPanel son interactivos, por lo que anteriormente he puesto algunos controladores de eventos en el código subyacente de UserControl. Si defino los elementos a través de la plantilla, ¿cómo puedo llegar a elementos específicos de la plantilla para realizar una lógica personalizada? ¿Sería posible especificar x: Name en los elementos de la plantilla en el código del control a través de FindName() o existe una forma mejor de lograr una lógica personalizada en los elementos definidos de la plantilla? –

+0

@kornelijepetak: Sí, puede usar 'x: Name' en la plantilla; sin embargo, desde el código dentro del control personalizado de las plantillas se accede utilizando el método protegido' GetTemplateChild'. 'FindName' ahora funcionará en el namescope del' UserControl' que contiene por qué este enfoque soluciona su problema inicial. – AnthonyWJones

+0

, pero ¿necesito usar FindName() en la página principal o puedo acceder al Bloque de texto utilizando la referencia tbxNothing? –

Cuestiones relacionadas