ナッキーの「Turbo Delphiはじめて奮戦記」 - 第15回 アンケートプログラムの機能拡張

投稿者:: Hitoshi Fujii

概要: お絵かきソフトも完成したし、今回からは新しい課題に挑戦します。まず、第3~4回で学習したアンケートプログラムを拡張しましょう。入力したデータを取っておいたり、読み込んだり、編集できるように作りかえます。

Hide image
nacky75

ナッキー

懐かしーい。ずいぶん前に作ったプログラムを見ると、自分の成長を感じちゃいますね。リストボックスの内容を使って、いろいろできるようにするんですね。

 

Hide image
takahashi75

高橋先生

もしも以前のプログラムが使えない方は、復習もかねてもう一度トライしてみよう。コンポーネントにNameプロパティを設定していないから、独自で設定してコードを読みやすくするのもいいね。


     [保存(S)]ボタンの作成

どんなプログラムだったか、ちょっと忘れちゃってるなぁ。どうでしたっけ?高橋先生!

高橋先生:名前や住所など入力した後[プロフィール]ボタンでリストボックスに内容を追加する、というプログラムだったよ。1度開いて、動かしてみればいいよ。


そうですよね、じゃあ動かしてみます。まずプロジェクトを開きます。Turbo Delphiを起動して、画面中央の「ホームページ」で「Profile.bdsproj」を選択。もし一覧に表示されていなければ、ツールバーの[プロジェクトを開く(Ctrl+F11)]ボタンをクリックします。「プロジェクトを開く」ダイアログボックスから「Profile.bdsproj」を探します。

表示できたら実行してみます。ツールバーの[すべて保存]ボタンで保存して、[実行]ボタンで実行します。項目に文字を入力して[プロフィール]ボタンをクリックします。

Hide image
01アンケートプログラム

図01 アンケートプログラム

せっかく入力した内容も、再起動したらなくなっちゃうのよね。どうやったら、入力した項目をずーっと維持できるのかな?教えて、高橋先生!

高橋先生:プログラムのほかに、ファイルを作って起動するたびに読み込むようにすればいいよ。文字列を保存するから、ファイルの形式はテキストファイルでいいね。TStrings型のプロパティには「SaveToFile」メソッドがある。これはデータをテキスト形式でファイルに保存することができるメソッドだったね。TListBoxコンポーネントのItemsプロパティはTStrings型だからSaveToFileメソッドを使ってファイルに保存しよう。

ナッキー:ファイルを作って、データを取っておくのね。そのファイルがある限りデータを維持できるんだ。


保存ボタンを作って、リストボックスの内容をファイルに保存します。アプリケーションを実行中の方は終了します。画面上部の「FormProfile」タブをクリックして表示するユニットを切り替えます。表示をフォームデザイナに変更して、TButtonコンポーネントを追加します。画面右下のツールパレット「Standard」カテゴリから「TButton」コンポーネントを探してフォームに配置します。配置する場所は[プロフィール]ボタンの右側あたりです。配置できたら表を参考にして、オブジェクトインスペクタのプロパティページでプロパティを設定します。

それと、「Caption」プロパティにはアクセラレータ文字を付けられるんだって。アクセラレータ文字に設定したキーとキーボードの[Alt]キーを同時に押すと、ボタンをクリックしたのと同じ動きになるなんて便利~。

[保存(S)]ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnSave

ローカライズ対象

Caption

保存(&S)


ボタンが配置できたら、イベントハンドラを作ります。ListBox1のItemsプロパティのSaveToFileメソッドを使って、ファイルに保存します。ファイル名は「Profile.txt」です。次にメッセージも表示します。btnSaveコンポーネントを選択して、オブジェクトインスペクタ、イベントページで「入力」カテゴリの「OnClick」をダブルクリックします。コードエディタに切り替わったら、太字部分を追加します。

procedure TForm2.btnSaveClick(Sender: TObject);
begin
  ListBox1.Items.SaveToFile('Profile.txt');
  ShowMessage('保存しました');
end;

コードを記述したらプロジェクトを保存します。

保存ボタンが完成しましたので、実行します。ツールバーの[実行]ボタンをクリックします。名前や住所など入力をして、[プロフィール]ボタンをクリックします。同様にして数件情報を入力します。できたら、作成した[保存(S)]ボタンをクリックして保存します。「保存しました」メッセージが表示できたら、実行を終了します。

Hide image
02保存しましたメッセージrep

図02 「保存しました」メッセージ

作成したファイルを確認します。メニューバーの[ファイル(F)|開く(O)...]をクリックして「開く」ダイアログボックスを表示します。「Profile.txt」ファイルを探して表示します(拡張子を省略する設定になっている場合には、「Profile」と表示されているメモ帳アイコンのファイルを選びます)。

Hide image
03テキストファイル表示

図03 テキストファイル表示

内容を確認したら、メモ帳を閉じます。

    [読み込み(R)]ボタンの作成

これで、リストボックスの情報をファイルに書き出すことができました。メソッドだけで処理できるので、それほど難しくなかったですね。この調子でファイルの読み込みもスムーズにできるといいな。

高橋先生:読み込みもメソッドだけで処理できるよ。今度は「LoadFromFile」メソッドを使うんだ。ただし注意してほしいことがある。さっき[保存(S)]ボタンを作ってテストしたから「Profile.txt」ファイルがあるよね。だけど、ファイルがなかった場合は実行時エラーが発生してしまうんだ。なるべくエラーは起こしたくないから、ファイルがなかったとき、ファイルを読み込まないようにしよう。

ナッキー:「ファイルがなかったとき」ってどうやって調べるんですか?

高橋先生:「FileExists」関数を使えばファイルの有無がわかるよ。パラメータはファイル名だ。今回は「Profile.txt」だね。ファイルがあるときには戻り値が「True」に、ファイルがないときには戻り値が「False」になるよ。

