2011-03-03 17 views
7

Estoy tratando de establecer el color Foreground en un Hyperlink usando un objeto Style en un ancestro Resources, pero no está teniendo ningún efecto. Incluso utilicé la sugerencia BasedOn de Changing Hyperlink foreground without losing hover color, pero no marcó la diferencia, aún obtengo un hipervínculo azul que está en rojo al pasar el mouse.¿Cómo propagar estilos a hipervínculos dentro de un DataTemplate?

Aquí está el XAML para mis controles, incluyendo un ItemsControl cuyos ítems se muestran utilizando un hipervínculo:

<StackPanel Background="Red" TextElement.Foreground="White"> 
    <StackPanel.Resources> 
    <Style TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}"> 
     <Setter Property="Foreground" Value="Yellow"/> 
     <Style.Triggers> 
     <Trigger Property="IsMouseOver" Value="True"> 
      <Setter Property="Foreground" Value="White"/> 
     </Trigger> 
     </Style.Triggers> 
    </Style> 
    </StackPanel.Resources> 
    <TextBlock>Data import errors</TextBlock> 
    <ItemsControl ItemsSource="{Binding Errors}"/> 
</StackPanel> 

Y los elementos de la ItemsControl están recogiendo la siguiente DataTemplate:

<DataTemplate DataType="{x:Type Importer:ConversionDetailsMessage}"> 
    <TextBlock> 
    <Run Text="{Binding Message, Mode=OneTime}"/> 
    <Hyperlink Command="Common:ImportDataCommands.ExplainConversionMessage" CommandParameter="{Binding}"> 
     <Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/> 
    </Hyperlink> 
    </TextBlock> 
</DataTemplate> 

Es También vale la pena señalar que no quiero simplemente establecer los diferentes colores directamente en el Hyperlink en el DataTemplate. Esto se debe a que la plantilla será utilizada por un número de diferentes objetos ItemsControl, la mayoría de los cuales estarán sobre un fondo blanco y pueden usar los colores estándar de hipervínculo. (Tenga en cuenta que el que está en el XAML de arriba, sin embargo, tiene un fondo rojo.)

En resumen, no quiero que el DataTemplate tenga que saber nada sobre el control en el que se está utilizando. Los estilos para los controles de la plantilla solo deberían filtrarse hacia abajo.

Entonces ... ¿alguien me puede decir por qué el estilo no se está filtrando y qué puedo hacer para solucionarlo?

Gracias.

Actualización:
Como no podía obtener la respuesta de Pavlo a trabajar en mi aplicación de producción, ya que he probado en una aplicación de prueba independiente. La aplicación es una aplicación de WinForms, con el formulario principal que contiene nada más que un ElementHost, que a su vez contiene un simple control de usuario de WPF. Aquí está su XAML:

<UserControl x:Class="DataTemplateStyling.StylingView" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:DataTemplateStyling="clr-namespace:DataTemplateStyling" 
      x:Name="root" Loaded="StylingViewLoaded"> 

    <UserControl.Resources> 
    <Style x:Key="MyDefaultHyperlinkStyle" BasedOn="{StaticResource {x:Type Hyperlink}}"/> 

    <DataTemplate DataType="{x:Type DataTemplateStyling:ImportMessage}"> 
     <DataTemplate.Resources> 
     <Style TargetType="{x:Type Hyperlink}" 
       BasedOn="{StaticResource MyDefaultHyperlinkStyle}"/> 
     </DataTemplate.Resources> 
     <TextBlock> 
     <Run Text="{Binding Message, Mode=OneTime}"/> 
     <Hyperlink NavigateUri="{Binding HelpLink.Item1}"> 
      <Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/> 
     </Hyperlink> 
     </TextBlock> 
    </DataTemplate> 
    </UserControl.Resources> 

    <Grid DataContext="{Binding ElementName=root}"> 
    <StackPanel Background="Red" TextElement.Foreground="White"> 
     <StackPanel.Resources> 
     <Style x:Key="MyDefaultHyperlinkStyle" TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}"> 
      <Setter Property="Foreground" Value="Yellow"/> 
      <Style.Triggers> 
      <Trigger Property="IsMouseOver" Value="True"> 
       <Setter Property="Foreground" Value="White"/> 
      </Trigger> 
      </Style.Triggers> 
     </Style> 
     </StackPanel.Resources> 
     <TextBlock>Data import errors</TextBlock> 
     <ItemsControl ItemsSource="{Binding Messages}"/> 
    </StackPanel> 
    </Grid> 
