2009-01-04 15 views
29

Estoy intentando crear un control personalizado, un botón, que tendrá varios estilos aplicados dependiendo del valor de una propiedad dentro del contexto de datos.Encuadernación para estilos WPF

lo que estaba pensando es utilizar algo similar a:

<Button Style="{Binding Path=ButtonStyleProperty, Converter={StaticResource styleConverter}}" Text="{Binding Path=TextProp}" /> 

Y en el código ... Implementar un IValueConverter el que hace algo similar al siguiente código en el método ConvertTo:

switch(value as ValueEnums) 
{ 
    case ValueEnums.Enum1: 
     FindResource("Enum1ButtonStyle") as Style; 
    break; 

    ... and so on. 
} 

Sin embargo, no estoy del todo seguro sobre cómo extraer el objeto de estilo e incluso si esto es posible en absoluto ...

Lo que estoy haciendo en t quiere decir que el tiempo es manejar el evento DataContextChanged, luego asociar un controlador al evento PropertyChanged del objeto que se vincula al botón, y luego ejecutar la instrucción switch allí.

No es perfecto, pero hasta que encuentre una solución mejor, parece que es lo que tendré que usar.

Respuesta

35

Si desea reemplazar todo el estilo (y no sólo los elementos del mismo), entonces se le probablemente sea almacenar esos estilos en recursos. Usted debe ser capaz de hacer algo en la línea de:

<Button> 
    <Button.Style> 
     <MultiBinding Converter="{StaticResource StyleConverter}"> 
      <MultiBinding.Bindings> 
       <Binding RelativeSource="{RelativeSource Self}"/> 
       <Binding Path="MyStyleString"/> 
      </MultiBinding.Bindings> 
     </MultiBinding> 
    </Button.Style> 
</Button> 

Mediante el uso de un MultiBinding y utilizando Ser como el primer enlace que a continuación, puede buscar recursos en nuestro convertidor. El convertidor necesita implementar IMultiValueConverter (en lugar de IValueConverter) y puede ser algo como esto:

class StyleConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     FrameworkElement targetElement = values[0] as FrameworkElement; 
     string styleName = values[1] as string; 

     if (styleName == null) 
      return null; 

     Style newStyle = (Style)targetElement.TryFindResource(styleName); 

     if (newStyle == null) 
      newStyle = (Style)targetElement.TryFindResource("MyDefaultStyleName"); 

     return newStyle; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

No es algo que hago muy a menudo, pero que debe trabajar desde la memoria :)

+0

Gracias Steve - esto hizo exactamente lo que yo estaba tratando de hacer :) –

+0

Sin preocupaciones. Como con todo en WPF, probablemente haya otros 10 modos de hacerlo, pero de esta manera parece bastante limpio y "amigable con el diseñador" :) –

+0

¡Gracias! Estaba tratando de encontrar algo así en este sentido para un convertidor StringToStyle y encontré esto que funcionó muy bien. – Rachel

14

Parece que necesita utilizar la clase DataTrigger. Le permite aplicar diferentes estilos a su botón en función de su contenido.

Por ejemplo siguiente estilo va a cambiar la propiedad de fondo del botón a rojo en función del valor de la propiedad del objeto de contexto de datos

<Style x:Key="ButtonStyle" TargetType="{x:Type Button}"> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding Path="Some property"}" 
        Value="some property value"> 
      <Setter Property="Background" Value="Red"/> 
     </DataTrigger> 
    </Style.Triggers> 
</Style> 
+0

Mientras que usted tiene razón de ser capaz de aplicar estilos en base a los datos de el contexto encuadernado. Prefiero simplemente cambiar estilos por completo; de lo contrario, se mezclarán demasiado ... –

8

Para aquellos de nosotros que no puedo usar el convertidor multivalor (te estoy viendo SL4 y WP7 :), gracias a la respuesta de Steven encontré una forma de usar un convertidor de valor ordinario.

La única suposición es que el valor del estilo está contenido en la propiedad del estilo que se está configurando.

Si está utilizando el patrón MVVM, se supone que el valor de estilo (como TextSmall, TextMedium, TextLarge) forma parte del modelo de vista, y todo lo que tiene que hacer es pasar el parámetro del convertidor que define el nombre de estilo.

Por ejemplo, digamos que su modelo de vista tiene la propiedad:

public string ProjectNameStyle 
{ 
    get { return string.Format("ProjectNameStyle{0}", _displaySize.ToString()); } 
} 

estilo Aplicación:

<Application.Resources> 
    <Style x:Key="ProjectNameStyleSmall" TargetType="TextBlock"> 
     <Setter Property="FontSize" Value="40" /> 
    </Style> 
    <Style x:Key="ProjectNameStyleMedium" TargetType="TextBlock"> 
     <Setter Property="FontSize" Value="64" /> 
    </Style> 
    <Style x:Key="ProjectNameStyleLarge" TargetType="TextBlock"> 
     <Setter Property="FontSize" Value="90" /> 
    </Style> 

la vista XAML:

<TextBlock 
     Text="{Binding Name}" 
     Style="{Binding ., Mode=OneWay, Converter={cv:StyleConverter}, ConverterParameter=ProjectNameStyle}"> 

Con su clase StyleConverter implementar IValueConverter:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    if (targetType != typeof(Style)) 
    { 
     throw new InvalidOperationException("The target must be a Style"); 
    } 

    var styleProperty = parameter as string; 
    if (value == null || styleProperty == null) 
    { 
     return null; 
    } 

    string styleValue = value.GetType() 
     .GetProperty(styleProperty) 
     .GetValue(value, null) 
     .ToString(); 
    if (styleValue == null) 
    { 
     return null; 
    } 

    Style newStyle = (Style)Application.Current.TryFindResource(styleValue); 
    return newStyle; 
} 

Tenga en cuenta que este es el código WPF, ya que el convertidor se deriva de MarkupExtension y IValueConverter, pero funcionará en SL4 y WP7 si usa recursos estáticos y agrega un poco más de trabajo de la pierna, ya que el método TryFindResource no funciona t existe.

la esperanza de que ayude a alguien, y gracias de nuevo Steven!

1

modelo de vista

private Style _dynamicStyle = (Style)Application.Current.FindResource("Style1"); 
     public Style DynamicStyle 
     { 
      get { return _dynamicStyle; } 
      set 
      { 
       _dynamicStyle = value; 
       OnPropertyChanged("DynamicStyle"); 
      } 

     } 

public event PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

Implementar una propiedad en su modelo de vista y luego cambiar dinámicamente el estilo que cada vez que desee, como a continuación.

DynamicStyle=(Style)Application.Current.FindResource("Style2");// you can place this code where the action get fired 

Ver

asentando después valor DataContext y luego aplicar el siguiente código en su vista

<Button Style="{Binding DynamicStyle,Mode=TwoWay}"/> 
+0

¿No es esto una violación del patrón mvvm? Dado que el vm sabe sobre un estilo ahora. –

Cuestiones relacionadas