Home > WPF > Menus with Style

Menus with Style

February 7, 2009

With all of the power that WPF provides, it’s easy to get lost in all of the details, especially when the tools that we have to create WPF programs with don’t always make things as easy as we’d like.

An interesting problem that I came across at work recently was a problem with menus and how to create a seamless transition from the menu item to the menu popup. Let me show you an example:

image 

This is the menu from the Windows Live Writer with a few modifications. One, the highlight color around the Edit menu item has been changed to match the menu background and the line that separates the Edit menu item from the menu has been removed. This makes a nicer looking menu in my opinion, so I’m going to show you how you can leverage WPF to create a menu just like this one.

Of course, the menu popup is only part of the menu, let’s look at what the whole thing will look like.

Standard Menu Look

image

Mouse Over Menu Look

image

Menu Item Focused

image 

Getting Started

There are a few ways that we can go about creating the templates for this menu, but probably the easiest will be to use Blend as it has the ability to generate the full template for the menu with just a few mouse clicks.

  1. Open Blend
  2. Create a new WPF Application, I named mine MenuTemplate
  3. Add a new Menu control to Window1.xaml
  4. Add a few menu items to the menu, i.e. File, Edit, View
  5. Right click on the menu (not the menu items) and choose Edit Control Parts (Template) –> Edit a Copy…
  6. You’ll be asked to name the style resource, I named mine WindowsLiveMenuStyle and selected Application from the Define in section.
  7. Click OK

Now Blend should have opened up the App.xaml and you should see the design-time for editing the menu border. What we need to do first is go over the Resources tab in Blend and find the MenuBackground resource. We’re going to change that so that it looks like our example.

  1. Find the MenuBackground resource under App.xaml in the Resources tab
  2. Open the brush editor for the resource
  3. Change to a solid color brush
  4. Set the RGBA slider values to R 229, G 238, B 248, A 100%

There we have it, our resource. Now, you might not see the update on the design surface and that’s because the menu color is being driven by a static resource and Blend isn’t reloading it correctly. Oh well. 🙂 Just switch back to Window1.xaml and you’ll see our newly colored menu.

That was the easy part, the menu isn’t much more than a Border and an ItemsPresenter. Now we’ll move on to the menu items and this is where the rest of our work will be.

The Menu Item

Like I mentioned above, this is actually where the majority of our work will be done. The reason for this is a little strange, but all of the menu items are retrieved from a ComponentResourceKey. If you want to find our more about it, use your favorite search engine as it’s really out of scope for what we’re doing here; we don’t need to know the why, just understand what to update. 🙂

Ok, we want to create the template for the menu item. Here’s one way to do that:

  1. Right click on the File menu item from the Objects and Timeline area and choose Edit Control Parts (Template) –> Edit a Copy…
  2. Give the style a name, I named mine WindowsLiveMenuItemStyle and define it in the Application again
  3. Click OK

Ok, now the menu item is selected and displayed in the Blend editor. Looking at the example above, we need to change the text color of the menu item. There are a couple of places that we can do this, but the best place is on the WindowsLiveMenuItemStyle itself. To do that:

  1. Switch to the Resources tab
  2. Find the WindowsLiveMenuItemStyle and click the Edit Resource icon (the icon to the right of the style name)
  3. This makes the style active and shown in the Blend designer
  4. Switch to the Properties tab
  5. Find the Foreground property
  6. Click the Solid Color Brush icon
  7. Set the slider values to R 100, G 100, B 100, A 100%
  8. Then turn this value into a resource by clicking the property marker (the little box on the far right of the foreground property) and select Convert to New Resource…, I named mine WindowsLiveMenuItemForeground
  9. Click Ok

Alright, all that and we got the color of the menu item to a dark gray! If you open up Window1.xaml, you’ll see that your menu item text is gray and the others are black. If they are all black, you probably edited the foreground for the WindowsLiveMenuStyle instead of the WindowsLiveMenuItemStyle. Not a problem, just reset the foreground property on the WindowsLiveMenuStyle using the property marker and change the foreground of the WindowsLiveMenuItemStyle to the new resource you just created (WindowsLiveMenuItemForeground).

The Hover Over

Well, things are starting to fall into place, but we still have a ways to go. Let’s focus on the hover over for the menu item right now. To start, we need to take a look at the template as there are some items in it that we aren’t going to be needing. There’s a couple of ways to do this, but one of the easiest is to:

  1. Switch to Window1.xaml
  2. Right click our menu item in the Objects and Timelines section
  3. Select Edit Control Parts (Template) –> Edit Template

In the Objects and Timelines section, you’ll see there are various parts of the template, including Outer Border, Bg, and Inner Border. If you observe the default behavior at runtime, you can see that the Inner Border is used to create the inner rectangle highlight for the menu item; we don’t need this for our template so you should go ahead and delete this. We also won’t be needing the Outer Border item so you can delete that too.