</UserControl> 

Tal como está arriba, esto genera una InvalidOperationException, indicando "Can única base en un estilo con el tipo de destino que es el tipo de base 'IFrameworkInputElement'."

Esto puede solucionarse poniendo TargetType="Hyperlink" en la definición Style inmediatamente dentro del elemento UserControl.Resources. Sin embargo, mientras que los mensajes están siendo mostradas, la parte de enlace de ellos todavía tiene el estilo hipervínculo azul por defecto:

Blue hyperlinks persist

En pocas palabras, que no está funcionando, lo que daría la bienvenida a cualquier otro sugerencias/correcciones. :(

Actualización 2:
Gracias a una solución alternativa de Pavlo, ahora es trabajo :)

Respuesta

7

Después de algunas google me encontré con este post:. http://www.11011.net/archives/000692.html.

Como se describe allí, resulta que los elementos que no se derivan de Control (como TextBlock y Hyperlink) no buscan estilos implícitos fuera de los límites DataTemplate.

Una vez más, como dice el artículo, la posible solución alternativa es especificar la clave de estilo explícitamente. En su caso, podría ser algo como esto:

<StackPanel Background="Red" TextElement.Foreground="White"> 
    <StackPanel.Resources> 
    <Style x:Key="MyDefaultHyperlinkStyle" TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}"> 
     <Setter Property="Foreground" Value="Yellow"/> 
     <Style.Triggers> 
     <Trigger Property="IsMouseOver" Value="True"> 
      <Setter Property="Foreground" Value="White"/> 
     </Trigger> 
     </Style.Triggers> 
    </Style> 
    </StackPanel.Resources> 
    <TextBlock>Data import errors</TextBlock> 
    <ItemsControl ItemsSource="{Binding Errors}"/> 
</StackPanel> 

A continuación, puede añadir un estilo implícito para Hyperlink que simplemente hace referencia a nuestro estilo nombrado en los DataTemplate recursos:

<DataTemplate DataType="{x:Type Importer:ConversionDetailsMessage}"> 
    <DataTemplate.Resources> 
    <Style TargetType="{x:Type Hyperlink}" 
      BasedOn="{StaticResource MyDefaultHyperlinkStyle}"/> 
    </DataTemplate.Resources> 
    <TextBlock> 
    <Run Text="{Binding Message, Mode=OneTime}"/> 
    <Hyperlink Command="Common:ImportDataCommands.ExplainConversionMessage" CommandParameter="{Binding}"> 
     <Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/> 
    </Hyperlink> 
    </TextBlock> 
</DataTemplate> 

Y debido a que la plantilla de datos se puede usar en diferentes lugares, existe la posibilidad de que el contenedor primario no defina un estilo con la clave "MyDefaultHyperlinkStyle". En este caso, se lanzará una excepción diciendo que no se puede encontrar el recurso "MyDefaultHyperlinkStyle". Para hacer frente a esto, se puede definir un estilo con dicha clave que sólo heredará estilo por defecto en algún lugar de App.xaml:

<Style x:Key="MyDefaultHyperlinkStyle" 
     BasedOn="{StaticResource {x:Type Hyperlink}}/> 

Actualización:

El código que incluyó en su actualización no funcionará porque de la naturaleza de los recursos estáticos, lo que significa que la siguiente referencia de recurso de plantilla de la fecha ...

BasedOn="{StaticResource MyDefaultHyperlinkStyle}" 

... siempre apuntará a la siguiente recurso (que es el estilo por defecto):

<Style x:Key="MyDefaultHyperlinkStyle" BasedOn="{StaticResource {x:Type Hyperlink}}"/> 

Las referencias de recursos estáticos se resuelven en tiempo de compilación, por lo tanto, se utiliza el recurso más cercano en el árbol.

Es posible que tenga la tentación de usar DynamicResource, pero desafortunadamente no es compatible con la propiedad BasedOn.

PERO, la propiedad Foreground admite recursos dinámicos, por lo que podemos utilizar el mismo truco con los pinceles que usamos dentro de nuestro estilo. Aquí está el control de usuario de prueba modificado para utilizar cepillos dinámicos:

<UserControl x:Class="DataTemplateStyling.StylingView" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:DataTemplateStyling="clr-namespace:DataTemplateStyling" 
      x:Name="root" 
      Loaded="StylingViewLoaded"> 

    <UserControl.Resources> 
     <SolidColorBrush x:Key="HyperlinkForeground" 
         Color="Blue" /> 

     <SolidColorBrush x:Key="HyperlinkHoverForeground" 
         Color="Gray" /> 

     <Style x:Key="MyDefaultHyperlinkStyle" 
       TargetType="Hyperlink" 
       BasedOn="{StaticResource {x:Type Hyperlink}}"> 
      <Setter Property="Foreground" 
        Value="{DynamicResource HyperlinkForeground}" /> 
      <Style.Triggers> 
       <Trigger Property="IsMouseOver" 
         Value="True"> 
        <Setter Property="Foreground" 
          Value="{DynamicResource HyperlinkHoverForeground}" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 

     <DataTemplate DataType="{x:Type DataTemplateStyling:ImportMessage}"> 
      <DataTemplate.Resources> 
       <Style TargetType="{x:Type Hyperlink}" 
         BasedOn="{StaticResource MyDefaultHyperlinkStyle}" /> 
      </DataTemplate.Resources> 
      <TextBlock> 
       <Run Text="{Binding Message, Mode=OneTime}" /> 
       <Hyperlink NavigateUri="{Binding HelpLink.Item1}"> 
        <Run Text="{Binding HelpLink.Item2, Mode=OneTime}" /> 
       </Hyperlink> 
      </TextBlock> 
     </DataTemplate> 
    </UserControl.Resources> 

    <Grid DataContext="{Binding ElementName=root}"> 
     <StackPanel Background="Red" 
        TextElement.Foreground="White"> 
      <StackPanel.Resources> 
       <SolidColorBrush x:Key="HyperlinkForeground" 
           Color="Yellow" /> 

       <SolidColorBrush x:Key="HyperlinkHoverForeground" 
           Color="White" /> 
      </StackPanel.Resources> 
      <TextBlock>Data import errors</TextBlock> 
      <ItemsControl ItemsSource="{Binding Messages}" /> 
     </StackPanel> 
    </Grid> 
</UserControl> 

funciona como se espera, es decir, todos los enlaces dentro de StackPanel será de color amarillo/blanco, mientras que fuera de ellos serán de color azul.

Espero que esto ayude.

+0

Awesome habilidades de google - gracias! Y una respuesta muy completa. Voy a probar esto y marcarlo como aceptado una vez que lo tenga funcionando. –

+0

Olvidé que mi aplicación es una aplicación de WinForms que utiliza un 'ElementHost', por lo que no tengo un archivo app.xaml. Aún así, el principio parecía sólido, así que traté de agregar un 'MyDefaultHyperlinkStyle' vacío inmediatamente antes de' DataTemplate' en el diccionario de recursos de 'UserControl' y dejé el' StackPanel' específico que se muestra arriba para reemplazarlo con su propia definición. Lamentablemente, todavía no está funcionando. Seguiré intentando cosas ... –

+0

@Mal - ¿Olvidaste incluir el estilo en la propia plantilla de datos: '