Showing posts with label Glassy Orb. Show all posts
Showing posts with label Glassy Orb. Show all posts

Monday, June 28, 2010

A Chrome and Glass Theme- Part 8

Introduction

In this post I’m going to show you how to make the radio buttons I blogged about previously. These buttons have gone through many iterations and I’m still not sure I’m quite happy with them just yet, but I’ll cover them anyway since they introduce a few interesting attached properties. Here is the sample I’ve been building up, now with the radio buttons:

You can grab the source here.

This post is the 8th in a series. If you are not familiar with styling controls in Blend, or working with Resource Dictionaries then I would recommend that you start at the beginning of the series.

The Radio Button

When I first attempted this button I ended up with horribly complicated arrangement of shapes to get it to scale properly. I had placeholders and spacers and grids within grids, with some of them having their height bound to their widths (which doesn’t work very well). And I had half circles on the ends with gradient blends merging seamlessly into the gradient blend on a rectangle in the middle. It was pretty awful.

The previous blog about Automatic Rectangle Radius X and Y should give you an idea of a simpler way to achieve the look. In the end I went with the attached properties since they work nicely both at run-time and design-time. If you grab the source from the link above, you may have to build it in Blend to get it to use the attached properties on the design surface.

The RadioButton inherits from the ToggleButton, as does the CheckBox. They all have a common theme of the button being checked, unchecked, or indeterminate; The radio button just has a different visual appearance, and adds the GroupName property. This style could be adapted for the ToggleButton too, but it wouldn’t really work as a style for a CheckBox.

The contents for this button are grouped in a grid with two columns; the left column contains the orb, the right column contains the ContentPresenter element.

Creating a custom style template for a radio button shouldn’t be difficult at all if you have been following along with the series, but I thought it might be worthwhile to look at some of the elements that make up this button.

Scalable Rounded Ends

A rectangle has the RadiusX and RadiusY properties that let you turn the sharp corners into rounded corners. For this button I want the rounded ends to stay rounded, at the right proportion, regardless of the size of the button. Unfortunately, there is no easy way to set those properties to be half of the height of the rectangle.

As mentioned in the post about Automatic Rectangle Radius X and Y I used an attached property to achieve the rounded ends since that gave me the best result when working with Blend. I use 3 rectangles in the button: one for the rim using the ChromeBorder resource brush, one for the main body using the ChromeFill resource brush, and one the same size that uses the ChromeDarkeningLinear resource brush to make the white text stand out better.

The ellipse is used on the right end of the button to add some dark shading to make the button look rounder, and the Orb grid uses the same principle as this post.

I created an attached property called IsRounded that, when true, uses another attached property called RoundCapsRatio (which defaults to 0.5 if unset) that it multiplies against height or width (whichever is smaller) and applies to the RadiusX and RadiusY properties of the rectangle it is attached to. You may have to build the solution once in Blend to have it applied properly, but then the corner radius automatically updates in blend as the rectangle it is applied to changes size.

Scalable Orb

The other attached properties I created are WidthRatio and HeightRatio (not used in this example). I use the WidthRatio attached property on the Orb grid to keep it perfectly square so the ellipses inside it stay round. I can’t have this happen without an attached property because there is no other way (that works in Blend) to set the width of an element to be relative to the height. I set the WidthRatio to the value “1” to achieve this. I could have just used a Boolean attached property, but having the ratio makes it more reusable.

If you use both the attached properties on a visual element, the handler will prefer the WidthRatio over the HeightRatio. Here is the method that applies the property values. It only applies the values if they have actually been set:

   1: private static void UpdateSizeRatio(FrameworkElement element)
   2: {
   3:     double ratio;
   4:     if (element.ReadLocalValue(WidthRatioProperty) != DependencyProperty.UnsetValue)
   5:     {
   6:         ratio = (double)element.GetValue(WidthRatioProperty);
   7:         double height = element.ActualHeight;
   8:         if (!double.IsNaN(height) && height > 0)
   9:         {
  10:             element.Width = height * ratio;
  11:         }
  12:     }
  13:     else if (element.ReadLocalValue(HeightRatioProperty) != DependencyProperty.UnsetValue)
  14:     {
  15:         ratio = (double)element.GetValue(HeightRatioProperty);
  16:         double width = element.ActualWidth;
  17:         if (!double.IsNaN(width) && width > 0)
  18:         {
  19:             element.Height = width * ratio;
  20:         }
  21:     }
  22: }

