1
votes

I want to do a map editor for a game. Its a program that will have Windows Forms UI (like propertyGrid to edit object's properties) but it will also have a panel on which map will be drawn.

What i want:

When focus is on the panel with the map, i'd like to use keyboard to move map around (arrow keys), add objects (number keys) etc. When focus isnt on this panel, i'd like the buttons to work as normal in windows forms - allow to tab between controls etc.

My form looks like this:

It has a ToolStripControl that has a menuStrip (for main menu) and a statusStrip (for status bar). In the middle of the form (or toolstripcontrol), SplitControl is docked (dock=fill) that has two panels. Panel 1 has the PanelMap - a Panel that displays the map, Panel 2 has all other stuff like propertygrid, tabcontrols, buttons etc.

I have KeyPreview of form set to true and process keyboard events in form's keydown event handler.

Now, what happens is if i assign focus to PanelMap, next time i press an arrow key, NO KeyDown event fires. Not a single one! Even form which is supposed to process all events because it has "KeyPreview" doesnt get its even to fire. When i press an arrow, PanelMap loses focus towards the SplitControl.

Okay, i thought, maybe PanelMap is not supposed to ever have focus, lets give focus to SplitControl (if i press arrow key while it has focus, i can handle it so it doesnt go further). But then, if anything like a textbox that is inside something that is inside SplitControl has focus, then SplitControl CANNOT get focus. .Focus() will do nothing - focus remains in the whichever control that had it!

Why does it act so strange? Why doesnt Form's KeyDown fire when panel has focus and arrow key is pressed? Why doesnt SplitControl get focused when i call .Focus() even though CanFocus=true?

And ultimately, how do i achieve what i want? Is there a way to do it?

2
Override the form's ProcessCmdKey() to swallow keystrokes. Simply return false if you don't went the keystroke to be processed any further.Hans Passant

2 Answers

1
votes

I think you're running into the widgets taking the keystrokes before your events get to them for navigation. I had this issue, and did this:

    private void RemoveCursorNavigation(Control.ControlCollection controls)
    {
        foreach(Control ctrl in controls)
        {
            ctrl.PreviewKeyDown += new PreviewKeyDownEventHandler(MainWin_PreviewKeyDown);
            RemoveCursorNavigation(ctrl.Controls);
        }
    }

I call this function in the main form's Load handler, like this:

RemoveCursorNavigation(this.Controls);

In your PreviewKeyDown handler, you need to do this:

    public void MainWin_PreviewKeyDown(Object sender, PreviewKeyDownEventArgs e)
    {
        switch(e.KeyCode)
        {
            case Keys.Up:
            case Keys.Down:
            case Keys.Left:
            case Keys.Right:
                e.IsInputKey = true;
                break;
            default:
                break;
        }
    }

The e.IsInputKey = true; tells the outside that you've used this event, and don't want it going anywhere else.

Now you get to see the keystrokes before they go to the widgets, and you won't get navigation between them from the cursor keys.

0
votes

I found an answer like this:

I made a textbox that is hidden under a panel (but enabled and visible). This textbox is given focus when i want to "lock" focus on my PanelMap. It has onkeydown even with e.suppress = true so that textbox never gets any keystrokes to affect it.

Crude workaround but works wonders... typical M$ business...