ナッキー:ファイルがあったら、「LoadFromFile」メソッドを使うんですね。


まずは[読み込み(R)]ボタンを配置します。表示をフォームデザイナに切り替えて、画面左側のツールパレット「Standard」カテゴリから「TButton」コンポーネントを探してフォームに配置します。配置する場所は[保存(S)]ボタンの右側あたりです。配置できたら、オブジェクトインスペクタのプロパティページで表を参考にして設定します。

[読み込み(R)]ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnRead

ローカライズ対象

Caption

読み込み(&R)


次にイベントハンドラを作成します。まず、「FileExists」関数を使って「Profile.txt」ファイルの有無を確認します。ファイルがあった場合は「ListBox1のItemsプロパティのLoadFromFile」メソッドを使って、「Profile.txt」ファイルを読み込みます。

では、フォームデザイナの画面で[読み込み(R)]ボタンを選択して、オブジェクトインスペクタ、イベントページで「入力」カテゴリの「OnClick」をダブルクリックします。コードエディタに切り替わったら、太字部分を追加します。

procedure TForm2.btnReadClick(Sender: TObject);
begin
  if FileExists('Profile.txt') then
    ListBox1.Items.LoadFromFile('Profile.txt');
end;

コードが記述できたら、保存して実行します。ツールバーの[すべて保存]ボタンで保存して、[実行]ボタンで実行します。[読み込み(R)]ボタンをクリックすると、前回保存した内容がリストボックスに表示されます。

Hide image
04読み込みボタンクリック

図04 [読み込み(R)]ボタンクリック

これで、追加した内容を保持できるようになりました。

    [削除(D)]ボタンの作成

[保存(S)]ボタンも[読み込み(R)]ボタンもできたから、アンケートを自由に保存できますね。これでバッチリですよね、高橋先生。

高橋先生:まだ、充分とは言えないな。もし間違えて追加してしまったときはどうする?選択している行を削除できるといいね。

ナッキー:あ、そうか。追加を取りやめたいときってありますよね。あと、間違って書いたとき、書き直しもできるといいな。

高橋先生:まあ、まあ。まずは[削除(D)]ボタンから考えよう。どの行を削除するか、リストボックスから選択してもらって[削除(D)]ボタンを押す。そうすると選択した行が削除される。というのでいいかな?削除は「ListBox1.Items.Delete」メソッド。パラメータは1行目を0番目として何番目のデータかを整数型でセットする。

ナッキー:OK、OK!バッチリじゃないですか。

高橋先生:もっと、よく考えてみて。もし間違えて[削除(D)]ボタンをクリックしたのだとしたら、どうする?

ナッキー:間違えたんだから、仕方ないってあきらめる。

高橋先生:それじゃあ少し不親切だね。第12回 お絵かきソフトにメニューバー追加で「MessageDlg」を使ったことを覚えている?

ナッキー:えっと、ボタンが複数ついたメッセージボックスでしたっけ?

高橋先生:そうそれだ。そのメッセージボックスを使って、「削除してもいいですか?」と聞いて、[OK]ボタンをクリックしたときだけ削除すれば間違えにくいね。

ナッキー:そのほうが親切なんですね。

高橋先生:メッセージの種類は確認を求める内容だから「?」マークのアイコンが入った「mtConfirmation」、ボタンの種類は[はい|いいえ]ボタンにしよう。ボタンの定数は[mbYes,mbNo]だ。[はい]ボタンをクリックしたとき、戻り値は「mrYes」になる。それから、リストボックスから何も選択しないで[削除(D)]ボタンをクリックすることも心配だね。

ナッキー:また、エラーになっちゃうんですか?

高橋先生:エラーにならず、何もしない。けれどユーザーにはどうなったかよくわからない、ということになるね。ちゃんと示してあげないと、削除したものだと思い込むかもしれない。

ナッキー:いろいろ、気配りが必要なんですね。「選択していない」ということはどうやったらわかるんですか?

高橋先生:どの行を選択しているかはリストボックスの「ItemIndex」プロパティに入っているよ。リストボックスの各行は「Items」プロパティに配列で格納されている。配列の添え字(番号)は1行目を「0」として連番がついていたね。選択していない場合は「-1」が入っているから、ItemIndexプロパティが「<0」かどうかを調べるIf文を作ればいいね。

ナッキー:ItemIndexプロパティが「0」だと何にも選択されていない感じがするけど、1件目が選択されているんですね。

高橋先生:ボタンの数が増えたから[プロフィール]ボタンでは機能を充分説明できていないね。Captionプロパティを「追加(&A)」に変えたほうが、わかりやすいね。


では先に[プロフィール]ボタンのCaptionプロパティを変更します。表示をフォームデザイナに切り替えて、[プロフィール]ボタンを選択します。CaptionプロパティのほかにNameプロパティも変更しましょう。オブジェクトインスペクタ、プロパティページで表を参考にして変更します。配置も左端に移動します。

[プロフィール]ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnAddData

ローカライズ対象

Caption

追加(&A)


次に[削除(D)]ボタンを配置します。画面左側のツールパレット「Standard」カテゴリから「TButton」コンポーネントを探してフォームに配置します。配置する場所は[追加(A)]ボタンの右側あたりです。配置できたら、オブジェクトインスペクタのプロパティページで表を参考にして設定します。

[削除(D)]ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnDelete

ローカライズ対象

Caption

削除(&D)


次にイベントハンドラを作成します。If文でリストボックスの選択がされているかどうか調べます。ListBox1.ItemIndexプロパティが「<0」だったとき、選択されていないことをメッセージボックスで知らせます。If文の条件を満たさなかったとき、つまり何かリストボックスで選択されていたとき、MessageDlgで[はい|いいえ]ボタンのついたメッセージボックスを表示します。メッセージの内容は「削除してもいいですか?」です。[はい]ボタンをクリックしたとき、「ListBox1.Items.Delete」メソッドで削除します。

