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.

6 Answers 6

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);
        }
    }
}
7
  • 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
    Commented Aug 25, 2010 at 2:43
  • @HansPassant In Which cases exactly should one use Focus() rather than Select()?
    – Medinoc
    Commented Apr 3, 2014 at 10:57
  • A proper answer is a long one, use the Ask Question button please. Commented Apr 3, 2014 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? Commented Sep 18, 2014 at 14:58
  • The focusrectangle drawing leaves refresh errors all over the place when I scroll the contents by changing the scrollbar values.
    – Nyerguds
    Commented Nov 19, 2016 at 12:00
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
1
  • 5
    SelectablePanel() is the constructur in C#, it should be Public Sub New here.
    – KekuSemau
    Commented May 12, 2013 at 18:51
0

call focus in click event

private void Panel_Click(object sender, EventArgs e)
    {
        Panel.Focus();
    }
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();
}
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();
-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

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.