먼저 예전에 "정성훈.해미" 님께서 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;
그럼 .. 이만...
http://www.delphi.co.kr/zboard/view.php?id=qanda&no=116256