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..
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
In the properties window, give
the keyDown event a name like genericKeyDown, myKeyDown (the name does not really matter), then press the
enter key,
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.