Conclusion


As far as radio buttons go, these are fairly limited in their application since they are so big. They are too big to use in the same way as you would normally use a radio button, but they do make quite good navigation buttons instead of using tabs.


The use of Attached Properties seems the best solution for adding behavior to a visual element that works at design time in Blend, but it’s not ideal – I would really rather have a way to do this using Blend that didn’t need me to swap into the XAML.


References:


The icons are from the Tango Desktop Project.

Thursday, April 1, 2010

A Chrome and Glass Theme - Part I

This is the first in a series of posts that will cover how to build a nice looking chrome and glass theme. The chrome style will be applied to controls and the glass look will be a balancing style to avoid an overload of shiny; it will also give us a nice gentle background appearance.

In this post we are going to define some gradients and color resources for a glass style that can be applied to a Border control. Here is what the finished button will look like:


We are going to end up with a resource dictionary that we can use with both the ImplicitStyleManager from the Silverlight Toolkit for Silverlight 3, or directly with Silverlight 4. The only difference between the two approaches is that we don't need to add the x:Key="StyleKeyName" attribute on each style, or set the Style property on each control if we want to use it in Silverlight 4.

Setting up our Theme
First we need to create our solution. Start Blend and create a new project (call it something like "ChromeAndGlassTheme". Under the Project menu select "Add new item...", and add a new Resource Dictionary called "ChromeGlass.xaml".

Styling From the Top
The first thing we want to define is the foreground color for text on most of the controls styles we will end up with - which will be white. So select the "[User Control]" root in the Objects tree, and change the foreground to white. We want this setting to be part of the resource dictionary we created, so we do the following to achieve that:
  1. Click the "Advanced Property Options" button (the little square button to the right of the Foreground box)
  2. Select "Convert to New Resource..."
  3. Give it the name (key) "WhiteForeground"
  4. Choose "Resource Dictionary" for the location to define it
  5. Click OK
Since we only have the one Resource Dictionary, it will have created the entry in the ChromeGlass.xaml.

Next, with the "[User Control]" still selected, open the "Object" menu and select the "Edit Style | Create Empty..." menus. Call the style "UserControlChromeStyle", make sure it is created in our ChromeGlass resource dictionary, and click OK. We are now editing the style resource, not the control. On the properties tab click the Advanced Property Options button for the foreground and select the "Local Resource" sub menu, and choose our "WhiteForeground" resource.

Why have we set this on the UserControl? So that we don't need to explicitly set the foreground color for our child controls (such as labels and buttons); Silverlight will search up to see if any of the parent controls have their foregrounds explicitly set before going back to the default value for the control. So since we have set the style of the UserControl to have it's foreground white, all child controls that we place inside it will use WhiteForeground unless we set them otherwise.

Now select the "LayoutRoot" grid and set it's background to black. We could have created a style like we did for the UserControl, but background is not one of the values that is propagated to child controls - and we won't want it passed down anyway.

The Gradients
Our chrome and glass styles are going to rely heavily on a couple of gradient brush resources we are going to define. First lets create the glass panel that can contain our controls. Create a new Border element and make it about 300px wide and about 120px high. Before we create a style for it, we should clear any changes that Blend has already made to the Border's properties. Click the Advanced Property Options box to the right of the Background property and select "Reset". Do this also for the BorderBrush, BorderThickness, and CorderRadius properties.

Now create a new empty style for it like we did for the UserControl - call it "BorderGlassStyle" and be sure to create it in our resource dictionary.

Select the "BorderBrush" property and select the Gradient Brush option below it. The gradient bar will have two markers - one at each end. Select the left marker and change it's color to White (both markers should now be white). Add another 7 markers by clicking on the gradient bar between the existing markers and set the following values for each marker (1 is leftmost, 9 is rightmost. The "Alpha" refers to the "A" part of the color):
  1. position: 0%. Alpha: 60%
  2. position: 8.5%. Alpha: 0%
  3. position: 37.6. Alpha: 20%
  4. position 41.8%. Alpha: 60%
  5. position 47.7%. Alpha: 20%
  6. position 60.5%. Alpha: 0%
  7. position 76.5%. Alpha: 20%
  8. position 80.7%. Alpha: 50%
  9. position 100%. Alpha: 5%
If you click the down-pointing arrow below the gradient bar you will see some other values we can set for this gradient. Set the the start and end points as follows:
  • Start Point: 0.013, 0.036
  • End Point: 1, 1.005
And set the Border Thickness to 2 for each edge and a Corner Radius of 8. This will make our border look like it is reflecting shafts of light.

We are going to reuse this glassy border brush again, so click the Advanced Property Options button for the BorderBrush property and convert it to a new resource called "GlassBorder".

Since we are editing the style resource, not the Border control itself, lets pop back out to the border to see what it looks like. You can do that by clicking on the "[Border]" part of the Style path at the top of the editing window:


Now we are back in our MainPage file with the Border control selected. Click the little Pac-Man (ok - it's an artist's pallet) shape to go back to editing the style. Now let's create a glassy border for the background.

Select the Background property of the style and again select the Linear Gradient option. We want a total of 5 markers on this gradient. This time we will set the hex values for each of the markers as follows:
  1. #26FFFFFF position: 0%
  2. #194C4C4C position: 40.4%
  3. #19FFFFFF position: 61.4%
  4. #4DFFFFFF position: 71.5%
  5. #18FFFFFF position: 100%
Also set the start and end points to the following:
  • Start point: 0.388, -0.015
  • End point: 0.769, 0.922
You can jump back out of the style now and see what the finished style looks like:

You can download a project containing the above resources here.

In the next post we will create the chrome gradients we are going to need for our controls and create our first control style for the Button control.

Sunday, March 14, 2010

A Scalable Orb Panel-Button-Thingy

I think I may have mentioned before that I'm not much of an artist - thankfully the internet is chock-full of inspirational ideas. When browsing around some Photoshop tutorial sites, I came across an avatar of enzudesign on a forum; the avatar looks like this.

That's a cool looking button. So credit where credit's due - enzudesign is a talented artist. I wanted to try create something similar in Silverlight that would work as a button, following the same kind of process that I used for the Radio Button I did recently. This one has a LOT more content to it. My first attempt at it required 14 ellipses; 11 of those requiring a blur effect. It also meant containing those ellipses in about 18 proportional columns and rows. It looked nice, but performed poorly. I suspect all those blurs were chewing through a lot of CPU. I don't know how much CPU effort it takes to update those proportional values into real values when the whole thing is animating, but I'm guessing it contributed to the poor performance. This is what it looked like with and without the blur effects:


The blur gives the chrome surrounding ring a rounder appearance, and makes the black look more "rubbery". In comparison, the image without the blur looks stark. I guess this is quite subjective, but to my eyes, the blurred button looks nicer. But it's performance is terrible. I turned it into a button template and the transitions from Normal to MouseOver were not smooth at all. I've included a (collapsed) copy of the above design in the download. So it looked great but it had to lose some detail.

The result, after trimming it down, is embedded below, but I wanted to do more with it than just use it as a button. It occurred to me that this design would make a nice border for a panel; cut it into quarters and you have the corners. All it would need is filling in some horizontal and vertical banding to join the corners. "All it would need" actually turned out to be somewhat fiddly, but before I bore you with the details, the effect I was after needed to have two features:
  1. The button itself would scale nicely, keeping proportion where it needed to
  2. The transition from round orb to rounded panel should be as seamless as possible.
Here is what the "finished" product looks like (download link at the bottom of the post):


The button now consists of only 10 ellipses; 4 of those with blur effects. I won't go through the whole process of compositing the button since that was covered here. But I will quickly cover the process I used of turning an ellipse into a rounded panel.

Setting up the Panel Grid and Corners

I wanted a panel where the corners stay the same and, as the panel grows or shrinks, the "filler" between the corners would expand or contract to fill the gap. The first thing to do was to work out how to get just the appropriate part of each corner to show. I played around with a few options such as using a clipping path, an opacity mask, or using the Path | Subtract tool to convert the ellipses into just the quarter that was needed. These approaches all had their own problems: I don't like clipping paths because they are high-maintenance to animate in storyboards; the Opacity Mask has similar problems since it essentially just a brush that covers the whole object; using the Path | Subtract tool gave the right shape, but the blur effects and the gradients were now applied to just that quarter and proved too fiddly to work with. This was compounded by the fact that the button is not symmetrical across all four quarters.

In the end I discovered that if I confined the whole thing into a grid cell, but sized it so that it was twice as wide and twice as high as the cell, then the Grid automatically clipped out everything except the bit that fitted in the cell. So the here is what the containing grid looks like with the four corners in place:


The first and last columns, and the first and last rows, are each set to a fixed width/height (in this case 50 pixels). The middle columns and rows have their width/height set to "1*" to take up all the gap between the fixed rows and columns. Each corner has an entire copy of the "button" shapes sized to 100x100 pixels, and has it's horizontal/vertical alignment set to the appropriate values for each corner.

Although this provides the corners that I need, I'm not entirely happy with this approach because I can't easily animate the column width and height of the corner cells if I want the radius of the corners to change. In the embedded Silverlight app above, it is the button itself that is changing radius - not the panel grid. I would like to come back and address this at a later date to work out how to achieve this more easily. I have in mind that creating a custom control with template parts for the corners and horizontal fills would work - I'll have to experiment when I get the time.

The Fillers

The next stage was to create the fills between the corners and in the middle. For the top and bottom fills, this was achieved by copying the whole button structure, changing the ellipses into rectangles, and removing the columns from the containing grid to make the shapes span the whole grid. I had to change into the XAML view to change the ellipse tags to rectangle tags.

The resulting grid is sized the same as the corner shapes (100 pixels high) and restricted to the middle top grid cell to clip out the bottom half.

The same result is copied and pasted for the bottom fill and placed in the middle bottom cell.

The vertical fills are done essentially the same except there were a couple of areas that were a bit trickier because the button is asymmetrical vertically. I was able to remove a few of the shapes for the vertical fill because it's mostly just black between the chrome ring and the blue orb center. The trickiest part was the center blue part of the orb. The bit that goes from the the left side of the blue center to the right side of the blue center. For the other rectangles I just tweaked the existing gradients, but for the center rectangle I created the gradient from scratch using the color picker tool to get a selection of colors across the width of it.

The last part of the grid was straight forward, it is just a rectangle with a solid color fill. It stretches horizontally and vertically to fill the middle space.

The only other thing to point out is that I had to set the margins of the horizontal and vertical fill edges that met the corners to -3 so that there was no obvious seam. If you look hard you can still make out where the parts meet, but I don't think you would notice them unless you were specifically looking for them.

The Animation

The animation is somewhat of a cheat. It looks like the button shrinks, and then expands into a panel containing the buttons. There is actually two groups of controls here: the button is shrinking down in size, but then it's opacity animates to 0 to reveal the panel behind it (in exactly the same location). It is then the panel that grows to reveal the fillers, and then displays the buttons inside it. The whole process is reversed when the panel is closed.

I really like the end result, but I would like to come back and revisit this later to make the whole thing easier to replicate with a different design.

Download, including source, from here.

Friday, February 26, 2010

A Scalable Orb Radio Button

I saw this great looking glassy "uber" orb on the Expression Gallery page a while ago and got inspired to have a go creating something similar.

I had a search around for a Photoshop tutorial for something along the same lines and found this one.

The end result can be found (and downloaded) here, and looks like this:


The one thing I didn't like about the orb button on expression gallery was that it was not scalable. Trying to resize it to make a normal size radio button just didn't work.

So my first goal was simply to try and reproduce the look of the button from the Photoshop tutorial.

I was able to pretty much follow the Photoshop tutorial instructions, but the main difference was when the tutorial said to feather a selection, I just used the Blur effect in Blend.

The interesting part came when it got to step 7/8 in the tutorial. To get the reflection effect, I downloaded and compiled the WPF Pixel Shader Effect Library from Codedplex. The compiled assembly is included in the sample download from the Microsoft Expression Gallery.

It's hardly worth a step by step description of how to make this button since the Photoshop tutorial can be referenced for the general approach. However, there were a couple of interesting techniques that I will cover here in order to make this behave nicely as a scalable button.

Blur is Your Friend

Sometimes it can just be painful getting a gradient brush to do exactly what you want it to do. Trying to get a nice graduated fade into the background using a gradient brush seems to have mixed results depending on which colours I'm using. The blur does a great job here however. Applying a blur with a radius of 10 smooths the front ellipse into the background ellipse, and only requires a solid brush for the fill instead of trying to tweak a radial gradient brush.

This also comes in handy when you already have a gradient fill that you want to apply, as shown in the second image. The top ellipse has a linear gradient brush and the blend smooths it into the background quite nicely.

This will scale nicely too, providing a fixed blend area as the button grows or shrinks. This may sound like it is going against the principle of having proportional scaling, but I prefer this particular part of it not to scale proportionally - I want the same appearance of a thin curved surface at the edge regardless of the size of the button.

Getting it to Scale Nicely

As mentioned above, the "uber" orb button that got me interested in the first place, didn't scale well. It was initially constructed in a Canvas container so resizing didn't do anything. Changing it's container to a Grid allowed me to resize, but the resize didn't make the contents scale nicely.

None of this is a reflection on the designer (Thebirdbath) - since the goal of having it scale nicely was mine, not the designers.

So the trick to having it scale nicely is to use a grid with lots of columns and rows specified in star (*) widths, which are proportional. The way I did this was to:
  1. Build the button at a fixed size inside a grid with horizontal and vertical alignment set to stretch and using margins to position everything
  2. Calculate the offsets for each object inside the grid
  3. Turn those offsets into proportional columns and rows.
  4. Position each object in the appropriate column and row, with the appropriate column and row spans - and remove the margins
Sometimes when drawing a shape, Blend will set one of the alignments to something other than stretch. Trying to set the alignment on the properties sheet will usually change the position or size of the shape. The easiest way to convert the alignment is to use the little link icons that appear between a shape and the grid edges when you select the shape.

The measurement and conversion is not difficult, just tedious, So, for example, the second ellipse is 3 pixels in from all edges, and the whole button is 172 pixels wide and high, as I have created it initially. 3 pixels is 1.74% of 172 which converts into a column of width="0.0174*".

The next shape has a margin of 6 pixels all sides, but since we already have a column spanning the equivalent of 3 pixels we only need to make the second column 3 pixels also. The way I actually calculated this was to write all the margins down on the piece of paper, going in from both sides (doing columns first) and letting the middle remaining column take up the slack. I then calculated the pixels differences between the margins and converted them to a percentage for the column width. Rinse and repeat for the rows. Boring, but it does the job.

The columns were especially important for the "reflection" in the button.

The Reflection

The reflection was tricky. I toyed with the idea of just creating the deformed rectangular shapes by hand as paths, but I didn't think I would be able to achieve such a natural look (I'm not much of an artist). That's when I remembered the WPF Pixel Shader Effect Library on Codeplex. It has a SmoothMagnifyEffect that worked nicely. I combined the eight rectangles into a combined path and applied the effect, fiddling with the settings until I was happy with it. However it didn't seem to scale nicely at first, even though it was in the grid. As I scaled the button the changing size of the path would cause the effect to make it go completely out of shape. In the end, I put the path inside another grid along with a transparent ellipse. The path was positioned using rows and columns and the ellipse was allowed to fill the whole grid, which was the same size as the parent grid. This allowed the SmoothMagnifyEffect to be applied to the whole button size, affecting the window shape path roughly the same way at different sizes.

The Result

So with all the bits and pieces positioned in the grid, I end up with a button that scales well.

Blend makes it ulta simple to turn my grid with shapes into a button. The technique is covered on many other web sites, but it's as easy as right clicking the container (probably easiest in the object tree), selecting "Make into Control", and selecting Radio Button from the list of controls.

The Uber Button was a normal button, but I've always considered round buttons like this to work better as a Radio Button.

So the only thing left to do is to create the changes for the various states such as MouseOver and Checked etc.


A couple of things to remember when recording states:
  • Don't change the property of an object in two states from different state groups. With a Button this is usually not an issue, but with a Radio Button you have the CommonStates AND the CheckedStates. I had to duplicate an object that I wanted to appear differently in those two state groups.
  • Changing the easing function from the default can make a difference. I didn't do that in this example, but have done many other times - it can make a simple animation look natural and sophisticated.
Feel free to grab the radio button and use it for your own projects. It would be nice if you left me a comment below to let me know if you find it useful.

And remember that you can put whatever content you like inside the button! It doesn't have to be simple text - in fact if I was a better artist I would have made some nice icons to go inside them.