では、どのようにすればよいのでしょうか。
キーボード入力を捕まえる必要があるので、WM_CHARメッセージを
処理する必要がありそうです。メッセージマップエントリはON_WM_CHAR()ですね。
また、この時活性化される関数はOnChar()関数であることは想像に難くないです。
OnChar()のところで正解か、不正解かを判断する。そして次の問題を作る。 また、ここで正解、不正解の数を勘定する、というストーリーができそうです。 そして最後にクライアント領域を無効化すれば再描画が起こり必要なものが 表示されるでしょう。
つぎに、OnPaint関数ではどうすればよいのでしょうか。 これは、単にTextOutで表示するだけで良いでしょう。 表示内容はOnChar()のところで準備してくれています。
一番最初はどうしたらよいのでしょうか。というのも上の ストーリーでは最初のキー入力があって以降のことです。 これでは、一番最初が困ります。問題が用意されていませんね。 そこで、WM_CREATEメッセージのところか、もしくは コンストラクターのところで問題を用意するということになります。
これで、何とかなりそうですね。おおまかなストーリーができたら 実際にプログラムを書いてみることです。最初には気が付かなかったことやら いろいろな問題が生じてきます。それは、その時に考えることにします。 では、早速サンプルを見てみます。
いつも同じではつまらないので、クラス定義部分をヘッダファイルにしてみました。 このファイルには#include <afxwin.h>がついていないことに注意してください。 と、いうのもソースファイルで最初に#include <afxwin.h>を記述して、 その後に#include "text03.h"を記述するからです。// text03.h class CMyApp : public CWinApp { public: virtual BOOL InitInstance(); }; class CMyWindow : public CFrameWnd { char szAbc[256]; char szKaito[256]; char cSeikai; int nOk, nNo, q; public: CMyWindow(); //コンストラクタ afx_msg void OnPaint(); afx_msg void OnChar(UINT, UINT, UINT); DECLARE_MESSAGE_MAP() };
szAbcは、問題の元となる文字列を格納するバッファです。
szKaitoは「正解です」「不正解です」を表示するためのバッファです。
cSeikaiは問題の文字です。
nOkは正解数、nNoは不正解の数、qはszAbcからどの文字を出題するかを決める数です。
たとえばq=0ならszAbcの最初の文字が問題となります。q=5だと6番目の文字です。
さて、これらの変数はこのクラスのメンバ関数以外からはアクセスされる
必要もなさそうなのでプライベートメンバにしてあります。
さて、ファイルが変わりました。"text03.h"をインクルードするのを忘れないでください。// text03.cpp #include <afxwin.h> #include "text03.h" CMyApp MyApp; BOOL CMyApp::InitInstance() { m_pMainWnd = new CMyWindow(); m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; }
この部分は初期化関係です。順番は異なりますが前章までと同じです。
CMyWindowクラスのメッセージマップです。BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd) ON_WM_PAINT() ON_WM_CHAR() END_MESSAGE_MAP()
コンストラクタです。 ウィンドウを作って、szAbcに問題の元となる文字列を作ります。 そして、正解・不正解数を0にしています。そして、一番最初の 問題を作成しています。乱数でqを作ってszAbc[q]を問題 (同時に正解)としています。CMyWindow::CMyWindow() { Create(NULL, "猫でもわかるタイプ表示", WS_OVERLAPPEDWINDOW, CRect(100, 100, 100+220, 100+120)); strcpy(szAbc, "abcdefghijklmnopqrstuvwxyz"); nOk = 0; nNo = 0; srand((unsigned)time(NULL)); q = rand() % 26; cSeikai = szAbc[q]; }
srand(),time(),rand()などを 使っていますがstdlib.hやtime.hなどはインクルードしていないことに 注意してください。実はafxwin.hに#include <afx.h>という 一文が含まれています。afx.hを調べてみるとstdlib.hとかtime.hなど Cでおなじみのヘッダーファイルがインクルードされています。 従っていちいち#include <time.h>などと書かなくて良いわけです。
クライアント領域に表示されるモノですね。中身は次のOnChar()関数で用意します。void CMyWindow::OnPaint() { char *str_org = "「%c」をタイプしなさい"; char str[256]; char *str_h = "正解:%2d 不正解:%2d"; int len; CPaintDC dc(this); len = wsprintf(str, str_org, szAbc[q]); dc.TextOut(10, 10, str, len); dc.TextOut(10, 30, szKaito, strlen(szKaito)); len = wsprintf(str, str_h, nOk, nNo); dc.TextOut(10, 50, str, len); }
ユーザーが問題を見て、キー入力があると呼ばれます。void CMyWindow::OnChar(UINT nChar, UINT nRep, UINT nFlag) { if (nChar == (UINT)cSeikai) { strcpy(szKaito, "正解です"); nOk++; } else { MessageBeep(MB_OK); strcpy(szKaito, "間違いです"); nNo++; } q = rand() % 26; cSeikai = szAbc[q]; InvalidateRect(NULL); }
nCharはキーの文字コードです。nRepCntはキーを押し続けたときキー操作が 発生する回数です。nFlagsは通常OnCharでは使いません。afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );
これで、ユーザーの入力したキーがわかります。正解と比較して 正誤をszKaitoにコピーしておきます。そして、次の問題を発生させます。 最後にクライアント領域を無効化して再描画させます。
lpRectをNULLにするとクライアント領域全体が再描画されます。 2番目の引数は更新リージョンの背景を消去するかどうかを指定します。 省略するとTRUEとみなされます。CWnd::InvalidateRect void InvalidateRect( LPCRECT lpRect, BOOL bErase = TRUE );
さて、これでだいたいわかったでしょうか。イベントドリブン式の プログラムになれていないともっとも苦労するのがクライアント領域への 描画です。あらかじめ、ストーリーを考えておいて紙にメモしておくと 良いかもしれません。いずれにせよ「慣れ」しかありません。
Update 12/Jan/1998 By Y.Kumei