Fixing MenuStrip, part 2: Visible vs. Available, and a repro case

Not all MenuStrips will exhibit the scrolling bug.

In a nutshell: if you ever hide any menu items, you’re living dangerously.

Visible and Available

First, an aside on how you go about hiding menu items.

ToolStripMenuItem has two visibility properties: Visible and Available. They both do the same thing, except when they don’t.

To be more specific, both their setters do the same thing. So if you want to hide a menu item, you can either set Visible to false, or you can set Available to false. Same thing. So why are there two properties for the same thing?

The difference is if you ever want to read the properties, to find out whether the item is already hidden. The Visible getter does not do what you want. Never use it. Reading Visible does not tell you “did I set Visible to true?” No, that’s what Available is for. (Obviously.) No, reading Visible tells you “is the menu currently popped up on the screen?” Which has a usefulness score of somewhere less than or equal to toe fungus.

Summary: always use Available. Never use Visible. The one exception is the form designer — Available isn’t shown in the Property Grid, so there you’re stuck with Visible.

The repro case(s)

Here’s the repro case I’ve been working with. There may be simpler repro cases, but this one is expressive enough to be interesting. Here are the contents of my File menu:

  • New
  • Open
  • Import (hidden)
  • Export (hidden)
  • Exit

If you write some simple code to create a MenuStrip and add those items, you’ll see the bug. If you use the form designer to do the same thing, you won’t see the bug.

That’s because the designer-generated code will instantiate all the menu items, then add them to the File menu’s DropDownItems collection, and then set all the menu items’ properties, including Visible. If you do that, with these five items, no bug. If you set Visible Available to false before adding the items to the menu, you see the bug. Don’t ask me, I didn’t write the buggy code.

However, if you only hide one of the menu items, not both, then you’ll still see the bug even with the designer. In fact, the menu will scroll so far you’ll only see the last item, followed by a whole bunch of white space.

If you show and hide menu items at runtime, you’ll never get back to the “bug with both hidden” state, at least not as far as I’ve been able to tell — that only happens if you hide them before you add them to the menu, and then never touch their Visible Available property again.

But anytime you’re only hiding one of the two — hiding Import and showing Export, or vice versa — you’ve got the bug. Design-time, runtime, whatever.

Simple repro

Create a new project and paste the following code into its Main method.

C#:

var Form = new Form();
var Menu = new MenuStrip();
var File = new ToolStripMenuItem("&File");
File.DropDownItems.AddRange(new[]{
    new ToolStripMenuItem("&New"),
    new ToolStripMenuItem("&Open"),
    new ToolStripMenuItem("&Import") {Available = false},
    new ToolStripMenuItem("&Export") {Available = false},
    new ToolStripMenuItem("E&xit")});
Menu.Items.Add(File);
Form.Controls.Add(Menu);
Form.Show();
Application.Run(Form);

Delphi Prism:

var Form := new Form();
var Menu := new MenuStrip();
var File := new ToolStripMenuItem("&File");
File.DropDownItems.AddRange([
  new ToolStripMenuItem("&New"),
  new ToolStripMenuItem("&Open"),
  new ToolStripMenuItem("&Import", Available := False),
  new ToolStripMenuItem("&Export", Available := False),
  new ToolStripMenuItem("E&xit")]);
Menu.Items.Add(File);
Form.Controls.Add(Menu);
Form.Show();
Application.Run(Form);

Feel free to play around with these. I did a test project with a couple of checkboxes so I could toggle the menu items’ visibility at runtime. It was kind of fun to poke at, but I still have no idea how they got it to screw up the way it does.

What’s next?

Tune in next time for the Microsoft-sanctioned workaround for their bug, and a bit of stumbling since it’s a pretty awful workaround. Hang in there, it’ll get better.

This post is part of the Fixing MenuStrip series.

Fixing MenuStrip, part 1: Introduction and screenshots

In .NET 2.0, Microsoft added MenuStrip and ContextMenuStrip controls to replace the old MainMenu and ContextMenu. The new ones support images next to menu items, edit boxes inside menus, etc. Fairly cool.

However, they’ve got a major bug that Microsoft doesn’t intend to fix. See the screenshot at right. Why the blank space at the bottom of the menu? And more importantly, what happened to the first menu item (the highlighted one) — why is it mostly cut off?

It’s easy to showcase this bug. For that matter, it’s easy to fix. (Why Microsoft can’t figure it out, I don’t know.)

In this “Fixing MenuStrip” series, I’ll demonstrate the problem, and then show how to make a very specific fix for a very specific scenario. From there, I’ll work my way up to progressively more general solutions. By the time I’m done, you’ll be able to fix every menu on the form (including context menus) with one method call.

The coolest thing is that there’s no need to descend from MenuStrip to fix it. You’ll be able to add a line of code saying “And by the way, this menu should support the keyboard,” and it will just start working. It’s basically declarative programming: you declare your intention — “I want my MenuStrip to work” — and somebody else takes over and figures out how to do it. Multicast delegates and lambda expressions FTW!

Along the way, I’ll provide code snippets in both C# and Delphi Prism (aka RemObjects Chrome, aka RemObjects Oxygene — somebody let me know when they stop changing the name). If people ask, I could probably cook up complete project files to download, though I’d rather spend my time writing about the interesting technical stuff. (And besides, my personal computer only has the command-line compiler for Prism, not the IDE, so project files would be a bit of a challenge — the project-file format appears to be undocumented.)

All of my Prism examples will work on .NET 2.0, since my main computer is a Windows 2000 laptop, and .NET 3.x requires XP. My C# examples, especially the later ones, will be for C# 3.0 since I want to use lambdas — I’ll just have to borrow Jennie’s computer when I want to write those articles.

Screenshots of the problem

Let’s start by showcasing the problem. Here are a couple of screenshots from a little sample app I wrote. In both, I’ve got a menu with three visible menu items: “New”, “Open”, and “Exit”. In the left picture, I used the mouse to highlight the “Exit” menu item. In the right one, I used the keyboard’s arrow keys to do the same thing.

The two should look identical. Selecting something with the mouse, selecting something with the keyboard — there’s no reason for those to work differently.

And even apart from that… I mean, just look at it. What the hell? The menu apparently just scrolls its contents up, even though everything fits without scrolling and there are no scroll arrows. It’s obvious that everything fits, because the left screenshot looks great — the selection rectangle isn’t cut off, and the margins are fine.

In this example, everything scrolls by 17 pixels, but that will vary in practice. I’ve seen one — a menu with around 10 or 15 menu items — that scrolled so far that you could only see the bottom half of the last menu item, hanging out there at the top of the menu. The rest of the menu was just blank white space.

So how serious is it?

In their response to the bug report, Microsoft notes that the issue is purely visual, and there’s no functionality loss. You can use the arrow keys to scroll the menu contents back into view.

I contend that that’s a poor excuse for making a crappy design. Little things matter. And Microsoft’s slipshod coding (and testing) makes my programs look like jokes, and me look like an amateur.

And besides, why put up with crappy code, when you can fix it? Tune in next time as I show code that reproduces the problem, and start working on a fix.

This post is part of the Fixing MenuStrip series.