私は非常に似たようなことをしようとしていました。これと他の検索ウェブを読むことから。CWnd (または HWND) を描画するための推奨されるメカニズムを縫い合わせ、その子を独自の CDC (または HDC) に印刷 API を使用することです。
CWnd にはメソッド Print と PrintClient があり、WM_PRINT を正しく送信します。Win32 メソッドもあります: PrintWindow.
最初はこれを機能させるのに苦労しましたが、最終的には正しいメソッドとパラメーターを取得しました。私のために働いたコードは次のとおりです。
void Vg2pImageHeaderRibbon::Update() {
// Get dimensions
CRect window_rect;
GetWindowRect(&window_rect);
// Make mem DC + mem bitmap
CDC* screen_dc = GetDC(); // Get DC for the hwnd
CDC dc;
dc.CreateCompatibleDC(screen_dc);
CBitmap dc_buffer;
dc_buffer.CreateCompatibleBitmap(screen_dc, window_rect.Width(), window_rect.Height());
auto hBmpOld = dc.SelectObject(dc_buffer);
// Create a buffer for manipulating the raw bitmap pixels (per-pixel alpha).
// Used by ClearBackgroundAndPrepForPerPixelTransparency and CorrectPerPixelAlpha.
BITMAP raw_bitmap;
dc_buffer.GetBitmap(&raw_bitmap);
int bytes = raw_bitmap.bmWidthBytes * raw_bitmap.bmHeight;
std::unique_ptr<char> bits(new char[bytes]);
// Clears the background black (I want semi-transparent black background).
ClearBackgroundAndPrepForPerPixelTransparency(dc, raw_bitmap, bytes, bits.get(), dc_buffer);
// To get the window and it's children to draw using print command
Print(&dc, PRF_CLIENT | PRF_CHILDREN | PRF_OWNED);
CorrectPerPixelAlpha(dc, raw_bitmap, bytes, bits.get(), dc_buffer);
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
CPoint ptSrc;
UpdateLayeredWindow(
screen_dc,
&window_rect.TopLeft(),
&window_rect.Size(),
&dc,
&ptSrc,
0,
&blend,
ULW_ALPHA
);
SelectObject(dc, hBmpOld);
DeleteObject(dc_buffer);
ReleaseDC(screen_dc);
}
これはそのままでうまくいきました。しかし、ウィンドウまたは子供が WM_PRINT をサポートしていない場合に備えて、CView クラスにどのように実装されているかを調べたところ、このクラスが OnDraw(CDC* dc) という仮想メソッドを提供し、描画する DC が提供されていることがわかりました。WM_PAINT は次のように実装されています。
CPaintDC dc(this);
OnDraw(&dc);
そして WM_PAINT が実装されています:
CDC* dc = CDC::FromHandle((HDC)wParam);
OnDraw(dc);
したがって、WM_PAINT と WM_PRINT の結果は OnDraw() になり、描画コードは 1 回実装されます。
基本的に、これと同じロジックを独自の CWnd 派生クラスに追加できます。Visual Studio のクラス ウィザードを使用すると、これができない場合があります。メッセージ マップ ブロックに以下を追加する必要がありました。
BEGIN_MESSAGE_MAP(MyButton, CButton)
...other messages
ON_MESSAGE(WM_PRINT, OnPrint)
END_MESSAGE_MAP()
そして私のハンドラー:
LRESULT MyButton::OnPrint(WPARAM wParam, LPARAM lParam) {
CDC* dc = CDC::FromHandle((HDC)wParam);
OnDraw(dc);
return 0;
}
注: すでにこれを自動的にサポートしているクラスにカスタム WM_PRINT ハンドラを追加すると、デフォルトの実装が失われます。OnPrint には CWnd メソッドがないため、Default() メソッドを使用してデフォルト ハンドラーを呼び出す必要があります。
次のことは試していませんが、うまくいくと思います:
LRESULT MyCWnd::OnPrint(WPARAM wParam, LPARAM lParam) {
CDC* dc = CDC::FromHandle((HDC)wParam);
// Do my own drawing using custom drawing
OnDraw(dc);
// And get the default handler to draw children
return Default();
}
上で、ClearBackgroundAndPrepForPerPixelTransparency と CorrectPerPixelAlpha という奇妙なメソッドをいくつか定義しました。これらにより、子コントロールを完全に不透明にするときに、ダイアログの背景を半透明に設定できます (これはピクセルごとの透明度です)。
// This method is not very efficient but the CBitmap class doens't
// give me a choice I have to copy all the pixel data out, process it and set it back again.
// For performance I recommend using another bitmap class
//
// This method makes every pixel have an opacity of 255 (fully opaque).
void Vg2pImageHeaderRibbon::ClearBackgroundAndPrepForPerPixelTransparency(
CDC& dc, const BITMAP& raw_bitmap, int bytes, char* bits, CBitmap& dc_buffer
) {
CRect rect;
GetClientRect(&rect);
dc.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(0,0,0));
dc_buffer.GetBitmapBits(bytes, bits);
UINT* pixels = reinterpret_cast<UINT*>(bits);
for (int c = 0; c < raw_bitmap.bmWidth * raw_bitmap.bmHeight; c++ ){
pixels[c] |= 0xff000000;
}
dc_buffer.SetBitmapBits(bytes, bits);
}
// This method is not very efficient but the CBitmap class doens't
// give me a choice I have to copy all the pixel data out, process it and set it back again.
// For performance I recommend using another bitmap class
//
// This method modifies the opacity value because we know GDI drawing always sets
// the opacity to 0 we find all pixels that have been drawn on since we called
// For performance I recommend using another bitmap class such as the IcfMfcRasterImage
// ClearBackgroundAndPrepForPerPixelTransparency. Anything that has been drawn on will get an
// opacity of 255 and all untouched pixels will get an opacity of 100.
void Vg2pImageHeaderRibbon::CorrectPerPixelAlpha(
CDC& dc, const BITMAP& raw_bitmap, int bytes, char* bits, CBitmap& dc_buffer
) {
const unsigned char AlphaForBackground = 100; // (0 - 255)
const int AlphaForBackgroundWord = AlphaForBackground << 24;
dc_buffer.GetBitmapBits(bytes, bits);
UINT* pixels = reinterpret_cast<UINT*>(bits);
for (int c = 0; c < raw_bitmap.bmWidth * raw_bitmap.bmHeight; c++ ){
if ((pixels[c] & 0xff000000) == 0) {
pixels[c] |= 0xff000000;
} else {
pixels[c] = (pixels[c] & 0x00ffffff) | AlphaForBackgroundWord;
}
}
dc_buffer.SetBitmapBits(bytes, bits);
}
これが私のテストアプリケーションのスクリーンショットです。ユーザーが「その他のボタン」ボタンの上にマウスを置くと、半透明の背景でダイアログ ボックスが作成されます。ボタン「B1」から「B16」は、CButton から派生した子コントロールであり、上記の Print() 呼び出しを使用して描画されています。ビューの右端とボタンの間に半透明の背景が表示されます。
