Encuadernación para estilos WPF


Estoy tratando de crear un control personalizado - un botón - que tendrá varios estilos aplicados en función del valor de una propiedad dentro del contexto de datos.

Lo que estaba pensando es usar algo similar a:

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

Y en código... Implementar un IValueConverter que haga algo similar al código de abajo en el método ConvertTo:

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

    ... and so on.
} 

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

Lo que estoy haciendo mientras tanto es manejar el evento DataContextChanged, luego adjuntar un controlador al evento PropertyChanged del objeto que está vinculado al botón, luego ejecutar la instrucción switch allí.

No es del todo perfecto, pero hasta que pueda encontrar una mejor solución, parece que eso es lo que tendré que usar.

Author: Matthew Savage, 2009-01-04

4 answers

Si desea reemplazar todo el estilo (en lugar de solo elementos de él), probablemente almacenará esos estilos en recursos. Usted debe ser capaz de hacer algo a lo largo de las líneas de:

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

Usando un MultiBinding y usando Self como primer binding podemos entonces buscar recursos en nuestro convertidor. El convertidor necesita implementar IMultiValueConverter (en lugar de IValueConverter) y puede verse 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 eso debería funcionar de memoria:)

 37
Author: Steven Robbins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-04 08:45:18

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

Por ejemplo, el siguiente estilo 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>
 14
Author: aku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-04 07:22:43

Para aquellos de nosotros que no podemos usar multi value converter (te estoy mirando SL4 y WP7:), gracias a la respuesta de Steven encontré una manera de usar un convertidor de valor ordinario.

La única suposición es que el valor de estilo está contenido dentro de la propiedad del estilo que se está estableciendo.

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

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

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

Estilo de 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>

Vista XAML:

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

Con su clase StyleConverter implementando 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 código WPF, ya que el convertidor se deriva de un MarkupExtension así como de IValueConverter, pero funcionará en SL4 y WP7 si usa static resource y agrega un poco más de trabajo de pierna ya que el método TryFindResource no existe.

Espero que ayuda a alguien, y gracias de nuevo Steven!

 8
Author: si618,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 08:19:07

ViewModel

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));
        }
    }

Implemente una propiedad en su ViewModel y luego cambie dinámicamente el estilo donde quiera, como se muestra a continuación.

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

Ver

Luego establezca el valor DataContext y luego implemente el siguiente código en su vista

    <Button Style="{Binding DynamicStyle,Mode=TwoWay}"/>
 1
Author: Chandru A,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-13 13:24:34