では、フォームデザイナの画面で[削除(D)]ボタンを選択して、オブジェクトインスペクタ、イベントページで「入力」カテゴリの「OnClick」をダブルクリックします。コードエディタに切り替わったら、太字部分を記述します。

procedure TForm2.btnDeleteClick(Sender: TObject);
begin
  if ListBox1.ItemIndex < 0 then
    ShowMessage('削除リストを選択してください')
  else
    if MessageDlg('削除してもいいですか?', mtConfirmation, [mbYes,mbNo],0)
         = mrYes then
      ListBox1.Items.Delete(ListBox1.ItemIndex);
end;

コードが記述できたら、ツールバーの[すべて保存]ボタンで保存して、[実行]ボタンで実行します。[読み込み(R)]ボタンでデータを読み込んだら、リストボックスから何も選択しないで[削除(D)]ボタンをクリックします。「削除リストを選択してください」というメッセージボックスが表示されます。次にリストボックスから1つ選択して、[削除(D)]ボタンをクリックします。「削除してもいいですか?」というメッセージボックスが表示されます。[はい]ボタンをクリックすると選択行を削除します。そして、リストボックスの変更を保存するときには[保存(S)]ボタンをクリックします。

Hide image
05削除してもいいですかメッ

図05 「削除してもいいですか」メッセージ

    [編集(E)]ボタンの作成

保存、読み込み、削除までできたわ。間違って書いたとき、書き直しもできるといいな。教えて、高橋先生!

高橋先生:次は、入力データの編集ができるといいね。一覧からデータを選択して、[編集(E)]ボタンをクリックすると、編集用の画面を表示するというのはどうかな?

ナッキー:書き直せると、たくさん入力するときには助かります。

高橋先生:イメージはできたよね。では、機能を紹介する前にボタンを先に作っておこう。


[編集(E)]ボタンを配置します。フォームデザイナに切り替えて、画面右側のツールパレット「Standard」カテゴリから「TButton」コンポーネントを探してフォームに配置します。配置する場所は[削除(D)]ボタンの右側あたりです。配置できたら、オブジェクトインスペクタのプロパティページで表を参考にして設定します。

[編集(E)]ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnEdit

ローカライズ対象

Caption

編集(&E)


このボタンをクリックすると編集ダイアログボックスを表示するように、これから作っていくんですって。では、ここまでを保存しておきますね。ツールバーの[すべて保存]ボタンで保存します。

    メインフォームを整える

[編集(E)]ボタンを使って表示するための、編集ダイアログボックスを作るのね。前回に引き続きテンプレートを使って作成するのかしら。教えて、高橋先生!

高橋先生:今回はテンプレートを使わない方法を紹介しようと思うんだ。テンプレートを使わない分、作業も増えるけど大丈夫?

ナッキー:挑戦してみまーす。

高橋先生:そこで、作業を簡単にするために、今設計しているメインフォームからコンポーネントをコピーして持ってくるつもりだ。だからメインフォームをもう少し整えておこう。まず、住所と誕生日のコンボボックスは初期値を設定していないので、設定しておこう。自分の住所や近い場所、自分の誕生日なんかを初期値に入力しておいてね。

ナッキー:メインフォームをもうちょっと、加工してからサブフォームを作るんですね。

高橋先生:そのほうがサブフォームも作りやすいよ。各入力項目にアクセラレータ文字を追加したり、フォームのCaptionプロパティに「アンケート」と入れてもらおうかな。

ナッキー:え?でも各コンポーネントのCaptionプロパティにアクセラレータ文字をつけるんですよね。エディットコンポーネントなどはCaptionプロパティを持っていませんよ。

高橋先生:そのために、ラベルがあるんだよ。TLabelコンポーネントに「FocusControl」プロパティがある。TLabelコンポーネントのCaptionプロパティにつけたアクセラレータ文字が、FocusControlプロパティに設定したコンポーネントに対して有効になるんだ。

ナッキー:うーん。ちょっと難しいな。

高橋先生:例えばLabel1のCaptionプロパティに「名前(&N)」と入力して、FocusControlプロパティに「Edit1」を設定する。実行中キーボードの[Alt]キーと[N]キーを押すとLabel1ではなくて、Edit1にフォーカスが移動する。TLabelコンポーネントはもともとフォーカスを持てないよ。

ナッキー:TLabelコンポーネントは、Captionを持たないコンポーネントの説明のほかに、アクセラレータ文字も用意できるのね。

高橋先生:それから、アプリケーションタイトルのつけ方は覚えてる?実行したときタスクバーに表示される名前をプロジェクト名ではなくて独自につけられる。

ナッキー:何かのオプション設定でしたっけ?

高橋先生:[プロジェクト(P)|オプション(O)]メニューで表示する「プロジェクトオプション」ダイアログボックスで設定できる。左側の項目一覧から「アプリケーション」を選択して表示される「アプリケーションの設定」の「タイトル(T)」欄に入力すると変更できる。「アンケート」と名前を付けよう。

ナッキー:いっぱい、やることありますねぇ。でもこれで以前より整うわ。


整理しましょう。えっと、やることは[住所]コンボボックスの初期値を設定すること、[誕生日] のTDateTimePickerコンポーネントの初期値を設定すること、それぞれのアクセラレータ文字を付ける、フォームのCaptionプロパティに「アンケート」と入力する、プロジェクトオプションでアプリケーションタイトルに「アンケート」と入れる、の5つね。

まず、[住所]コンボボックスの初期値を設定します。フォームデザイナの画面で、[住所]コンボボックスを選択します。オブジェクトインスペクタ、プロパティページで「ローカライズ対象」カテゴリの「Text」プロパティに、コンボボックスの「Items」プロパティ一覧に載っている住所どれか1つを選んで入力します。

