TextOut関数を使ってみよう
今まで作成してきたものはMDIのNotepadとダイアログボックスでしたね。これらはユーザがなにかアクションをすれば、何か表示されたりしたものでした。でもプログラマーがソフトに対して直接何かを出力したい時もありますよね。そこで今回はTextOut関数を使ってプログラマーによる(強制?)表示させる練習をします。
1. デバイスコンテキストを学ぶ
この章でこれからちょくちょく出てくるのが、このコトバ「デバイスコンテキスト(DC)」。デバイスコンテキストクラスのときは、CDCと書く事もあります。さて、最近はグラフィックボードも多彩になってきて、どれを買うべきか本当に悩むところかもしれません。でも、グラフィックボードが多彩になったからといって、こんな事が起きたら大変です。

「グラフィックボードが古くて(新しくて)プログラムがうまく動かない(うまく表示されない)」

確かに、DirectXなどはCPUの性能にもよるし、グラフィックボードに全然変わっちゃうのでしょうがありません。でもWindowsに標準添付されているpaintで、グラフィックボードのせいで円を描くことができないとなると、それは大変です。ここでデバイスコンテキストが有効になります。WIndowsのプログラムでは、デバイスコンテキストを使用しているので、具体的なグラフィックボードやそのドライバ、あるいはプリンタやプリンタドライバの存在やバージョンを気にすることなく、画一的に描画することが可能です。つまりこのデバイスコンテキストを使用しないと、Windowsアプリケーションで描画することができないのです。

続いて「描画」について簡単に説明しましょう。描画、すなわち「お絵描き」には、紙と文房具が必要ですね。先ほどから耳にたこができるぐらい聞いている?デバイスコンテキストは、実はこの「紙」のことなのです。また文房具にも、「ペン」とか「ハケ」とかたくさんの種類があります。これらはGDIオブジェクトと呼ばれるものです。つまり、Windowsでは、すべての描画をこの決まった「紙」と「文房具」で行うので、グラフィックボードを気にする必要がないのです。

VisualC++ではこのデバイスコンテキストの操作のために、CDC(デバイスコンテキストクラス)が用意されています。このクラスはWindowsプログラムの中枢といっていいほど大切なクラスなので、是非マスターしてみたいものの1つといえます。実際は、このCDCクラスのデバイスを取得して、好きなプログラムを実行し、デバイスを解放するというのがセオリーなんですが、詳しくは以下に紹介しようと思います。
2. 早速書いてみよう--TextOut関数は簡単--
AppWizardでいつものようにプロジェクトをつくります。
■プロジェクト名:Text
■作成するアプリケーションの種類:SDI
■その他:ディフォルト

では今回は手っ取り早く、コードを書いてそれから解説しようと思います。OnDraw関数に書込みます。
以下のコードを記述してください。1行だけですが・・・(TextView.cpp内)
void CTextView::OnDraw(CDC* pDC){
CTextDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

pDC->TextOut(0,0,"Textを表示しよう");//書いたのはこの1行だけ
}

このOnDraw関数はMFCの中で使用頻度が高い関数です。ウィンドウに文字列を書込んだり、図形を書き込んだりするのも、ほとんどここでコードを書いてあげれば実行できるようになっています。これからも、この関数はよく使用されるので要チェックですよ。

ところで、上のソースを実行してみると、↑のようになるはずですね。ね、簡単でしょ。ここでTextOut関数について簡単に解説します。初めの0,0,はウィンドウ上の座標です。0,0,の場合は左上をさします。0,0,の左側が左右の移動量(pix)右側が上下の移動量(pix)なので、TextOut(50,100,"MFC");とすれば、左から50ピクセル、上から100ピクセルのところに"MFC"と表示されるのです。

ところで、「pDC->」と新しい表現がありますが、これはポインタのアクセスと言います。この関数の引数を見ると分かるかもしれませんが、CDC(デバイスコンテキストクラス)のポインタ「*」変数pDCとされていますよね。これが、先ほど言ったデバイスコンテキストを操作する変数となります。この「変数pDCに対して、TextOut関数を実行しなさい」という時に、pDC->と書くことによって、アクセスを行っていると解釈すればいいと思います。この使い方よく出てくるので、一度覚えてみるのもいいかも。

