Windows API实现窗口居中

本文详细解析了如何使用Windows API函数实现窗口居中显示的功能,包括获取屏幕尺寸、窗口坐标,以及设置窗口位置的过程。重点介绍了GetSystemMetrics、GetWindowRect、SetWindowPos这三个关键函数的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码如下:

 //居中窗口
   int scrWidth, scrHeight;
   RECT rect;

   scrWidth = GetSystemMetrics(SM_CXSCREEN);
   scrHeight = GetSystemMetrics(SM_CYSCREEN);
   GetWindowRect(hWnd, &rect);
   SetWindowPos(hWnd, HWND_TOPMOST, (scrWidth - rect.right) / 2, (scrHeight - rect.bottom) / 2, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW);


具体说明:

要实现窗口居中显示并不难,只要在窗体创建的时候我们设置窗口的位置就可以了.在这里我们主要用到3个API函数,分别是:GetSysMetrics,GetWindowRect,SetWindowPos.
我们用GetSysMetrics来获的屏幕的宽度和高度,然后用GetWindowRect来获取窗口的坐标.通过运算后计算出居中窗口的位置,最后用SetWindowPos来设置窗体的位置.整个过程就是如此,而这些部步骤我们都要在窗口创建之前设置好,所以我们要把这些实现方法写进WM_CREATE消息中.
现在让我们看一下,以上3个API函数的解释,下面的说明是从<Windows API参考大全>中截取出来的:
GetSystemMetrics
函数功能:返回与windows环境有关的信息。
函数原型:int GetSystemMetrics(int nIndex)
参数:
nIndex:常数,指定欲获取的信息。由于取值很多,所以在这里不一一列举了,本例子只用到两个常数值:
   SM_CXSCREEN:屏幕宽度。
   SM_CYSCREEN:屏幕高度。

GetWindowRect
 
函数功能:该函数返回指定窗口的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出。
函数原型:BOOL GetWindowRect(HWND hWnd,LPRECTlpRect);
参数:
hWnd:窗口句柄。
lpRect:指向一个RECT结构的指针,该结构接收窗口的左上角和右下角的屏幕坐标。
返回值:如果函数成功,返回值为非零:如果函数失败,返回值为零。
SetWindowPos
 
函数功能:该函数改变一个子窗口,弹出式窗口式顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。
函数原型:BOOL SetWindowPos(HWN hWnd,HWND hWndlnsertAfter,int X,int Y,int cx,int cy,UNIT.Flags);
参数:
hWnd:窗口句柄。
hWndlnsertAfter:在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄,或下列值之一:
  HWND_BOTTOM:将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶级位置,并且被置在其他窗口的底部。
  HWND_DOTTOPMOST:将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后)。如果窗口己经是非顶层窗口则该标志不起作用。
  HWND_TOP:将窗口置于Z序的顶部。
  HWND_TOPMOST:将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。
     查g看该参数的使用方法,请看说明部分。
x:以客户坐标指定窗口新位置的左边界。
Y:以客户坐标指定窗口新位置的顶边界。
cx:以像素指定窗口的新的宽度。
cy:以像素指定窗口的新的高度。
uFlags:窗口尺寸和定位的标志。

