Introduction
I spent a few days scouring the tech articles trying to make
WPF act like WinForms in respect to creating a ToolTip style menu, including
images and drop-down menus. I found a
lot of hacks, but deep down I just knew that the WPF developers had to have implemented
some slick XAML method for creating menus that involved relative referencing of
images and vertical drop-down menus that weren’t hacked-together WinForm
throwbacks.
I was right.
However, I have yet to find a definitive reference. So I’m creating one. Hopefully this helps someone out there so
that you don’t go through the few days of trial-and-error that I went
through!
First step: Repeat after me: WPF is not WinForms. Repeat that about 100 times. Remove everything you know about embedded
resources from your mind. Also, erase
all knowledge of ToolTip driven menus. Ok… now that we have that out of the way, time to rebuild that knowledge
from a WPF worldview.
Tutorial
First, go create a WPF test project (or use your own, because we
all know that we all hate it when someone says, "Let’s do this in a test
project!" because really, you just want it to work in your current project). This will work whether you are using a
UserControl or a Window. It will work
whether you are compiling as a DLL or an EXE. I promise. I’ve tested it. And all the code below works ... because I’ve
compiled it, run it and tested it. I pinky-promise
that one… because my biggest pet-peeve with technet articles is code that you
know never saw the inside of the compiler.
- In your XAML, add a DataContext directive to
bind relative to self:
<Window x:Class="VisualMenus.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350" Width="525"
DataContext="{Binding
RelativeSource={RelativeSource Self}}">
Setting this property guarantees that you will bind to the
resources relative to the current data context (i.e., it won’t reach back to a
parent, out to a child, or off into the cloud… or any other such nonsense… lord
knows you wouldn’t want to bind to your Facebook photos by accident.
- Add your images. If you’re going to be using boring text menus, skip this step. But then again, if you’re reading this
tutorial, you’re not going to want to skip this step!
- Add a folder to your project called Images, or
Resources, or Foo… it really doesn’t matter.
- Add your images to your new folder. Easiest way to do this is to go and copy them
into the folder that was just created in Windows Explorer, and then suck them
into the project as existing files.
- Now, and this is REALLY important. For each and
every file, make sure that the Build Action for the image is set to "Resource",
and Copy to Output Directory is set to "Do Not Copy." This is the new-and-improved way to define
resources in WPF. You don’t need to add
them directly to your build preferences, and you don’t have to generate a RESX
file (remember, I said to forget all that!!) (I know, I’m the one who just brought it up… I can’t help it… I’ve been
trained over so many years… progress, not perfection, right?)
- Now, the fun part ... create your visual menu.
- Go back to your XAML for your Window or User
Control. Create a Menu. You can drag and drop the control from your
toolbox, or you can just type it out. I
prefer the latter ... old school, I guess.
- Pretty, huh? Ok, now we get to add the fun stuff. First, define a MenuItem.Icon tag for one of your menu items.
- Now, still in your XAML, go create a section
called <Window.Resources> above your <Grid>… or in the case of a
UserControl, you’re going to create a section called
<UserControl.Resources> and place it before your DockPanel, Grid or
whatever other top visual element exists in your XAML file. Add a <BitmapImage> resource definition
for the image you want to tie to your MenuItem. And then tie it to your MenuItem in the Image tag. Check out the example.
You will notice I added a couple of extra "prettification"
tags ... alignment, height, width, etc. All
of that is all you… go ahead and make the visuals appear how you want
them.
And this is what it looks like on the preview window. I love preview windows… just sayin’.
At this point, I’m going to run the app to show what this
menu looks like running, and to show that the image does, in fact, appear as
expected. Check it out!! It works!! (If you’ve just come across this
tutorial after a couple days of trying to get this to work, you’ll understand
my exuberant gleefulness.)
And look here ... the images are showing up, but not copied to
the bin directory:
- Ok, now it’s time to wire up the rest of ‘em and
move on to the drop-down portion of this fun, exciting happiness. Under your <Window.Resources> (or
<UserControl. Resources>) section, add a reference for each image you
want to add to your menu, then tie each reference to your menu by adding
MenuItem Icon Images and reference by Resource Key. Make sure each resource image has a unique
Key!!
Let’s test again, shall we?
- At this point, I’m going to go ahead and wire up
some event handlers.
- Menus are nothing if they don’t actually do
something, right? I’ve found the fastest
way to do this is to go to each MenuItem, type in Click="" and IntelliSense
will pop up the option for you to create a New Event Handler by double-clicking
the alert. You can rename the handlers
later, if you so choose.
And here’s the code-behind. I know… this is painfully obvious… but it might not be to some
people. Don’t judge!
- And now for those drop-down menus. You know the ones… the ones that were easy to
create using WinForms, but seem to be totally elusive in WPF? Yes, those. They’re not as hard as you
think. Go back to the XAML, and under
add the following code under one of your menu items:
<MenuItem.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel
Orientation="Vertical"/>
</ItemsPanelTemplate>
</MenuItem.ItemsPanel>
Now, add menu items under that
block of code and give them event handlers, too. Yes, it is THAT easy.
And ... the code-behind:
Wire up what you want it to do,
and you’re done!!
And yes, you can add images to
those drop-down menu items the same exact way you did above, and they appear
nicely to the left of the text. And
because I pinky-promised, here’s the complete code running:
Have fun with this, and happy
coding!!
Points of Interest
The one thing I learned during this exercise is that ... if you're going to write a code tutorial... try not to make assumptions! When you say something like "Change the Build Action to Resource" it would be exceedingly helpful to call out exactly WHERE to make that change. Everything in the project has a Build Action associated with it. And if you're reading a code tutorial (even mine) and something is unclear ... ask for clarification, because ... the rest of the world reading it is likewise wondering what was going through the author's mind when he or she was writing.
History
First draft