我有一个对话框,应该有一个自定义图像作为背景。
我没有将整个图像作为位图或任何其他格式,所以我必须从头开始绘制。
我使用icon
(屏幕截图左下角的男性),一个EMF
文件(下面屏幕截图中的地图),其余部分由绿色渐变画笔组成,浅灰色阴影画笔和文字 - 全部使用GDI
绘制。我的绘图结果如下(屏幕截图也包含控件):
当我将对话框移动到左侧然后将其移回原始位置时,我面临的问题就会显现出来。发生的工件在下一个屏幕截图的图片底部显示:
对话框是无模式对话框,然后我在WM_CTLCOLORDIALOG
中绘制整个图片,然后返回NULL_BRUSH
。
以下是WM_CTLCOLORDIALOG
处理程序的相关代码段(请注意,我直接在DC上绘制,没有双缓冲。原因是它很快测试代码很快就出错了):
case WM_CTLCOLORDLG:
{
RECT rect; // dialog's client rectangle
GetClientRect( hwnd, &rect );
// ligh gray brush for hatched brush
HBRUSH hbPozadina = CreateSolidBrush( RGB( 242, 242, 242 ) );
FillRect( (HDC)wParam, &rect, hbPozadina );
// cleanup
DeleteObject( hbPozadina );
// draw grid "manualy"
LOGBRUSH lbPozadina;
HGDIOBJ hPenPozadina = NULL, hOldPenPozadina;
lbPozadina.lbColor = RGB( 255, 255, 255 );
lbPozadina.lbHatch = 0;
lbPozadina.lbStyle = BS_SOLID;
hPenPozadina = ExtCreatePen( PS_COSMETIC | PS_SOLID, 1, &lbPozadina, 0, NULL);
hOldPenPozadina = SelectObject((HDC)wParam, hPenPozadina);
// draw vertical lines
for( int i = rect.left + 12; i< rect.right; i += 12)
{
MoveToEx((HDC)wParam, i, rect.top, NULL );
LineTo((HDC)wParam, i, rect.bottom - rect.top + 1 );
}
// draw horizontal lines
for( int i = rect.top + 12; i< rect.bottom; i += 12)
{
MoveToEx((HDC)wParam, rect.left, i, NULL );
LineTo((HDC)wParam, rect.right - rect.left + 1, i );
}
//clean up
SelectObject((HDC)wParam, hOldPenPozadina);
DeleteObject(hPenPozadina);
// draw metafile of the map
HENHMETAFILE hemf = GetEnhMetaFile( L".\\resources\\KartaDlg.emf" );
ENHMETAHEADER emh;
GetEnhMetaFileHeader( hemf, sizeof(emh), &emh );
// remove the "status bar" from the calculation
RECT r;
r.top = rect.top;
r.bottom = rect.bottom - 30;
r.left = rect.left;
r.right = rect.right;
// calculate rescaled metafile
UINT o_height = emh.rclFrame.bottom - emh.rclFrame.top,
o_width = emh.rclFrame.right - emh.rclFrame.left;
float scale = 0.5;
scale = (float)( r.right - r.left ) / o_width;
if( (float)( r.bottom - r.top ) / o_height < scale )
scale = (float)( r.bottom - r.top ) / o_height;
int marginX = ( r.right - r.left ) - (int)( o_width * scale );
int marginY = ( r.bottom - r.top ) - (int)( o_height * scale );
marginX /= 2;
marginY /= 2;
r.left = r.left + marginX;
r.right = r.right - marginX;
r.top = r.top + marginY;
r.bottom = r.bottom - marginY;
// Draw the picture.
PlayEnhMetaFile( (HDC)wParam, hemf, &r );
// Release the metafile handle.
DeleteEnhMetaFile(hemf);
// this function draws green gradient and icon
drawFooter( (HDC)wParam, rect,
RGB( 0x48, 0xAC, 0xC6), RGB( 0x31, 0x83, 0x99 ) );
//========= draw right text in status bar =============//
SetBkMode( (HDC)wParam, TRANSPARENT );
SIZE sBaner; // needed for proper positioning
HFONT hf, hfOld;
long lfHeight;
lfHeight = -MulDiv( 8, GetDeviceCaps( (HDC)wParam, LOGPIXELSY), 72 );
hf = CreateFont( lfHeight, 0, 0, 0, FW_BOLD, TRUE,
0, 0, 0, 0, 0, 0, 0, L"Arial Black" );
hfOld = (HFONT)SelectObject( (HDC)wParam, hf ); // needed for proper cleanup
GetTextExtentPoint32( (HDC)wParam,
L"ЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ",
wcslen(L"ЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ"),
&sBaner );
// position it properly
r.bottom = rect.bottom;
r.right = rect.left + sBaner.cx + 30;
r.left = rect.left + 30;
r.top = rect.bottom - rect.top - 30;
// draw it
DrawTextEx( (HDC)wParam,
L"РУДАРСКО ГЕОЛОШКИ ФАКУЛТЕТ\nЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ",
wcslen(L"РУДАРСКО ГЕОЛОШКИ ФАКУЛТЕТ\nЦЕНТАР ЗА ОБНОВЉИВЕ ВОДНЕ ЕНЕРГЕТСКЕ РЕСУРСЕ"),
&r, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_WORDBREAK, 0 );
SelectObject( (HDC)wParam, hfOld ); // proper cleanup
DeleteObject( hf );
//============== right text in the status bar ==================//
lfHeight = -MulDiv( 10, GetDeviceCaps( (HDC)wParam, LOGPIXELSY), 72 );
hf = CreateFont( lfHeight, 0, 0, 0, FW_BOLD, TRUE,
0, 0, 0, 0, 0, 0, 0, L"Arial" );
hfOld = (HFONT)SelectObject( (HDC)wParam, hf ); // needed for proper cleanup
GetTextExtentPoint32( (HDC)wParam,
L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана",
wcslen(L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана"),
&sBaner );
// position it properly
r.bottom = rect.bottom;
r.right = rect.right - 10;
r.left = rect.right - rect.left - sBaner.cx - 10;
r.top = rect.bottom - rect.top - sBaner.cy;
// draw it
DrawTextEx( (HDC)wParam,
L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана",
wcslen(L" Дејан Миленић & Ана Врањеш © 2013 сва права задржана"),
&r, DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_WORDBREAK | DT_NOPREFIX, 0 );
// perform proper cleanup
SelectObject( (HDC)wParam, hfOld );
DeleteObject(hf);
}
return (INT_PTR)GetStockObject(NULL_BRUSH);
为了使这更加完整,我提交了drawFooter
函数及其辅助函数:
// Fills triangle with gradient brush
void GradientTriangle( HDC MemDC,
LONG x1, LONG y1, LONG x2, LONG y2, LONG x3, LONG y3,
COLORREF top, COLORREF bottom )
{
TRIVERTEX vertex[3];
vertex[0].x = x1;
vertex[0].y = y1;
vertex[0].Red = GetRValue(bottom) << 8;
vertex[0].Green = GetGValue(bottom) << 8;
vertex[0].Blue = GetBValue(bottom) << 8;
vertex[0].Alpha = 0x0000;
vertex[1].x = x2;
vertex[1].y = y2;
vertex[1].Red = GetRValue(top) << 8;
vertex[1].Green = GetGValue(top) << 8;
vertex[1].Blue = GetBValue(top) << 8;
vertex[1].Alpha = 0x0000;
vertex[2].x = x3;
vertex[2].y = y3;
vertex[2].Red = GetRValue(bottom) << 8;
vertex[2].Green = GetGValue(bottom) << 8;
vertex[2].Blue = GetBValue(bottom) << 8;
vertex[2].Alpha = 0x0000;
// Create a GRADIENT_TRIANGLE structure that
// references the TRIVERTEX vertices.
GRADIENT_TRIANGLE gTriangle;
gTriangle.Vertex1 = 0;
gTriangle.Vertex2 = 1;
gTriangle.Vertex3 = 2;
// Draw a shaded triangle.
GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);
}
// draw the window's footer ( "status bar" )
void drawFooter( HDC MemDC, RECT r, COLORREF top, COLORREF bottom )
{
// bottom triangle
GradientTriangle( MemDC,
r.right, r.bottom,
( r.right - r.left ) / 2,
r.bottom - r.top - 15,
r.left,
r.bottom,
top, bottom );
// upper triangle
GradientTriangle( MemDC,
r.right, r.bottom - r.top - 30,
( r.right - r.left ) / 2,
r.bottom - r.top - 15,
r.left,
r.bottom - r.top - 30,
top, bottom );
// left triangle
GradientTriangle( MemDC,
r.left, r.bottom,
( r.right - r.left ) / 2,
r.bottom - r.top - 15,
r.left,
r.bottom - r.top - 30,
top, bottom );
// right triangle
GradientTriangle( MemDC,
r.right,
r.bottom - r.top - 30,
( r.right - r.left ) / 2,
r.bottom - r.top - 15,
r.right,
r.bottom,
top, bottom );
// draw icon
DrawIconEx( MemDC, r.left, r.bottom - r.top - 30,
hiAdmin, // a global variable for icon
30, 30, NULL, NULL, DI_NORMAL );
}
视觉样式已启用 - 这可能很重要,我不知道。
我没有处理WM_ERASEBKGND
,WM_SIZE
或WM_MOVE
(对话框无法调整大小。)。我尝试了但它没有帮助(TRUE
返回WM_ERASEBKGND
,InvalidateRect
和WM_SIZE
返回WM_MOVE
。我没有在互联网上找到任何帮助我的东西。
问题:如何更改我的代码以修复我遇到的错误?
答案 0 :(得分:1)
您正在滥用WM_CTLCOLORDLG
消息。它旨在提供一种简单的方法来更改对话框的背景颜色,而不是自定义绘制它。
您应该在那里返回NULL_BRUSH
,或者甚至完全忽略该消息,并在WM_ERASEBKGND
中进行背景绘画。
或者甚至更好,您可以忽略WM_ERASEBKGND
并在WM_PAINT
中进行绘画,就像任何其他窗口一样。
更新:在下面的几条评论之后,问题似乎是调用GradientFill()
三角形中顶点的顺序。那就是:
或许是另一种方式,我永远无法分辨......
无论如何,仍然有一个神秘的问题,为什么有时它无论顺序如何都有效,为什么有时它只适用于特定的顺序。而且,这在任何地方都有记录吗?
我猜这可能是驱动程序/ 2D加速问题...所以它将取决于DC是在显示器上还是在内存上,但很难说。
答案 1 :(得分:0)
你正在处理错误的信息。
您不应在WM_CTLCOLORDLG
消息上绘制任何内容。只需将您的绘画代码移动到WM_ERASEBKGND
处理程序。
答案 2 :(得分:0)
行。你的下一个问题是你正在重新绘制完整的窗口。请注意,窗口的左侧部分( 需要更新)被绘制好。问题是窗口的主要(右)部分,从左侧移动窗口后实际上并不需要更新。
在WM_ERASEBKGND
处理程序中,您可以调用GetUpdateRect()
来获取需要更新的矩形。
如果您不想更改代码,可以至少拨打电话,例如在绘制任何内容之前IntersectClipRect()
API。这会将所有绘画剪辑到该矩形。