Version 7.02
デバイスコンテキスト
「まずは前回の復習から」
void CAnimeDlg::OnBDraw()
{
COLORREF dwColor = RGB( 255, 0, 0 );
HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
::SetPixel( hDC, 10, 10, dwColor );
}
『これで点を描いたんだよね』
「そうでした。1行目はやったから、今回は2行目と3行目」
『3行目もほとんどやっちゃったけどね』
「だから今回は2行目中心」
HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
『よくわかんない……』
「かもね。まず、ここで使ってるのは全部 API 。 MFC のじゃないから」
『頭に :: 付けてるからそうだよね。あ、でも
m_cCanvasStatic.GetSafeHwnd() は MFC だよね』
「そう、それはダイアログに貼り付けた IDC_S_CANVAS のウィンドウハンド
ルを取得してる部分」
『 IDC_S_CANVAS を m_cCanvasStatic って変数に結び付けたんだよね。で
もあんなんでもウィンドウハンドルがあるんだ』
「 Ver 5.23 ( No.088 ) で言ったでしょ、ダイアログコントロールもウィ
ンドウの一種だって」
『それは知ってるけど、でもこれってなんか機能あまりないっぽいし』
「そういう例外はないってことだね。さて、その IDC_S_CANVAS のウィンド
ウハンドルを GetDC() って API に渡して、 HDC って型の変数で受け取っ
てます」
『 HDC 、 H が付いてるって事はハンドルなんだよね』
「そう、ウィンドウズの機能を操作する時はたいがいハンドルを使うから
ね。で、これが何のハンドルかが重要」
『もったいぶらないで言ってよ』
「はいはい。これは〈デバイスコンテキスト〉っていうののハンドル」
『でばいすこんてきすと?』
「英語で書くと Device Context 」
『だから HDC なんだ』
「 Device Context を直訳すると〈仕組みの状況〉」
『わけわからん……』
「うん、そう思う。そこで、デバイスコンテキストって言うのは〈画家〉だ
と思って」
『がかぁ?』
「そう、画家。ウィンドウに描画する時には、必ずこの画家に頼まなきゃい
けないんです。その〈頼んだ〉例のひとつが」
::SetPixel( hDC, 10, 10, dwColor );
「この SetPixel() は、第1引数に渡したデバイスコンテキストに〈1ドッ
トの点を打ってください〉って頼む API 」
『へー。なんか、こういうのってウィンドウに対してしそうだけど。たとえ
ば ::SetPixel( hWnd, 10, 10, dwColor ); みたいな?』
「そこがみそ。デバイスコンテキストは必ずしも〈ウィンドウ専属〉の画家
じゃなくて、たとえばプリンタとか、その他もろもろの出力用に使えるんで
す」
『ってことは今の hDC がプリンタ用のデバイスコンテキストのハンドルだ
ったら?』
「プリンタに、1ドットの点を打つように命令するわけ」
『ほー』
「って言っても、今回はウィンドウのことだけやるけどね」
『あ、そういえば、ウィンドウならどんなウィンドウでもデバイスコンテキ
スト持ってるの? 持ってるんだろうけど』
「持ってます、 IDC_S_CANVAS だって持ってるくらいなんだから」
『はいはい』
「たとえばさ、ボタンが〈ボタンらしく〉見えるのは、内部でデバイスコン
テキスト使ってそう〈ボタンらしく〉描画してるから」
『そっか、画面に見えてるののほとんどはウィンドウ、ってことはそのほと
んどがソトヅラ作るのにデバイスコンテキストが必要なんだ』
「そゆこと。だからダイアログもデバイスコンテキスト持ってます」
void CAnimeDlg::OnBDraw()
{
COLORREF dwColor = RGB( 255, 0, 0 );
HDC hDC = ::GetDC( GetSafeHwnd() );
::SetPixel( hDC, 5, 5, dwColor );
}
『どこ違う?』
「 GetSafeHwnd() を直接呼んでるでしょ。ってことは CAnimeDlg 自信、つ
まり Anime ダイアログのだから」
『ダイアログに描画できるの? 試してみよ……あ! ちゃんと左上に赤い
点が!』
「タイトルバーのアイコンの下くらいだね」
『あれ? でも座標が?』
「 0, 0 でもタイトルバーには被らないでしょ。実は、ウィンドウには
〈クライアントエリア〉と〈ノンクライアントエリア〉っていうのがあるん
です」
『くらいあんとえりあ?』
「ウィンドウは基本的にはウィンドウズのものでしょ」
『だからハンドルとか使わないと操作できないんだもんね』
「で、〈 Client 〉ってのは〈お客さん〉って意味。ここでのクライアント
は、そのウィンドウを使わせてもらってる側」
『つまりそのハンドルとか使ってる、あたし達ってことね』
「そゆこと。クライアントエリアはその使ってる側に提供されてる空間。だ
から、描画とかは普通ここにすることになってるんです」
『ってことはタイトルバーなんかはその〈ノンクライアントエリア〉って方
なんだ』
「そう、クライアントエリアじゃない、普通は描画できない部分を〈ノンク
ライアントエリア〉って言います」
『でも…… IDC_S_CANVAS だと、 0, 0 はちゃんと左上だよ?』
「うん、ノンクライアントエリアがあるのはダイアログとか普通のウィンド
ウとかだけ。たいがいは全部クライアントエリアかな」
『なーんだ』
「それに、ノンクライアントエリアもちゃんと描画できるんだけどね」
『そなの?』
「やってみようか」
void CAnimeDlg::OnBDraw()
{
COLORREF dwColor = RGB( 255, 0, 0 );
HDC hDC
= ::GetDCEx
( GetSafeHwnd()
, NULL
, DCX_WINDOW | DCX_PARENTCLIP );
::SetPixel( hDC, 5, 5, dwColor );
}
『 GetDC() が GetDCEx() になった』
「これを使えば、ノンクライアントエリアも含めた、ウィンドウ全体に描画
できるデバイスコンテキストを取得できるから」
『うぉ、タイトルバーに赤い点が……』
「今はウィンドウのタイトルバーってデフォルトでグラデーションしてるけ
ど、前はなってなくて、自前でグラデーションさせるときに」
『こうやってタイトルバーに描画してたわけね。そういえば、こういうふう
にダイアログに描けるのに、なんでわざわざ IDC_S_CANVAS に描くようにし
たの?』
「あとあとのことを考えて、ってとこかな。元に戻して」
void CAnimeDlg::OnBDraw()
{
COLORREF dwColor = RGB( 255, 0, 0 );
HDC hDC = ::GetDC( GetSafeHwnd() );
::SetPixel( hDC, 5, 5, dwColor );
}
「これで SetPixel() の座標を変えてみるとわかるけど、 IDC_S_CANVAS ど
ころか、ボタンとかの上にも描画できるから試してみて」
『ちょっと値変えてビルドして実行……変えてビルドして実行……』
「面倒だったら for とか使ってみても」
『うげ、ホントだ! ボタンの上に赤い点が……』
「って言っても」
void CAnimeDlg::OnBDraw()
{
COLORREF dwColor = RGB( 255, 0, 0 );
HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
::SetPixel( hDC, 10, 10, dwColor );
}
「で、また少しずつ SetPixel() の座標を変えてみて」
『げ! IDC_S_CANVAS を余裕ではみ出ちゃう!!』
「でしょう。こういうふうに、外の領域も描けちゃうんです、デバイスコン
テキストは。まぁダイアログの外は描けないけど」
『あ、これは大丈夫なんだ』
「で、あとで IDC_S_CANVAS からはみ出ないようにする方法を教えるから、
そのときに〈この範囲だけに描画する〉って目印に IDC_S_CANVAS を使うか
ら」
『ダイアログに直に描くと、そういうの面倒だもんね』
「ま、これはもうちょっと先の話だけどね。今回最後は、このコードの MFC
版の紹介」
『そういえば、ずっと API だけどその理由は?』
「ひとつは、ホントに操作するのは API だから」
『 MFC 使っても、結局は中で API 使ってるんだもんね』
「特にデバイスコンテキストとかはウィンドウズの中身に結構関わってくる
から、そういう〈中の仕組み〉をちゃんと知っとくのが重要かなって思うか
らね」
『なんか難しそ……』
「もうひとつの理由は、デバイスコンテキストまわりは、 MFC を使うと逆
に使いづらいっていうことがあるんだよね」
『そなの? MFC って便利に使うためのものじゃないの?』
「使いようによるっていうか、使い方にクセがあるっていうか……。ま、と
りあえずは見てみて」
void CAnimeDlg::OnBDraw()
{
COLORREF dwColor = RGB( 255, 0, 0 );
CDC *pcDC = m_cCanvasStatic.GetDC();
pcDC->SetPixel( 10, 10, dwColor );
}
『んーと、 HDC を使うクラスが CDC なんだよね。 HWND と CWnd の関係と
同じ?』
「そう、ほとんど同じ。こういうのは、 MFC の中で統一されてるからね」
『んーと、 m_cCanvasStatic から GetDC() ってメンバ関数呼んでるんだ』
「これは CWnd のメンバ関数。だから CDialog でも CEdit でも」
『呼べて、それでデバイスコンテキスト取得できるってことね。あれ? な
んで CDC じゃなくてポインタなの?』
「そこが重要。それは次回に持ち越して、最後にその CDC のメンバ関数」
「 CDC::SetPixel() を呼ぶわけね。ってゆーか、 CDC のメンバ関数一覧見
たらなんかすんごくいっぱいあるね」
『描画って言ってももちろん1ドット打つだけじゃないからねー。これから
少しずつ紹介してくから。というわけで次回に続く!』
/*
Preview Next Story!
*/
『それにしても API の数とか、ウィンドウズの機能ってすごく多いよね』
「プログラマーはそういうのも憶えないとね」
『ウィンドウズプログラマーはってこと?』
「今はシステムが複雑だから、 OS やシステムごとに憶えることあるかな」
『どんなプログラミングでも大変ってことね……』
「というわけで次回」
< Version 7.03 アタッチとデタッチ >
『につづく!』
「でもウィンドウズはかなり複雑な方だと思うけどね」
『げ! そなの?』
「ま、ウィンドウズの憶えとけば他にも応用効くから」
『その言葉を信じるしかないか』