3

I have several TextBoxes and I want the cursor to move from one TextBox to another using arrow keys.

How can I do that?

It looks like this, also the tap moves vertically which is weird.

Thanks.

enter image description here

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Liban
  • 641
  • 5
  • 19
  • 32

4 Answers4

4

You can override the form's ProcessCmdKey method, and handle the key press and focusing on text boxes in it.

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

namespace TextBoxes
{
    public partial class Form1 : Form
    {
        List<TextBox[]> _textBoxes;
        public Form1()
        {
            InitializeComponent();

            //Getting a 2 dimentional structure to hold textboxes
            this._textBoxes= new List<TextBox[]>();

            TextBox[] row1 = new TextBox[4];
            row1[0] = textBox1;
            row1[1] = textBox2;
            row1[2] = textBox3;
            row1[3] = textBox4;

            TextBox[] row2 = new TextBox[4];
            row2[0] = textBox5;
            row2[1] = textBox6;
            row2[2] = textBox7;
            row2[3] = textBox8;

            TextBox[] row3 = new TextBox[4];
            row3[0] = textBox9;
            row3[1] = textBox10;
            row3[2] = textBox11;
            row3[3] = textBox12;

            TextBox[] row4 = new TextBox[4];
            row4[0] = textBox13;
            row4[1] = textBox14;
            row4[2] = textBox15;
            row4[3] = textBox16;

            this._textBoxes.Add(row1);
            this._textBoxes.Add(row2);
            this._textBoxes.Add(row3);
            this._textBoxes.Add(row4); 
        }

         /// <summary>
         /// Processes a command key.
         /// </summary>
         /// <param name="msg">A <see cref="T:System.Windows.Forms.Message"/>, passed by reference, that represents the Win32 message to process.</param>
         /// <param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys"/> values that represents the key to process.</param>
         /// <returns>
         /// true if the keystroke was processed and consumed by the control; otherwise, false to allow further processing.
         /// </returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            //A key was pressed! 
            Control activeCtrl = this.ActiveControl;
            TextBox txb = activeCtrl as TextBox;
            //Handle it only if it was sent when a textbox was focused
            if (txb != null)
            {
                int row;
                int column;
                //find out which text box is currently active
                this.GetTextBoxIndexes(txb, out row, out column);

                //change indexes according to key stroke
                if (keyData == Keys.Up)
                {
                    row--;
                }
                else if (keyData == Keys.Down)
                {
                    row++;
                }
                else if (keyData == Keys.Left)
                {
                    column--;
                }
                else if (keyData == Keys.Right)
                {
                    column++;
                }
                //Make sure we are not in negative / out of ranfe index
                row = Math.Abs(row + 4) % 4;
                column = Math.Abs(column + 4) % 4;

                //focus requested text box
                TextBox slected = this._textBoxes[row][column];
                slected.Focus();

            }
            //don't forget not to break the chain...
            return base.ProcessCmdKey(ref msg, keyData);
        }

        /// <summary>
        /// Gets the text box indexes.
        /// </summary>
        /// <param name="txb">The texbox.</param>
        /// <param name="row">The out row index.</param>
        /// <param name="column">The out column index.</param>
        private void GetTextBoxIndexes(TextBox txb, out int row, out int column)
        {
            row = -1;
            column = -1;
            for (int rowIdx = 0; rowIdx < this._textBoxes.Count; rowIdx++)
            {
                TextBox[] currRow = this._textBoxes[rowIdx];
                for (int colIdx = 0; colIdx < currRow.Length; colIdx++)
                {
                    TextBox currTextBox = this._textBoxes[rowIdx][colIdx];
                    if (currTextBox.Equals(txb))
                    {
                        row = rowIdx;
                        column = colIdx;
                        return;
                    }
                }
            }
        }
    }
}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Avi Turner
  • 10,234
  • 7
  • 48
  • 75
  • I wrote the code for a 4x4 matrix of textboxes. It could easily be modified to 5x4... – Avi Turner Jun 04 '13 at 06:03
  • i changed 5x4, but still is `out of index`, i changed this : `row = Math.Abs(row + 5) % 5; column = Math.Abs(column + 4) % 4;` – Liban Jun 04 '13 at 08:00
3

The following solution works if there is some text in the text boxes.

First create a KeyDown event handler:

private void textBoxLeft_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode.Equals(Keys.Right))
    {
        e.Handled = true;
        //SendKeys.Send("{TAB}");
        textBoxRight.Focus();
    }
}

private void textBoxRight_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode.Equals(Keys.Left))
    {
        e.Handled = true;
        //SendKeys.Send("+{TAB}");
        textBoxLeft.Focus();
    }
}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
mck
  • 978
  • 3
  • 14
  • 38
2

Just made this off the top of my head creatively. This will give you exactly what you want.

Step 1: Create a TableLayoutPanel and make the columns 4 and the rows 5. Delete all your original text boxes

Step 2: Add this to your code.

void tableLayoutPanel1_KeyDown(object sender, KeyEventArgs e)
{ // this moves the box focus up down left or right 
    if (e.KeyData == Keys.Left)
    {
        for (int i = 0; i < text.Length; i++)
        {
            if (sender.Equals(text[i]))
            { text[i - 1 < 0 ? text.Length - 1 : i - 1].Focus(); break; }
        }
    }
    else if (e.KeyData == Keys.Right)
    {
        for (int i = 0; i < text.Length; i++)
        {
            if (sender.Equals(text[i]))
            { text[i + 1 > text.Length - 1 ? 0 : i + 1].Focus(); break; }
        }
    }
    else if (e.KeyData == Keys.Up)
    {
        for (int i = 0; i < text.Length; i++)
        {
            if (sender.Equals(text[i]))
            { text[i - 4 < 0 ? i - 4 + text.Length : i - 4].Focus(); break; }
        }
    }
    else if (e.KeyData == Keys.Down)
    {
        for (int i = 0; i < text.Length; i++)
        {
            if (sender.Equals(text[i]))
            { text[i + 4 > text.Length - 1 ? i + 4 - text.Length : i + 4].Focus(); break; }
        }
    }
}

This will allow you to navigate up down left right with the arrow keys and it will fix your issue with the tab so Tab will now go right and Shift+tab will go back. Additionally this solution is nice because if you go up or down it will cycle back around as you would intuitively expect. Also if you are feeling zesty you can move diagonally now.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
CodeCamper
  • 6,609
  • 6
  • 44
  • 94
  • seems good solution, but i dont want to delete my original textboxes, the change will be huge.. anyway thanks. – Liban Jun 04 '13 at 10:55
  • This solution works great from scratch but if you want you can actually assign your existing TextBoxes to an array, make sure you do it in order and if you add them all to the same KeyDown event I have created it will work without the TableLayout. The logic works from the TextBoxes being in chronological array. In any event you can always use it next time you need to make a form from scratch and don't want to align your boxes manually and save a few seconds. – CodeCamper Jun 04 '13 at 12:54
0

Try handling the keypress event
→,← can force tab,shift+tab key press respectively.
Just a rough idea, as I have never tried this.

Jawahar
  • 1
  • 1