Skip to main content

WPF Flat Button FOR REAL!


Three different solutions are available to create a "flat button" from a standard button. A flat button is one which only pops up when you point your mouse to it or gives it focus by tabbing with the keyboard. Watch the video, try the one that best suits you, and leave your comments. Note that in all three solutions, events of your created button will work as per normal.

In the video, buttons in the first column contain a Polygon. Those in the second column contain an Image, and those in the third contain simple text.

Solution 1: Property-based WPF Flat Buttons

To get these flat buttons, all you have to do is set both Background and BorderBrush properties of a standard button to Transparent. These buttons also show natively the fade-in and fade-out effects when they gain focus or are pointed to.

<Button Background="Transparent" BorderBrush="Transparent" />

However, these flat buttons fail when Windows is using the Classic theme or a High Contrast theme (as you can see from the video). If your application is targeted to work on all themes, try Solution 2 or 3.

Solution 2: Style-based WPF Flat Buttons (non-animated)

Add the following to your application's resources (usually Window.Resources or Page.Resources):

<Style x:Key="FlatButton" TargetType="Button">
 <Setter Property="IsTabStop" Value="False" />
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="Button">
    <Grid>

     <Button> <!-- Inherit properties here with TemplateBinding -->
      <Button.Style>
       <Style TargetType="Button">
        <Style.Triggers>
         <MultiTrigger>

          <MultiTrigger.Conditions>
           <Condition Property="IsFocused" Value="False" />
           <Condition Property="IsMouseOver" Value="False" />
          </MultiTrigger.Conditions>

          <Setter Property="Opacity" Value="0" />

         </MultiTrigger>
        </Style.Triggers>
       </Style>
      </Button.Style>
     </Button>

     <Label HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="3" IsHitTestVisible="False">
      <ContentPresenter />
     </Label>

    </Grid>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

With this style in place, create your button as follows:

XAML:
<Button Style="{StaticResource FlatButton}" />

Code-behind:
Button myButton = new Button();
myButton.Style = (Style)this.FindResource("FlatButton");

The inner button in the style definition will not automatically inherit some properties such as Background that you set to the above button. If you need to apply any such property that doesn't inherit automatically, you will have to inherit it by editing the button inside the style definition as follows, with Background as an example:

<Button Background="{TemplateBinding Background}" />
 <!-- Inherit properties here with TemplateBinding -->

However, you must not inherit the Opacity property! If you do that, the button will completely fail at being flat. You may add an Opacity property to the inner button to help yourself visualize the buttons during design-time. But you must remove the Opacity property of inner button before runtime, or your created buttons will be stuck at the added opacity value permanently.

If you want to control the runtime opacity, simply set the Opacity property of your created button (there is no need to inherit it to the inner button). Alternatively, for a global effect, you may also change the value in the MultiTrigger's Setter (or) use solution 3 (edit the DoubleAnimation's by setting the duration to "0:0:0" and setting the opacities to your desired values).

Solution 3: Style-based WPF Animated Flat Buttons

Add the following to your application's resources (usually Window.Resources or Page.Resources):

<Style x:Key="AnimatedFlatButton" TargetType="Button">
 <Setter Property="IsTabStop" Value="False" />
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="Button">
    <Grid>

     <Button Opacity="0.1"> <!-- Inherit properties here with TemplateBinding -->
      <Button.Style>
       <Style TargetType="Button">
        <Style.Triggers>
         <MultiTrigger>

          <MultiTrigger.Conditions>
           <Condition Property="IsFocused" Value="False" />
           <Condition Property="IsMouseOver" Value="False" />
          </MultiTrigger.Conditions>

          <MultiTrigger.EnterActions>
           <BeginStoryboard>
            <Storyboard>
             <DoubleAnimation To="0.0" Duration="0:0:0.5" Storyboard.TargetProperty="Opacity" />
            </Storyboard>
           </BeginStoryboard>
          </MultiTrigger.EnterActions>

          <MultiTrigger.ExitActions>
           <BeginStoryboard>
            <Storyboard>
             <DoubleAnimation To="1.0" Duration="0:0:0.25" Storyboard.TargetProperty="Opacity" />
            </Storyboard>
           </BeginStoryboard>
          </MultiTrigger.ExitActions>

         </MultiTrigger>
        </Style.Triggers>
       </Style>
      </Button.Style>
     </Button>

     <Label HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="3" IsHitTestVisible="False">
      <ContentPresenter />
     </Label>

    </Grid>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

With this style in place, create your button as follows:

XAML:
<Button Style="{StaticResource AnimatedFlatButton}" />

