How to Set Focus to Any Form Control


Form1.gif

This article will show you how to move focus from one control to the next without the need to name the controls.

There are two methods that most programmers use to shift focus to the next control in order of preference, the first is to use the TAB key (the lazy way), second is to write specific code in the key down event for each control in a group.

Using the TAB key requires that the TabStop property is set to true and the TabIndex property is set to the sequential index for each control, (1,2,3,4,5,6,7...) but this does not allow you to have two sets of sequential indexes (1,2,3,4 …) or (1,3,2,4,7,5,8,...)

In the above screen shot there are 3 distinct groups of controls (13 controls in all) none have been given Name's to distinguish them from each other yet pressing the enter key on any one of them will move focus to the next control in that groups sequence.


So how is this done, the answer to this is in the keyDown event of each control, the sender is parsed, from sender we can get the type by checking the sender.GetType().Name and casting it to the appropriate control type, textBox, ComboBox etc..

Form2.gif

When you place a control on a form, panel or a page on a tabbed control you can double click on the keyDown event in the properties window to generate its keyDown handler, example.

private void button2_KeyDown(object sender, KeyEventArgs e)
{
}

This is OK if you want an event handler for each control and write code in each and every one of them, not a really good idea if you have a dozen or more controls on your form, a better way is to create a generic keyDown handler to handle ALL controls regardless of who owns the control.

The way to do this is easy; select all the controls that you want the your event handler to manage

Form3.gif
In the properties window, give the keyDown event a name like genericKeyDown, myKeyDown (the name does not really matter), then press the enter key,

Form4.gif
private void genericKeyDown(object sender, KeyEventArgs e)
{
}


At this point you have associated all the selected controls with your event handler, the next thing to do is to decide what the handler will do.

In this example I will use the Enter key as the trigger to determine which control on the form / Panel / Tabbed Control or other to focus on next.

How will your handler know which is the next control in order of entry,  Unlike TabIndex, the Tag property is the used, it allows you to assign an int value representing the rank order of each control beginning with 1 or whatever other number you want to start with but best of all you can have controls grouped on panels, tab pages or group boxes with the same numbers whereas the TabIndex will only allow one occurrence of a tabIndex on a form.

Lets begin by showing some basic form code.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using LizardKeyDownWorker;

namespace WindowsFormsApplication1
{
  public partial class frmMain : Form
  {
    lizardHandler control = new lizardHandler();

    public frmMain()
    {
      InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
 
    }

    private void genericKeyDown(object sender, KeyEventArgs e)
    {
      if(e.KeyCode == Keys.Enter)
        control.moveNext(sender, tabControl1);
    }
 
 }
}

Without seeing the form design it would be impossible to know that there are 3 groups and 13 controls on the form, yet the keyDown event handler can shift focus to any control on the form without explicitly writing code to do so.

So how is it done, it is not as hard as it seems, in fact it is really quite easy, you can do it in the form that has the controls or write a class that does all the logic for you and the only thing you need to do is to send the object to a method / function in the specialized class.

The best way is of course to write a class that can be used in any project or form.

Before the form constructor, instantiate the lizardHandler class, this will make it global to the form.

lizardHandler control = new lizardHandler(); //class

There is only one publicly exposed method in the class; you can have others like movePrevious, moveFirst, moveLast, it is up to you what you do with this class.

The main method / function is

control.moveNext(sender); //method / function

All you then need to do in your key down event is.

private void genericKeyDown(object sender, KeyEventArgs e)
{
 
  if(e.KeyCode == Keys.Enter)
    control.moveNext(sender);
}

This takes care of the form side of things…

------------------------------------------------------        ooooOOOOOooooo        ------------------------------------------------------       

This is the LizardKeyDownWorker class

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Collections;

