//---------------------------------------------------------------------------
#ifndef TEditH
#define TEditH
#include <Clipbrd.hpp>
//#include "Trace.h"
//---------------------------------------------------------------------------
// 숫자 입력만 받게도 가능한 TEdit 교체식 컴포넌트
// 금액 입력이나, 과학기술용 소수를 가진 숫자를 입력할때 사용할 수 있다.
// 실무에 쓰기에 충분할 것으로 생각하나, 혹시 모를 버그는 적당히 피해가시길.
//
// TEdit 컴포넌트명을 TEditNumber 라고 바꾸면 컴포넌트로 인스톨해서 디자인시 사용하는 것도 가능하다.
// 반드시 UseApoint UseComma 프로퍼티 둘 중 하나만 사용할 것. 다 쓴다고 이상이 있거나 한 것은 아니지만.
//
//
// Written by 김태성
class TEdit : public Stdctrls::TEdit // 컴포넌트 교체식
//class TEditNumber : public Stdctrls::TEdit // 컴포넌트 설치식
{
private:
bool bNumberOnly; // 숫자입력만 받게 제한할 것인가?
bool bUseApoint; // 숫자입력받을 시 '.' 의 입력 허가할 것인가?
bool bUseComma; // 숫자입력받을 시 자동으로 , 를 추가할 것인가?
bool bUseMinus; // 숫자입력받을 시 마이너스 값도 입력 받을 것인가?
TAlignment FAlignment; // 정렬 위치 조정.
private:
// 여기서 숫자만 입력받도록 제한한다.
MESSAGE bool __fastcall WMChar(Messages::TWMKey *msg)
{
USHORT& Key = msg->CharCode;
if (Key >= '0' && Key <= '9')
return true;
if (Key == 8 || Key == 13)
return true;
if (bUseApoint && (Key == '.' && Text.Pos(".") == 0))
return true;
if (bUseMinus && (Key == '-' && Text.Pos("-") == 0 && SelStart == 0)) // -는 처음만 입력가능하게.
return true;
Key = 0;
return false;
}
// 스트링속에 숫자요소 외는 제거한다. '.'는 단 하나만 허용한다.
String NumDataFilter(String str)
{
// 델파이 클래스 상속자는 new char[] 가 먹지 않아 malloc free로 대치한다.
char *pbase = (char *)malloc(str.Length() + 1);
char *p = pbase;
*p = 0;
for(int c = 1; c < str.Length() + 1; c++)
{
if (isdigit(str[c]))
*p++ = str[c];
else if (str[c] == '.' && (strchr(pbase, '.')==NULL && Text.Pos(".") == 0)) // .은 하나만 허용
*p++ = str[c];
else if (str[c] == '-' && p == pbase && (strchr(pbase, '-')==NULL && Text.Pos("-") == 0)) // .은 하나만 허용
*p++ = str[c];
*p = 0;
}
str = pbase;
free(pbase);
return str;
}
// 스트링에 3칸마다 콤마를 찍은 상태로 반환한다.
String ConvertCommaString(const String& str)
{
int len,idx,iSub;
String ret;
len = str.Length();
iSub = len + 1;
if (len > 3)
idx = len - 2;
else
idx = 0;
while(idx > 1)
{
ret = "," + str.SubString(idx, iSub-idx) + ret;
iSub = idx;
idx = idx - 3;
}
if (iSub > idx)
ret = str.SubString(1, iSub-1) + ret;
if (ret.SubString(1,2) == "-,") // - 값인경우는 -,123 식으로 되는 경우도 있어 이를 방지.
ret.Delete(2,1);
return ret;
}
// Change가 일어난 경우
void __fastcall CMTextChanged(TMessage& Message)
{
String org = Text;
int iSelStart = SelStart;
int iOrgLen = org.Length();
String s = StringReplace(org, ",", "", TReplaceFlags() << rfReplaceAll);
int pos = s.Pos("-");
if (pos >= 2) // - 가 중간에 있으면 그건 삭제한다.
s = s.Delete(pos, 1);
s = ConvertCommaString(s);
Text = s;
SelStart = s.Length() - iOrgLen + iSelStart; // 캐럿 위치가 원래 위치를 유지하도록 조정.
}
protected:
void __fastcall SetEditAlignment(TAlignment align)
{
if (FAlignment == align)
return;
FAlignment = align;
int style = GetWindowLong(Handle, GWL_STYLE);
switch(align)
{
case taLeftJustify :
style = style & ~ES_RIGHT;
break;
case taRightJustify :
style = style | ES_RIGHT;
break;
case taCenter :
style = (style & ~ES_RIGHT) | ES_CENTER;
break;
default :
return;
}
SetWindowLong(Handle, GWL_STYLE, style);
Invalidate();
}
virtual void __fastcall Dispatch(void *Message)
{
TMessage *msg = (PMessage)Message;
switch (msg->Msg)
{
case WM_PASTE :
if (bNumberOnly)
{
// 숫자요소외는 제거 한다.
String str = Clipboard()->AsText;
Clipboard()->AsText = NumDataFilter(str);
}
break;
case WM_CHAR :
if (bNumberOnly)
if (!WMChar((TWMKey*)msg))
return;
break;
case CN_COMMAND :
if (msg->WParamHi == EN_CHANGE)
{
if (bNumberOnly)
CMTextChanged(*msg);
}
break;
}
Stdctrls::TEdit::Dispatch(Message);
}
__published:
__property bool NumberOnly = {read=bNumberOnly, write=bNumberOnly, default = false};
__property bool UseApoint = {read=bUseApoint, write=bUseApoint, default = true};
__property bool UseComma = {read=bUseComma, write=bUseComma, default = false};
__property bool UseMinus = {read=bUseMinus, write=bUseMinus, default = false};
__property TAlignment Alignment = {read=FAlignment, write=SetEditAlignment, default = taLeftJustify};
};
#define TEdit ::TEdit
#endif
이 컴포넌트를 쓰는 예.
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// 컴포넌트 교체식으로 쓸때는 코딩으로 처리해야 한다.
// __property 의 default는 먹지 않기 때문이다.
// 또 교체식인 경우는 오브젝트 인스펙터에 추가된 프로퍼티가 나타나지 않는 이유다.
// 금액 입력을 위한 방법. - 값도 입력 가능.
Edit1->NumberOnly = true; // false로 하거나 생략하면 일반 TEdit로 동작한다.
//Edit1->UseApoint = true;
Edit1->UseComma = true;
Edit1->UseMinus = true;
Edit1->Alignment = taRightJustify; // 정렬 방법 설정.
}
//---------------------------------------------------------------------------
별 것도 아닌데 만드는데 생각보다 많은 시간이 걸렸네요.
만들다 실험하다 서핑하다 TV 봤다가... 하다가 ㅡ,.ㅡ;
빌더 버전에 관계 없이 사용할 수 있습니다.
단, 빌더 2009에서 유니코드 모드인 경우는 이 파일을 포함하기 전에
#define String AnsiString 같은 처리가 필요합니다.
빌더 2009의 유니코드 모드에서의 실험은 하지 않아 더 고쳐야 할 필요가 있을지 모르지만
이 정도는 매우 간단히 처리할 수 있는 것이라 필요하다면 누구나 수정 가능할 것입니다.
3칸 마다 콤마를 찍는 루틴은 장성호님이 만든 procedure를 참고 했습니다.
TEdit 는 실무에 워낙 많이 쓰이는 컨트롤이라서
아마도 실무에 매우 유용하게 사용할 수 있을 것으로 전망됩니다.
교체식 컴포넌트이니 헤더에 1줄만 추가하면 됩니다.
새로 추가된 기능을 쓰려면 Edit1->NumberOnly = true; 식으로 해주어야 하고
그렇지 않다면 그냥 일반적인 아무 문자나 허용하는 TEdit로 동작합니다.
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
#include "TEdit.h" // <------------------ 여기
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TEdit *Edit2;
TLabel *Label1;
TButton *Button1;
void __fastcall Edit1KeyPress(TObject *Sender, char &Key);
void __fastcall FormCreate(TObject *Sender);
void __fastcall Edit1KeyUp(TObject *Sender, WORD &Key, TShiftState Shift);
void __fastcall Button1Click(TObject *Sender);
void __fastcall Edit2Change(TObject *Sender);
void __fastcall Edit1Change(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
그럼 ^^
|
아래 메소드로 바꾸면 됩니다. 사실 2줄만 추가하면 되는 것이죠.
// 여기서 숫자만 입력받도록 제한한다.
MESSAGE bool __fastcall WMChar(Messages::TWMKey *msg)
{
USHORT& Key = msg->CharCode;
if (Key >= '0' && Key <= '9')
return true;
if (Key == 8 || Key == 13)
return true;
if (Key == 3 || Key == 22) // CTRL+C CTRL+V 도 먹게 허용한다.
return true;
if (bUseApoint && (Key == '.' && Text.Pos(".") == 0))
return true;
if (bUseMinus && (Key == '-' && Text.Pos("-") == 0 && SelStart == 0)) // -는 처음만 입력가능하게.
return true;
Key = 0;
return false;
}