VC++5.0入門26
チュートリアル読書会10 98/6/7

こんにちは。今回は「ダイアログボックスの追加」を終了させます。
ついでに、VC5.0入門もとりあえず終了とします。(ご挨拶は後程、、、。;;)
このプログラムはふたつのペン幅「太いペン幅」と「細いペン幅」を持っていて、それを交互に使い分けるというものです。今回は、その「太いペン幅」と「細いペン幅」の値をダイアログボックスを通じてユーザが変更できるようにします。
私がはじめてチュートリアルを読んで悩んだのは、幅を変えられるなら、ふたつのペン幅なんかいらないんじゃないか、、、ということでした。なんか、勘違いしてるのかなぁ、、と。しかし、そういうプログラムなんですね。みなさんは変だと思いませんでしたか?たぶんチュートリアルとしていろいろなテクニックを見せるため、こんな仕様にしているのだろうと、私は思っています。

さて、「ダイアログボックスコントロールのメッセージハンドラ関数の宣言」からですね。チュートリアルにも書いてありますが、この節の内容はメニュー項目にハンドラを付けたやり方とほぼ同じです。メニューのときに何をやったか忘れてしまった人は、面倒でもそちらを先に復習した方がよいと思います。ダイアログ無しのアプリはあってもメニュー無しのアプリはあまりないですから。^^)
、、、さて、メニューの方を思い出せれば、この節の内容、「デフォルトボタンへのハンドラの追加」は簡単ですね。ただ、その関数の定義が後に出てくるので、ちょっとやですが。
新しい話で、ややわかりづらいのが、「コントロールとメンバ変数のマッピング」でしょう。しかし、この節の(大筋の)内容は、絶対に理解する必要があります。
ちょっと、考えてみましょう。しつこく、言ってきたように、ダイアログボックスは、リソースと呼ばれるもので、C++のプログラムとは一応独立したものです。とすれば、ダイアログをビジュアルに編集し、入力用の小窓(エディットボックスコントロール)なんかを付けましたが、ユーザがここに入力した値をプログラムはどうやって知るのでしょう?
これには、VC/MFCが特別な方法を用意していてくれるのです。まず、ダイアログボックスの作りを思い出してくださいね。入力用のエディットボックスコントロールはふたつあり、それぞれにIDC_THIN_PEN_WIDTHとIDC_THICK_PEN_WIDTHというID(つまり名前)が付けて有りました。ClassWizardを使うと、これらのエディットボックスコントロールに対応する(つまりこれらから入力を受け取る)変数をプログラム内に作ることができるのです。それがこの節の内容です。
できあがるのは、CPenWidthDlgs(ダイアログを表すクラスでした)のメンバ変数、m_nThinWidthとm_nThickWidthですね。チュートリアル通りにやれば、intの変数になっているでしょう。
その後DoDataExchangeやらDDXやらDDVなんて話があります。ここで言いたいことは、要するに、ダイアログボックス内のコントロールに与えられた値は、ちゃんと今作った変数に転送される(あるいは、状況に応じて逆の方向にも転送される)ということです。初心者は、字面を追って悩まない方がよいかもしれません。というのは、この辺を自分でいじることは、当分ないだろうからです。(普通は、みんな自動でClassWizardなんかが必要なことを書いてくれます。)
実際に、プログラマがデータの転送を命令したいときには、次節に出てくるUpdateDataという関数を使います。普通、プログラマはこの関数を使うだけなのですが、その裏で仕事をしてくれているのが、DoDataExchange君だというわけです。
さて、「メッセージハンドラのインプリメント」で、ようやくやり残した「デフォルトボタンのハンドラ」の定義を書きます。デフォルトでは細いペンの幅が2太いぺんの幅が5ですね。この値をm_nThinWidthとm_nThickWidthに代入すればよいのですが、それだけでは、ダイアログボックス中のエディットボックスにこの値が表示されません。これをするのがUpdateData関数です。この関数の引数はわかりづらいですが、UpdateData(FALSE);なら、変数(m_nThinWidthとm_nThickWidth)の値がエディットボックスに転送され表示され、UpdateData(TRUE);なら、エディットボックスの値が変数に転送されるのです。
さあ、最後です。あとは、メニューで「ペン幅...」が選ばれたときに、このダイアログを開くようにコードを書くだけです。ここでDoModalとCreateの話がありますが、とりあえず、DoModalの方しか使いませんね。実際のコード
    CPenWidthsDlg dlg;
    dlg.m_nThinWidth=m_nThinWidth;
    dlg.m_nThickWidth=m_nThickWidth;

    if(dlg.DoModal()==IDOK){
        m_nThinWidth=dlg.m_nThinWidth;
        m_nThickWidth=dlg.m_nThickWidth;
        ReplacePen();
    }