namespace theLizard
  {
  //a generic handler for any control on forms, panels, tab control pages etc...
  //uses the Tag property for the order of entry or which control to focus on after the enter key is pressed
 
//this can be used on any form for any control with a key down event.

  //the key to how it works is casting the object to an appropriate control type, getting the current tag index then finding the next tag index

  In its simplest form

 /*

   if(nextObjectTagIndex == thisObjectTagIndex + 1)
    {
    object.Controls[whichObjectIndex].Focus();
    }

  */
 
  struct stats
    {
    public int last_index;
    //.... add any other member that may be useful for you
    //....
    }
 
//----------------------------------------------------------------------
class lizardHandler
    {
    int i = 0, next = -1, thisIndex = -1;

Declare a controls collection to hold a reference to all the controls on the parent container.

   Control.ControlCollection Ctrls = null;

 Instantiate the stats (give this any name you want)

public stats args = new stats();
//--------------------------------------------------------------------

This is the method that shifts the focus for you

public bool moveNext(object o)
  {
  next = -1;
 
//get the controls collection from the control parent....


The first thing that we need to know is who owns the control and how many controls are there on the parent container.

  if((Ctrls = ownerControls(o)).Count <= 0)
     return(false);


Next we need to know what the Tag index is of the current control is using the internal method getTagIndex(object o), if this returns negative one (-1) there is nothing to do so return false.

  //get the tag property of the control
  if ((thisIndex = getTagIndex(o)) == -1) 
    return(false); 

At this point we have thisIndex so look for the NEXT index to focus on, again using the getTagIndex(object o) method.

We also have the collection of controls on the parent container by reference (Ctrls[n]) so we do not care who owns them

//have the controls collection of objects parent
 for (i = 0; i < Ctrls.Count; i++) 
   {
  
//is there a control with 1 more than thisIndex in the parents control collection?


Compare the index of the control Ctrls[I] with thisIndex, is it thisIndex +1?

   if ((next = getTagIndex(Ctrls[i])) == thisIndex + 1)
     {

We have the next control in order, set focus on it and set the args.last_index value

     Ctrls[i].Focus();
     args.last_index = next;
     return (true);
     }
   }
     
   //if it gets here, we have reached the end of the line, nothing more to do...
  return (false);
  }
 
//---------------------------------------------------------------------
internal int getTagIndex(object o)
      {

Get the index from the tag property by casting object o to the type of control determined by GetType().Name => ((TextBox)o).Tag.ToString())

      //cast object to control type, get the tag and return as integer
      switch (o.GetType().Name)
        {
        case "TextBox":  
          return(int.Parse(((TextBox)o).Tag.ToString()));
        case "ComboBox":                               
          return (int.Parse(((ComboBox)o).Tag.ToString()));
        case "CheckBox":                               
          return (int.Parse(((CheckBox)o).Tag.ToString()));
        case "ListBox":                               
          return (int.Parse(((ListBox)o).Tag.ToString()));
        case "DateTimePicker":                        
          return (int.Parse(((DateTimePicker)o).Tag.ToString()));
        case "TreeView":                               
          return (int.Parse(((TreeView)o).Tag.ToString()));
        case "RichTextBox":                           
          return (int.Parse(((RichTextBox)o).Tag.ToString()));
        }

      //if it gets here, the control type has not been declared
      return (-1);
      }
    //---------------------------------------------------------------------
    internal Control.ControlCollection ownerControls(object o)
      {
//get the type of control parsed by object o and return the controls collection of the controls parent;

Get the controls collection from the controls parent by casting object to control type determined by GetTYpe().Name and accessing its Parent property => (((TextBox)o).Parent.Controls)

      switch (o.GetType().Name)
        {
        case "TextBox":
          return (((TextBox)o).Parent.Controls);      
        case "ComboBox":
          return (((ComboBox)o).Parent.Controls);
        case "CheckBox":
          return (((CheckBox)o).Parent.Controls);
        case "ListBox":
          return (((ListBox)o).Parent.Controls);
        case "DateTimePicker":
          return (((DateTimePicker)o).Parent.Controls);
        case "TreeView":
          return (((TreeView)o).Parent.Controls);
        case "RichTextBox":
          return (((RichTextBox)o).Parent.Controls);
        }
 
      //return the owner of the control as an object
      return (null);
      }
    }
  }


To be able to focus on other controls not shown in these switch statements, simply add to the switch the type of control you want covered.

I have shown you how to move from one control to another for ANY form in ANY project without writing ANY specific code for the form or project, all you need to do is include the class in your project(s).

Conclusion, by careful thought you can create classes that will do mountains of work for you with little code, all you need to do is think outside the square and apply the same principles to other repetitive tasks you may have.