文章目录
三种绘图句柄与三大坐标系(三大CDC派生类)简介以及应用
三种HDC句柄
a)标准绘图消息WM_PAINT:,必须使用BeginPaint和EndPaint来绘图。
BeginPaint和EndPaint只能在WM_PAINT中使用。
b)在其他任何消息中使用的绘图句柄叫做临时绘图句柄。
(GetDC和ReleaseDC)
c)标准非客户户区绘图:(不推荐使用)
GetWindowDC和ReleaseDC
非客户区如同一个桌子的桌面,桌面上有桌布,露出了四角就是。
这里要说明的问题是:非客户区不是只有四角。
三大CDC派生类
a)客户区标准绘图:必须由CPaintDC派生类来调用,不能直接调用由CDC基类;
因为CPaintDC类构造函数是调用::BeginPaint,析构函数调用::EndPaint;
CPaintDC类必须在WM_PAINT消息中使用
cpp
CPaintDC::CPaintDC(CWnd* pWnd)
{
ASSERT_VALID(pWnd);
ASSERT(::IsWindow(pWnd->m_hWnd));
if (!Attach(::BeginPaint(m_hWnd = pWnd->m_hWnd, &m_ps)))
AfxThrowResourceException();
}
CPaintDC::~CPaintDC()
{
ASSERT(m_hDC != NULL);
ASSERT(::IsWindow(m_hWnd));
::EndPaint(m_hWnd, &m_ps);
Detach();
}
b)非客户区标准绘图,必须由CWindowDC来调用,必须在WM_NCPAINT下执行;
(不推荐使用)内部是构造函数调用GetWindowDC,析构函数调用ReleaseDC
cpp
CWindowDC::CWindowDC(CWnd* pWnd)
{
ASSERT(pWnd == NULL || ::IsWindow(pWnd->m_hWnd));
if (!Attach(::GetWindowDC(m_hWnd = pWnd->GetSafeHwnd())))
AfxThrowResourceException();
}
CWindowDC::~CWindowDC()
{
ASSERT(m_hDC != NULL);
::ReleaseDC(m_hWnd, Detach());
}
c)客户区临时绘图除了以上两个消息之外,客户区的其他消息都调用CClientDC来执行绘图;
内部是构造函数调用GetDC,析构函数调用RleaseDC。
cpp
CClientDC::CClientDC(CWnd* pWnd)
{
ASSERT(pWnd == NULL || ::IsWindow(pWnd->m_hWnd));
if (!Attach(::GetDC(m_hWnd = pWnd->GetSafeHwnd())))
AfxThrowResourceException();
}
CClientDC::~CClientDC()
{
ASSERT(m_hDC != NULL);
::ReleaseDC(m_hWnd, Detach());
}
什么是放泄露架构
利用析构函数,无论任何条件返回,析构函数都系统帮你防止泄露
cpp
class CPaintDC : public CDC
{// Attributes
protected:
HWND m_hWnd;
public:
explicit CPaintDC(CWnd* pWnd); // BeginPaint
public:
PAINTSTRUCT m_ps; // actual paint struct!
// Implementation
public:
virtual ~CPaintDC();
};
使用HDC句柄进行常见图形绘制演示
HPEN和HBRUSH句柄
a)HPEN是修饰边框,HBRUSH是修饰填充;
b)HPEN可修饰闭合和非闭合图形,HBRUSH只能修饰闭合图形;
c)所有GDI对象的摧毁用DeleteObject,所有GDI对象句柄的信息获取都用GetObject。
d)GetObject反函数是,由详细信息创建句柄,CreateXXXIndirect
!!关注:SelectObject的返回值,是旧对象(OldPen,OldBrush,OldFont等等)
cpp
GetObject支持哪些句柄(对应的结构体)
LOGPEN,LOGBRUSH,LOGFONT,BITMAP,
LOGBRUSH logBrush;
GetObject(hBrush, sizeof(LOGBRUSH), &logBrush);
HPEN的创建
cpp
HPEN CreatePen( int fnPenStyle, int nWidth, COLORREF crColor);
HPEN CreatePenIndirect( const LOGPEN* lplgpn);
typedef struct tagLOGPEN
{
UINT lopnStyle;
POINT lopnWidth;
COLORREF lopnColor;
} LOGPEN;
cpp
PS_SOLID Creates a solid pen.
PS_DASH Creates a dashed pen. Valid only when the pen width is 1 or less, in device units.
PS_DOT Creates a dotted pen. Valid only when the pen width is 1 or less, in device units.
PS_DASHDOT
Creates a pen with alternating dashes and dots. Valid only when the pen width is 1 or less, in device units.
PS_DASHDOTDOT
Creates a pen with alternating dashes and double dots. Valid only when the pen width is 1 or less, in device units.
PS_NULL Creates a null pen.
PS_INSIDEFRAME Creates a pen that draws a line inside the frame of closed shapes produced by the Windows GDI output functions that specify a bounding rectangle (for example, the Ellipse, Rectangle, RoundRect, Pie, and Chord member functions). When this style is used with Windows GDI output functions that do not specify a bounding rectangle (for example, the LineTo member function), the drawing area of the pen is not limited by a frame.
Invalidate刷新函数的功能和用法简介
InvalidateRect函数用于使指定的矩形区域失效,从而触发重绘操作。其刷新原理如下:
a) InvalidateRect函数会发送WM_PAINT消息给窗口,告诉窗口需要进行绘图操作。窗口收到WM_PAINT消息后,会调用窗口过程中的处理函数,进行标准的绘图操作。
b) 在进行绘图之前,窗口会接收到WM_ERASEBKGND消息,用于擦除当前窗口的背景。这个消息会导致窗口的背景被擦除,以准备进行绘图操作。
c) InvalidateRect函数的第三个参数可以是TRUE或FALSE。当为TRUE时,表示需要同时刷新前景和背景;当为FALSE时,表示只刷新前景。默认情况下,第三个参数为TRUE。
d) UpdateWindow函数用于立即执行窗口的重绘操作,类似于SendMessage函数。它会强制窗口立即进行绘图操作,而不需要等待下一次消息循环。这样可以立即刷新窗口的内容。与之相对,InvalidateRect函数只是标记了指定区域为失效,需要等待下一次绘图消息的到来才会进行绘图操作。
cpp
case WM_SIZE:
{
//FALSE 前景覆盖 (WM_PAINT)单层覆盖 TRUE 前景和背景(WM_EARSEBGGND)同时刷新 双层覆盖
RECT rect{ 300,200,100,200 };
InvalidateRect(hDlg, NULL, FALSE);
//当窗口大小发生变化时 对其刷新 第二个参数为NULL 代表全区域刷新 CRect 可指定
}
break;
应用
Win32下
cpp
#include<Windows.h>
#include<windowsx.h>
#include<tchar.h>
#include "resource.h"
void OnPaint(HWND hDlg, HDC hdc)
{
RECT rect;
GetClientRect(hDlg, &rect);
HBRUSH hbr = CreateSolidBrush(RGB(102, 250, 230));
SelectObject(hdc, hbr);
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
DeleteObject(hbr);
hbr = CreateSolidBrush(RGB(0, 0, 230));
SelectObject(hdc, hbr);
Ellipse(hdc, 10, 10, 300, 200);
DeleteObject(hbr);
hbr = CreateSolidBrush(RGB(0, 250, 0));
SelectObject(hdc, hbr);
RoundRect(hdc, 300, 300, 500, 400, 50, 50);
DeleteObject(hbr);
hbr = CreateSolidBrush(RGB(255, 250, 0));
SelectObject(hdc, hbr);
POINT pts[] = { {433,221},{444,555},{777,544} ,{877,444} };
Polygon(hdc, pts, _countof(pts)); //闭合图形
DeleteObject(hbr);
hbr = CreateSolidBrush(RGB(255, 250, 0));
SelectObject(hdc, hbr);
POINT pts2[] = { {733,521},{244,155},{377,744} ,{277,244}, {733,521}};
Polyline(hdc, pts2, _countof(pts2));//非闭合图形 填充颜色也无效
DeleteObject(hbr);
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, _T("这世间本没有控件"), 8, &rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
}
INT_PTR CALLBACK theProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
HDC hdc = GetDC(hDlg); //临时绘图DC 会被冲毁
Rectangle(hdc, x - 5, y - 5, x + 5, y + 5);
ReleaseDC(hDlg, hdc);
return TRUE;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hDlg, &ps); //hdc 理解为绘图句柄 绑定什么 就绘图什么
OnPaint(hDlg,hdc);
//TextOut(hdc,_T("这世间本没有控件"),8);
EndPaint(hDlg, &ps); //成对使用
}
return TRUE;
case WM_SIZE:
{
//FALSE 前景覆盖 (WM_PAINT)单层覆盖 TRUE 前景和背景(WM_EARSEBGGND)同时刷新 双层覆盖
RECT rect{ 300,200,100,200 };
InvalidateRect(hDlg, NULL, FALSE);
//当窗口大小发生变化时 对其刷新 第二个参数为NULL 代表全区域刷新 CRect 可指定
}
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == IDCANCEL)
EndDialog(hDlg, IDCANCEL);
}
break;
}
return FALSE;//return 0代表系统默认要执行的 return 1代表执行系统以外的
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_GDI_DLG), NULL, theProc);
return 0;
}
MFC下
cpp
#include "pch.h"
#include "framework.h"
#include "MFC_GDI.h"
#include "CMainDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CMainDlg::CMainDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFC_GDI_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMainDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_SIZE()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
BOOL CMainDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMainDlg::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文 不用请不要删掉
RECT rect;
GetClientRect(&rect);
CPen p1,p2,p3;
p1.CreatePen(PS_SOLID, 5, RGB(255, 0, 255));
auto hOldPen = dc.SelectObject(p1);
LOGPEN logPen;
p1.GetObject(sizeof(logPen), &logPen);
HBRUSH hbr = CreateSolidBrush(RGB(102, 250, 230));
dc.SelectObject( hbr);
dc.Rectangle(rect.left, rect.top, rect.right, rect.bottom);
dc.SelectObject(hOldPen);//恢复
DeleteObject(hbr);
hbr = CreateSolidBrush(RGB(0, 0, 230));
dc.SelectObject(hbr);
dc.Ellipse( 10, 10, 300, 200);
DeleteObject(hbr);
LOGPEN lp{ PS_NULL };
p3.CreatePenIndirect(&lp);
dc.SelectObject(p3);
dc.RoundRect( 300, 300, 500, 400, 50, 50);
p2.CreatePen(PS_DASHDOTDOT, 1, RGB(255, 0, 255));
dc.SelectObject(p2);
GetObject(p2, sizeof(logPen), &logPen);
dc.SelectObject(hOldPen);//恢复
hbr = CreateSolidBrush(RGB(255, 250, 0));
dc.SelectObject( hbr);
POINT pts[] = { {433,221},{444,555},{777,544} ,{877,444} };
dc.Polygon( pts, _countof(pts)); //闭合图形
DeleteObject(hbr);
hbr = CreateSolidBrush(RGB(255, 250, 0));
dc.SelectObject( hbr);
POINT pts2[] = { {733,521},{244,155},{377,744} ,{277,244}, {733,521} };
dc.Polyline( pts2, _countof(pts2));//非闭合图形 填充颜色也无效
DeleteObject(hbr);
dc.SetBkMode(TRANSPARENT);
dc.DrawText( _T("这世间本没有控件"), 8, &rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
}
cpp
HCURSOR CMainDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMainDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
this->Invalidate(FALSE);
}
void CMainDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CDialogEx::OnLButtonDown(nFlags, point);
CClientDC dc(this);
dc.Rectangle(point.x - 10, point.y - 10, point.x + 10, point.y + 10);
}
附录
cpp
DrawText() Format Flags
#define DT_TOP 0x00000000
#define DT_LEFT 0x00000000
#define DT_CENTER 0x00000001
#define DT_RIGHT 0x00000002
#define DT_VCENTER 0x00000004
#define DT_BOTTOM 0x00000008
#define DT_WORDBREAK 0x00000010 过长折回
#define DT_SINGLELINE 0x00000020 单行