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

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[818] [VCL] FindComponent 활용과 플러스 알파..
장성호 [nasilso] 11936 읽음    2008-10-16 15:59
먼저 예전에 "정성훈.해미" 님께서 FindComponent 에 대해 한번 얘기한적이 있다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=361


FindComponent 메소드는

TComponent 클래스에 있는 메소드로써  String으로 원하는 컴포넌트를 찾을때 사용하는 함수이다.

비슷한 이름의 비슷한 컴포넌트에 비슷한 작업을 해야 하는경우에 ... 종종 쓰인다.

//가장 단순한 방법
void __fastcall TForm1::Button1Click(TObject *Sender)
{  
     Edit1->Text="";
     Edit2->Text="";
     Edit3->Text="";
     Edit4->Text="";
     Edit5->Text="";
     Edit6->Text="";
     Edit7->Text="";
     Edit8->Text="";
     Edit9->Text="";
     Edit10->Text="";
}

//FindComponent를 이용한 방법
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    String sName;
    TEdit *Edt;
    for(int i=1;i<=10;i++)
    {
        Edt=(TEdit *)FindComponent("Edit"+IntToStr(i));
        if(Edt!=NULL)Edt->Text="";
    }
}


위와같은 식으로 하면 매우 강력한데 한가지 단점을 얘기하자면  루프 반복이 많다.

FindComponent  의  VCL 소스를 보면 다음과 같다.
function TComponent.FindComponent(const AName: string): TComponent;
var
  I: Integer;
begin
  if (AName <> '') and (FComponents <> nil) then
    for I := 0 to FComponents.Count - 1 do
    begin
      Result := FComponents[I];
      if SameText(Result.FName, AName) then Exit;
    end;
  Result := nil;
end;



Find할때 로직이 전체 컴포넌트 숫자만큼 루프를 돌면서 하나씩 이름을 체크한다.
그리고 컨트롤에 대해서만 하는것이 아니라 모든 컴포넌트에 대해서 한다.
그래서 TEdit 나 TButton같은 컨트롤 뿐만 아니라

다음과 같이 TOpenDialog같은 non-visual 컴포넌트로 가능하다.

procedure TForm1.Button2Click(Sender: TObject);
var
  OpenDlg: TOpenDialog;
begin
  OpenDlg:=TOpenDialog(FindComponent('OpenDialog1'));
  if Assigned(OpenDlg) and OpenDlg.Execute then
  begin
    ShowMessage(OpenDlg.FileName);
  end;
end;



그런데 대부분의 경우에  FindComponent 메소드는  Visual 컴포넌트인
TGraphicControl 이나 TWinControl을 찾는데 쓰인다.

그렇다면 FindComponent  대신 FindControl 이라는 메소드를 만들어서 쓰는것이 아주 쬐금더 좋지 않을까...

function TForm1.FindControl(sCtrlName: String): TControl;
var
  idx: Integer;
begin

  for idx:=0 to Self.ControlCount -1 do
  begin
    if( Controls[idx].Name=sCtrlName)then
    begin
      Result:=Controls[idx];
      Exit;
    end;

  end;
  Result:=nil;
end;



TControl* __fastcall TForm1::FindControl(String sCtrlName)
{
    for(int i=0; iControlCount;i++)
    {
        if(Controls[i]->Name==sCtrlName)
            return Controls[i];
    }
    return NULL;
}



하지만 이것가지고는 거의 속도의 이득이 없다.


이제 진짜 하고 싶은 얘기...

위에 맨처음에 사용예 코드에서 봤듯이 문제는 FindComponent 가 반복해서 호출될때 이다.

예를들어
폼위에 각종 컴포넌트가 100개 정도 올려져 있고 그중에  Edit가 20개 정도 된다고 가정할때
FindComponent로 20개의 Edit를 모두 찾는다면 최대 1800회 정도의 루프를 돌게 될것이다.

그러니까 이런 경우는 FindComponent 를 쓰기보다는
ControlCount만큼(100회이하) 직접 루프를 돌리면서  Name을 체크하는것이 빠를것이다.

