2

I'm embedding a Colour Picker into a Context Menu using the Windows.Forms.ToolStripControlHost class. The picker displays fine and handles all mouse events properly:

enter image description here

The problem arises when one of the channel sliders is double clicked. This causes the control to add a Windows.Forms.TextBox into the parent control with the same dimensions as the slider so users can enter numeric values. When Enter is pressed while the TextBox has focus, it should assign the value and hide the textbox (which it does), but it also closes the entire menu structure. So, how do I keep the menu alive?

enter image description here

There's an awful lot of code involved but I'll post it if needed.

David Rutten
  • 4,716
  • 6
  • 43
  • 72
  • 2
    Wow, yeah. This seems like an incredibly fragile design setup. I'd barely trust myself not to inadvertently dismiss the picker before I was actually finished using it, much less my grandmother. For what it's worth, I'd *much* prefer a non-modal, floating tool window... – Cody Gray - on strike Jan 26 '11 at 12:19
  • @Cody, it actually works surprisingly well. I lock the other menu items when the picker receives focus so the only way to close the menu is to click outside of it, press Enter or Escape. – David Rutten Jan 26 '11 at 16:54
  • That might make this somewhat more workable. But how do you manage that, given the separation between the menu and its contents that you described in a comment to my answer below? Obviously I'm not sure how you've implemented this, but it seems like it would be a simple matter of extending that "locking" functionality to include trapping and suppressing presses of the Enter key as well. The hacky solution is to set a global flag. – Cody Gray - on strike Jan 27 '11 at 03:21
  • @Cody, I go into the parent menu and recursively iterate over all child items, locking them as I go. But you're right, I could use a similar mechanism to enable/disable response to Enter and Escape presses. – David Rutten Jan 30 '11 at 14:08

1 Answers1

1

Somehow, you'll need to eat the Enter key presses before they reach your context menu. Obviously, it's default behavior is to "select" the currently highlighted item when the user presses Enter, just like every other menu control known to man.

You would do that by subclassing the ContextMenuStrip control (if you're not doing so already), and overriding its ProcessCmdKey method. Watch for a keyData value corresponding to Keys.Enter, and when you detect that value, return True to indicate that the character was already processed by the control and prevent it from being passed on for any further processing. Everything else, of course, you'll let the base class process so the behavior of other keys (such as the arrow keys) is unchanged.

For example (I just tested this and it works fine):

public class CrazyContextMenuStrip : ContextMenuStrip
{
    protected override bool ProcessCmdKey(ref Message m, Keys keyData)
    {
        if (keyData == Keys.Enter)
        {
            // Eat it when the user presses Enter to
            // prevent the context menu from closing
            return true;
        }

        // Let the base class handle everything else
        return base.ProcessCmdKey(m, keyData);
    }
}

And of course, you could add extra checks to the above code so that the Enter key presses are only eaten when your color picker is visible, allowing things to work as expected all the rest of the time,

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Hmm, that's pretty bad news. The menu itself is not supposed to know about its content (it is filled out by several different classes at runtime), so deciding when to ignore an Enter press and when to act on it will be tricky on that level. I'll probably just disable the double-click-textbox-input feature on embedded colour pickers if this is the alternative. – David Rutten Jan 26 '11 at 16:52
  • @David: I'm not sure what you were expecting, then. I understand both the design and practicality merits of the menu not knowing about its contents, but it's difficult to imagine how you'd override its behavior in specific instances *without* that knowledge. I don't mean to sound like a broken record, but you should be prepared for bad news when you start trying to change behavior that is so ingrained in your platform's toolset. – Cody Gray - on strike Jan 27 '11 at 03:17
  • well I was hoping for a solution that didn't involve subclassing winforms controls and potentially breaking inheritance hierarchy. Maybe an existing property I managed to miss or an event I overlooked. I'm trying out your solution at the moment but I'm having a pretty hard time since I also have to deal with sub-menus. I'm now up to my eyeballs in overridden winforms classes and am running into internal framework types. To quote Lando Calrissian; "this deal's getting worse all the time!" – David Rutten Jan 30 '11 at 14:38
  • @David: Done right, subclassing should not be able to break the inheritance hierarchy. Polymorphism works in your favor to make everything "just work". I'm not sure what's going wrong in your design to be giving you this much trouble, or I'd offer a better solution. The `ToolStrip` control and it's brethren were *designed* to be subclassed (unlike the `MainMenu` and `ContextMenu` controls, which wrap the standard API menus), and their design is pretty thoroughly object-oriented for that reason. – Cody Gray - on strike Jan 31 '11 at 04:02