Search code examples
wpfxamlwpf-controlswpf-style

How to create a single style for multiple mouse over events?


I have four buttons and four text boxes where each button is linked to one of the textblocks. When the mouse is over the button I want the corresponding textblock to fade in (and out on mouse leave). There are plenty of examples of this showing a single button and textblock where you can simply bind a datatrigger to the button name in the textblock style.

Here's what I've got so far (all of this is in a textblock style):

<DataTrigger Binding="{Binding ElementName=UpdateButton, Path=IsMouseOver}" Value="True">
    <DataTrigger.EnterActions>
         <BeginStoryboard>
              <Storyboard TargetProperty ="Opacity" Duration="00:00:01">
                   <DoubleAnimation From="0" To="1" Duration="00:00:01"/>
              </Storyboard>
         </BeginStoryboard>
    </DataTrigger.EnterActions>
    <DataTrigger.ExitActions>
         <BeginStoryboard>
              <Storyboard TargetProperty ="Opacity" Duration="00:00:01">
                   <DoubleAnimation From="1" To="0" Duration="00:00:01"/>
              </Storyboard>
         </BeginStoryboard>
    </DataTrigger.ExitActions>
</DataTrigger>

As of right now, when I mouse over the Update Button, all of the textblocks show instead of just the one associated with the Update Button.

To fix this I could create styles for each textblock by their name and bind to the appropriate button, but this is a huge amount of repetition. I could likely used "BasedOn" to separate the button binding, but then we're still duplicating all of the code for the Storyboards and whatnot. But does anyone know a better way?

It would seem like there should be a way create this all in a single style using a single generic binding but link the specific buttons to their textblocks, so the button only triggers the Storyboard for it's linked textblock. Anyone know how to do this, or a better way?


Solution

  • A good way to handle this is to create a custom inherited TextBlock that can store reference to a button.

    Example

    using System.Windows;
    using System.Windows.Controls;
    
    //Custom TextBlock
    public class SpecialTextBlock : TextBlock
    {
        //This will be the button reference
        public Button BoundButton { get; set; }
    
        //Register the BoundButton as a dependency to allow binding
        public static readonly DependencyProperty ButtonProperty = DependencyProperty.Register
        (
            "BoundButton",
            typeof(Button),
            typeof(SpecialTextBlock),
            new FrameworkPropertyMetadata(default(Button))
        );
    }
    

    Now that your new SpecialTextBlock is set up, you can create a new style for it. Use your original style, but apply it to TargetType="local:SpecialTextBlock" instead of TargetType="TextBlock".

    Then update your DataTrigger from your example within the style so that the trigger binds to itself (the SpecialTextBlock), and then looks at the referenced Button path.

    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=BoundButton.IsMouseOver}" Value="True">
    ...
    

    Now you are set up and can create your TextBlocks like so without having to restyle.

    //Set your BoundButton binding to specify which button triggers the animation.
    <local:SpecialTextBlock BoundButton="{Binding ElementName=UpdateButton}" />