// 마지막으로 ControlCount 만큼 직접 루프를 돌리면서 하는 방법
void __fastcall SetEditsText(TWinControl *wParent,String sEditPreName,int iNumSt,int iNumEt,String sText)
{
    if(sEditPreName.Length()<1)return;

    int iPLen=sEditPreName.Length();

    int iNum;
    for(int i=0;iControlCount;i++)
    {
        if(wParent->Controls[i]->InheritsFrom(__classid(TCustomEdit)))
        {
            if(wParent->Controls[i]->Name.Length()<(iPLen+1))continue;

            if(!TryStrToInt(wParent->Controls[i]->Name.SubString(iPLen+1,wParent->Controls[i]->Name.Length()-iPLen),iNum)) continue;

            if(iNum>=iNumSt && iNum<=iNumEt)
                ((TCustomEdit *)wParent->Controls[i])->Text= sText;
        }
    }
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    SetEditsText(this,"Edit",1,10,"");
}


사실 요즘은  컴퓨터 사양이 좋아서 FindComponent를 이용해도 별로 느린것을 못느낄 것이다.


활용...
오늘 델마당에 Edit1 ~ Edit25까지 Text가 를 한번에 비교하는 구문이 있냐는 질문이 올라왔다.

FindComponet 를 이용해서 구현하면 아래와 같이 될것이다.
bool __fastcall TForm1::EditsIsEmpty(int iNumSt,int iNumEt)
{
    TEdit *Edt;
    for(int i=iNumSt;i<=iNumEt;i++)
    {
        Edt=(TEdit *)FindComponent("Edit"+IntToStr(i));
        if(Edt==NULL)continue;
        if(Edt->Text!="")return false;
    }
    return true;
}
void __fastcall TForm1::Button3Click(TObject *Sender)
{
   if(EditsIsEmpty(1,25))ShowMessage("Edit1 ~ Edit25 모두 Empty");
   else ShowMessage("Edit1 ~ Edit25 중 Text가 Empty아닌것이 있음");
}
//---------------------------------------------------------------------------


그런데 Controls 루프를 돌리면서 구현하면 아래와 같이 된다.

//C++빌더 버젼
bool __fastcall EditsTextCompare(TWinControl *wParent,String sEditPreName,int iNumSt,int iNumEt,String sCompareText)
{
    if(sEditPreName.Length()<1)return false;

    int iPLen=sEditPreName.Length();

    int iNum;
    for(int i=0;iControlCount;i++)
    {
        if(wParent->Controls[i]->InheritsFrom(__classid(TCustomEdit)))
        {
            if(wParent->Controls[i]->Name.Length()<(iPLen+1))continue;

            if(!TryStrToInt(wParent->Controls[i]->Name.SubString(iPLen+1,wParent->Controls[i]->Name.Length()-iPLen),iNum)) continue;

            if(iNum>=iNumSt && iNum<=iNumEt)
                if(((TCustomEdit *)wParent->Controls[i])->Text != sCompareText)return false;
        }
    }
    return true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
   if(EditsTextCompare(this,"Edit",1,25,""))ShowMessage("Edit1 ~ Edit25 모두 Empty");
   else ShowMessage("Edit1 ~ Edit25 중 Text가 Empty아닌것이 있음");
}


//델파이 버젼
function TForm1.IfEditEqualStr(sNamePre:String;iNumSt,iNumEt: Integer; sCompareStr: String): boolean;
var
  idx,iNum,iPreLen: Integer;
  sName,sNum: String;
  edt: TCustomEdit;
  wCtrl: TWinControl;
begin
  Result:=false;
  iPreLen:=Length(sNamePre);
  if(iPreLen<1)then Exit;

  for idx:=0 to Self.ControlCount -1 do
  begin

    if(Self.Controls[idx].InheritsFrom(TCustomEdit)) then
    begin
      edt:=TCustomEdit(Self.Controls[idx]);
      if(Length(edt.Name)<=iPreLen)then Continue; //Edit123 같이 sNamePre+숫자 형식이라고 가정한 코딩

      sName:=edt.Name;
      sNum:=Copy(sName,iPreLen+1,Length(sName)-iPreLen);
      if not TryStrToInt(sNum,iNum) then continue;

      if(iNum>=iNumSt) and (iNum<=iNumEt) then
      begin
        if(edt.Text<>sCompareStr)then Exit;
      end;
    end;
  end;
  Result:=true;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   sCompareStr: String;
begin
  sCompareStr:='aaa';

  if (IfEditEqualStr('Edit',1,25,sCompareStr)) then
    ShowMessage('Edit1 ~ Edit25까지 모두 Text 가 '+sCompareStr+' 이다.')
  else
    ShowMessage('Edit1 ~ Edit25까지에서 Text 가 '+sCompareStr+' 아닌것이 있다.');

end;


그럼 .. 이만...

+ -

관련 글 리스트
818 [VCL] FindComponent 활용과 플러스 알파.. 장성호 11936 2008/10/16
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.