日記帳 MyDiaryのドキュメント




日記帳(MyDiary)
ダウンロードはこちらから

●製作の動機
 一日一日を整理したり振り返ったりすることなく過ごしてしまう人は多いことでしょう.「今日はこういうことがあった」とか振り返ったり,「明日は○○をしよう」とか決心をつけたりしても,たいていの場合はそれを心に閉ざしたまま外に吐き出しはしないと思います.そんな人々のために,一日を総括して整理するためのツールとして,このソフトを作りました.毎日の出来事を気軽に整理すれば,自分にとってためになることは間違いありません.知らぬ間に月日を無駄に過ごしてしまうのではなくて,日記をつけることによって,充実した毎日を送りたいものです.

●日記帳(MyDiary)の概要
 まず起動すると,その日の日記編集画面になるので,そのまま日記をつける事ができます.天気(デフォルトでは「不明」になっている)をコンボボックスで選択し,その日の満足度(デフォルトでは「普通」になっている)をラジオボタンで選択します.そしてエディットボックスで本文を書いて,保存ボタンをクリックすれば保存されます.他の日の日記を見るには,日時指定ボックスで日付を選択すればすぐにその日の日記が見られます.その場合その日記を,編集することも可能です.編集途中で終了をクリックしたり,日付を変更しようとすると,その日の日記を保存するかどうか尋ねてくるようにしました.以下にその様子を示します.
保存の確認


●日記帳(MyDiary)の構造
 日記帳(MyDiary)はダイアログベースのアプリケーションです.Visual C++ではおなじみのドキュメント/ビューアーキテクチャを持っているわけではありません.ダイアログベースのアプリケーションはダイアログリソースとダイアログクラスを結びつけた構造を持っています.(正確に言えば,リソースをクラスに取り込んだ形をしている).つまり,ダイアログベースアプリケーションを考える場合は,これらリソースとクラスがどのように連携しているかを知らなければなりません.そこで,以下にそれらの関係を示します. 

 CDiaryDlgクラスはCDialogクラスを継承しています.ごらんのようにCDiaryDlgクラスはダイアログリソース(IDD_DIARY_DIALOG)を内蔵しています.リソース上には様々なコントロール類が実装されていて,アプリケーションのユーザはこのコントロールを介してCDiaryDlgクラスと情報を交換することができます.リソース上には様々なコントロールがあるのですが,それらにはそれぞれ独自のIDがつけらてていて,CDiaryDlgクラス内ではそのIDを使ってアクセスすることができます.
 Visual Studioではリソースの編集とクラスの編集を独立して行う仕組みになっているので,初めてダイアログベースプログラミングにとりつく人は混乱してしまいがちですが,実際はリソースとクラスを別々に分けて編集する方が効率的なプログラミングが行えるのです.たとえば,コントロールには実にたくさんのオプションがつけられていて,これをプログラミングで設定するとなると非常に厄介なことになります.これを回避するために,リソースの編集でコントロールのオプションを操作可能です.例えばエディットボックス(IDC_EDIT1)は,デフォルトでは一行のテキストしか編集できません.しかし,オプションで少々変更することにより,複数行の編集も可能となります.またスクロールバーも使えるようになります.具体的には製作した日記帳(MyDiary)ではオプションとして複数行,垂直水平スクロールバー有り,改行可を設定しました.このようなことをプログラムソースで記述するとなると大変なことです.Visual Studioではリソースの編集とクラスの編集とを分けることで,プログラミングが実に明快になっているのです.
 IDを利用してCDiaryDlgクラスはコントロールにアクセスできると述べましたが,多くの場合,プログラマはIDを直接操作せずにコントロールにアクセスすることができます.それがDDXという機能です.プログラマはクラスウィザードを使って,コントロールに対応させたメンバ変数を作成することができます.コントロールに対応させたメンバ変数とは,int型であったり,CString型であったり,コントロールのラッパークラスであったりします.日記帳(MyDiary)では,以下のような対応をつけました.

日時指定コントロール
CDateTimeCtrl m_DateDiary;
コンボボックス
CComboBox m_Weather;
エディットボックス
CEdit m_EditDiary;

 次はこれらのコントロールと,ここでは取り上げられていないラジオボタンについて解説します。


●各コントロールについて
 ここでは様々なコントロールを取り上げます.基本的なものから順にボタン,コンボボックス,エディットボックス,ラジオボタンと解説します.そして,最後に日記帳に特徴的なコントロールである,日時指定コントロールを説明します.コントロールは一体どんなものなのか,あるいはどうやってCDiaryDlgクラスで使われるのか,などをわかっていただけたら嬉しいです.
