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

投稿者: : Hitoshi Fujii

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

目次

nacky75

ナッキー

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

 

takahashi75

高橋先生

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


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

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

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


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

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

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)]ボタンをクリックして保存します。「保存しました」メッセージが表示できたら、実行を終了します。

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

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

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

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)]ボタンをクリックすると、前回保存した内容がリストボックスに表示されます。

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)]ボタンをクリックします。

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]ボタンをクリックします。

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

06メインフォーム

図06 メインフォーム

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

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

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

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

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

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

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

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