33

I am continuing to program some kind of keyboard navigation in my simple graphic program (using C#). And I ran into trouble once again.

alt text

My problem is that I want to process the keyboard input to move a layer around. Moving the layer with the mouse already works quite well, yet the control doesn't get the focus (neither KeyUp/KeyDown/KeyPress nor GotFocus/LostFocus is triggered for this control). Since my class derives from Panel (and overwrites a couple of events), I've also overwritten the events mentioned above, but I can't succeed in getting those events triggered.

I think I could manage to implement keyboard response either using something like Keyboard.GetState() or ProcessCmdWnd or something. However: I still have to be able to tell when the control got the focus.

Is there an more or less elegant way to add this ability to a user control (which is based on Panel)?

I've checked many threads in here and I might use this approach for keyboard input. The focus problem however still remains.

Thank you very much for information in advance!

Igor.

p.s.: I am programming in C# .NET v3.5, using VS2008. It's a Windows.Forms application, not WPF.

Community
  • 1
  • 1
Igor
  • 1,582
  • 6
  • 19
  • 49

6 Answers6

70

The Panel class was designed as container, it avoids taking the focus so a child control will always get it. You'll need some surgery to fix that. I threw in the code to get cursor key strokes in the KeyDown event as well:

using System;
using System.Drawing;
using System.Windows.Forms;

class SelectablePanel : Panel {
    public SelectablePanel() {
        this.SetStyle(ControlStyles.Selectable, true);
        this.TabStop = true;
    }
    protected override void OnMouseDown(MouseEventArgs e) {
        this.Focus();
        base.OnMouseDown(e);
    }
    protected override bool IsInputKey(Keys keyData) {
        if (keyData == Keys.Up || keyData == Keys.Down) return true;
        if (keyData == Keys.Left || keyData == Keys.Right) return true;
        return base.IsInputKey(keyData);
    }
    protected override void OnEnter(EventArgs e) {
        this.Invalidate();
        base.OnEnter(e);
    }
    protected override void OnLeave(EventArgs e) {
        this.Invalidate();
        base.OnLeave(e);
    }
    protected override void OnPaint(PaintEventArgs pe) {
        base.OnPaint(pe);
        if (this.Focused) {
            var rc = this.ClientRectangle;
            rc.Inflate(-2, -2);
            ControlPaint.DrawFocusRectangle(pe.Graphics, rc);
        }
    }
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 3
    Amazing! It really works and it was so easy to implement. I didn't know of the ControlStyles-class and thus wouldn't know that I could change it. Thank you very much :). – Igor Aug 25 '10 at 02:43
  • @HansPassant In Which cases exactly should one use `Focus()` rather than `Select()`? – Medinoc Apr 03 '14 at 10:57
  • A proper answer is a long one, use the Ask Question button please. – Hans Passant Apr 03 '14 at 11:03
  • @HansPassant It seems to me that OnEnter and OnLeave are not called reliably. Which means that the invalidation might be omitted. Is there some obvious mistake I've made or is this a known limitation? – NobodysNightmare Sep 18 '14 at 14:58
  • The focusrectangle drawing leaves refresh errors all over the place when I [scroll the contents by changing the scrollbar values](http://stackoverflow.com/q/14181304/395685). – Nyerguds Nov 19 '16 at 12:00
  • Add the Scroll event, call `this.Invalidate();` – Hans Passant Nov 19 '16 at 12:08
  • @HansPassant Ah, thanks. Should've known; I had to do the same thing to fix the scrollbars when scrolling through `OnPreviewKeyDown`. – Nyerguds Nov 28 '16 at 12:42
6

The code from Hans Passant translated to VB.NET

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Public Class SelectablePanel
    Inherits Panel

    Public Sub New()
        Me.SetStyle(ControlStyles.Selectable, True)
        Me.TabStop = True
    End Sub
    
    Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
        Me.Focus()
        MyBase.OnMouseDown(e)
    End Sub

    Protected Overrides Function IsInputKey(ByVal keydata As Keys) As Boolean
        If (keydata = Keys.Up OrElse keydata = Keys.Down) Then Return True
        If (keydata = Keys.Left OrElse keydata = Keys.Right) Then Return True
        Return MyBase.IsInputKey(keydata)
    End Function

    Protected Overrides Sub OnEnter(ByVal e As EventArgs)
        Me.Invalidate()
        MyBase.OnEnter(e)
    End Sub

    Protected Overrides Sub OnLeave(ByVal e As EventArgs)
        Me.Invalidate()
        MyBase.OnLeave(e)
    End Sub

    Protected Overrides Sub OnPaint(ByVal pe As PaintEventArgs)
        MyBase.OnPaint(pe)
        If (Me.Focused) Then
            Dim rc As Rectangle = Me.ClientRectangle
            rc.Inflate(-2, -2)
            ControlPaint.DrawFocusRectangle(pe.Graphics, rc)
        End If
    End Sub

End Class
0

call focus in click event

private void Panel_Click(object sender, EventArgs e)
    {
        Panel.Focus();
    }
Robert
  • 5,278
  • 43
  • 65
  • 115
0

To get the focus,check for MouseEnter event in Properties window.

Write below code:

private void mainPanel_MouseEnter(object sender, EventArgs e)
{
    mainPanel.Focus();
}
Digambar Malla
  • 335
  • 2
  • 4
  • 20
0

The simplest trick I use when for any reason I can’t use the parent Form KeyPreview property to make the Form handle key events, is to put a Textbox on

The panel:

Panel.Controls.Add(_focusTextBox = new TextBox() { Visible = true , Left = -300, TabIndex = 0});   

And use it to capture KeyDown event:

_focusTextBox.KeyDown += panel_KeyDown;

The last step is to set focus to this TextBox when other controls on the panel clicked:

_focusTextBox.Focus();
JNYRanger
  • 6,829
  • 12
  • 53
  • 81
Eng. M.Hamdy
  • 306
  • 1
  • 3
  • 12
-1

Panels are not getting focus, you have to select the panel if you want to track leave and enter events

call panel1.Select() in MouseClick Event

Taryn
  • 242,637
  • 56
  • 362
  • 405