Eugenio Miró
Eugenio Miró

Reputation: 2428

WPF: Is there a way to change conditionally a property value in XAML or code behind (VB/C#) is needed?

I started a new little WPF application which has something like an entry form that is placed over a picture. That picture has the placeholders for the data that should be entered and where TextBoxes are placed so the user is actually entering the data into a form as she would do by hand or in a typewriter. I wanted to add an effect, partiali hiding the textbox when the user leaves the field so placed some event handlers in one of the fields adding this code:

<TextBox Height="23" VerticalAlignment="Top" Width="237" 
         HorizontalAlignment="Left" Margin="641,25,0,0" 
         Name="txtAmount" LostFocus="txtAmount_LostFocus" />

private void txtAmount_LostFocus(object sender, RoutedEventArgs e)
{
    Opacity = .5;
}

That worked so I continued adding code to set the same behavior in all textBoxes, so I changed the code above with the code below:

<Window.Resources>
    <Storyboard x:Key="FadeOut">
        <DoubleAnimation From="1.0" To="0.5" 
                         Duration="0:0:0.2"
                         Storyboard.TargetProperty="Opacity">

        </DoubleAnimation>
    </Storyboard>
    <Storyboard x:Key="FadeIn">
        <DoubleAnimation From="0.5" To="1.0" 
                         Duration="0:0:0.2"
                         Storyboard.TargetProperty="Opacity"/>
    </Storyboard>
    <Style x:Key="InTheShadow" >
        <Style.Setters>
            <Setter Property="TextBox.Opacity" Value="0.5" />
        </Style.Setters>
        </Style.Setters>
        <Style.Triggers>
            <EventTrigger RoutedEvent="TextBox.GotFocus">
                <BeginStoryboard Storyboard="{StaticResource FadeIn}"/>
            </EventTrigger>
            <EventTrigger RoutedEvent="TextBox.LostFocus">
                <BeginStoryboard Storyboard="{StaticResource FadeOut}"/>
            </EventTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

The first code, also had GotFocus event handled but it's almost the same code, so the second code implemented that, and that was placed on all textBoxes setting the style.

Now, this worked too but, I wanted to add the same effect for MouseEnter/MouseLeave

            <EventTrigger RoutedEvent="TextBox.MouseEnter">
                <BeginStoryboard Storyboard="{StaticResource FadeIn}"/>
            </EventTrigger>
            <EventTrigger RoutedEvent="TextBox.MouseLeave">
                <BeginStoryboard Storyboard="{StaticResource FadeOut}"/>
            </EventTrigger>

but, here my question comes, those events obviously changed the Opacity always, and that was not my intention. I didn't want to change the Opacity for the TextBox that had the Focus, I wanted to keep that Opacity in 100%.

Upvotes: 2

Views: 2050

Answers (1)

Paul Stovell
Paul Stovell

Reputation: 32725

MultiTriggers to the rescue!

Care also had to be taken to stop the old storyboards. Hopefully this works as you expected:

<Style x:Key="InTheShadow" TargetType="TextBox">
    <Setter Property="TextBox.Opacity" Value="0.5" />
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsFocused" Value="False" />
                <Condition Property="IsMouseOver" Value="True" />
            </MultiTrigger.Conditions>
            <MultiTrigger.EnterActions>
                <StopStoryboard BeginStoryboardName="fadeOut1" />
                <StopStoryboard BeginStoryboardName="fadeOut2" />
                <BeginStoryboard x:Name="fadeIn1" Storyboard="{StaticResource FadeIn}"/>
            </MultiTrigger.EnterActions>
            <MultiTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="fadeIn1" />
                <StopStoryboard BeginStoryboardName="fadeIn2" />
                <BeginStoryboard x:Name="fadeOut1" Storyboard="{StaticResource FadeOut}"/>
            </MultiTrigger.ExitActions>
        </MultiTrigger>

        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsFocused" Value="True" />
            </MultiTrigger.Conditions>
            <MultiTrigger.EnterActions>
                <StopStoryboard BeginStoryboardName="fadeOut1" />
                <StopStoryboard BeginStoryboardName="fadeOut2" />
                <BeginStoryboard x:Name="fadeIn2" Storyboard="{StaticResource FadeIn}"/>
            </MultiTrigger.EnterActions>
            <MultiTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="fadeIn1" />
                <StopStoryboard BeginStoryboardName="fadeIn2" />
                <BeginStoryboard x:Name="fadeOut2" Storyboard="{StaticResource FadeOut}"/>
            </MultiTrigger.ExitActions>
        </MultiTrigger>


    </Style.Triggers>
</Style>

Usage:

<StackPanel Margin="7">
    <TextBox Margin="7" Style="{StaticResource InTheShadow}" />
    <TextBox Margin="7" Style="{StaticResource InTheShadow}" />
    <TextBox Margin="7" Style="{StaticResource InTheShadow}" />
    <TextBox Margin="7" Style="{StaticResource InTheShadow}" />
    <TextBox Margin="7" Style="{StaticResource InTheShadow}" />
</StackPanel>

Edit: Also, you should remove the "from" in your storyboards, that way if the text box already had mouse over and you focus, it won't re-animate:

<Storyboard x:Key="FadeOut">
    <DoubleAnimation 
        To="0.5" 
        Duration="0:0:0.2"
        Storyboard.TargetProperty="Opacity" FillBehavior="HoldEnd"
        />
</Storyboard>
<Storyboard x:Key="FadeIn">
    <DoubleAnimation 
        To="1.0" 
        Duration="0:0:0.2"
        Storyboard.TargetProperty="Opacity" FillBehavior="HoldEnd"
        />
</Storyboard>

Upvotes: 6

Related Questions