Code-behind:
Button myButton = new Button();
myButton.Style = (Style)this.FindResource("AnimatedFlatButton");

The inner button in the style definition will not automatically inherit some properties such as Background that you set to the above button. If you need to apply any such property that doesn't inherit automatically, you will have to inherit it by editing the button inside the style definition as follows, with Background as an example:

<Button Opacity="0.1" Background="{TemplateBinding Background}" />
 <!-- Inherit properties here with TemplateBinding -->

You may edit the Opacity property to help yourself visualize when designing your form. But remember to set it back to zero or 0.1 because an opacity too high will give the unwelcome visual effect of all your flat buttons fading into flatness when you launch the application window.

If you want to control the runtime opacity, simply set the Opacity property of your created button (there is no need to inherit it to the inner button). To control the fade speed of the buttons, edit the DoubleAnimation's by setting the duration to your desired values. You may also change the opacity values in the DoubleAnimation's if desired.

What if I don't want the button to remain visible after I click?

Simple! Just replace the <MultiTrigger> opening tag with the following tag and delete the <MultiTrigger.Conditions /> tag including its contents. Remember to change all remaining instances of the term MultiTrigger with Trigger. (Note: this is applicable for solutions 2 and 3 only.)

<Trigger Property="IsMouseOver" Value="False">

Admittedly, a true flat button must be "not flat" only when the mouse is pointing at it. I overlooked this the first time and enabled my WPF flat buttons to be "not flat" when they are focused (after click or by pressing Tab key) even if the mouse is not pointing at it.

With this simple change, that anomaly can be removed and you can have a true flat button. When user navigates controls on the form using Tab key, a dotted rectangle appears around the button label to indicate focus. Hence the button itself can remain flat, only showing up when the user points mouse at it.

Backstory:
After searching everywhere on the Internet for a perfect solution, I could only find half-baked solutions of either inheriting the toolbar button style (which looks very ugly with a simple blue box) or hacking up the button and defining your own style (which makes you lose the standard button look and gradients).

I ended up spending two days trying different ways and coming up with a good solution that actually works. The solutions 2 and 3 were created and perfected by me before I stumbled upon solution 1 here. I'm glad I didn't see solution 1 before. Initially I was surprised that the problem could be very easily solved with solution 1, but upon further testing, I found it unreliable if an application is to be deployed in an environment which may use the Classic theme or its derivatives.

Remember to leave your comments if you found it useful or encountered problems.

Comments

Popular posts from this blog

Disable auto save in JetBrains IDE software (IntelliJ IDEA, PyCharm, PhpStorm)

JetBrains provides the following IDE software: IntelliJ IDEA PhpStorm PyCharm RubyMine WebStorm AppCode CLion Google also provides Android Studio which is powered by the IntelliJ platform. If you come from a different IDE such as Eclipse, you will be unpleasantly surprised to find that JetBrains-branded IDEs automatically save everything the moment you look away. The proponents argue that as you work on your project, you should not have to worry about saving files. But to others, this auto-save behavior which is enabled by default is a curse that catches them by surprise, and a shocking departure from the workflow they are very much used to. You can change the behavior by altering some settings.

Make Samsung DVD-C350 region-free

Update 2: An anonymous commentator has shown me a way to make Region 1 players (such as DVD-H1080R) region-free by first converting it to Region 3, then applying my region-free hack below. For details, click here or look for a comment by an Anonymous user dated 18 April 2011. Update: The instructions in the original post below did not make the DVD player region-free. Instead it only locked it to region 1. Many thanks to Anonymous who posted the first comment on this post, I now have alternate instructions. Note: If you have edited the numbers menu (see original post) , I suggest you return it to the original settings you had backed up. A modified numbers menu may prevent the instructions below from working properly.

Group, Ungroup and Regroup disabled in Word

I was editing a Microsoft Word document which had a collection of shapes and text boxes grouped together. I wanted to modify some of the shapes, and therefore I had to ungroup them. But when I right-click the group and open the Group menu, all three options namely Group, Ungroup and Regroup are completely disabled or grayed out. I couldn’t figure out what’s wrong. This group of objects is perfectly ungroupable, and I can even select objects within the group. However, Microsoft Word 2007 is not letting me ungroup it. I searched the Internet for a solution, but did not find anything very useful. The closest I came across is this statement: “The type of Text Wrapping doesn't make any difference as long as it isn't In Line with Text.” ( Link here ) Anyway, I changed the text wrapping of the group of objects from ‘In line with Text’ to ‘Tight’ and viola! I could now ungroup it and edit it. The document got a bit messed up when I did so, but after I ungrouped, edited and regro...