・ボタン
 ボタンは,日記帳(MyDiary)では「保存」とか「終了」と書かれているもので,ユーザがアプリケーションに対して指示をするために使われます.つまり,これはダイアログに対しての入力インタフェースです.ボタンがクリックされたときにはボタンからメッセージが発生するので,ボタンがクリックされたことを検出するには,それに対応したメッセージハンドラ関数を用意すればいいのです.メッセージハンドラの作成はClass Wizardを使って行います.日記帳(MyDiary)で「保存」がクリックされたときに行なわれる処理を以下に示します.


・コンボボックス
 コンボボックスは日記帳(MyDiary)では天気を選択するのに使っています.ボタンはユーザからの入力が大きな役割を持ちましたが,コンボボックスはボタンと違って,コンボボックスにいろいろな(「晴れ」とか「雨」などの)情報を載せなければなりません.これらの情報はリソース編集の場面で設定することも可能ですが,日記帳(MyDiary)ではプログラムソース内で情報を掲載するようにしています.具体的には,コンボボックスを取り扱うためのインスタンス(クラスの実体)をDDX変数で宣言しているのです.そのインスタンス名はm_Weatherで,コントロールであるコンボボックスの管理をm_Weatherオブジェクトで行えるようにしています.つまり,m_Weatherオブジェクトは天気を管理するコンボボックスを扱う専用オブジェクトなのです.
 もちろん,上記のボタンの場合と同じように,コンボボックスをユーザが操作すれば,コンボボックスからメッセージが発生します.具体的にはCBN_SELCHNGE(選択内容が変化),CBN_SETFOCUS(コンボボックスがフォーカスを受けた)などのメッセージ(11種類)があります.ところが,コンボボックスがどうされようとかまわない場合はこれらのメッセージハンドラを作る必要はありません.実際,日記帳(MyDiary)では一つもハンドラ関数を設けていません.

・エディットボックス
 日記帳(MyDiary)での本文を編集する部分にエディットボックスを使用しています.エディットボックスは,ユーザとアプリケーションとが相互に文字情報を交換する場(インタフェース)です.アプリケーションが文字をエディットボックスに送ったり読み込んだり,あるいはユーザがエディットボックスの文章を読んだり文字を打ち込んだりします.編集の機能を備え持ったのがエディットボックスですから,このコントロールを実装すれば,プログラマは簡単にアプリケーションとユーザの文字情報交換インタフェースを提供することができます.
 日記帳(MyDiary)では,このコントロールを管理するために,DDX変数のm_EditDiary(CEdit型)インスタンスを設けました.m_EditDiary.SetWindowText(st);で文字列stをエディットボックスに送り,m_EditDiary.GetWindowText(st);でエディットボックスの文字列をstに格納することができます.

・ラジオボタン
 一日の満足度はたいてい「最高」「満足」「普通」「不満」「最悪」の5つに分けられるでしょう.確かに,気分が「最高」であれば「満足」なわけですから,「最高」と「満足」が共存することはありえます.しかし,ここでは一日の満足度を5つに分割して,上から順に「最高」「満足」・・・「最悪」となると考えましょう.このような場合,ユーザにどれか一つを選択してもらうことになります.選択するならば,コンボボックスやリストボックスを使う手もあるのですが,このように項目数が少ない場合や順位(順番)があるような場合にはラジオボタンを使うほうがすっきりします.
 ラジオボタンはグループボックスで区切られた中でどれか一つが選択されている状態になります.このような機能で使うばあい,ラジオボタンはDDX変数を使うことはできないため.直接コントロールIDを参照するプログラムを作らなければなりません.IDからそのコントロールを扱うためにはCWnd::GetDlgItem関数を使います.この関数はオブジェクトIDを指定するとそのオブジェクトへのハンドル(CWnd*型)を返すので,それを(CButton*)へキャストして使います(ラジオボタンを扱うクラスはCButtonクラス).

・日時指定コントロール
 日記帳は日時の取り扱いを避けて通れません.日時情報を管理するプログラムを作ってもいいのですが,幸いWindowsには日時指定コントロールがあって,MFCにそれを扱うクラスとしてCDateTimeCtrlクラスがあるので,これを利用してみました.実行場面は次のようになります.


このコントロールの取り扱いもDDX変数(m_DateDiaryインスタンス:CDateTimeCtrl型)で行っています.なお日記帳(MyDiary)ではユーザによる日付の変更に対して,情報を新しく(選択した日の情報があればそれを表示,なければ初期化)するために,イベント検出を行っています.つまり,日時指定コントロールの日付が変更されると,DTN_DATETIMECHANGEメッセージが発生するので,そのメッセージハンドラを設けています.そのメッセージハンドラ内での動作は後ほど解説します.

