Button

[Updated] How to create a pure icon button for your UWP app

[Updated: the first version of this blog post was using a UserControl. Thanks to a discussion on Twitter, I realized that wrapping it into a UserControl is overblown (yes, sometimes I tend to write more code than necessary). This is the updated version using only a Style for the button.]

When writing apps, you often come along a point where you want to style controls differently than the original style. Today, I‘ll show you a pure icon button that does not show the surrounding shape and borders. Instead, it highlights the icon of the button when hovering over it, while it changes the color to the user’s accent color when it gets pressed. Here is a little animation of what I am talking about:cromeless button demo

It all begins with the default style of the Button control, which you can see here. We are going to modify this Style until it matches the above animation. The first thing we change is the BackgroundBrush – set it to ‘Transparent’ to get rid of the grey shape that the button control comes with when hovering over it or pressing it:

<Setter Property="Background" Value="Transparent"/>

As we want an icon button, we need to choose a common source for the icons as well. I am using a Path Shape as the icon source, as it allows modifications to be done in XAML. So the next step is to add a Path shape to the Style:

                            <Path x:Name="PathIcon"
                                  Data="{TemplateBinding Content}"
                                  Stretch="Uniform"
                                  Fill="{TemplateBinding Foreground}"
                                  Stroke="Transparent"
                                  StrokeThickness="1"
                                  Margin="4"
                                  RenderTransformOrigin="0.5,0.5">
                                <Path.RenderTransform>
                                    <TransformGroup>
                                        <TransformGroup.Children>
                                            <RotateTransform Angle="0" />
                                            <ScaleTransform ScaleX="1" ScaleY="1" />
                                        </TransformGroup.Children>
                                    </TransformGroup>
                                </Path.RenderTransform>
                            </Path>

In this case, as we just want to use the icon within our button, we can safely remove the ‘ContentPresenter’ part in the Style. We have made quite some progress already, but that all does not make the control behaving like in the animation yet.

Now it is the time to modify the CommonStates of the Button’s style. Our Button uses only an icon, so we need to add the color states for the Path’s ‘Fill (=Foreground)’ to the states. Here are the modifications:

‘PointerOver’ state:

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseHighBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>

‘Pressed’ state:

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlForegroundAccentBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>

‘Disabled’ state:

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>

To get the icon’s outline highlighting, we are going to use the Path’s ‘Stroke (=Border)’ property. Add these modifications to the Style in XAML:

‘PointerOver’ state:

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>

‘Pressed’ state:

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                            </ObjectAnimationUsingKeyFrames>

‘Disabled’ state:

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                            </ObjectAnimationUsingKeyFrames>

All that is left is to use the Style on any desired button:

    <Button x:Name="BaseButton" Style="{StaticResource TransparentButtonStyle}"></Button>

If you now use this one in an application, you will get the same result as in the initial animation.

For easier use, here is the complete code:

        <Style x:Key="TransparentPathIconButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
            <Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundTransparentBrush}"/>
            <Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}"/>
            <Setter Property="Padding" Value="8,4,8,4"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
            <Setter Property="FontWeight" Value="Normal"/>
            <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
            <Setter Property="UseSystemFocusVisuals" Value="True"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal">
                                        <Storyboard>
                                            <PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="PointerOver">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseHighBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlForegroundAccentBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <PointerDownThemeAnimation Storyboard.TargetName="RootGrid"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Stroke" Storyboard.TargetName="PathIcon">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>

                            <Path x:Name="PathIcon"
                                  Data="{TemplateBinding Content}"
                                  Stretch="Uniform"
                                  Fill="{TemplateBinding Foreground}"
                                  Stroke="Transparent"
                                  StrokeThickness="1"
                                  Margin="4"
                                  RenderTransformOrigin="0.5,0.5">
                                <Path.RenderTransform>
                                    <TransformGroup>
                                        <TransformGroup.Children>
                                            <RotateTransform Angle="0" />
                                            <ScaleTransform ScaleX="1" ScaleY="1" />
                                        </TransformGroup.Children>
                                    </TransformGroup>
                                </Path.RenderTransform>
                            </Path>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

As always, I hope this post is helpful for some of you. If you have questions/ideas for improvements or just want to talk about the control, feel free to leave a comment below.

Happy Coding, everyone!

Posted by msicc in Dev Stories, UWP, 3 comments

How to generate a round image button for your Windows Phone 8.1 app (to use everywhere)

Recently, I experimented a bit because I wanted a round button that contains an image that can be used everywhere where I can add a standard button (and not just in the AppBar). I managed to get a simple style out of these experiments (sample at the end of this post).