<think>我们使用WindowsAPI实现窗口缩放自适应功能。这意味着当用户调整窗口大小时,窗口内的控件(如按钮、文本框等)也会相应地调整大小和位置,以适应新的窗口尺寸。在WindowsAPI编程中,我们通常使用WM_SIZE消息来处理窗口大小变化的事件。在WM_SIZE消息的处理函数中,我们可以重新计算每个控件的位置和大小,并调用MoveWindow或SetWindowPos函数来调整控件。然而,手动为每个控件计算位置和大小是繁琐的。因此,我们可以设计一个简单的布局管理器,记录每个控件相对于窗口的位置和大小(例如使用百分比),然后在窗口大小改变时更新。这里我们给出一个简单的示例,使用Win32API和C++语言。我们将创建一个窗口,并在其中放置一个按钮。当窗口大小改变时,按钮将保持在窗口的中央,并且按钮的宽度和高度随窗口按比例调整(例如,按钮宽度为窗口宽度的50%,高度为窗口高度的20%)。步骤:1.注册窗口类2.创建窗口3.在窗口中创建按钮控件4.处理WM_SIZE消息:当窗口大小改变时,重新设置按钮的位置和大小。注意:为了简化,我们只处理一个按钮,但可以扩展到多个控件。代码示例:```cpp#include<windows.h>//按钮控件的ID#defineIDC_BUTTON1001//全局变量,保存按钮的句柄HWNDhButton;//窗口过程函数LRESULTCALLBACKWndProc(HWNDhWnd,UINTmessage,WPARAMwParam,LPARAMlParam){switch(message){caseWM_CREATE:{//创建按钮hButton=CreateWindow(L"BUTTON",//预定义按钮类L"自适应按钮",//按钮文本WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,//样式0,//x位置(初始,稍后会在WM_SIZE中调整)0,//y位置0,//宽度(初始)0,//高度(初始)hWnd,//父窗口句柄(HMENU)IDC_BUTTON,//控件ID(HINSTANCE)GetWindowLongPtr(hWnd,GWLP_HINSTANCE),NULL);}break;caseWM_SIZE:{//当窗口大小改变时,调整按钮的大小和位置intwidth=LOWORD(lParam);//窗口的新宽度intheight=HIWORD(lParam);//窗口的新高度//设置按钮的新位置和大小:我们让按钮占据窗口宽度的50%,高度的20%,并且居中intbtnWidth=width*0.5;//按钮宽度为窗口宽度的50%intbtnHeight=height*0.2;//按钮高度为窗口高度的20%intbtnX=(width-btnWidth)/2;//水平居中intbtnY=(height-btnHeight)/2;//垂直居中//移动按钮到新位置并调整大小MoveWindow(hButton,btnX,btnY,btnWidth,btnHeight,TRUE);}break;caseWM_DESTROY:PostQuitMessage(0);break;default:returnDefWindowProc(hWnd,message,wParam,lParam);}return0;}intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnCmdShow){//注册窗口类WNDCLASSEXwcex;wcex.cbSize=sizeof(WNDCLASSEX);wcex.style=CS_HREDRAW|CS_VREDRAW;wcex.lpfnWndProc=WndProc;wcex.cbClsExtra=0;wcex.cbWndExtra=0;wcex.hInstance=hInstance;wcex.hIcon=LoadIcon(hInstance,IDI_APPLICATION);wcex.hCursor=LoadCursor(NULL,IDC_ARROW);wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName=NULL;wcex.lpszClassName=L"WindowClass";wcex.hIconSm=LoadIcon(wcex.hInstance,IDI_APPLICATION);if(!RegisterClassEx(&wcex)){MessageBox(NULL,L"窗口类注册失败!",L"错误",MB_ICONERROR);return1;}//创建窗口HWNDhWnd=CreateWindow(L"WindowClass",//窗口类名L"窗口缩放自适应示例",//窗口标题WS_OVERLAPPEDWINDOW,//窗口样式CW_USEDEFAULT,CW_USEDEFAULT,500,300,//初始位置和大小NULL,NULL,hInstance,NULL);if(!hWnd){MessageBox(NULL,L"窗口创建失败!",L"错误",MB_ICONERROR);return1;}ShowWindow(hWnd,nCmdShow);UpdateWindow(hWnd);//消息循环MSGmsg;while(GetMessage(&msg,NULL,0,0)){TranslateMessage(&msg);DispatchMessage(&msg);}return(int)msg.wParam;}```解释:1.在`WM_CREATE`消息中,我们创建了一个按钮控件。初始位置和大小都是0,因为我们会在第一次`WM_SIZE`消息(在窗口显示时也会发送)中调整它。2.在`WM_SIZE`消息处理中,我们根据当前窗口的宽度和高度计算按钮的新位置和大小。这里我们让按钮的宽度为窗口宽度的50%,高度为窗口高度的20%,并且居中显示。3.使用`MoveWindow`函数来改变按钮的位置和大小。这种方法对于单个控件或者少量控件是可行的,但是当控件很多时,我们需要为每个控件存储其相对于窗口的位置和大小关系(比如使用百分比),并在`WM_SIZE`中为每个控件计算新的位置和大小。扩展:我们可以设计一个结构体来存储控件句柄以及它在父窗口中的相对位置和大小(例如左、上、右、下边距的百分比),然后在`WM_SIZE`中遍历所有控件并更新。§§相关问题§§:1.如何处理多个控件的自适应布局?2.除了使用MoveWindow,还有哪些API可以调整控件大小和位置?3.在WindowsAPI中,如何实现控件随窗口缩放时保持比例不变?4.如何实现更复杂的布局,例如类似网格或表格的布局?</think>要基于 Windows API 实现窗口缩放自适应功能,需要处理 WM_SIZE 消息并动态调整子控件的位置和大小。以下是使用 C++ 和 Win32 API 实现的详细解决方案: ```cpp #include <windows.h> // 控件ID定义 #define IDC_BUTTON1 101 #define IDC_EDIT1 102 // 存储控件原始位置和大小的结构 struct ControlLayout { int id; RECT origRect; // 原始位置和大小(相对于父窗口) }; // 全局变量 ControlLayout g_layouts[] = { {IDC_BUTTON1, {10, 10, 110, 40}}, // 按钮初始位置和大小 {IDC_EDIT1, {10, 50, 210, 200}} // 编辑框初始位置和大小 }; int g_controlCount = sizeof(g_layouts) / sizeof(g_layouts[0]); RECT g_origWindowRect = {0}; // 窗口初始大小 // 窗口过程函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { // 创建子控件 HWND hButton = CreateWindow(L"BUTTON", L"自适应按钮", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, g_layouts[0].origRect.left, g_layouts[0].origRect.top, g_layouts[0].origRect.right - g_layouts[0].origRect.left, g_layouts[0].origRect.bottom - g_layouts[0].origRect.top, hWnd, (HMENU)IDC_BUTTON1, NULL, NULL); HWND hEdit = CreateWindow(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL, g_layouts[1].origRect.left, g_layouts[1].origRect.top, g_layouts[1].origRect.right - g_layouts[1].origRect.left, g_layouts[1].origRect.bottom - g_layouts[1].origRect.top, hWnd, (HMENU)IDC_EDIT1, NULL, NULL); // 存储初始窗口大小 GetClientRect(hWnd, &g_origWindowRect); break; } case WM_SIZE: { // 获取当前窗口大小 RECT currRect; GetClientRect(hWnd, &currRect); // 计算缩放比例 float scaleX = static_cast<float>(currRect.right) / g_origWindowRect.right; float scaleY = static_cast<float>(currRect.bottom) / g_origWindowRect.bottom; // 更新所有控件位置和大小 for (int i = 0; i < g_controlCount; i++) { HWND hControl = GetDlgItem(hWnd, g_layouts[i].id); if (!hControl) continue; // 计算新位置和大小 int newLeft = static_cast<int>(g_layouts[i].origRect.left * scaleX); int newTop = static_cast<int>(g_layouts[i].origRect.top * scaleY); int newWidth = static_cast<int>((g_layouts[i].origRect.right - g_layouts[i].origRect.left) * scaleX); int newHeight = static_cast<int>((g_layouts[i].origRect.bottom - g_layouts[i].origRect.top) * scaleY); // 应用新位置和大小 SetWindowPos(hControl, NULL, newLeft, newTop, newWidth, newHeight, SWP_NOZORDER | SWP_NOACTIVATE); } break; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // 主函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 注册窗口类 WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), NULL, L"AdaptiveWindowClass", NULL }; RegisterClassEx(&wc); // 创建窗口 HWND hWnd = CreateWindow(L"AdaptiveWindowClass", L"自适应窗口示例", WS_OVERLAPPEDWINDOW | WS_SIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // 消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } ``` ### 代码解释: 1. **数据结构设计**: - `ControlLayout` 结构存储每个控件的原始位置和大小 - `g_layouts` 数组保存所有控件的初始布局信息 - `g_origWindowRect` 存储窗口初始大小 2. **WM_CREATE 处理**: - 创建按钮和编辑框控件 - 记录控件的初始位置和大小 - 获取窗口初始客户端区域大小 3. **WM_SIZE 处理(核心逻辑)**: ```cpp case WM_SIZE: { // 1. 获取当前窗口大小 RECT currRect; GetClientRect(hWnd, &currRect); // 2. 计算缩放比例 float scaleX = static_cast<float>(currRect.right) / g_origWindowRect.right; float scaleY = static_cast<float>(currRect.bottom) / g_origWindowRect.bottom; // 3. 遍历所有控件计算新位置和大小 for (int i = 0; i < g_controlCount; i++) { // ... 计算新坐标 ... // 4. 应用新布局 SetWindowPos(/*...*/); } break; } ``` - 缩放比例计算:当前窗口尺寸 / 初始窗口尺寸 - 控件新位置 = 原始位置 × 缩放比例 - 控件新大小 = 原始大小 × 缩放比例 4. **控件更新**: - 使用 `SetWindowPos()` API 更新控件位置和大小 - `SWP_NOZORDER` 标志保持控件Z顺序不变 - `SWP_NOACTIVATE` 防止控件意外获得焦点 ### 关键点说明: 1. **比例保持**: - 控件位置和大小按窗口尺寸变化比例缩放 - 保持控件与窗口的相对位置关系 2. **客户端区域**: - 使用 `GetClientRect()` 获取不含边框的内部区域 - 确保计算基于实际可用空间 3. **高效更新**: - 只在窗口大小改变时重新计算布局 - 避免不必要的重绘 4. **扩展性**: - 可添加锚定系统(如左侧固定、右侧拉伸) - 支持最小尺寸限制 - 添加布局约束(如宽高比保持) ### 进阶优化方向: 1. **锚定系统**: ```cpp enum AnchorStyle { AnchorLeft = 1, AnchorTop = 2, AnchorRight = 4, AnchorBottom = 8 }; struct ControlLayout { int id; RECT origRect; int anchors; // 锚定组合 }; ``` 2. **DPI 自适应**: - 使用 `GetDpiForWindow()` 获取DPI值 - 根据DPI缩放比例调整布局 3. **布局管理器**: - 实现网格布局、流式布局等复杂系统 - 支持嵌套容器和布局组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值