본문 바로가기

.NET/WinForm

[WinForm] 텍스트박스 입력 무시/숫자만 입력 가능하도록 하기

텍스트박스는 일반적으로 포커스가 컨트롤에 있고 키보드 입력이 발생하면 일단 문자가 삽입된다.

텍스트박스에 입력을 불가능하게 하거나, 특정 문자만 입력을 허용하도록 하는 방법을 소개한다.

 

우선, 이러한 동작을 가능토록 하려면 KeyPress 이벤트를 사용해야 한다.

KeyPress 이벤트는 KeyPressEventArgs 매개변수를 갖는데, 이 매개변수의 Handled 속성을 사용하여 입력 값을 제어할 수 있다.

KeyPressEventArgs.Handled

Key Event의 발생 순서는 KeyDown -> KeyPress -> 컨트롤 처리 -> KeyUp 순서로 진행되는데, 만약 KeyPress 이벤트에서 Handled를 true로 변경한다면 컨트롤 처리 부분은 진행되지 않는다. 여기서 컨트롤 처리는 컨트롤에 입력된 문자를 삽입하는 과정을 포함하고 있다.

 

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
	e.Handled = true;
}

따라서 단순히 위처럼 e.Handled를 true로 설정하는 것만으로도 텍스트박스에 입력된 문자를 출력하지 않도록 할 수 있다.

 

만약 특정 문자 집합에 대해서만 입력을 허용하고 싶다면 위 코드를 조금 응용하면 된다.

KeyPressEventArgs는 Handled 말고 KeyChar라는 속성도 가지고 있는데, KeyChar는 입력된 키에 해당하는 ASCII 문자를 가지고 있다. 따라서 KeyChar를 검사하여 Handled를 true로 할지 말지 판단할 수 있다.

 

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
	// KeyChar가 'A'와 'Z' 사이에 속하지 않고, 'a'와 'z' 사이에도 속하지 않는다면 알파벳이 아님
	if(!('A' <= e.KeyChar && e.KeyChar <= 'Z') && !('a' <= e.KeyChar && e.KeyChar <= 'z')) e.Handled = true;
}

이 코드는 알파벳 입력만을 허용하는 코드이다. 덧붙이자면 char는 -128~127까지의 정수 값을 가질 수 있으므로 char형 값 간의 크기 비교가 가능하다.

 

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
	// KeyChar가 '0'와 '9' 사이에 속하지 않는다면 숫자가 아님
	if(!('0' <= e.KeyChar && e.KeyChar <= '9')) e.Handled = true;
}

숫자 입력만을 원하면 이렇게 한다.

 

KeyChar 속성은 읽기/쓰기 모두 지원하는 속성이기 때문에 KeyPress 이벤트에서 입력된 키 정보를 임의로 변경할 수도 있다.

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
	if('a' <= e.KeyChar && e.KeyChar <= 'z') e.KeyChar = (char)(e.KeyChar - 32);
}

이 코드는 입력된 KeyChar가 알파벳 소문자인 경우 ASCII 값에서 32를 빼주어 알파벳 대문자로 키 입력을 변경한다. 텍스트박스에 알파벳 소문자를 입력할 경우 대문자로 출력되는 것을 확인할 수 있을 것이다.

 

문제점은 텍스트박스의 내용을 지우려고 Backspace키나 Delete 키를 누른다 하더라도 이 이벤트에서 모조리 걸러진다는 것이다. 하지만 Backspace키, Delete키의 경우 ASCII 코드표에 8 (BS)로 할당되어 있기 때문에 KeyPress 이벤트에서 예외로 처리할 수 있다.

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
	// KeyChar가 '0'와 '9' 사이에 속하지 않는다면 숫자가 아니므로 처리하지 않음
    // KeyChar가 ASCII 8인 경우는 예외로 처리
	if(!('0' <= e.KeyChar && e.KeyChar <= '9') && e.KeyChar != (char)8) e.Handled = true;
}

 

만약 여러 가지 조건을 추가/제거해야 한다면 아래와 같이 메서드를 정리하는 것이 조금 더 깔끔할 수 있다.

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
	if('0' <= e.KeyChar && e.KeyChar <= '9') return;
    else if(e.KeyChar != (char)8) return;
    
    // 위에서 return 되지 않고 넘어온 모든 입력값에 대해 e.Handled를 true로 하여 처리를 중단
    e.Handled = true;
}

 

위에서 사용한 내용을 바탕으로 실수 값만을 입력할 수 있는 텍스트박스를 만들어 보자.

private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
	if('0' <= e.KeyChar && e.KeyChar <= '9') return;
    else if(e.KeyChar != (char)8) return;
    // 입력된 키가 '.' 이고, 텍스트박스에 '.'이 존재하지 않으며, 커서가 시작부분이 아닌 경우에만 '.'을 추가하도록 허용
    else if(e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') == -1 && (sender as TextBox).SelectionStart != 0) return;
    
    // 위에서 return 되지 않고 넘어온 모든 입력값에 대해 e.Handled를 true로 하여 처리를 중단
    e.Handled = true;
}