First, you should check if you have already installed Syncfusion’s free Metro Studio (we will need it later). It is a powerful helper if you need icons, so if you do not have it, go straight ahead and download it here: http://www.syncfusion.com/downloads/metrostudio

Still here/back? Great! Ok, let’s start. In our project, generate a new button:

<Button Width="72" Height="72"></Button>

If you want your round button to have a smaller size, feel free to adjust the 72 pixels mine has to your preferred value.

The next step is to generate a new Style. Right click on the Button, and select ‘Edit Template’, followed by ‘Edit a Copy’.

Screenshot (407)

 

Set the name of your style in the next window, and save define it as an app-wide Style or on your page:

Screenshot (408)

This should open your App.xaml file and display the button as well as the newly generated style.

We are starting with our custom style modifications right at the top:

image

Set both Doubles to 0 and the Thickness to 0,0.

Next, scroll down to find the Border Element of the Button Template (closing ‘VisualStateManager.VisualStateGroups’ helps a lot).

Click on the Border element and add/adjust the ‘CornerRadius’ property. At a size of 72, the minimum value is 38 for the radius. This should be fine for most cases, but it may be higher/smaller if you are using another size. Don’t worry if your button looks like this at them moment:

image

We are going to fix it right now by setting the Height and Width properties of our Border element:

Height="{Binding Path=Height, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Width="{Binding Path=Width, RelativeSource={RelativeSource Mode=TemplatedParent}}"

This binds the Width and Height properties of our Button to the Style. Now we just need to define the Height and the Width of our Button to make it actually look really round. Setting both to 72 will result in a nice round button.

Like you can imagine, displaying text does not make a lot of sense in this case. Round Buttons should contain an image. You could add one through adding a background, but this will result in a strange looking button when it gets pressed. Also, it does not reflect changes like a color change. To solve this, we are going to add code that is able to draw a shape for us. This is achieved with the Path Class  in XAML. The Path class draws lines into a FrameworkElement like a Canvas or a Border.

To enable our Style to work with Path Data, we need to add some code before the ‘Template’ property Setter in our Style:

<Setter Property="ContentTemplate">
    <Setter.Value>
        <DataTemplate>
            <Path Stretch="Uniform"
                  RenderTransformOrigin="0.5,0.5"
                  Margin="2,6,2,2"
                  Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                  Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"></Path>
        </DataTemplate>
    </Setter.Value>
</Setter>

What does this code do? The ContentTemplate allows us to add rich content to our UIElement, the Button. To make it resuable, we are setting it up in our custom button style. The RenderTransforOrigin property value of 0.5,0.5 centers our Path drawn shape within the border. However, I found out that some shapes do not look good with that alone. That’s why I adjusted the Margin property together with it. This should fit most icon shapes, but you might adjust this for your own needs.

The most important aspects are the Fill property as well as the Data property. Binding the Fill Brush to the Foreground Brush property is necessary to reflect changes like theme changes as well as changes in the VisualState. Only this way it behaves like a native Button. Binding the Data property allows us to enter the Path string into the Content property of a button that uses our Style without any conversion. This makes it very simple to generate a button with our desired icon.

And this is where Syncfusion’s MetroStudio comes in handy. It allows you not only to generate icons as png, but also as shape in XAML. To get the relevant Data, open MetroStudio, search for your icon. Below the icon, there is an Edit Button. Tap it to open the icon settings page. On that settings page, you set up your button. Play around a little bit to get used to it (it’s pretty easy).

Once you have your desired icon on the screen, click on the </>XAML Button. Copy the highlighted part of the XAML code:

image

Back in Visual Studio, add this copied code to the Content property of our Button:

Content="F1M181.003,-1898.78L207.077,-1902.33 207.089,-1877.18 181.027,-1877.03 181.003,-1898.78z M207.065,-1874.28L207.085,-1849.1 181.023,-1852.69 181.022,-1874.45 207.065,-1874.28z M210.226,-1902.79L244.798,-1907.84 244.798,-1877.5 210.226,-1877.22 210.226,-1902.79z M244.807,-1874.04L244.798,-1843.84 210.226,-1848.72 210.177,-1874.1 244.807,-1874.04z" 
Height="72" 
Width="72"
Style="{StaticResource RoundButtonStyle}" 
VerticalAlignment="Center" 
HorizontalAlignment="Center"/>

Which will result in this nice looking round button with a Windows logo on it:

image

If you run the sample project, you can see that the Button behaves like a native Button with text. Download the sample project here.

I am pretty sure this can be improved. I will continue to play around with this, and if I have found enough optimizations, I will write another post about them. Until then, this should help you to get started with your own round button – and the best thing: you can use it like any standard button wherever you want in your Windows (Phone) 8.1 app!

Happy coding, everyone!

Posted by msicc in Dev Stories, windev, 1 comment