web-dev-qa-db-fra.com

Liaison pour les styles WPF

J'essaie de créer un contrôle personnalisé - un bouton - auquel plusieurs styles seront appliqués en fonction de la valeur d'une propriété dans le contexte de données.

Ce que je pensais, c'est utiliser quelque chose de similaire à:

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

Et dans le code ... Implémentez un IValueConverter qui fait quelque chose de similaire au code ci-dessous dans la méthode ConvertTo:

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

    ... and so on.
} 

Cependant, je ne suis pas tout à fait sûr de savoir comment retirer l'objet de style et même si cela est possible du tout ...

Ce que je fais en attendant, c'est gérer l'événement DataContextChanged, puis attacher un gestionnaire à l'événement PropertyChanged de l'objet lié au bouton - puis exécuter l'instruction switch là-dedans.

Ce n'est pas tout à fait parfait, mais jusqu'à ce que je puisse trouver une meilleure solution, il semble que c'est ce que je vais devoir utiliser.

34
Matthew Savage

Si vous souhaitez remplacer l'ensemble du style (plutôt que seulement des éléments de celui-ci), vous stockerez probablement ces styles dans les ressources. Vous devriez pouvoir faire quelque chose comme:

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

En utilisant une MultiBinding et en utilisant Self comme première liaison, nous pouvons ensuite rechercher des ressources dans notre convertisseur. Le convertisseur doit implémenter IMultiValueConverter (plutôt qu'IValueConverter) et peut ressembler à ceci:

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

Ce n'est pas quelque chose que je fais très souvent, mais cela devrait fonctionner de mémoire :)

38
Steven Robbins

Il semble que vous devez utiliser la classe DataTrigger . Il vous permet d'appliquer différents styles à votre bouton en fonction de son contenu.

Par exemple, le style suivant changera la propriété d'arrière-plan du bouton en rouge en fonction de la valeur de la propriété de l'objet de contexte de données

<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>
15
aku

Pour ceux d'entre nous qui ne peuvent pas utiliser le convertisseur de valeurs multiples (je vous regarde SL4 et WP7 :), grâce à la réponse de Steven, j'ai trouvé un moyen d'utiliser un convertisseur de valeurs ordinaire.

La seule hypothèse est que la valeur de style est contenue dans la propriété du style en cours de définition.

Donc, si vous utilisez le modèle MVVM, la valeur de style (telle que TextSmall, TextMedium, TextLarge) est supposée faire partie du modèle de vue, et tout ce que vous avez à faire est de passer le paramètre du convertisseur définissant le nom du style.

Par exemple, supposons que votre modèle de vue possède une propriété:

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

Style d'application:

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

Vue XAML:

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

Avec votre classe StyleConverter implémentant 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;
}

Notez qu'il s'agit de code WPF, car le convertisseur est dérivé d'un MarkupExtension ainsi que d'IValueConverter, mais il fonctionnera dans SL4 et WP7 si vous utilisez des ressources statiques et ajoutez un peu plus de travail de jambe car la méthode TryFindResource n'existe pas.

J'espère que cela aide quelqu'un, et merci encore Steven!

8
si618

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

Implémentez une propriété dans votre ViewModel, puis changez dynamiquement le style où vous le souhaitez comme ci-dessous.

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

Voir

Ensuite, définissez DataContext valeur, puis implémentez le code suivant dans votre vue

    <Button Style="{Binding DynamicStyle,Mode=TwoWay}"/>
1
Chandru A