Problem setting Foreground with Visual State Manager
Asked Answered
P

1

9

I have a WPF application, and I am attempting to style a TextBox using the .Net v4 Visual State Manager. Specifically, I am attempting to set the colors of the Foreground and Background for the MouseOver state.

What is happening is that, while the background and border are changing perfectly, the foreground is not. If the brushes that I am using get their color via a StaticResource, then the foreground does not change at all. If the brushes that I am using get their color via a DynamicResource, then when I mouse over a TextBox, the foreground of all TextBoxes change. Clearly, either I'm doing something wrong, or what I want to do is simply not possible with VSM (which would be rather disappointing).

Here are the resources that I am using:

<Color x:Key="ControlBackgroundColor" R="178" G="178" B="178" A="255" />
<Color x:Key="ControlForegroundColor" R="0" G="0" B="0" A="255" />
<Color x:Key="BorderColor" R="127" G="127" B="127" A="255" />
<Color x:Key="MouseOverControlBackgroundColor" R="0" G="0" B="0" A="255" />
<Color x:Key="MouseOverControlForegroundColor" R="255" G="255" B="255" A="255" />
<Color x:Key="MouseOverBorderColor" R="178" G="178" B="178" A="255" />

<SolidColorBrush PresentationOptions:Freeze="True" x:Key="ControlBackgroundBrush" Color="{DynamicResource ControlBackgroundColor}" />
<SolidColorBrush PresentationOptions:Freeze="True" x:Key="ControlForegroundBrush" Color="{DynamicResource ControlForegroundColor}" />
<SolidColorBrush PresentationOptions:Freeze="True" x:Key="BorderBrush" Color="{DynamicResource BorderColor}" />

<SolidColorBrush PresentationOptions:Freeze="True" x:Key="MouseOverControlBackgroundBrush" Color="{DynamicResource MouseOverControlBackgroundColor}" />
<SolidColorBrush PresentationOptions:Freeze="True" x:Key="MouseOverControlForegroundBrush" Color="{DynamicResource MouseOverControlForegroundColor}" />
<SolidColorBrush PresentationOptions:Freeze="True" x:Key="MouseOverBorderBrush" Color="{DynamicResource MouseOverBorderColor}" />

<Style TargetType="{x:Type TextBox}" >
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Padding" Value="2"/>
    <Setter Property="Margin" Value="1" />
    <Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}" />
    <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}" />
    <Setter Property="Foreground" Value="{DynamicResource ControlForegroundBrush}" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TextBox">
                <Grid x:Name="RootElement">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" To="{DynamicResource MouseOverBorderColor}" Duration="0:0:0.3"/>
                                    <ColorAnimation Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="{DynamicResource MouseOverControlBackgroundColor}" Duration="0:0:0.3"/>
                                    <ColorAnimation Storyboard.TargetName="PART_ContentHost" Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" To="{DynamicResource MouseOverControlForegroundColor}" Duration="0:0:0.3"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
                        <Grid x:Name="ContentGrid">
                            <Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent" Background="Transparent">
                                <ScrollViewer x:Name="PART_ContentHost" Padding="{TemplateBinding Padding}" Foreground="{TemplateBinding Foreground}" BorderThickness="0" IsTabStop="False"/>
                            </Border>
                        </Grid>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

It is very odd to me that the background and boarder brushes, which are created and animated in exactly the same way, work perfectly regardless of whether I use Static or Dynamic resources, but that the Foreground color does not.

If anyone has any ideas, or if there is a better way to do this, I'd love to hear it.

David Mullin IMA Technologies

Purr answered 5/9, 2010 at 4:2 Comment(0)
C
17

The VisualStateManager cannot control properties whose values are set via a binding. In your example, both Background and BorderBrush are set to local values (Transparent) and therefore the VSM can animate them. On the other hand, Foreground is set using a TemplateBinding and so the VSM will be unable to animate it if a binding value is in effect.

This is a general limitation of the VisualStateManager and you will see it in all the examples where it is used. A typical strategy to work around the problem is to use layers and opacity to give the illusion of color animation when really what is happening is a fade from one element to another. This works because you have total control over the hidden layer and don't have to bind it to anything. Unfortunately this won't work for your needs though because the element is not static; you cannot have two text boxes.

The net effect is that I do not think you can both animate the text foreground color and allow the foreground color to be specified by the user.

Chante answered 24/4, 2011 at 22:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.