Which button looks like it was thrown together by a developer with the artistic skills of a blind monkey, and which one looks (a bit more) like a professionally designed UI?
I think we can all agree that the second button looks much more professional. To be fair, it’s not just that developers typically aren’t heavy into the artistic skills; the tools available to the developers to date have been limited. In a Windows Forms application you could change the backcolor of a button, the size, and even the font color, but that’s where your ability to change things stopped. To get a professional looking button you had an artist draw you a pretty picture in an art program and give you the image file, and you put that on top of your button. And you usually had to have more than one picture file, since the button usually looks differently when it is pressed down. Imagine a basic calculator with 10 digits on it and 6 symbols, that is 32 pictures that the artist has to provide you. And if you decide to change the color scheme, that is 32 image files that the artist has to individually update.
Well it’s a lot easier with WPF. One of the premises of WPF is that a control has a default look, and it has a default behaviour, but both are completely changeable by the developer without having to inherit the control. So I decided to show you how to make a button that looks a little better (with the help of a book).
The basic idea of making a “glassy” button is to have a bright background, then a layer with a shadow effect, your button, and then a layer on top to make it shiny. We put the number behind the shiny layer. This is all done using XAML, and the ContentTemplate is simply a composed set of layers for the display. To make this button it looks like this. It’s not as hard as it looks.
First we make the button have a slightly different color for the text, and we bold it. Without the bold it has trouble being seen through the shiny layer, and the color change makes it look a bit faded (the first 2 bytes of the color are a transparency code, FF is solid, 00 is transparent).
Next we redefine the Control Template that is used to display the button. Note that we are still going to be using the button control, we are just changing how it looks. So first we draw a bright yellow rounded rectangle to provide the glow through the button. The rounding is done by the RadiusX & RadiusY properties. By itself it looks like this.
Next we add a dark shadow color in the bottom right so it looks like the button is sitting on the grid in 3D. This is done using a gradient fill, which simply starts at one color (almost black in this instance) and moves towards another color (transparent in this case). The RadialGradientBrush starts at a point in the canvas and moves out from that point. The coordinates for the button are 0.0 for the top left, and 1,1 for the bottom right. So this is saying “Starting at coordinates 0.9,0.9, paint color #101010 until you get 10% of the size of the button away from that point, and then fade to transparent, and be completely transparent by the time you get 80% of the size away from the initial point.” Notice that it fades slower going horizontally compared to vertically, that’s because the button is wider than it is tall.
Now we add our actual button color on top, making it partly opaque so that we can see our other 2 layers that we have so far. We also add a border that is not opaque at all.
Then we add our ContentPresenter to the button. In this case our content is a text string “7” but it could easily be more complex than that, such as a picture, or a spinning piece of text, or even a bunch of other controls. The content of the button is set on the button itself (think “Text” property, but with more options) and we are just making a template here, so we are not actually setting the text to “7”, just providing instructions on where to put the content when it is provided.
Finally we add a top shine layer that makes it look like we are reflecting light off of the top part of the button. This one is white fading to transparent as it goes down the button, and the LinearGradiantBrush goes in straight line. In this case, it is starting from the middle of the top of the button and going to the middle of the bottom. If we wanted the shine to be angled so that the light was coming from the top left, we could start the gradient closer to the top left corner and head towards the bottom right.
And voila, we have a shiny, glassy button that looks a little rounded. And we didn’t have to involve any artists or their arcane art programs. I’m sure they could make it look much better than I did, but this is far and above what buttons normally look like in my apps. And because it was declared as a Window.Resources at the top of the XAML, any button on the window is automatically going to get this content template. It could also be made a resource at the application level so all windows can use it, or even externalized into a “MyCompanyTemplates” file that is imported.
One of the really cool things is that, because this content template is built in a Grid control (with everything in the first grid cell), and we didn’t use any hard-coded units, this template will automatically resize itself. If you need a 300×300 button, this thing would look the same as it does at these smaller sizes, without a pixel to be found, and all without a line of code to make it work. Of course, this button is not complete, it is still missing the “pressed” animation for instance, and there are some other things we could do to make it better for the user’s experience, but I’ll cover those in another blog.
Oh, and I didn’t come up with this on my own. Credits for the design go to the authors of the “WPF in Action with Visual Studio 2008” book from Manning Publications.