●ファイル処理(シリアル化)
 日記を保存するためにファイル処理を行います.データを保存して,それをいつの日にか正確に読み出すためには,まずしっかりとしたフォーマットを作らなければなりません.以下にそのフォーマットを示します.これは上の日記データをテキストエディタで見たものです.ファイル名は2003_02_26.txtです.ファイル名もフォーマットの一部であることに注意してください.
2003年02月26日の日記
天気:晴れ
満足度:A
今日はこのプログラム(Ver1.0)を完成させることができて,最高の気分です.
このプログラム開発を通して,さまざまなコントロールの取り扱い方を学びました.
具体的には,
日時指定コントロール・・・CDateTimeCtrlクラス
コンボボックス・・・・・・・・・CComboBoxクラス
ラジオボタン・・・・・・・・・・・CButtonクラス
エディットボックス・・・・・・CEditクラス
などの対応を理解しました.
また,CTimeクラスを初めて使いました.

なお今回のプログラム開発では,実際のプログラミングに入る前に,綿密な計画と実験を行っています.
Windowsプログラミングでは,この手法が非常に有効であると思われます.
 ファイルを処理するためにはCFileクラスまたはCStdioFileクラスを使えばできます.しかし,ここではMFCの基本であるシリアル化という操作でファイル処理をしました.しかしそうは言っても,実はCFileクラスを使わなければなりません.シリアル化はSerialize関数内で行いますが,Serialize関数を呼び出すためには,CFileクラスとCArchiveクラスを使う必要があるのです.まず、これらの関係を示します.

 Serialize関数内ではCFileに結びつけられたCArchiveを使って,ファイルに対する具体的な処理を行います.ただ,この図から見るとCFileインスタンス(fread)はCArchiveクラスのメンバのように見えますが,実際はCArchiveクラスはCFileインスタンスへのポインタを持っているだけです.つまり,CFileインスタンスはCArchiveインスタンスとは別に作成しなければなりません.この様子を以下に示します.これは日記帳(MyDiary)のソースの一部です.
CFile fread;
CString fName;

fName=m_Date.Format("%Y_%m_%d.txt");
if(!fread.Open(fName,CFile::modeRead,NULL)){
       :
       :
       :
       :
}else{
  CArchive arch(&fread,CArchive::load,4096,NULL);
  Serialize(arch);
  arch.Close();
  fread.Close();
       :
       :
}
 CArchiveクラスコンストラクタの引数を見てください.CFileインスタンスへのポインタを渡していることがわかります.このようにしてファイル処理への準備はできあがります.具体的にファイル内のフォーマットをどう実現するかはSerialize関数内での記述によることになります.



●モーダルダイアログボックス
 日記をつけている途中で誤って「終了」をクリックしてしまったときに,アプリケーションがそのまま終了してしまっては困ります.せっかく書いた日記が誤った操作によって消えてしまうということはできるだけ避けなければなりません.そのためにはアプリケーションのほうからユーザに問い合わせるという機構がどうしても必要になります.これを実現するのもダイアログボックスです.以下ではこのダイアログボックスをどのようにしてメインのアプリケーションに実装していくかを見ていくことにしましょう.
・リソースの作成
 初めのほうで説明したようにダイアログはリソースの設計とクラスの設計とを別々に行います.そこで新たなダイアログボックスをアプリケーションに取り込みたいときはまずリソースの作成をします.具体的には,Visual C++メニューバーの「挿入」欄の「リソース」をクリックすることでプロジェクトに挿入したいリソースを選ぶことができます.リソースにはいろいろなタイプがありますが,今はダイアログボックスを作りたいのでダイアログリソースを作成します.

・クラスの作成
 リソースを扱うためにはそれに対応するクラスが必要です.ダイアログボックスを作りたいのでCDialogクラスを継承させたクラスにリソースを埋め込む必要があります.これはClass Wizardを使えば実現可能です.以下のような感覚でクラスを作成することができます.

 保存するか否かを確認する(confirm)ダイアログということで,派生クラスの名前をCSaveConfirmDlgクラスとしました.ダイアログIDというのはリソースに付けられた(Visual C++が付けた)名前です.このようにして,リソースを取り込んだクラスを作ることができるのです.

・アプリケーションとの結合
 さてここでは,作成したダイアログボックスをどのようにしてアプリケーションユーザに提供できるかを見ていきましょう.ダイアログボックスを表示したいのはユーザが「終了」をクリックしたときですから,そのイベントを処理するハンドラ関数内でCSaveConfirmDlgインスタンスを作成すればよいことになります.そこでは以下のような流れを組みます.
CSaveConfirmDlg SCD;

SCD.m_text=m_Date.Format("「%Y年%m月%d日の日記」は修正されています.")+"\x0d\x0a保存しますか?";