でもこれではつまらないです。1行空けてとかやってみたいものですよね。例えば、文字を回転させてみたりとか出来ないかな?って思って私もいろいろやってみました。
3.バリエーションは豊富にね!
◇任意の場所にテキスト表示
その1.任意の座標にテキスト表示
これは先ほどもちょっと書きましたね。ここで、一応クライアント領域(ウィンドウの有効領域)での座標とTextOut関数を簡単に説明します。

pDC->TextOut(100,50,"Textを表示しよう");

を実行すると、右の画面のように、左端から100pix、上から50pixの位置に""で囲まれた文字「Textを表示しよう」を表示させることが出来ます。つまり、座標をいじくってあげればどこにでも文字を出力することは可能です。

それでは、全角3文字分右にずらしたいという時もあるはずです。こんな時は全角文字サイズを取得し、「そのサイズ× 3」で3文字分右にずらすことが出来ます。後はどうやって記述するかですが・・・こんな風にして記述します。
CSize  sz = pDC->GetTextExtent("あ");
pDC->TextOut(sz.cx * 3 , 0, "全角3文字右にずれました");
CSize sz =pDC->GetTextExtent("あ");
適当な全角文字(この場合「あ」)のサイズを取得します。サイズを保存するのに使う変数は、CSize型の変数(この場合sz)です。デバイスコンテキストに対して、GetTextExtent関数を適用し、文字サイズを測定させています。このとき、Csize型のメンバ変数のcxとcyには、全角文字の縦方向の長さがcyに、横方向の長さがcxに代入されます。
pDC->TextOut(sz.cx * 3 , 0, "全角3文字右にずれました");
右に3文字分ずらすのだから、上のように書くことができるのはお分かりですよね。
ところで変数szは 、メンバ変数cxとcyを持つように、C++ではある型の変数が内部に幾つかの変数を持っていることがあります。これらは、メンバ変数と呼ばれるもので、CSize型szのcxを参照する時には、sz . cxとします。

◇n行改行して表示
改行と書かれると多少難しいイメージですが、言い換えると、全角文字サイズの縦方向の長さcyを測定し、そのn倍だけ、座標を下にしてTextOutすればOKです。要するにさっきと同じですね(笑)。例えば2行改行したい場合は下のように書きます。
CSize  sz = pDC->GetTextExtent("あ");
pDC->TextOut( 0, sz.cy * 2, "いろんなことができます");
楽勝ムードです(笑)。簡単なので「センタリング」と「右揃え」に挑戦しましょう。

その2.センタリング
センタリングとは、ウィンドウの真ん中にテキストを持っていく作業の事です。センタリングするためには、テキストをどこから書き始めたらよいか指定する必要があります。右の図を見ても分かるように、

(書き始めのx座標) ={ (Window(クライアント)の幅) - (文字列の幅) } ÷ 2

ですね。この座標を使って、TextOutするだけで、指定した文字をセンタリング文字にすることができるでしょう。で、問題はどんなコードを書くかということ。先ほどやっととおり、出力する文字列の幅は

sz = pDC->GetTextExtant("***");

で文字列の幅と高さを取得できました。しかし、ウィンドウの幅を取る関数はまだ知りません。これもしっかり関数化されているので、プログラマは、この関数を呼んであげればOKです。

ここで新しく勉強するクラスがあります。矩形(四角形全般)を扱うクラスであるCRectクラスです。
まずCRect型変数 rctを定義したとしましょう。ウィンドウは、基本的に四角形ですから、windowクライアント領域(白いエリアの部分)の大きさをrctに代入してあげれば、四角形の高さと幅を取得する事ができるのです。具体的には以下にようにやります。

void  CTextView::OnDraw(CDC* pDC){
CRect  rct;
GetClientRect(&rct);
CSize  sz = pDC->GetTextExtent("真ん中?");//出力する文字列の幅の取得
pDC->TextOut((rct.Width() - sz.cx) / 2 , sz.cy ,"真ん中?");//出力
}

簡単に解説していきましょう。といっても始めの2つの命令だけで十分ですよね
CRect  rct;
CRect型変数rctを定義しました。初期化の必要は特にありません。
GetClientRect(&rct);
クライアントの四角形領域(Rect)を取得する関数です。それをrctに代入していると思ってください。
このrct.Width( )が、クライアント領域の幅を表わします。先ほどもちょっと言いましたが、rct.Width( )は、CRectクラスの変数rctに関してのメンバ関数Width( )(rctの幅を返す関数)を起動させるという意味です。このようにC++では変数が、メンバ変数とメンバ関数というものをもっているのです。

