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 の原因になります