は完全に理解してください。内容は、チュートリアルに詳しく書いてあるので、それを読めばよいのですが、蛇足で私なりの言葉でも書いておきましょうか。
まず、最初の行はダイアログオブジェクトを作っているのです。ただ、ダイアログオブジェクトが作られただけでは、ダイアログは表示されません。(この辺がミソなんですね。)dlg.DoModal()とやると表示されるのですが、その前に2行有りますね。ここに出てくるdlg.m_nThinWidthとdlg.m_nThickWidthは、つい先ほど作った変数です。もう一方のm_nThinWidthとm_nThickWidthってなんでしょう?これはCScribbleDoc(つまりこのプログラムのデータを保持するクラス)の変数です。(これらは「メニュー」関連で新しく付け加えたものです。これらは、はじめはInitDocumentで2と5に初期化されていましたね。)この値をまずダイアログのエディットボックスに対応する変数に入れ、ついでdlg.DoModal()を実行してダイアログを表示すると、ダイアログが開いた時点で、初期の値がエディットボックスに表示されるのです。
さて、if(dlg.DoModal()==IDOK)はいかにもC++(またはC)らしい書き方です。こう書くと、まずdlg.DoModal()が実行され、ダイアログが開かれます。ユーザがそのダイアログを閉じるとこの関数は終了し、後に戻り値を残すのですが、その値はユーザの閉じ方によって違ってきます。もし、ユーザがダイアログをOKボタンで閉じた場合はIDOKという値が、もし、キャンセルボタンで閉じたのならIDCANCELという値が戻されます。この戻り値がIDOKなら以下のことをやりなさい、というのがこの行の意味です。つまり、ダイアログを開いて、そのダイアログがキャンセルボタンで閉じられたなら、何もしない。もし、OKボタンで閉じられたなら、ペンの幅をエディットボックスから取ってこい、というコードです。(少し、くどかったですか?)
そう思えば、ifの後のコードは理解できると思います。なお、チュートリアルにはUpdateDataは適当なタイミングで自動で呼び出されているから心配無いと書いてあります。ちょっと読んでおくとよいかもしれません。
さあ、完成です。ビルドして実行してみてください。ようやくそれっぽいプログラムになってきましたね。
前にも書きましたが、ビルドに失敗したら、なんとかがんばって直してください。このつらい作業をこなすことはプログラミングの腕を磨く上で、決して無駄では無い、むしろお金を払ってでもするべき修行だと、確信します。

さて、みなさん、今までVC/MFC入門にお付き合いくださって、ありがとうございました。少々、中途半端ですが、ここでとりあえずの終了とさせてください。ここまでできた人は、チュートリアルを読み進んだり、あるいは別の人の参考書を読んだりできると思います。是非、今後もがんばって続けてください。
さて、7月からは私が訳した「C++再考」という本の訳注を書きます。この本は「標準的なC++」の中級以上を目指す人にはとても良い本だと思うのですが、私のC++入門を読んだばかりのような初心者が読むにはつらいところもあります。この辺の訳注を本に書くのは、もちろん、原著者に失礼(というか越権)なので遠慮した(当然です)のですが、このHPには私自身のぶっちゃけた感想も含めて書いてしまおうかなと思っているのです。まあ、こう言うのもなんですが、興味のある方は買ってみてください。(^^;)
みなさん、ありがとうございました。
目次のページ
前のページ