VC++ 画图:使用 GetTextExtent 函数实现 X 坐标右对齐(剪贴板问题解决方案)
以下是一个实现 x 坐标右对齐的 VC++ 代码示例:
#include <windows.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("RightAlignXAxis");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Right Align X Axis"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClient, cyClient;
static int cxChar, cyChar;
static int cchMaxXAxisLabel;
static std::wstring strXAxisLabel;
static int iXAxisMin = 0, iXAxisMax = 100;
static int iYAxisMin = 0, iYAxisMax = 100;
static int iXScale = 10, iYScale = 10;
static int iXAxisLabelStep = 10, iYAxisLabelStep = 10;
static int iXAxisLabelOffset = 0, iYAxisLabelOffset = 0;
HDC hdc;
PAINTSTRUCT ps;
int i, x, y;
TCHAR szBuffer[10];
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
cxChar = LOWORD(GetDialogBaseUnits());
cyChar = HIWORD(GetDialogBaseUnits());
ReleaseDC(hwnd, hdc);
cchMaxXAxisLabel = 10;
strXAxisLabel.resize(cchMaxXAxisLabel, TEXT(' '));
return 0;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Draw X and Y axes
MoveToEx(hdc, 0, cyClient / 2, NULL);
LineTo(hdc, cxClient, cyClient / 2);
MoveToEx(hdc, cxClient / 2, 0, NULL);
LineTo(hdc, cxClient / 2, cyClient);
// Draw X axis labels
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
for (i = iXAxisMin; i <= iXAxisMax; i += iXAxisLabelStep)
{
x = cxClient / 2 + i * cxChar * iXScale - iXAxisLabelOffset;
if (x >= 0 && x <= cxClient)
{
wsprintf(szBuffer, TEXT("%d"), i);
ExtTextOut(hdc, x, cyClient / 2 + cyChar / 2, ETO_OPAQUE, NULL, szBuffer, lstrlen(szBuffer), NULL);
}
}
// Draw Y axis labels
SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM);
for (i = iYAxisMin; i <= iYAxisMax; i += iYAxisLabelStep)
{
y = cyClient / 2 - i * cyChar * iYScale + iYAxisLabelOffset;
if (y >= 0 && y <= cyClient)
{
wsprintf(szBuffer, TEXT("%d"), i);
TextOut(hdc, cxClient / 2 - cxChar / 2, y, szBuffer, lstrlen(szBuffer));
}
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CLIPBOARDUPDATE:
if (IsClipboardFormatAvailable(CF_BITMAP))
{
OpenClipboard(hwnd);
HBITMAP hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
CloseClipboard();
if (hBitmap != NULL)
{
BITMAP bm;
GetObject(hBitmap, sizeof(bm), &bm);
int cxImage = bm.bmWidth;
int cyImage = bm.bmHeight;
// Calculate X axis label offset for right alignment
int cxMaxXAxisLabel = 0;
for (i = iXAxisMin; i <= iXAxisMax; i += iXAxisLabelStep)
{
wsprintf(szBuffer, TEXT("%d"), i);
strXAxisLabel.replace(0, lstrlen(szBuffer), szBuffer);
int cxLabel = GetTextExtentPoint32(hdc, strXAxisLabel.c_str(), lstrlen(strXAxisLabel.c_str()), NULL).cx;
if (cxLabel > cxMaxXAxisLabel)
{
cxMaxXAxisLabel = cxLabel;
}
}
iXAxisLabelOffset = cxMaxXAxisLabel;
// Draw image with right-aligned X axis labels
hdc = CreateCompatibleDC(NULL);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdc, hBitmap);
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
for (i = iXAxisMin; i <= iXAxisMax; i += iXAxisLabelStep)
{
x = cxImage / 2 + i * cxChar * iXScale - iXAxisLabelOffset;
if (x >= 0 && x <= cxImage)
{
wsprintf(szBuffer, TEXT("%d"), i);
ExtTextOut(hdc, x, cyImage / 2 + cyChar / 2, ETO_OPAQUE, NULL, szBuffer, lstrlen(szBuffer), NULL);
}
}
SelectObject(hdc, hOldBitmap);
DeleteDC(hdc);
}
}
return 0;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
在上面的代码中,我们在窗口的WM_PAINT消息处理程序中使用SetTextAlign函数设置了文本对齐方式为右对齐,并使用ExtTextOut函数绘制 X 轴标签。为了实现右对齐,我们需要在 X 轴标签的位置上减去标签字符串的长度,这里使用了GetTextExtentPoint32函数来获取标签字符串的长度。
在窗口的WM_CLIPBOARDUPDATE消息处理程序中,我们首先检查剪贴板中是否有位图格式的数据。如果有,我们使用GetClipboardData函数获取位图句柄,然后使用GetObject函数获取位图的信息,包括宽度和高度。接着,我们计算出 X 轴标签的最大长度,并将其保存在变量iXAxisLabelOffset中。最后,我们使用ExtTextOut函数绘制右对齐的 X 轴标签,并将位图复制到剪贴板中。
需要注意的是,在窗口的WM_CREATE消息处理程序中,我们使用GetDialogBaseUnits函数获取对话框基本单元的宽度和高度,用于计算字符宽度和高度。在窗口的WM_SIZE消息处理程序中,我们保存窗口的客户区宽度和高度,用于绘制 X 和 Y 轴。在窗口的WM_DESTROY消息处理程序中,我们调用PostQuitMessage函数退出程序。
解决方案:
在复制到剪贴板后,我们创建了一个兼容的设备上下文(DC),并使用 SelectObject 将位图选择到该 DC 中。然后,我们再次使用 GetTextExtentPoint32 函数获取每个标签字符串的长度,并使用 ExtTextOut 函数在位图上绘制标签。最后,我们释放 DC 和选择对象。
修改后的代码:
// ... (其他代码)
case WM_CLIPBOARDUPDATE:
if (IsClipboardFormatAvailable(CF_BITMAP))
{
OpenClipboard(hwnd);
HBITMAP hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
CloseClipboard();
if (hBitmap != NULL)
{
// ... (获取位图信息)
// Calculate X axis label offset for right alignment
int cxMaxXAxisLabel = 0;
for (i = iXAxisMin; i <= iXAxisMax; i += iXAxisLabelStep)
{
wsprintf(szBuffer, TEXT("%d"), i);
strXAxisLabel.replace(0, lstrlen(szBuffer), szBuffer);
// Use GetTextExtentPoint32 on compatible DC
int cxLabel = GetTextExtentPoint32(hdc, strXAxisLabel.c_str(), lstrlen(strXAxisLabel.c_str()), NULL).cx;
if (cxLabel > cxMaxXAxisLabel)
{
cxMaxXAxisLabel = cxLabel;
}
}
iXAxisLabelOffset = cxMaxXAxisLabel;
// Draw image with right-aligned X axis labels
hdc = CreateCompatibleDC(NULL);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdc, hBitmap);
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
for (i = iXAxisMin; i <= iXAxisMax; i += iXAxisLabelStep)
{
x = cxImage / 2 + i * cxChar * iXScale - iXAxisLabelOffset;
if (x >= 0 && x <= cxImage)
{
wsprintf(szBuffer, TEXT("%d"), i);
ExtTextOut(hdc, x, cyImage / 2 + cyChar / 2, ETO_OPAQUE, NULL, szBuffer, lstrlen(szBuffer), NULL);
}
}
SelectObject(hdc, hOldBitmap);
DeleteDC(hdc);
}
}
return 0;
// ... (其他代码)
通过使用兼容的 DC 来获取标签字符串的长度,我们解决了剪贴板问题,并确保在复制到剪贴板时 X 轴标签可以正确地右对齐。
原文地址: https://www.cveoy.top/t/topic/gEnW 著作权归作者所有。请勿转载和采集!