フォーム上にエディットボックスを動的に生成するプログラムを作っています。
(ユーザーがコントロール数をテキストで指定し、その数に応じた、
動的にエディットボックスを表示するようなプログラムを作っています。)
そこで、以下の様なTEdit.Createを作成するプログラムを実装しました。
(サンプル用に加工していますのでご了承ください。)
■修正前のコード
//Panel1…複製したコントロールを表示するパネル
procedure TForm1.Button1Click(Sender: TObject);
var
i, Num : Integer;
begin
Num := 100; //実験として100個複製するものとする
for i := 0 to Num do
begin
with TEdit.Create(Panel1) do
begin
Parent :=Panel1;
//複製できたか確認するため少しずらして表示する
Left := TPanel(Parent).ControlCount * 2;
end;
end;
end;
しかし、上記のコードですと、
コントロール数が1000を超えるような場合に処理が遅くなってしまうため、
高速化できないか悩んでいます。
そこで、以下の様なTMemoryStreamを使用したコードを
作成したのですが効果がみられませんでした。
(修正前よりも若干遅くなってしまいました)
■修正後のコード
//Panel1…複製したコントロールを表示するパネル
procedure TForm1.Button1Click(Sender: TObject);
var
MemoryStream : TMemoryStream;
Edit : TEdit;
Writer : TWriter;
Reader : TReader;
i, Num : Integer;
begin
Edit := TEdit.Create(nil); //コピー元コントロールを1つ作成
Edit.Name:= ''; //Nameプロパティを一時的に空にする(複製時にエラーにならないようにするため)
//コピー元のエディットコントロールをメモリストリームにコピー
MemoryStream := TMemoryStream.Create;
try
Writer:=TWriter.Create(MemoryStream, SizeOf(Edit));
try
Writer.RootAncestor := nil;
Writer.Ancestor := nil;
Writer.Root := Control.Owner;
Writer.WriteSignature;
Writer.WriteComponent(Edit);
Writer.WriteListEnd;
finally
Writer.Free;
end;
MemoryStream.Position:=0;
Reader:=TReader.Create(MemoryStream, SizeOf(Edit));
try
Num := 100;
for i := 0 to Num do
begin
Reader.ReadComponents(Panel1.Owner,Panel1,ReadComponentsProc);
Reader.Position := 0;
end;
finally
Reader.Free;
end;
finally
MemoryStream.Free;
end;
Edit.Free;
end;
procedure TForm1.ReadComponentsProc(Component:TComponent);
begin
//複製できたか確認するため少しずらして表示する
TEdit(Component).Left := TPanel(TEdit(Component).Parent).ControlCount * 2;
end;
修正後のコードはメモリレベルでコピーとなりますので、
修正前より早くなることを期待していたのですが、
何か方法が悪いのか?と考えています。。。
■質問内容
もし、上記「修正後のコード」にて、パフォーマンス的な観点で
悪い点あればご教授いただいてもよろしいでしょうか?
また、もし他に良い方法(TMemoryStream以外を使用する方法など)が
あればお知らせいただいてもよろしいでしょうか?
長文となってしまい申し訳ありませんが、よろしくお願いいたします。
エディットボックスはタダの文字列などとは違って
ウィンドウを持つコントロールなので、単純なコピーで
作成ってわけにはいかないと思います。
どのように工夫するにしても、Createを呼んでインスタンスを作成する作業は
コントロールの数分だけ実行しなくてはならないでしょう。
コントロール数が1000を超えると遅くなるそうですが
この1000個のコントロールは1画面に表示されるものですか?
スクロールボックスに置いてスクロール表示するってのも考えられますが
もし画面上に一度に表示する数が数十個に収まるなら
「次へ」「前へ」などのボタンを用意して、コントロールの内容だけを
分割表示するようにした方が良いような気がしますが。
エディットボックスが同時に1000個以上必要なシチュエーションを
想像できないので、的をはずしているかもしれません。
差し支えなければ、何をしたいのかを具体的に提示してもらえば
別の方法もあるかもしれません。
WriteComponentResFile と ReadComponentResFile
Quest様、KHE00221様、
ご回答、情報提供いただきありがとうございます。
また、質問の背景などをご説明をしておらず大変申し訳ありません。
背景といたしまして、
ある特定の顧客様向けに開発している、業務アプリケーションの開発を行っているのですが、ある画面の入力項目だけは可変的に増やせるようにしたいという要件をいただいています。
数に関しては、制限は設けない仕様としていますが、
実際は「数1000」そこまではいくことはないと思います。
ただし、場合によっては、数100規模になる場合もあること、
また、実際はデータベースとの連携処理も入っている関係で、
1分ぐらいフリーズしてしまう状況です。
(ボトルネックがコントロールの生成処理となっています。)
ご指摘いただいた通り、数100を超えるような場合も、
画面上に一度に表示せず、スクロールで表示しますので、
下の方のコントロールは、スクロールされるまで、生成しないようにする
などの代案の方法も検討しております。
(ただ、できれば代案を使用せずにパフォーマンスを改善したいと思っています。)
また、WriteCompnentsRes、ReadComponentsResを使用する方法に
変更したのですが、処理速度が改善が変わりませんでした。
変更後のコードを以下に記載いたします。
■WriteCompnentsRes/RaedComponentsResを使用するコード
procedure TForm1.Button1Click(Sender: TObject);
var
MemoryStream : TMemoryStream;
Edit, Edit2 : TEdit;
i, Num : Integer;
begin
Edit := TEdit.Create(nil); //コピー元コントロールを1つ作成
Edit := Edit1;
Edit.Name:= ''; //Nameプロパティを一時的に空にする(複製時にエラーにならないようにするため
//コピー元のエディットコントロールをメモリストリームにコピー
MemoryStream := TMemoryStream.Create;
try
MemoryStream.WriteComponentRes('Edit', Edit);
Num := 100; //実験として100個複製するものとする
for i := 0 to Num do
begin
MemoryStream.Position := 0;
Edit2 := TEdit.Create(nil);
MemoryStream.ReadComponentRes(Edit2);
with Edit2 do
begin
Parent :=Panel1;
//複製できたか確認するため少しずらして表示する
Left := TPanel(Parent).ControlCount * 2;
end;
end;
finally
MemoryStream.Free;
end;
Edit.Free;
end;
修正前コードと処理時間が変わらない理由ですが、
ReadComponentsResの対象コントロール(Edit2)に
あらかじめCreateの命令が入れているためと思います。
(あらかじめTEdit.Createをしないと
ReadComponentsResの処理でアドレス違反のエラーが発生してしまうため、
直前にCreateするように記述しています。)
ReadComponentsResを使用する場合も、やはりCreateを使用するしかないのでしょうか?
(それとも、使い方に誤りがあるのでしょうか?)
インターネットにてReadComponentsResのサンプルコードを探したのですが、
見つけることができず、使い方も不安な状況でございます…
なお、Delphiのバージョンは2007となっています。(いまさらで申し訳ありません。。。)
もしわかればで構いませんのでご教授いただければ幸いです。
>エディットボックスが同時に1000個以上必要なシチュエーションを
>想像できないので、的をはずしているかもしれません。
私も,そのような状況が想像できなかったし,たとえ数100個としても,
それを選択する,つまり利用する方の状況が何とも不思議な感じがします.
ところで,一般に,コントロールの描画には時間がかかります.
描画の更新を停止しておけば,気持ちですが,速くなります.
以下のコードは Windows 7 U64(SP1) + Delphi XE で確認していますが,
Delphi 2 以降,Windows 95 以降であれば動作するハズです.
描画の「更新を停止」すると速くなるという参考です.
今回の用途に応用できるかどうかは分かりません.
速いとか遅いとかを問題にしているので,当然,時間を計測しています.
uses MMSystem;
const
Num = 1000;
//--------------------------------------------------------------
// オブジェクトの生成だけなら速い
//--------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
StartTime : DWORD;
begin
for i := Panel1.ControlCount - 1 downto 0 do begin
Panel1.Controls[i].Free;
end;
StartTime := TimeGetTime;
for i := 0 to Num do begin
with TEdit.Create(Panel1) do begin
Visible := False;
Parent :=Panel1;
Left := 20;
top := 50;
end;
Application.ProcessMessages;
end;
ShowMessage(IntToStr(timeGetTime - StartTime) + ' ms');
end;
//--------------------------------------------------------------
// 描画の更新を停止してオブジェクトを生成すれば少しは速くなる
//--------------------------------------------------------------
procedure TForm1.Button2Click(Sender: TObject);
var
i : Integer;
StartTime : DWORD;
begin
for i := Panel1.ControlCount - 1 downto 0 do begin
Panel1.Controls[i].Free;
end;
StartTime := TimeGetTime;
LockWindowUpdate(Panel1.Handle);
for i := 0 to Num do begin
with TEdit.Create(Panel1) do begin
Visible := True;
Parent :=Panel1;
Left := 20;
top := 50;
end;
Application.ProcessMessages;
end;
LockWindowUpdate(0);
ShowMessage(IntToStr(timeGetTime - StartTime) + ' ms');
end;
//--------------------------------------------------------------
// 描画の更新を停止しないと遅くなる
//--------------------------------------------------------------
procedure TForm1.Button3Click(Sender: TObject);
var
i : Integer;
StartTime : DWORD;
begin
for i := Panel1.ControlCount - 1 downto 0 do begin
Panel1.Controls[i].Free;
end;
StartTime := TimeGetTime;
for i := 0 to Num do begin
with TEdit.Create(Panel1) do begin
Visible := True;
Parent :=Panel1;
Left := 20;
top := 50;
end;
Application.ProcessMessages;
end;
ShowMessage(IntToStr(timeGetTime - StartTime) + ' ms');
end;
# 私は,環境を提示しない質問は,ほとんど冗談だと思っています(笑)
# 後から提示するというのは,「自分の書き込みをチェックしろ」というのと同じだと思っています.
うーん、そのたくさんのエディットボックスって別に不規則に並べるわけじゃなくて、たぶん升目状になるんですよね? そういう用途ならStringGridを使うものなんじゃないでしょうか。
仮にエディットたくさん並べたとしても、どうせ編集するのは一度に一箇所だけですし。
何かしらStringGridではダメな事情があるとしても、実際に編集を行うエディットボックスは一つだけにして
残りはLabelなどもっと軽量なモノで代用する方向で考えた方がいいのではないかと思います。
Parent :=Panel1;
Left := TPanel(Parent).ControlCount * 2;
を
Left := TPanel(Parent).ControlCount * 2;
Parent :=Panel1;
にする
レスが遅くなってしまい申し訳ありません。
あれから試行錯誤しておりましたが、
エディット複数ですと高速化が難しかったため、
皆様からいただいたアドバイスをもとに
顧客と交渉の上、StringGrid化することで解決いたしました。
ありがとうございました!
ツイート | ![]() |