VC++5.0入門6
基礎知識 デバイスコンテキスト97/8/18

こんにちは。最近、このHPの訪問者が30000人を超えたのでとても感動しました。ほんとのことを言うと、このHPはもともとHTMLを勉強しようと思って、とりあえず、自分の好きな話題を書いてみたのです。はじめの頃は、1週間に10人位の訪問者で、自分自身の閲覧の方が多いんじゃないかと思ったほどでした。(笑)それが今ではこんなに多くの人に見ていただいて、とても光栄です。(ところでHTMLの勉強はぜんぜん進みませんでした。最近Javaもちょこっと勉強してます。)

とてもゆっくりのカメ講座ですが、今日はデバイスコンテキストというのを勉強します。一般に、画面に何かを表示しようとした場合に、デバイスコンテキストオブジェクト(簡単にデバイスコンテキスト)というものを生成しなければなりません。そして、画面に何かを表示するには、そのデバイスコンテキストに、描画をお願いするのです。
C++入門のようなテキストベースのプログラムで、文字列を表示したいときには、
    cout<<"hello!";
などとしました。ここでcoutが画面を表していて、そこにhelloという文字列を渡し、表示してもらったのですが、ウインドウズの場合、同じようなことをするのに、例えば、
    CClientDC dc(this);
    dc.TextOut(0,0,"こんにちは。");
などと書きます。はじめにCClientDCとあるのは、デバイスコンテキストのクラス名です。他のデバイスコンテキストクラスもあるのですが、CClientDCはウインドウのクライアント領域(ウインドウのフレームを除いた部分ですね。)への描画に使われます。
dcはそのオブジェクト(インスタンス)名です。この名前は何でも良いのですが、上のようにdcなどと書く人が多いようです。その後の括弧thisは、このウインドウについての描画だという意味です。(少し、難しい話をすると、C++のクラスのインスタンス(オブジェクト)にはthisという名のポインタが付いています。これはそのオブジェクトそのものを表すポインタなのです。thisは、これ、という意味ですね。^^)まとめると、CClientDC dc(this);は

  このウインドウのクライアント領域に何か書きたいので
  そのデバイスコンテキストを用意せよ。
  その名前はdcとする。
という意味なのです。
そして、2行目のdc.TextOut(0,0,"こんにちは。");はこのdcを使って字を書いているのです。
つまり、CClientDC(の基底クラス)にはたくさんのメンバ関数が定義されていて、TextOutはそのうちのひとつ、文字列を書く関数なのです。(正確にはCClientDCはCDCというクラスの派生クラスなんですが、TextOutはそのクラスの関数です。)TextOutの最初の引数と2番目の引数は、文字を書き出す場所のx座標、y座標を表しています。ちょっと実験してみればわかりますが、この座標では、クライアント領域の左上端が原点(0,0)を表し、右へ行けばx座標が増え、下へ行けばy座標が増えるようになっています。(中学、高校のころの数学とちょっと違いますね。)もちろん(?)、最後の引数が書き出される文字列を表します。(うるさく正確に言うと、文字列の最初の文字へのポインタ、というのでしたね。^^;)
この2行を前回のサンプルのOnLButtonDownの定義のMessageBoxの呼び出しの代わりに入れてみてください。一応、書き換えを書いておくと、
#include "afxwin.h"

class CMyWindow: public CFrameWnd
{
public:
    CMyWindow();
    void OnLButtonDown(UINT, CPoint);
    void OnRButtonDown(UINT, CPoint);

    DECLARE_MESSAGE_MAP()
};

class CMyApp:public CWinApp
{
public:
    virtual BOOL InitInstance();
};

CMyWindow::CMyWindow()
{
    Create(NULL, "Hello!", WS_OVERLAPPEDWINDOW);
}

void CMyWindow::OnLButtonDown(UINT, CPoint)
{
    CClientDC dc(this);
    dc.TextOut(0,0,"こんにちは。");
}

void CMyWindow::OnRButtonDown(UINT, CPoint)
{
    MessageBox("右ボタンが押されたよ。","メッセージである",MB_OK);
}

BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd)
  ON_WM_LBUTTONDOWN()
  ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()

BOOL CMyApp::InitInstance()
{
    m_pMainWnd=new CMyWindow;
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();

    return TRUE;
}

CMyApp theApp;
あとは実行して様子を見てください。たぶん、ゆっくり考えればだいたいわかると思いますが、どうでしょうか。わかったら、TextOutの最初のふたつの引数を少し変えて、dc.TextOut(10,50,"こんにちは。");などとして、実行してみてください。座標は描画のための単位を使っていてかなり細かいものなので、(10,50)などとしても原点からそれほど離れていないと思います。
この辺でちょっとOnLButtonDownの引数について考えてみましょう。この関数の宣言や定義には
    void OnLButtonDown(UINT, CPoint);
などとあります。これは、この関数はUINTとCPointというふたつの型の引数を持つという意味です。初心者風のオーソドックスな書き方では、
    void OnLButtonDown(UINT f, CPoint p);
などとするところです。上でfとpは引数にプログラマが付けた名前です。もちろん、勝手に付ける名前ですから何でも良いのです。
例えば、(文字ベースのプログラムで、)整数をふたつ受け取ってそれを掛けたものを表示する関数なら、
    void kakezan(int x, int y){
        //思いっきり単純な関数^^;)
        cout<<x*y;
    }
なんて書くのでした。「UINT f, CPoint p」はこの「int x, int y」に相当するものです。最初のサンプルでfやpのような名前を付けなかったのは、どうせこれらの引数を使わなかったからです。このように存在しても使わない変数には名前を付けなくても良いのです。
でも、以下ではちょっと使ってみましょう。
まず、UINTですが、簡単に言うと正の整数という意味です。また、CPointはMFCのクラスのひとつで「点の座標」を表すものです。(ちょっとわかりづらいですが、がまんしてとりあえず最後まで読み続けてください。)これらの引数は、この関数が呼び出されたときに与えられるものです。この関数が呼び出されたときというのは、マウスの左ボタンが押されたときです。つまり、マウスの左ボタンが押されると、上の定義では、自然にfという正の整数とpという「点の座標」が与えられるのです。このfはマウスボタンが押されたときに他のキーが押されていたかどうかなどを表す量ですが、私のサンプルでは使いません。したがって、fなどと書くのはやっぱり止めます。pで与えられるのは、マウスボタンが押された点の座標です。これを使えば、マウスの左ボタンが押された位置に好きな文字列を書くことができます。ひとつ知っておくべきことは、pで表される点のx座標、y座標の求め方です。これは、簡単です。
    p.x と p.y
なのです。これを使って、OnLButtonDownの定義を次のように書き換えて遊んで見てください。
void CMyWindow::OnLButtonDown(UINT, CPoint p)  //CPonitだけ使う
{
    CClientDC dc(this);
    dc.TextOut(p.x, p.y, "猫");
}
クラスの宣言部分は
class CMyWindow: public CFrameWnd
{
public:
    CMyWindow();
    void OnLButtonDown(UINT, CPoint p);
    void OnRButtonDown(UINT, CPoint);

    DECLARE_MESSAGE_MAP()
};
と直しても、直さなくても、どちらでも良いです。
まあ、どうというプログラムではありませんが、実行して様子を見て、遊んでください。マウスの左ボタンを押す(クリックする)度に、その場所に猫という文字が書かれます。だんだん、ウインドウズプログラミングの要点がわかってきたのではないでしょうか。
また、猫ですか、、、。まあ、いいじゃないですか。私は上のようなプログラムを印刷可能に書いて、猫という文字を並べて猫の顔を書いて、打ちだしたことがありますが、ごくごくローカルには受けました。すんません。
今回は印刷までできませんが、みなさんの好きな言葉を表示するプログラムに書き換えてみてください。

今まで、AppWizardを使わないMFCプログラミングをちょっと見てきました。最初に、たぶんこんな使い方は普通しない、教育的使い方だ、と書きましたが、そう書いた根拠は、実は、あまりありません。ただ、このようなやり方の参考書がほとんどなかったからそう思ったのです。
MFCの説明に、こういう基本から入るとわかりやすいのではと思って、教育用にやってみたのですが、私自身はちょっとはまりそうになってしまいました。AppWizardを使うとすばらしい枠組みが自動で与えられてしまい、それは便利です。開発会社の人などは、たぶん、当然AppWizradを使うのだと思います。しかし、一方、例えば、自分のためだけにちょっとしたプログラムを組みたいと思った場合、今日までやってきた方法を極めてみるのもおもしろいかな、なんて思ったりしてぇ〜。(しゃ○Qの「言い訳」風に読んでください。ちょっと古い?)でも、この方法の解説がほとんどないので、極めるのはちょっと苦労しそうですし、そんな苦労をするなら、みんなが使っているAppWizardを使った方が楽でしょう。
というわけで、次回は、はじめてのAppWizardです。
目次のページ
前のページ
後のページ