ところでですが、実行されたウィンドウのサイズを変更してみてください。いつでもクライアント領域を監視していますので、サイズを変更しても必ず真ん中に位置するはずです。

その3.右揃え
これも先ほどとほとんど同様です。ようするに、どこから書き始めたらよいかを変えてあげるだけです。そうですウィンドウのクライアント領域の幅から、文字列の幅を引いたところならOKですよね。ということで、
CRect  rct;
GetClientRect(&rct);
CSize sz = pDC->GetTextExtent("右に揃え!");
pDC->TextOut((rct.Width() - sz.cx) , sz.cy ,"右に揃え!");
これで、左の図のように右揃えが完了します。

◇背景カラーを付ける
こんどは出力する文字列の背景に色を付けてみましょう。むむむ、背景色か・・・ダイアログでは結構手強かったからな・・・と思うかもしれません。でも要領さえわかれば、なーんのそのですよ。手順はこんなに簡単なんです。

手順
①出力したいテキストのサイズを取得する
②背景カラーを設定する
③TextOut関数を実行する

なんと上の3つだけでいいのです。ではコードを作ってみましょう。
まず、テキストのサイズを取得します。先ほどのように、CSize型変数szを定義し、デバイスコンテキストに対してGetTextExtent関数を実行します。今回は"VisualC++"という文字列に背景色(水色)を付けてみましょう。


void CTextView::OnDraw(CDC* pDC){
CTextDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CSize sz = pDC->GetTextExtent("VisualC++");
pDC->SetBkColor(RGB(0,255,255));//背景色の設定
pDC->TextOut(0,0,"VisualC++");//表示
}



問題なのはSetBkColorです。「これはこの命令の後にペンやブラシやテキストなどを使って描画した時に、ついでに背景色も塗ってしまう」関数です。これを実行した画面が上の画像です。しっかり背景色がついていることが分かります。これはそんなに難しいことではないですよね。

◇テキストにカラーを付ける
これもさほど難しくはないです。先ほどSetBkColorで背景色を設定した通り、テキストのカラーを設定して、TextOut関数を実行するだけですから。さっきの1行下に"でソフトを作ろう"を表示させてみましょう。赤字のソースを挿入してみてください。

void CTextView::OnDraw(CDC* pDC){
CTextDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CSize sz = pDC->GetTextExtent("VisualC++");
pDC->SetBkColor(RGB(0,255,255));//背景色の設定
pDC->TextOut(0,0,"VisualC++");//表示
pDC->SetTextColor(RGB(255,0,0));
pDC->TextOut(0,sz.cy,"でソフトを作ろう");
}

きっと赤い字で「でソフトを作ろう」と書かれるはずですが・・・

あれ・・・でもこれでは、背景色を変更したくないところまで背景色が水色にされてしまいました。これでは不便ですね。時には背景色を無視したい時もあるはずです。こんな時に役立つのがCDCクラスのメンバ変数である、SetBkMode関数です。

pDC->SetBkMode(引数);

引数を変えることによって、2つの動作を行います。
引数の種類と動作
OPAQUE
背景色をSetBkColorで設定された色で塗りつぶす
TRANSPARENT
背景色を一時無効にして、透過性を出す
この関数の直後のペン・ブラシ・テキスト描画からこのモードを使用することになります。モードをTRANSPARENTにしてTextOutすると、文字だけ出力されて、背景色は塗られません。このことをうまく利用すればいいのです。


void CTextView::OnDraw(CDC* pDC){
CTextDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CSize sz = pDC->GetTextExtent("VisualC++");
pDC->SetBkColor(RGB(0,255,255));//背景色の設定
pDC->TextOut(0,0,"VisualC++");//表示
pDC->SetTextColor(RGB(255,0,0));
pDC->SetBkMode(TRANSPARENT);
pDC->TextOut(0,sz.cy,"でソフトを作ろう");
}


TextOut関数もそうですが、CDCクラスではたくさんの便利な関数があります。VisualC++のCD-ROMの中にCDCクラスのライブラリがありまして、そこをみればもっと奥まで知ることができますが、あんまり深追いすると、なかなか抜けられなくなるのでこの辺にしておきましょう。次はダイアログに戻って、各種コントロールに挑戦します。ラジオボタンやリスト・コンボボックスなどです。

Back to Index