MFCで描画処理を書くとき、最初に必要になるのがデバイスコンテキスト(DC)です。MFCにはCPaintDC、CClientDC、CWindowDCの3つのDCラッパークラスがありますが、それぞれ用途が異なります。
目次
3つのDCクラスの比較
| クラス | 対応API | 描画領域 | 使用場面 |
|---|---|---|---|
| CPaintDC | BeginPaint / EndPaint | クライアント領域(無効化された部分) | OnPaint内のみ |
| CClientDC | GetDC / ReleaseDC | クライアント領域(全体) | OnPaint以外での即時描画 |
| CWindowDC | GetWindowDC / ReleaseDC | ウィンドウ全体(タイトルバー・枠線含む) | 非クライアント領域への描画 |
実行結果では、3種のDCがどの領域を対象にするのかを模式図で並べて確認できます。CPaintDCは無効化されたクライアント領域だけ、CClientDCはクライアント領域全体、CWindowDCはタイトルバーや枠線を含むウィンドウ全体を扱う、という対応関係が見て取れます。

CPaintDC:OnPaintの定番
CPaintDCはWM_PAINTメッセージハンドラ(OnPaint)でのみ使うクラスです。コンストラクタでBeginPaint、デストラクタでEndPaintを呼びます。
void CMyView::OnPaint()
{
CPaintDC dc(this); // BeginPaint が呼ばれる
dc.TextOut(10, 10, _T("Hello, MFC!"));
// デストラクタで EndPaint が呼ばれる
}
OnPaint内ではCPaintDCを使ってください。CClientDCをOnPaint内で使うと、BeginPaintによるupdate regionのクリアが行われず、WM_PAINTが繰り返し発生して高CPUの原因になります。
CClientDC:OnPaintの外で描画する
CClientDCはOnPaint以外の場所で即座に描画したいときに使います。よく使うのはマウス操作中のリアルタイム描画です。
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
if (nFlags & MK_LBUTTON)
{
CClientDC dc(this); // GetDC が呼ばれる
dc.SetPixel(point, RGB(0, 0, 0));
// デストラクタで ReleaseDC が呼ばれる
}
CView::OnMouseMove(nFlags, point);
}
ただし、CClientDCで描いた内容は永続しません。ウィンドウが最小化されて復帰したり、他のウィンドウに隠されて再表示されたりすると消えます。消えてほしくない内容は、データをメンバ変数に持っておき、OnPaintでも描くようにしてください。
CWindowDC:タイトルバーや枠線に描く
CWindowDCはウィンドウ全体(非クライアント領域を含む)を描画対象とします。タイトルバーにカスタム描画するときなどに使います。
void CMyFrame::OnNcPaint()
{
CWindowDC dc(this); // GetWindowDC が呼ばれる
CRect rc;
GetWindowRect(&rc);
rc.OffsetRect(-rc.left, -rc.top);
// タイトルバー領域に描画
dc.FillSolidRect(0, 0, rc.Width(), 30, RGB(50, 50, 50));
// デストラクタで ReleaseDC が呼ばれる
}
OSバージョンやテーマで挙動が変わりやすいので、互換性まわりは注意してください。
よくある間違い
| 間違い | 結果 | 正しい方法 |
|---|---|---|
| OnPaint内でCClientDCを使う | WM_PAINTが繰り返し発生し高CPUの原因になる | OnPaintではCPaintDCを使う |
| OnPaint外でCPaintDCを使う | WM_PAINT用のDCを前提外で使うことになり、再描画の説明と実装がずれる | OnPaint外ではCClientDCを使う |
| DCをメンバ変数に保持する | リソースリーク。DCは短期間で解放すべき | 必要なときにスタック上で生成する |
まとめ
- CPaintDCはOnPaint専用で、BeginPaint / EndPaintを自動で呼びます
- CClientDCはOnPaint以外で即時描画するときに使います
- CWindowDCはタイトルバーなど非クライアント領域への描画用で、出番はほとんどありません
- OnPaintでCClientDCを使うとWM_PAINTが繰り返し発生し、高CPUの原因になります