Looking at our sample screenshot above, we know that we want our corners to be square and not rounded, so we need to make some modifications.

  1. Select Bg
  2. In the Properties tab, change the RadiusX and the RadiusY value to 0

Now comes some of the more tricky parts, well, if you’re not familiar with Blend and the way it allows you to change properties for triggers. Above the Objects and Timelines section there is one called Triggers. In there you’ll see a listing of all of the current triggers for your selected items. We’re going to need to change the IsHighlighted = True trigger so that we can change the color of the background and the color of the edges.

image

 

To do this do the following:

  1. Select Bg from the Objects and Timelines section
  2. Select the IsHighlighted = True trigger
  3. In the Properties tab change the Stroke brush to R 186, G 189, B 193, A 100%
  4. Change the Fill brush to R 212, G 218, B 226, A 100% (you’ll have to click on the property marker and select Convert to Local Value as it is currently being driven by a resource)

If you’d like to convert those into resources, that is a completely reasonable thing to do and really is the right thing to do. For brevity, I’m not going to do it or explicitly tell you to convert color values into resources as I’ve already shown you how to do that in the previous section.

Ok, we now how the mouse highlight working. In order to really move any further with our example, we need to add some menu items under each top level menu item (i.e. File, Edit, View).

Here’s what our menu currently looks like in action.

image

image

 

Things are looking pretty good, though there are some sore spots. If you look really closely, you’ll see that the corners aren’t square anymore and there there seems to be an inner border of some sort projecting color. Our menu items should be looking like this:

image

The problem is that when we added submenu items to the top level menu item, we actually change the type information that is used for the key lookup for the control template to use. How do we fix this? Well, that’s the easy part. Now that we know what the problem is, we just go back and get rid of the InnerBorder and the OuterBorder of this control template by doing the following:

  1. Switch to Window1.xaml
  2. Right click on the File menu item from the Objects and Timelines section
  3. Select Edit Control Parts (Template) –> Edit Template

Now we can delete the InnerBorder and the OuterBorder items just as we did before. Now when we run our app we get the following when we expand the menu:

image

Notice the square edges and the missing inner highlight. The background color is still wrong, but that is due to work we’ve yet to do.

 

Cleaning up the Colors

Alright, let’s tackle cleaning up the colors of the expanded menu. You should notice that there are some problem areas compared to what our intended screen shot is.

  1. The background color for the menu item
  2. There is a gutter area with a separator line in the menu
  3. The menu item is gray instead of black

Let’s start by fixing the background color for the menu item.

For the background color, there are actually two different triggers that we will need to address:

  1. IsKeyboardFocused = True
  2. IsSubmenuOpen = True

The first is for when the user presses the ALT key and arrow keys left and right through the menu. What we want here is the same behavior as if the mouse was hovering over the item. The second is for when the menu is opened. This is where we want the background color to be the same as the popup menu.

To fix the background color for the keyboard focus trigger we need to make sure we are editing the template for the menu item and then:

  1. Select Bg
  2. Select the IsKeyboardFocused = True
  3. In the Properties tab change the Fill to the resource used for the hover over background (you did make that a resource didn’t you ;)).
  4. Change the Stroke to the resource used for the hover over stroke

Note: You will also need to update the IsHighlighted = True trigger Fill and Stroke properties as cleanup work due to adding new menu items.

To fix the submenu trigger, we need to take a look at our screen shot for what we want to do.

image 

If you’re like a lot of new WPF developers you won’t really notice what the problem is right away, and I’ve somewhat deceptively didn’t tell you about the problem. 🙂 If you look at the picture, we need to have the left, top, and right edges have a color and the bottom edge have no color for the menu item.

Still wondering what the problem is, aren’t you?

Well, Bg is a Rectangle and the Rectangle control doesn’t allow you to specify that information. However, there is a control that does, and that is a Border control.

I know… you’re probably thinking, “Man, I wish this guy would have just told me this upfront.” Well, I thought about it, but it seemed too much like “magic”. I wanted to teach you how to do the menu modifications in the most linear approach possible (i.e. the way that I think most people would have attacked the problem) and show you how to fix your problems as they surface instead of provide all of the answers to you at once. Ok… enough of my excuse as to why I torment you, let’s fix it.

We’re going to need to head into the XAML land to make this change. Make sure that Bg is selected and click the Split item to show both the Designer and the XAML.

Now we need to make a few modifications:

  1. Change Rectangle to Border
  2. Change Fill to Background
  3. Change Stroke to BorderBrush
  4. Change StrokeThickness to BorderThickness
  5. Change RadiusX and RadiusY to CornerRadius=”0″