次に、[誕生日]デートタイムピッカーの初期値を設定します。[誕生日]デートタイムピッカーを選択します。オブジェクトインスペクタ、プロパティページで「その他」カテゴリの「Date」プロパティに、適当な年の「1/1」を入力します。今回は「1980/1/1」としています。

それからアクセラレータ文字をつけます。ラベルには、FocusControlプロパティに設定するコンポーネントを選びます。フォームのCaptionも設定します。表を参考にしてプロパティを設定してみます。

[名前]ラベル(Label1)

カテゴリ名

プロパティ名

設定値

リンク

FocusControl

Edit1

ローカライズ対象

Caption

名前(&N)


[住所]ラベル(Label2)

カテゴリ名

プロパティ名

設定値

リンク

FocusControl

ComboBox1

ローカライズ対象

Caption

住所(&J)


[誕生日]ラベル(Label3)

カテゴリ名

プロパティ名

設定値

リンク

FocusControl

DateTimePicker1

ローカライズ対象

Caption

誕生日(&B)


[性別]ラジオグループ(RadioGroup1)

カテゴリ名

プロパティ名

設定値

ローカライズ対象

Caption

性別(&M)


[ペットを飼っている]チェックボックス(CheckBox1)

カテゴリ名

プロパティ名

設定値

ローカライズ対象

Caption

ペットを飼っている(&P)


[Form2]フォーム(Form2)

カテゴリ名

プロパティ名

設定値

ローカライズ対象

Caption

アンケート


次は、 アプリケーションタイトルを入力するんですね。メニューバーの[プロジェクト(P)|オプション(O)]メニューをクリックします。「Profile.exeプロジェクトオプション」ダイアログボックス画表示されたら、左側の項目から「アプリケーション」を選択します。画面右側、「アプリケーションの設定」の項目「タイトル(T)」に「アンケート」と入力して、[OK]ボタンをクリックします。

これで、画面を整えられました。配置や幅などは適当に調整しておきます。できたら、ツールバーの[すべて保存]ボタンで保存します。

Hide image
06メインフォーム

図06 メインフォーム

高橋先生:次は、コードを使って整えよう。まず、現状ではリストボックスに誕生日が掲載されていない。以前は関数をあまり使えなかったからね。誕生日を入力するTDateTimePickerは日付をTDate型で「Date」プロパティに持っている。これを、string型に変換してリストボックスに載せよう。

ナッキー:「変換して」って関数を使って変換するんですか?

高橋先生:そうなんだよ。「DateToStr」関数でTDate型からstring型に変換できる。年月日の区切り目は「/」になる。逆は「StrToDate」でstring型からTDate型に変換できるよ。

ナッキー:わざわざ関数で変換することが必要ってことは、日付を扱う型って文字じゃなかったんですねぇ。

高橋先生:実は、文字よりも数値のほうが近い。1899/12/30を基準として、1日を「1」として扱うんだ。そうしないと日付や時間の計算ができないからね。[追加(A)]ボタンのOnClickイベントハンドラに追加すればいいよ。

ナッキー:そっか、日数で計算しますよね。じゃあ、[追加(A)]ボタンをクリックしたとき、TDate型からstring型に変換して、リストボックスのデータとして登録すればいいんですね。

高橋先生:さらに2つ変えてほしいところがある。まずは全角の「:」を半角の「:」にする。リストボックスに登録した、名前や住所のデータの区切り目は全角の「:」だったね。でも、このままではデータの読み込みがとても面倒になってしまうんだ。半角文字を使うと読み込みやすくなる方法があるから、全角の「:」をすべて半角の「:」に書き直してほしい。読み込みの方法はあとで紹介するね。

ナッキー:全角の「:」だと、高橋先生の考えている方法が使えないってことですね。

高橋先生:そういうこと。さらに、「:」の個数に注目しよう。ペットを飼っている人はが3つ、飼っていない人は2つと、データによって異なっている。これだとコードが難しくなるので、ペットを飼っていない人も「:」が付くようにする。ここでは、ペットを飼っていない人は「:」だけ追加するようにコードを書き換ればOK。

ナッキー:この変更は全部、[追加(A)]ボタンのClickイベントハンドラで記述できますね。


画面をコードエディタに切り替えて、「btnAddDataClick」イベントハンドラを探します。誕生日をリストボックスに追加することからはじめます。「DateToStr」関数を使ってDateTimePicker1の「Date」プロパティをパラメータにして、文字列に変換します。追加する場所は住所と性別の間にします。[住所]コンボボックスの情報を「Profile_Data」変数に代入する文に、誕生日を代入する文を追加します。以前のコメント部分は省略しています。太字部分を書き換えます。

procedure TForm2.btnAddDataClick(Sender: TObject);
var
  Profile_Data : string;
begin
//コメント部分は省略
  Profile_Data := Edit1.Text;
  Profile_Data := Profile_Data + ':' + ComboBox1.Text
    + ':' + DateToStr(DateTimePicker1.Date);
  if RadioGroup1.ItemIndex = 0 then
  //以下略
end;

次に、「btnAddDataClick」イベントハンドラで全角の「:」を半角の「:」に変えます。そして、CheckBox1の「Checked」プロパティを判別しているIf文にelse文を追加して、「:」を追加する文を追加します。「:」と太字部分を変更します。

procedure TForm2.btnAddDataClick(Sender: TObject);
var
  Profile_Data : string;
begin
  Profile_Data := Edit1.Text;
  //中略
  if CheckBox1.Checked then
    Profile_Data := Profile_Data + ':' + 'ペットを飼っている'//セミコロン削除
  else
    Profile_Data := Profile_Data + ':'; 
  if Edit1.Text = '' then
    ShowMessage('名前が入力されていません')
  else
    ListBox1.Items.Add(Profile_Data);
end;

これで、メインフォームが整いました。保存して実行します。ツールバーの[すべて保存]ボタンで保存して、[実行]ボタンで実行します。

[読み込み(R)]ボタンでデータを読み込んだら、すでに入っているリストボックスのデータはすべて削除してしまいます。リストを選択して、[削除(D)]ボタンで全件を削除します。できたら新しいデータを入力して[追加(A)]ボタンをクリックします。誕生日が追加されているか、全角の「:」から半角の「:」に変わっているか、ペットを飼っていないとき末尾に「:」が表示されているかの3つをチェックしておいてね、って高橋先生が言ってました。何件か入力できたら、[保存(S)]ボタンをクリックして保存しておきます。

Hide image
07メインフォームの実行

図07 メインフォームの実行

    編集ダイアログボックスの作成

今度こそ、編集ダイアログボックスを作るんですね。テンプレートを使わないとどう違うんだろう?教えて、高橋先生!

高橋先生:フォームを新規に作成するんだけど、表示の種類をダイアログボックスのスタイルに変更する必要がある。ダイアログボックスはタイトルバーに[最大化|最小化]ボタンがないスタイルだよ。フォームの表示の種類は「BorderStyle」プロパティで設定できる。また、表示するとき「ShowModal」で表示するのが一般的。

ナッキー:テンプレートを使わないと、プロパティを設定する手間も増えるんですか。フォームとダイアログボックスでは表示の種類がなんとなく違うなと思ってたけど、はっきり違いがあるのね。

高橋先生:編集用の画面だから、画面の中央に表示されたほうが操作しやすいね。「Position」プロパティで「poScreenCenter」に設定すると、画面中央に表示できるよ。

ナッキー:フォームの表示位置を今まで気にしていなかったけど、設定できるんですね。

高橋先生:画面の構成はリストボックスの項目と同じ、名前、住所、性別、ペットを飼っているが並んで、更新を反映するボタンと、キャンセルするボタンがあればいいね。ボタン以外はメインフォームからコピーして貼り付けよう。プロパティもそのままコピーできるよ。

ナッキー:全部コピーできるなら、時間短縮ですね。

さっそく、編集ダイアログボックスを作ります。今回はメニューを使用して作るんですって。メニューバーから「ファイル(F)|新規作成(N)」サブメニューで「フォーム - Delphi for Win32(E)」をクリックします。サイズが小さいので入力用のコンポーネントを配置することを考えて少し広げます。フォームのプロパティも設定しておきます。表を参考にしてプロパティを設定します。ここではフォーム名を「Form3」として説明しますが、手順によってはフォーム名が異なる場合があります。

Form3フォーム

カテゴリ名

プロパティ名

設定値

その他

Name

frmEdit

その他

Position

poScreenCenter

ローカライズ対象

Caption

編集

表示

BorderStyle

bsDialog


できたら名前をつけて保存します。ツールバーの[すべて保存]ボタンをクリックすると「名前をつけて保存」。ダイアログボックスが表示されます。「FormEdit」と入力して[保存(S)]ボタンをクリックします。

次に、コンポーネントを配置します。コンポーネントをメインフォームからコピーしてきます。画面上部の「FormProfile」タブをクリックして切り替えます。表示をフォームデザイナにして、入力項目すべて(CheckBox1、ComboBox1、DateTimePicker1、Edit1、Label1、Label2 、Label3、RadioGroup1)を選択します。確認できたらコピーします。[編集(E)|コピー(C)]メニューをクリックするか、キーボードの[Ctrl]+[C]キーを押してコピーします。次に、編集ダイアログボックスに表示を切り替えます。画面上部の「FormEdit」タブをクリックします。表示をフォームデザイナにして、フォーム上にコンポーネントを貼り付けます。[編集(E)|貼り付け(P)]メニューをクリックするか、キーボードの[Ctrl]+[V]キーを押して貼り付けます。ほかに、TButtonコンポーネント2つを配置します。画面右下のツールパレット「Standard」カテゴリの「TButton」をクリックして、画面下部に配置します。もう1つのTButtonコンポーネントも同様にして隣に並べます。

配置できたら、プロパティを設定します。コピーして配置したコンポーネントもNameプロパティを設定します。表を参考にして配置したコンポーネントのプロパティを設定します。

[名前(N)]エディット(Edit1)

カテゴリ名

プロパティ名

設定値

その他

Name

edtName


[住所(J)]コンボボックス(ComboBox1)

カテゴリ名

プロパティ名

設定値

その他

Name

cmbAddress


[誕生日(B)]デートタイムピッカー(DateTimePicker1)

カテゴリ名

プロパティ名

設定値

その他

Name

dtpBirthday


[性別(M)]ラジオグループ(RadioGroup1)

カテゴリ名

プロパティ名

設定値

その他

Name

rgpSeibetu


[ペットを飼っている(P)]チェックボックス(CheckBox1)

カテゴリ名

プロパティ名

設定値

その他

Name

cbxPet


Button1ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnUpdate

ローカライズ対象

Caption

更新(&U)


Button2ボタン

カテゴリ名

プロパティ名

設定値

その他

Name

btnCancel

ローカライズ対象

Caption

キャンセル(&C)


これで編集ダイアログボックスの外観は完成しました。ツールバーの[すべて保存]ボタンをクリックして保存します。

Hide image
08編集ダイアログボックス

図08 編集ダイアログボックス

    TStringListへデータを読み込む

選択しているリストボックスのデータを編集ダイアログボックスに読み込むんですよね。でも、1人分のデータは1行にまとめてあるから、名前は名前、住所は住所としてバラバラにしなくっちゃ。文字数は決まっていないのにどうやって分けるのかしら?教えて、高橋先生!

高橋先生:さっき、「:」を半角にしてもらったから簡単に分けられるよ。データをTStringList型の変数に入れると、あるプロパティを使って分けることができるんだ。

ナッキー:変数に代入してから、どんなふうに分けるんですか?

高橋先生:TStringList型は複数の文字列を持つことができる型だよ。TStrings型に似ているね。TStringListはTStrings型に少し機能を追加したものと考えてほしい。

長い文字列を区切るには、まず区切り文字を「Delimiter」プロパティに設定する。区切り文字として、半角で1文字分しか設定できないから「:」を半角にしてもらったんだ。あとは区切り文字が混じっている長い文字列を「DelimitedText」プロパティに代入する。あとは1つ1つに分けられた短い文字列を配列形式で持つ「Strings」プロパティを0番目から順に参照すればOK。

ナッキー:へぇ。決まった方法でデータを代入して、決まった方法を使えばデータを区切って取り出せるんですね。

高橋先生:やり方さえ習得できれば難しくないよ。TStringList型は使う前に生成する必要がある。

ナッキー:生成するってなんでしたっけ?

高橋先生:Createメソッドでメモリを確保するのが生成だよ。「第10回お絵かきソフトでオブジェクト生成」の回で紹介したよ。

ナッキー:そういえば、引き出しを用意する感じでしたっけ。

高橋先生:書き方は

変数 := TStringList.Create;

でいいよ。

ナッキー:変数がTStringList型でメモリに読み込まれるってことですね。

高橋先生:ほかのコンポーネントの値も見てみよう。性別は、「'男性'」ならラジオグループの「ItemIndex」プロパティを「0」にして、そうでなければ「1」にする。ペットの有無は「''」(長さ0の文字列)だったらチェックボックスの「Checked」プロパティを「False」にして、そうでなければ「True」を代入すればいいよ。

ナッキー:If文でチェックして、値を代入するのね。これを、OnCreateイベントハンドラに記述すればいいんですよね。

高橋先生:OnCreateイベントハンドラって何回働くと思う?

ナッキー:フォームを表示するときに呼び出されるんじゃないんですか?

高橋先生:厳密に言うとフォームがメモリに読み込まれたときだね。そして、このフォームは表示するたびにメモリに読み込むんじゃないんだよ。起動したときに1度だけメモリに読み込まれたら、あとは読み込まれたままなんだ。

ナッキー:じゃあ、どんなイベントに書けばいいんですか?

高橋先生:OnShowというイベントがある。これは「Show」メソッドや「ShowModal」メソッドが呼び出されたあとに発生するイベント。だから表示するたびに処理できる。

ナッキー:表示されるとき、ってことですね。

高橋先生:FormProfileユニットのリストボックスの値を利用するから、[ファイル(F)|ユニットを使う(U)...]でuses節に追加してね。

ナッキー:これは、前回やったから覚えてますよ。


では、先にuses節にユニットを追加します。メニューバーで[ファイル(F)|ユニットを使う(U)...] をクリックします。「ユニットの使用」ダイアログボックスが表示されたら、「FormProfile」ユニットを選択して[OK]ボタンをクリックします。画面をコードエディタにして実装部のuses節に追加されていることを確認します。

implementation
uses FormProfile;
{$R *.dfm}

次にイベントハンドラを作成します。画面はFormEditユニットのフォームデザイナで、frmEditフォームを選択します。画面左側のオブジェクトインスペクタ、イベントページで「表示」カテゴリの「OnShow」イベントをダブルクリックします。変数を用意しておきます。リストボックスで何番目のリストが選択されているかを格納するためのInteger型の変数と、リストボックス内の1人分のデータを処理するためのTStringList型の変数を宣言します。コードエディタに切り替わったら、太字部分を追加します。

procedure TfrmEdit.FormShow(Sender: TObject);
var
  i : Integer;
  stlData : TStringList;
begin

end;

では、リストボックスのデータを読み込みます。まず、リストボックスのデータのうち何番目が選択されているかを「i」に代入しておきます。次にTStringListの変数を生成して、「Delimiter」プロパティに半角の「':'」を代入します。そうして、「DelimitedText」プロパティにデータを代入します。リストボックスで選択されている文字列は「ListBox1.Items[0]」のように読み込めます。

「ListBox1」はForm2の上のコンポーネントなので、フォーム名もつけておきます。太字部分を追加します。

procedure TfrmEdit.FormShow(Sender: TObject);
var
  i : Integer;
  stlData : TStringList;
begin
  i := Form2.ListBox1.ItemIndex;
  stlData := TStringList.Create;
  stlData.Delimiter := ':';
  stlData.DelimitedText := Form2.ListBox1.Items[i];
end;

次はデータを各項目ごとに分割して、それぞれのコンポーネントに設定します。項目はTStringListの「Strings」プロパティに配列で入っています。リストボックスに入っていた項目の左から順に「0」番から「4」番まで5つの項目に分かれているんですって。それじゃあ、名前は0番目、住所は1番目ってことね。コンポーネントに合わせて代入します。

名前と住所は、コンポーネントの「Text」プロパティに代入できます。誕生日は文字列から日付型にする「StrToDate」関数を使えば型を変えられるんだったわね。日付型にできれば、TDateTimePickerコンポーネントの「Date」プロパティに代入できます。性別は「ItemIndex」プロパティに何番目が選択されているかが入っているので、「男性」なら「0」、そうでなければ「1」を代入するようにします。ペットのチェックボックスは「''」(長さ0の文字列)のとき、「Checked」プロパティを「False」に、そうでなければ「True」にします。FormShowイベントハンドラに太字部分を追加します。

procedure TfrmEdit.FormShow(Sender: TObject);
var
  i : Integer;
  stlData : TStringList;
begin
  i := Form2.ListBox1.ItemIndex;
  stlData := TStringList.Create;
  stlData.Delimiter := ':';
  stlData.DelimitedText := Form2.ListBox1.Items[i];
  edtName.Text := stlData.Strings[0];
  cmbAddress.Text := stlData.Strings[1];
  dtpBirthday.Date := StrToDate(stlData.Strings[2]);
  if stlData.Strings[3] = '男性' then
    rgpSeibetu.ItemIndex := 0
  else
    rgpSeibetu.ItemIndex := 1;
  if stlData.Strings[4] = '' then
    cbxPet.Checked := False
  else
    cbxPet.Checked := True;
  FreeAndNil(stlData);
end;

これで、編集ダイアログボックスにデータを読み込むことができますね。実行はしないけど、念のために保存しておきます。ツールバーの[すべて保存]ボタンをクリックして保存します。

    ModalResultプロパティ

これでやっと、編集ダイアログボックスを表示できるんですね。「ShowModal」で表示するのかな?教えて、高橋先生!

高橋先生:もうちょっと、編集ダイアログボックスで作業が残っている。以前はテンプレートを使ってフォームを作ったから、気にしてないと思うけど。フォームを表示したあと、どうやってフォームは閉じると思う?

ナッキー:うーん、[更新(U)]ボタンか[キャンセル]ボタンをクリックすればいいんじゃないかな?

高橋先生:まだ、[更新(U)]ボタンも[キャンセル]ボタンもコードを書いていないよ。テンプレートを使うと、コードに何も書かなくても[OK]ボタンや[キャンセル]ボタンでフォームを閉じるように機能するんだ。でも今回はコードに書く必要がある。

ナッキー:じゃあ、「Close」メソッドを書けばいいのね。

高橋先生:それじゃあ、[更新(U)]ボタンと[キャンセル]ボタンのどちらがクリックされたかわからないよね。自動的にフォームを閉じて、しかも、どちらがクリックされたかの目印を設定することができるプロパティがあるんだ。

ナッキー:目印を設定ってどういうことですか?

高橋先生:「ModalResult」プロパティはフォームのプロパティなんだ。これに「mrOK」とか「mrCancel」などの定数を代入する。そうするとフォームが閉じられて、「ShowModal」を呼び出したところに、その定数が戻り値として返されるんだ。

ナッキー:んー。ちょっとよくわかりません。「戻り値として返される」ってどういうことですか?

高橋先生: If文の条件文として

if frmEdit.ShowModal = mrOK then

というように記述すれば、「ModalResult」に何を代入したかが、わかるようになっているんだ。

ナッキー:「ModalResult」プロパティに定数を代入すると、①自動的にフォームを閉じる、②そのフォームを表示したイベントハンドラに代入した定数を知らせる、ということですね。

高橋先生:だから、編集ダイアログボックスの場合は[更新(U)]ボタンのOnClickイベントで「ModalResult」に「mrOK」を代入する。[キャンセル(C)]ボタンのOnClickイベントで「ModalResult」に「mrCancel」を代入する。

ナッキー:ここまで記述すれば編集ダイアログボックスは完成なんですね。テンプレートって、いろいろやってくれていたんだなぁ。


[更新(U)]ボタンと[キャンセル(C)]ボタンのイベントハンドラを作成します。まずは[更新(U)]ボタンから作ります。FormEditのフォームデザイナ画面で[更新(U)]ボタンを選択します。画面左側のオブジェクトインスペクタ、イベントページで「入力」カテゴリの「OnClick」イベントをダブルクリックします。太字部分を追加します。

procedure TfrmEdit.btnUpdateClick(Sender: TObject);
begin
  ModalResult := mrOk;
end;

次に[キャンセル(C)]ボタンを作ります。FormEditのフォームデザイナ画面で[キャンセル(C)]ボタンを選択します。画面左側のオブジェクトインスペクタ、イベントページで「入力」カテゴリの「OnClick」イベントをダブルクリックします。太字部分を追加します。

procedure TfrmEdit.btnCancelClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

記述できたら、ツールバーの[すべて保存]ボタンをクリックして保存します。編集ダイアログボックスが完成しました。

高橋先生:じゃあ、次はメインフォームの[編集(E)]ボタンを作成しよう。

ナッキー:これで、フォームを呼び出せますね。

高橋先生:まあ、あわてないで。リストボックスで何も選択しないで[編集(E)]ボタンをクリックする場合もあるだろうから、チェックしよう。[削除(D)]ボタンでチェックしたのと同じだよ。

ナッキー:じゃあ、コピーして作ってもいいですね。

高橋先生:メッセージとか書き直すのを忘れないでね。そして、リストボックスで何か選択していたらフォームを呼び出す。そして、戻り値もチェックしよう。

if frmEdit.ShowModal = mrOk then

[更新(U)]ボタンがクリックされて戻り値が「mrOK」だったとき、frmEditフォームの各コンポーネントに入っている値を結合して文字列にしよう。そして、リストボックスで選択している行に代入する。あと、今回も異なるフォームの値を参照するから実装部のuses節にユニットを追加してね。

ナッキー:わあ、いよいよ実行できるんですね。


先にユニットを追加します。画面上部の「FormProfile」タブをクリックしてメインフォームに表示を切り替えます。メニューバーで[ファイル(F)|ユニットを使う(U)...] をクリックします。「ユニットの使用」ダイアログボックスが表示されたら、「FormEdit」ユニットを選択して[OK]ボタンをクリックします。画面をコードエディタにして実装部のuses節に追加されていることを確認します。

implementation
uses FormEdit;
{$R *.dfm}

[編集(E)]ボタンのOnClickイベントハンドラを作ります。画面をフォームデザイナに切り替えて、[編集(E)]ボタンを選択します。オブジェクトインスペクタ、イベントページで「入力」カテゴリの「OnClick」をダブルクリックします。コードエディタに切り替わったら、太字部分を追加します。

procedure TForm2.btnEditClick(Sender: TObject);
var
  s : string;
begin
  if ListBox1.ItemIndex < 0 then
    ShowMessage('編集リストを選択してください')
  else
end;

次にfrmEditフォームを「ShowModal」メソッドで表示して、戻り値をチェックするIf文を作成します。複数文入るので「begin」と「end;」で囲みます。

procedure TForm2.btnEditClick(Sender: TObject);
var
  s : string;
begin
  if ListBox1.ItemIndex < 0 then
    ShowMessage('編集リストを選択してください')
  else
    if frmEdit.ShowModal = mrOk then
    begin

    end;
end;

frmEditのコンポーネントに入っている各項目を文字列として結合してs変数に代入します。誕生日は「DateToStr」関数で型を変換します。性別はrgpSeibetuラジオグループの「ItemIndex」プロパティが0だったら「'男性'」、そうでなければ「'女性'」を代入します。ペットはcbxPetチェックボックスの「Checked」プロパティが「True」だったら「'ペットを飼っている'」を代入します。最後にリストボックスの選択行「ListBox1.Items[ListBox1.ItemIndex]」にs変数を代入して、更新したことをメッセージボックスで知らせます。太字部分を追加します。

unit FormProfile;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls, ExtCtrls;

type
  TForm2 = class(TForm)
    ListBox1: TListBox;
    Panel1: TPanel;
    btnAddData: TButton;
    CheckBox1: TCheckBox;
    ComboBox1: TComboBox;
    DateTimePicker1: TDateTimePicker;
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    RadioGroup1: TRadioGroup;
    btnRead: TButton;
    btnSave: TButton;
    btnDelete: TButton;
    btnEdit: TButton;
    procedure btnAddDataClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure btnEditClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form2: TForm2;

implementation

uses FormEdit;

{$R *.dfm}

procedure TForm2.btnEditClick(Sender: TObject);
var
  s : string;
begin
  if ListBox1.ItemIndex < 0 then
    ShowMessage('編集リストを選択してください')
  else
    if frmEdit.ShowModal = mrOk then
    begin
      s := frmEdit.edtName.Text + ':' +
        frmEdit.cmbAddress.Text + ':' +
        DateToStr(frmEdit.dtpBirthday.Date) + ':';
      if frmEdit.rgpSeibetu.ItemIndex = 0 then
        s := s + '男性' + ':'
      else
        s := s + '女性' + ':';
      if frmEdit.cbxPet.Checked then
        s := s + 'ペットを飼っている';
      ListBox1.Items[ListBox1.ItemIndex] := s;
      ShowMessage('更新しました');
    end;
end;

procedure TForm2.btnReadClick(Sender: TObject);
begin
  if FileExists('Profile.txt') then
    ListBox1.Items.LoadFromFile('Profile.txt');
end;

procedure TForm2.btnSaveClick(Sender: TObject);
begin
  ListBox1.Items.SaveToFile('Profile.txt');
  ShowMessage('保存しました');
end;

procedure TForm2.btnAddDataClick(Sender: TObject);
var
  Profile_Data : string;
begin
//  コメント部分略
  Profile_Data := Edit1.Text;
  //「:」を半角に書き換え
  Profile_Data := Profile_Data + ':' + ComboBox1.Text;
  if RadioGroup1.ItemIndex = 0 then
    Profile_Data := Profile_Data + ':' + '男性'
  else
    Profile_Data := Profile_Data + ':' + '女性';
  if CheckBox1.Checked then
    Profile_Data := Profile_Data + ':' + 'ペットを飼っている'
  else
    Profile_Data := Profile_Data + ':'; //追加しないと編集画面でエラーになります。

  if Edit1.Text = '' then
    ShowMessage('名前が入力されていません')
  else
    ListBox1.Items.Add(Profile_Data);
end;

procedure TForm2.btnDeleteClick(Sender: TObject);
begin
  if ListBox1.ItemIndex < 0 then
    ShowMessage('削除リストを選択してください')
  else
    if MessageDlg('削除してもいいですか?',mtConfirmation,[mbYes,mbNo],0)
         = mrYes then
      ListBox1.Items.Delete(ListBox1.ItemIndex);
end;

end.

やっと、完成しました。保存して実行します。ツールバーの[すべて保存]ボタンで保存して、[実行]ボタンで実行します。[読み込み(R)]ボタンでデータを読み込んだら、リストボックスから1つ選択して[編集(E)]ボタンをクリックします。成功したら、書き換えて[更新(U)]ボタンをクリックします。リストボックスの内容を確認して変更できたら完成です。

Hide image
09編集ダイアログボックス実

図09 編集ダイアログボックスの実行

Hide image
10完成図

図10 完成図

ナッキー:やっとできましたー。大変だったなぁ。

高橋先生:リストボックスの内容を切り分けたり、くっつけたりいろいろあったよね。これを簡単にする方法があるんだ。でもそれだと根本的に変えないとね。

ナッキー:簡単にできるんなら、先に紹介してくださいよ。

高橋先生:苦労しないと、簡単な方法のありがたさがわからないでしょ。方法は簡単だけど、内容がちょっと難しいかもしれない。コードをあまり書かずにプロパティ設定だけで、ある程度のプログラムができちゃうよ。

ナッキー:えー、コード書かなくていいんですか!

高橋先生:次回は今回作ったプログラムを、データベースで作ってみよう。

ナッキー:わ、データベースって難しそう。

高橋先生:ビシビシっといくぞ!


ナッキーの「Turbo Delphiはじめて奮戦記」

Prev | Next | Index


次からのサーバー応答:: ETNASC04