if(SCD.DoModal()==IDOK){
  OnSave();
}
 ここでCSaveConfirmDlgのm_textメンバはCString型で,STATICコントロールに表示する文字列を管理しています.このプログラムで注目してもらいたいのはSCD.DoModal()です.この関数を実行すると,次のようなダイアログボックスが表示され,ユーザに応答を求めることになります.
保存の確認ダイアログ
 この問いかけに対し,ユーザが「はい」や「いいえ」をクリックすると,このダイアログボックスは閉じられます.このときSCD.DoModal()はクリックされたものに応じた値を返すので,それを使って次の処理に移ることができます.具体的には「はい」がクリックされると,SCD.DoModal()は「IDOK」という値を返すので,上のプログラムではこれを検出して保存操作に入ることができるわけです.なお,「いいえ」がクリックされた場合には「IDCANCEL」が帰ってきます.
 さて,ここで「注意すべきこと」を述べておきましょう.CDiaryDlgに上のような記述を加えるわけですが,ただこれを記述しただけではいけません.これをコンパイル処理の観点から見てみましょう.CDiaryDlgクラスの本体が定義されているDiaryDlg.cppをコンパイルする場合,CSaveConfirmDlgクラスのプロトタイプが必要になります.つまり,CSaveConfirmDlgクラスが一体どのようなものなのかを,コンパイラに知らせなければなりません.コンパイル処理についてあまりよくわからない人は難しく聞こえるかもしれませんが,要するにDiaryDlg.cppの上のほうで「#include "SaveConfirmDlg.h"」と一行付け加えればいいのです."SaveConfirmDlg.h"にはクラスの定義が書かれていて,これをインクルードすることによってコンパイラにCSaveConfirmDlgとはどのようなものかを知らせることができるのです.
以上,新たなダイアログボックスをアプリケーションに実装する流れを見てきました.これはあくまで実装の流れですので,好みに合わせたリソースの編集やクラスの編集を行う必要はあります.


●処理の流れ
 以上で述べてきたことは,重要な概念ばかりでしたが,これだけの知識ではプログラムを完成させることはできません.オブジェクト指向プログラミングに注目してばかりいると,どうしても疎かになりがちなのが「処理の流れ」の把握です.処理の流れをフローチャートを用いて説明しましょう.

・初期化処理
 コントロール類の初期化はCDiaryDlg::OnInitDialog関数内で行います.この関数はダイアログボックスの表示直前に呼び出されます.日記帳(MyDiary)では以下の処理をしています.(App Wizardで生成される処理は除いてある.)

 

 NewDocument関数はCDiaryDlgクラスのメンバ関数で,独自に作成した関数です.後ほどこの関数のフローチャートを示します.


・イベント処理(日付の変更検出)・・・OnNewDiary関数
 日時指定コントロール(ID:ON_IDC_DATETIMEPICKER1)が変更されると,DTN_DATETIMECHANGEメッセージが発生します.このメッセージハンドラとして,OnNewDiary関数を用意しました.このメッセージハンドラのおかげで,日付が変更されるとその日の日記を自動的に読み出すことができます.ただし,変更前の日記が保存されてない場合は,それを保存するかどうか確認するようになっています.フローチャートは以下の通りです.エディットボックスが変更されているかどうかはCEdit::GetModify関数を使って判別できます.



・イベント処理(「保存」ボタンがクリックされた)・・・OnSave関数
 保存ボタンがクリックされたときに,OnSave関数が呼び出されるようにしました.OnSave関数内ではファイルのオープンやCArchiveオブジェクトの作成を行いSerialize関数を呼び出します. 

・イベント処理(「終了」ボタンがクリックされた)・・・OnEnd関数
 「終了」ボタンがクリックされれば,OnEnd関数が呼び出されます.OnEnd関数では次の処理を行います.



・NewDocument関数
 新しい日記を見るときには,この関数を呼び出せばすべてが済むようになっています.以下にフローチャートを示します.




●最後に
 日記帳(MyDiary)はとても単純なアプリケーションです. しかし,これほど単純なアプリケーションを作るのにも,いろいろな知識や技術が必要なのです. 「単純なアプリケーションなのに多くの知識が必要」と考えると,本格的なWindowsプログラミングは,ひょっとすると遠い存在のように思われるでしょう. 確かに,まだまだ学ぶべきことはたくさんあるはずです. しかし,基本的な部分をしっかりマスターしていけば,高度なプログラミング技術を手にするのも時間の問題です. がんばってプログラミング技術を磨きましょう.
 なお、ご質問等はこちらで受け付けております。ご気軽にご利用ください。



Copyright © 2004 Multisoft-lab   All rights reserved.