C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

컴포넌트/라이브러리
Delphi/C++Builder Programming Components&Libraries
[554] 숫자 입력만 받게도 가능한 TEdit 교체식 컴포넌트
김태선 [cppbuilder] 13289 읽음    2009-01-24 07:08
//---------------------------------------------------------------------------

#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



그럼 ^^
김태선 [cppbuilder]   2009-01-24 15:41 X
실무에서 CTRL+C, CTRL+V가 먹지 않아도 문제 될 것은 없으나, 허용하고 싶으면
아래 메소드로 바꾸면 됩니다. 사실 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;
    }
김태선 [cppbuilder]   2009-01-25 10:58 X
보통의 TEdit에 아주 간단히 숫자 정수값(양수)만 입력 받게 하고 싶으면 아래처럼
TEdit의 윈도 스타일을 바꾸어주는 방법을 써도 됩니다.

    DWORD style = GetWindowLong(Edit2->Handle, GWL_STYLE);
    SetWindowLong(Edit2->Handle, GWL_STYLE, style | ES_NUMBER);
김태선 [cppbuilder]   2009-02-11 02:32 X
컴포넌트 교체식 기법은 컴포넌트가 설치되지 않는 TurboC++ Explorer 버전에서 매우 유용하게 사용할 수 있습니다.
디자인 타임의 편의성도 어느정도 도움을 받을 수 있어, 프로그래밍이 더욱 편해지죠.
반짝반짝 [flea80]   2010-01-11 15:37 X
첨부 파일에는 맨뒤에  #define TEdit            ::TEdit  이부분이 빠져있네용 ;; 이거 없으니깐 Ambiguity between 'TEdit' and 'Stdctrls::TEdit' 이 뜨네요.. 혹시 왜 그런지 알려주실수있나요?

+ -

관련 글 리스트
554 숫자 입력만 받게도 가능한 TEdit 교체식 컴포넌트 김태선 13289 2009/01/24
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.