Search code examples
wpfbuttontriggerstextblock

WPF -- it's gotta be easier than I'm making it


I'm having the darndest time figuring this out: say I've got two Button and three TextBlocks. I want either button to trigger a simple Storyboard on ALL TextBlocks. Currently I'm trying to define a generic Textblock style that contains the Storyboard, and then the trigger comes from any Button click. This is the closest I've come but the app crashes on startup...what am I don't wrong here:

<Window.Resources>

<Style TargetType="TextBlock" >
    <Setter Property="Foreground" Value="Blue" />
    <Style.Resources>
        <Storyboard x:Key="TextBlockOpacity" Storyboard.TargetProperty="Opacity">
            <DoubleAnimation From="0" To="1" />
        </Storyboard>
    </Style.Resources>      
</Style>

<Window.Triggers>
    <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button">
        <BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}"/>
    </EventTrigger>
</Window.Triggers>


<Grid x:Name="LayoutRoot">
    <Button x:Name="button" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>

    <TextBlock x:Name="textBlock1" Margin="228,54,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="TextBlock" TextWrapping="Wrap" />
    <TextBlock x:Name="textBlock2" Margin="228,103,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="Hello" TextWrapping="Wrap"/>
</Grid>

Solution

  • Based on kek444's Xaml-only solution, I present a slightly improved version that doesn't rely on the DataContext of the button and can have multiple triggers.

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="WpfApplication1.MainWindow"
        x:Name="Window"
        Title="MainWindow"
        Width="640" Height="480">
        <Window.Resources>
            <UIElement x:Key="OpacityCounter" Opacity="0"/>
            <Style TargetType="TextBlock">
                <Setter Property="Opacity" Value="{Binding Source={StaticResource OpacityCounter}, Path=Opacity}" />
            </Style>
            <Storyboard x:Key="OnClick1">
                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.Target="{StaticResource OpacityCounter}" Storyboard.TargetProperty="(UIElement.Opacity)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                    <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
        </Window.Resources>
        <Window.Triggers>
            <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
                <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
            </EventTrigger>
            <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button2">
                <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
            </EventTrigger>
        </Window.Triggers>
    
        <Grid x:Name="LayoutRoot">
            <StackPanel>
                <StackPanel Orientation="Horizontal">
                    <Button x:Name="button1" Width="131" Height="37" Content="Button 1" Margin="0,0,0,22"/>
                    <Button x:Name="button2" Width="131" Height="37" Content="Button 2" Margin="0,0,0,22"/>
                </StackPanel>
                <TextBlock x:Name="textBlock" Height="27" Text="TextBlock 1" TextWrapping="Wrap" />
                <TextBlock x:Name="textBlock1" Height="27" Text="TextBlock 2" TextWrapping="Wrap" />
                <TextBlock x:Name="textBlock2" Height="27" Text="TextBlock 3" TextWrapping="Wrap" />
                <TextBlock x:Name="textBlock3" Height="27" Text="TextBlock 4" TextWrapping="Wrap" />
            </StackPanel>
        </Grid>
    </Window>
    

    To use a ListBox as a trigger mechanism (provided you have a ListBox named "listbox1" someplace, add the following to Window.Triggers:

    <EventTrigger RoutedEvent="Selector.SelectionChanged" SourceName="listbox1">
        <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
    </EventTrigger>
    

    or to trigger off a specific ListBoxItem, you'll need (where item1 is a named ListBoxItem):

    <EventTrigger RoutedEvent="ListBoxItem.Selected" SourceName="item1">
        <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
    </EventTrigger>