12

I'm creating a custom control derived from UserControl that I would like to set focus to.

The custom control contains a ComboBox control and I draw some strings beside it.

The ComboBox can receive focus, but I would like to be able to set focus to the UserControl itself. My OnPaint handler is set up to draw the control slightly differently if it has focus but I call CanFocus() from the parent form when I create my custom control and it returns false.

Is there a property or something to set?

Sambo
  • 1,472
  • 5
  • 21
  • 33

7 Answers7

22

UserControl will fight you tooth and nail to avoid getting the focus. It has code that automatically passes the focus to a child control (if any) if it does get the focus. You'll at a minimum have to override WndProc() and trap the WM_SETFOCUS message. There might be other surgery needed, like ControlStyles.Selectable and the TabStop and TabIndex properties.

Your next issue is that UserControl won't respond meaningfully to, say, keyboard messages when it does have focus. You'll need to detect clicks on the UC background to handle mouse messages, as well as override the painting so it is obvious to the user that the UC has the focus (use ControlPaint.DrawFocusRectangle). If this starts to sound unattractive, it's because UC was really meant to be a container control.

Scott Baker
  • 10,013
  • 17
  • 56
  • 102
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    This seemed to do the job. I'll continue to play around with it to sort out the intricacies you mentioned. Thanks! – Sambo Mar 02 '10 at 12:48
  • 1
    +1 Glad to have another great answer from NoBugz. Curious: in this case do you think the OP might do better using a Form instead of a UserControl ? – BillW Mar 02 '10 at 14:20
  • 2
    @Bill: the Form class is a container control as well, although it won't fight back quite the same way. My advice would have to be to avoid non-standard UI practices. It confuses the user as well. – Hans Passant Mar 02 '10 at 15:49
  • 1
    If UserControl was meant to be a container control, is there something we can derive from that was meant to be a custom control? Should we just derive from Control or ScrollableControl? – Trevor Elliott Jun 22 '12 at 18:11
  • I know, it's years ago...but somebody else might trap on this question (like me). This here [link](http://stackoverflow.com/a/3562449/424478) should answer the question and is very simple – infero Aug 06 '12 at 22:08
1

In some cases it is also desireable to not let the focus move to child elements of a UserControl.
In this case you also need to set ControlStyles.ContainerControl to false.

Public Sub New()
    InitializeComponent()

    Me.SetStyle(ControlStyles.ContainerControl, False)
    Me.SetStyle(ControlStyles.Selectable, True)

End Sub
A.J.Bauer
  • 2,803
  • 1
  • 26
  • 35
1

From http://msdn.microsoft.com/en-us/library/system.windows.forms.control.canfocus.aspx

Remarks

In order for a control to receive input focus, the control must have a handle assigned to it, and the Visible and Enabled properties must both be set to true for both the control and all its parent controls, and the control must be a form or the control's outermost parent must be a form.

Ensure you have meet these prerequesits.

Community
  • 1
  • 1
David Waters
  • 11,979
  • 7
  • 41
  • 76
  • 1
    Thanks for your response... I've confirmed that my control has a value for Handle. The Control is Visible and Enabled too and the Panel I display it inside of is Visible and Enabled as well...and it all appears on a Form...! That seems to meet the pre-requisites... I overrode OnGotFocus() and set a breakpoint but it never got hit. Also, I set up a handler in my form object for the control's GotFocus event and, once again, the code never got hit. Any other ideas...? – Sambo Mar 02 '10 at 11:42
  • These prerequisites apply to Control, but UserControl sabotages this as @HansPassant mentioned above. – Scott Baker Sep 23 '14 at 17:01
1

too long for a comment, includes link, and code ... but this is a comment ...

Lots of people have complained about a UserControl not firing the 'GotFocus() event. For example : UserControl and GotFocus() fyi : LostFocus() will fire as expected, in my experience. In the past, in a multiple Forms project, I've experimented with implementing 'Enter and 'Leave event handlers on a UserControl on each Form, and found that 'Enter is only called on Form load, once.

Evidently the Controls on the UserControl "take focus" (in a way I can't explain, but perhaps one of SO's WinForms gurus will). Perhaps this is related to the fact that UserControl descends from ContainerControl ?

I experimented with writing one 'GotFocus() handler :

    private void Control_GotFocus(object sender, EventArgs e)
    {
        Console.WriteLine("Control GotFocus : " + ((sender as Control).Name));
    }

And then, in the UserControl 'Load event, wired up all the Controls on the UserControl to that event handler : what I observed was that the Control on the UserControl with the lowest TabIndex would fire the 'GotFocus event just on launching the app, and on switching between Forms.

The only other thing I've seen mentioned in this situation is to make sure the 'IsTabStop property of the UserControl is set to 'True : this was from Shawn Wildermuth at MS in the context of a SilverLight related question, so no idea if this might apply in your case.

Another suggestion, which was to write a MouseDown or MouseClick event handler for the UserControl, and in that call: this.SetFocus(); led me nowhere.

Hope you get an answer !

BillW
  • 3,415
  • 4
  • 27
  • 46
0

Say, you have a picture on your user control and you want to highlight it mimicking the "GetFocus" event (say focus on your user control takes this picture). The focus on your user control will be handled by drawing an outline dashed line to the PictureBox. This is done through your user control OnEnter and OnLeave events. Here is the highlight procedure...

Public Sub highlightImage()
    Dim l As Single() = {2, 2, 2, 2}
    Dim p As New Pen(Color.Gray, 1)
    p.DashPattern = l
    Dim g As Graphics = picColor.CreateGraphics()
    g.DrawRectangle(p, 0, 0, picColor.Width - 1, picColor.Height - 1)
End Sub

These two overrides will do the job.

Protected Overrides Sub OnEnter(e As EventArgs)
    MyBase.OnEnter(e)
    Me.highlightImage()
End Sub
Protected Overrides Sub OnLeave(e As EventArgs)
    MyBase.OnLeave(e)
    MyBase.Refresh()
End Sub
Sam Saarian
  • 992
  • 10
  • 13
0

If UserControl gets focus, it internally passes the focus to its child control.

So you'll need to skip execution of the code which sets focus to child control. For that you'll need to override WndProc() skip execution of any WM_SETFOCUS message.

 public class FocusableUserControl : UserControl
 {
    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case (int)Win32Constants.WM_SETFOCUS:
            //Returning from here will skip setting focus to child controls.
            //It will not skip setting focus to this control.

                Console.WriteLine("FocusableUserControl is focused: " + Focused);
                return;
        }

        base.WndProc(ref m);
    }
}

Where WM_SETFOCUS is "0x0007".

Kamalesh Wankhede
  • 1,475
  • 16
  • 37
0

Hans is right, a UserControl will do everything it can to hand off focus to a child control, expect in this one sneaky case:

  • All the children have Enabled == false

What this means is that with a little prep-work, the UserControl will begrudgingly accept focus. Try something to this effect:

var controls = this.Controls.Cast<Control>().ToList();
controls.ForEach(control => control.Enabled = false);
this.ActiveControl = null; //the UserControl will try to remember its ActiveControl
this.Focus();
controls.ForEach(control => control.Enabled = true);
Ryan Naccarato
  • 674
  • 8
  • 12