// 위 이벤트는 텍스트의 가장 마지막을 .으로 종료하는 것에 대해서는 처리하지 못하므로
// 텍스트박스가 포커스를 잃었을 때 마지막 문자가 '.'인 경우 '0'을 맨 뒤에 붙여준다.
// 이 메서드는 컨트롤이 포커스를 잃었을 때 발생하는 Leave 이벤트에 등록한다.
private void textBox_Leave(object sender, EventArgs e)
{
	var box = sender as TextBox;
    if(box.Text.Length != 0 && box.Text[box.Text.Length - 1] == '.') box.Text += '0';
}

 

마지막으로 보조키에 대한 처리를 해보자.

KeyChar에는 안타깝게도 Control, Shift, Alt 등 키 조합에 사용되는 보조키에 대한 정보가 담기지 못한다. 따라서 보조키에 대한 처리는 별도로 해줘야 하는데, 예를 들어 숫자만 입력 가능하도록 작성한 텍스트박스에서 복사/붙여넣기를 허용하고 싶다면 'c' 또는 'v'를 허용해줘야 KeyPress 이벤트를 통과하여 복사/붙여넣기가 실행될 수 있게 되나, 이렇게 되면 숫자만 입력 가능한 텍스트박스에 뜬금없이 알파벳 'c'와 'v' 또한 입력할 수 있게 되어 버린다.

보조키에 대한 입력 정보는 KeyPressEventArgs에서는 확인할 수 없고, KeyEventArgs.KeyCode에서 확인 가능하다. KeyCode는 입력된 키의 정보를 Keys 열거형 타입으로 가지고 있다. 따라서 이 값을 KeyDown 이벤트에서 확인하여 보조키가 눌린 경우에는 KeyPress 이벤트가 동작하지 않도록 조치할 수 있다.

 

KeyDown 이벤트에서 KeyCode가 Keys.ControlKey인 경우 isControlKeyPressed를 true로 하고,

KeyPress 이벤트는 isControlKeyPressed가 true일 때 아무 검사도 하지 않고 메서드를 종료한다.

마지막으로 KeyUp 이벤트에서는 KeyCode가 Keys.ControlKey 즉, Control 키가 떼어진 경우 isControlKeyPressed를 false로 하여 KeyPress 이벤트가 다시 정상적으로 수행될 수 있도록 한다.

private bool checkChar(char c)
{
	if('0' <= c && c <= '9') return true;
    else if(c == (char)8) return true;
    
    return false;
}

bool isControlKeyPressed = false;
private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
	// Control 키가 눌려진 상태라면 검사하지 않는다.
	if(isControlKeyPressed) return;
    
	if(checkChar(e.KeyChar)) return;
    // 입력된 키가 '.' 이고, 텍스트박스에 '.'이 존재하지 않으며, 커서가 시작부분이 아닌 경우에만 '.'을 추가하도록 허용
    else if(e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') == -1 && (sender as TextBox).SelectionStart != 0) return;
    
    // 위에서 return 되지 않고 넘어온 모든 입력값에 대해 e.Handled를 true로 하여 처리를 중단
    e.Handled = true;
}

private void textBox_KeyDown(object sender, KeyEventArgs e)
{
	if(e.KeyCode == Keys.ControlKey) isControlKeyPressed = true;
}
private void textBox_KeyUp(object sender, KeyEventArgs e)
{
	// Control 키가 떼어진 경우
	if(e.KeyCode == Keys.ControlKey)
    {
    	isControlKeyPressed = false;
        
        // 만약 복사/붙여넣기를 통해 숫자가 아닌 문자들이 입력되었을 수 있으니,
        // 텍스트 박스의 내용을 검사하여 숫자가 아닌 문자들을 지운다.
        var box = sender as TextBox;
        var builder = new StringBuilder();
        bool dotFound = false;
        for(var i = 0; i < box.Text.Length; i++)
        {
        	if(checkChar(box.Text[i])) builder.Append(box.Text[i]);
            // '.'은 한 개만 허용되므로 발견된 첫 번째 '.'에 한해서 옮긴다.
            else if(box.Text[i] == '.' && !dotFound)
            {
            	builder.Append('.');
                dotFound = true;
            }
        }
        box.Text = builder.ToString();
    }
}

// 위 이벤트는 텍스트의 가장 마지막을 .으로 종료하는 것에 대해서는 처리하지 못하므로
// 텍스트박스가 포커스를 잃었을 때 마지막 문자가 '.'인 경우 '0'을 맨 뒤에 붙여준다.
// 이 메서드는 컨트롤이 포커스를 잃었을 때 발생하는 Leave 이벤트에 등록한다.
private void textBox_Leave(object sender, EventArgs e)
{
	var box = sender as TextBox;
    if(box.Text.Length != 0 && box.Text[box.Text.Length - 1] == '.') box.Text += '0';
}

KeyUp 이벤트에서 위 코드와 같이 Control 키가 떼어진 경우 혹시 복사/붙여넣기를 통해 숫자나 소수점이 아닌 다른 문자가 입력되었을 경우를 대비하여 텍스트박스에 입력된 문자들을 한 번 검사하여 적절한 문자만 옮겨주면 좋을 것이다. 


참고.

TextBox의 내용물을 비밀번호 문자로 표시하는 속성인 PasswordChar는 KeyPress 이벤트 후에 적용된다. 

TextBox의 속성인 CharacterCasing 속성이 CharacterCasing.Upper 또는 CharacterCasing.Lower로 되어 있더라도 KeyChar는 원본에 해당하는 아스키 값을 가진다.