Alright, the Designer should now be in a valid state again and showing you a preview of the template. Of course, you’re going to have to go back and fix your trigger settings based on Fill and Stroke changing.

If you try and F5 your program now, you’ll also see some errors in the error window. You should just have to delete those lines from the XAML and you’ll be back in good shape.

We have two more things to do to fix up the menu item for when the menu is open:

  1. Select Bg
  2. Select the IsSubmenOpen = True trigger
  3. Change the Background property to R 240, G 240, B 240, A 100% (might be smart to make this a resource ;))
  4. Change the bottom Border Thickness to 0

If I haven’t messed you up too much your menu should look something like this:

image

This wraps up the work that needs to be done for the menu item. The final steps will be to cleanup the menu popup.

Final Steps: The Menu Popup

I hope I haven’t confused you too much up until this point and that you’ve been able to create the menu. I know I kind of gave you a wrench on that last one so I’ll take the less “abrasive” route this time and not make you do too many steps that you’ll only have to go back and repeat with new controls. 🙂

First thing we’re going to do is remove the gutter visuals from the menu.

In the Resources tab you’ll need to edit the resource for the SubmenuContent

image 

Ok, once you have the resource up in the designer, you’ll want to delete the three Rectangle controls.

Your menu should now look like this:

image

Now the color of the menu items is not quite what is should be. In the initial setup I said that we should put the color in the style for the WindowsLiveMenuItemStyle… well, that turns out to be problematic. 😉 (Of course is it does). What we really want to do is to remove that from the style and simple change the Foreground on our top level menu items to be WindowsLiveMenuItemForeground.

Be sure to update all of the menu items to use the WindowsLiveMenuItemStyle or they will inherit the Foreground from the root item and all be gray.

After those modifications our menu’s look like this:

image 

The last thing to do is to fix the borders around the popup menu and change the background color there, so let’s get to it.

In the Resources tab we need to select the SubmenContent item again (as we did above).

  1. Right click on the root Border control and select Group Into –> Grid
  2. Clear out the Width and the Height of the Grid so that they are Auto
  3. Move the ContentPresenter to be a child of the newly created Grid
  4. Delete the Grid that is the child of the Border
  5. Right click on the Border control and select Group Into –> DockPanel
  6. Add another Border to the DockPanel
  7. Move the DockPanel to be the first child of the Grid
  8. Select the first child Border of the DockPanel
  9. Set its Dock property to Left
  10. Set the Background brush to WindowsLiveMenuPopupBackground
  11. Set the Border brush to WindowsLiveMenuItemHoverEdge
  12. Set the BorderThickness to Left 1, Top 0, Right 0, Bottom 1
  13. In XAML, set the add the following attribute Width=”{Binding ElementName=Bg, Path=ActualWidth}”
  14. Select the DockPanel
  15. Check the LastChildFill property
  16. Select the second Border child of the DockPanel
  17. Set the Background brush to WindowsLiveMenuPopupBackground
  18. Set the Border brush to WindowsLiveMenuItemHoverEdge
  19. Set the BorderThickness to Left 0, Top 1, Right 1, Bottom 1
  20. Set the Margin to Left –1, Top 0, Right 0, Bottom 0

The hierarchy should look like this:

image

And when we F5 the project we should get a menu that looks like this:

image

And there we have it!

Final Cleanup

One piece of cleanup that I did was to remove the WindowsLiveMenuStyle x:Key value from the style that we created so that we only had the x:TargetType specified. This way I don’t need to go through and set each of the styles to that key for all of the changes to get applied. I also went and changed the Foreground for all of the root items to be WindowsLiveMenuItemForeground.

The astute reader probably noticed that I didn’t mention how to go about changing the highlight of the menu item. Well, I’ll go ahead and do that now, but I suggest that you try it out for yourself to check your understanding and to see if you really did learn anything from this post.

You’re going to want to find the SubmenuHeaderTemplateKey item and click on the Edit Resource button.

image

With that opened, we’ll go ahead and delete the InnerBorder as we won’t be needing that.

Now we just follow these quick steps and we’ll be on our way:

  1. Select Bg
  2. Set the RadiusX and RadiusY values to 0
  3. Set the Stroke brush to R 159, G 191, B 200, A 100% (make it a resource?)
  4. Set the Background brush to R 212, G 222, B 244, A 100%
  5. Set the Margin to Left 2, Top 0, Right –2, Bottom 0

You’ll also want to expand the menu on Window1.xaml until you get into the submenu items and right click Edit Control Parts (Template) –> Edit Template so that you can edit the template for the SubmenuItemTemplateKey as well.

After doing all of that you should have yourself a nice looking menu that looks something like this:

image

Here’s the link to the sample project: MenuTemplate.zip

Advertisements
Categories: WPF Tags:
%d bloggers like this: