views:

2603

answers:

2

I'm trying to create a custom control - a button - which will have multiple styles applied to it depending on the value of a property within the data context.

What I was thinking is using something similar to:

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

And in code... Implement an IValueConverter which does something similar to the code below in the ConvertTo method:

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

    ... and so on.
}

However I'm not entirely sure about how to pull out the style object and even if this is possible at all...

What I am doing in the mean time is handling the DataContextChanged event, then attaching a handler to the PropertyChanged event of the object being bound to the button - then running the switch statement in there.

Its not quite perfect but until I can find a better solution it seems like that is what I'll have to use.

+4  A: 

It seems that you need to use DataTrigger class. It allows you to apply different styles to your button based on it's content.

For example following style will change button's background property to red based on value of data context object's property

<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>
aku
While you are right with being able to apply styles based on the data of the bound context I'd much prefer just to switch styles entirely - otherwise they become too intermingled...
Matthew Savage
+3  A: 

If you want to replace the whole style (rather than just elements of it) then you'll probably be storing those styles in resources. You should be able to do something along the lines of:

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

By using a MultiBinding and using Self as the first binding we can then lookup resources in our converter. The converter needs to implement IMultiValueConverter (rather than IValueConverter) and can look something like this:

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

It's not something I do very often, but that should work from memory :)

Steven Robbins
Thanks Steve - this did exactly what I was trying to do :)
Matthew Savage
No worries. As with everything in WPF, there's probably another 10ways to do it, but this way seems pretty clean and "designer friendly" :)
Steven Robbins