GetWindowRect()和GetClientRect();OnPaint()和OnDraw()等

本文详细解释了GetWindowRect()与GetClientRect()的区别及应用,OnPaint()与OnDraw()在视图重画中的作用,Invalidate()与UpdateAllViews()在窗口重画和文档视图同步中的差异,以及UpdateData()如何实现用户输入数据与控件值的双向同步。

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

1.GetWindowRect()和GetClientRect()

GetWindowRect()是取得窗口在屏幕坐标系下的RECT坐标(包括客户区和非客户区),这样可以得到窗口的大小和相对屏幕左上角(0,0)的位置。

GetClientRect()取得窗口客户区(不包括非客户区)在客户区坐标系下的RECT坐标,可以得到窗口的大小,而不能得到相对屏幕的位置,因为这个矩阵是在客户区坐标系下(相对于窗口客户区的左上角)的。  

ClientToScreen()把客户区坐标系下的RECT坐标转换为屏幕坐标系下的RECT坐标.

ScreenToClient()把屏幕坐标系下的RECT坐标转换为客户区坐标系下的RECT坐标.     

我们对同一个窗口先GetWindowRect取得一个RECT,再用ScreenToClient转换到客户坐标系。然后 GetClientRect取得一个RECT,再用ClientToScreen转换到屏幕坐标系。显然,GetWindowRect取得的矩阵不小于 GetClientRect取得的矩阵。因为前者包含了非客户区,而后仅包括了客户区。   

  对GetWindowRect取得的矩阵ScreenToClient后,矩阵的大小没有变小,(-3,-29)是窗口的左上角的坐标,相对窗口客户区左上角。对GetClientRect取得的矩阵ClientToScreen后,矩阵也没有变大,新得到的矩阵是窗口客户区在屏幕坐标系上的RECT。


2.OnPaint()和OnDraw()

当窗口改变后,会产生无效区域,这个无效的区域需要重画。一般Windows会发送两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化)。非客户区的重画系统自己搞定了,而客户区的重画需要我们自己来完成,这就需要OnDraw()或OnPaint()来重画窗口。

OnDraw()和OnPaint()有什么区别呢?首先:我们先要明确CView类派生自CWnd类。而OnPaint()是CWnd的类成员,同时负责响应WM_PAINT消息。OnDraw()是CView类的一个纯虚函数,没有响应消息的功能,要使用必须重载它。

其次,要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含输出设备(不单指你17寸的纯屏显示器,还包括打印机之类的输出设备)的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响应,而CPaintDC支持重画。

当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。可见在OnPaint()里调用了OnDraw()。通常我们不必编写重写的OnPaint处理成员函数,而由于OnDraw是虚拟函数,可以自己改写,所以就实现了不同的绘图目的。

///CView默认的标准OnPaint()

void CView::OnPaint()
{

CPaintDC dc(this);

OnPreparDC(&dc);

OnDraw(&dc); //调用了OnDraw
}

//自己写的OnDraw()

///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。
void CMyView::OnDraw( CDC* pDC )
{

CMyDoc* pDoc = GetDocument();

CString s = pDoc->GetData(); // Returns a Cstring

CRect rect;

GetClientRect( &rect );

pDC->SetTextAlign( TA_BASELINE | TA_CENTER );

pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );

}

因此我们一般用OnPaint维护窗口的客户区(例如在窗口客户区加一个背景图片),用OnDraw维护视图的客户区(例如通过鼠标在视图中画图)。

补充:我们还可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重画窗口。

3. Invalidate()和UpdateAllViews()

Invalidate()是让程序重画窗口,使系统向其发WM_PAINT消息,使的程序的OnPaint被调用重画客户区。

UpdateAllViews()是在DOC/VIEW结构中,当一个视图的数据改变后,通知所有视图作相应的改变。UpdateAllViews()是文档与视之间的联系,调用从它会使程序与此文档相关的所有视的UpdateView被调用,至于是否重画以及怎么画是由各视的UpdateView来决定的。

Invalidate()是Cwnd的成员函数,与DOC-VIEW无关;

UpdateAllViews()是CDocument的成员函数,具体体现DOC-VIEW的精神。

Invalidate()函数产生一条WM_PAINT消息,并送入windows消息队列中,是窗口产生重画。而UpdateAllViews 并不进入windows消息队列中,直接产生重画。

如果仅重画当前窗口用 this->Invalidate();如果通知所有和当前文档相关的窗口重画用GetDocument()->UpdateAllViews()(在View中)或this->UpdateAllViews()(在Doc中)。

还有一个函数:ONERASEBKGND()画背景用的,先于OnPaint()运行。

4. UpdateData(true)和UpdateData(false)

如果你为控件分配了变量:

UpdateData(true): 是使用户输入的数据能够反映到变量上,在函数调用之后变量才被赋值。

UpdateData(false): 是当你给控件对应的变量赋过值之后,在控件上体现出来,在函数调用之前,控件的值是不会随变量变化的。


<think>我们正在讨论Windows API中与窗口几何相关的三个函数:GetWindowRect, GetClientRect, GetWindowRgn。用户想知道它们的使用方法。 根据引用内容,我们可以知道: - GetWindowRect:获取窗口的矩形(屏幕坐标) - GetClientRect:获取窗口客户区的矩形(相对于窗口左上角) - GetWindowRgn:获取窗口的区域(一个更复杂的形状,可能不是矩形) 虽然引用中没有详细说明每个函数的参数用法,但我们可以根据已知的Windows API知识来回答。 下面将分别介绍这三个函数: 1. **GetWindowRect**: - 功能:获取窗口的边界矩形(包括标题栏、边框等),以屏幕坐标表示。 - 函数原型: ```c BOOL GetWindowRect( HWND hWnd, LPRECT lpRect ); ``` - 参数: - `hWnd`:目标窗口的句柄。 - `lpRect`:指向一个`RECT`结构体的指针,该结构体将接收窗口的屏幕坐标。 - 返回值:如果成功,返回非零值;否则返回0。 2. **GetClientRect**: - 功能:获取窗口客户区的矩形。客户区是指窗口中不包括标题栏、边框、滚动条等的内部区域。注意,这个矩形是相对于窗口客户区的左上角(即客户区的左上角坐标为(0,0))。 - 函数原型: ```c BOOL GetClientRect( HWND hWnd, LPRECT lpRect ); ``` - 参数: - `hWnd`:目标窗口的句柄。 - `lpRect`:指向`RECT`结构体的指针,该结构体将接收客户区的尺寸(宽度高度)。注意,矩形左上角总是(0,0),右下角是(client_width, client_height)。 - 返回值:如果成功,返回非零值;否则返回0。 3. **GetWindowRgn**: - 功能:获取窗口的区域(region)。窗口区域定义了窗口的形状,可以是矩形,也可以是任意形状(例如圆形、多边形)。这个函数获取的是窗口当前设置的区域。 - 函数原型: ```c int GetWindowRgn( HWND hWnd, HRGN hrgn ); ``` - 参数: - `hWnd`:目标窗口的句柄。 - `hrgn`:一个已存在的区域句柄,函数将把窗口区域复制到这个区域中。 - 返回值:返回值的类型表示获取的结果,可能是: - NULLREGION:区域为空。 - SIMPLEREGION:区域是一个单一的矩形。 - COMPLEXREGION:区域是多个矩形或复杂形状。 - ERROR:发生错误。 注意:使用`GetWindowRgn`之前,通常需要先创建一个区域(使用`CreateRectRgn`、`CreateEllipticRgn`等函数创建),然后将其作为`hrgn`参数传入。函数会将窗口区域复制到这个区域对象中。 下面是一个简单的示例代码,展示如何使用这三个函数: ```c #include <Windows.h> void Example(HWND hwnd) { RECT rectWindow; RECT rectClient; HRGN hRgn = CreateRectRgn(0, 0, 0, 0); // 创建一个初始为空的区域 // 获取窗口矩形(屏幕坐标) if (GetWindowRect(hwnd, &rectWindow)) { // rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom 是屏幕坐标 } // 获取客户区矩形(相对于窗口客户区左上角) if (GetClientRect(hwnd, &rectClient)) { // rectClient.left=0, rectClient.top=0, rectClient.right=客户区宽度, rectClient.bottom=客户区高度 // 注意:这个矩形的坐标是相对于窗口客户区的,所以左上角总是(0,0) } // 获取窗口区域 int regionType = GetWindowRgn(hwnd, hRgn); if (regionType != ERROR) { // 成功获取区域,hRgn现在包含了窗口的区域 // 可以根据regionType判断区域类型 } // 使用完毕后,不要忘记删除区域对象 DeleteObject(hRgn); } ``` 注意事项: - 屏幕坐标客户区坐标的转换:可以使用`ScreenToClient``ClientToScreen`函数进行坐标转换。 - 窗口区域(region)是一个GDI对象,使用完后需要调用`DeleteObject`来释放。 - 如果窗口没有设置区域(即默认的矩形窗口),那么`GetWindowRgn`返回的区域可能是一个覆盖整个窗口的矩形区域,或者函数可能返回NULLREGION(表示没有区域,但通常窗口默认有一个矩形区域,所以返回SIMPLEREGION)。 希望这些信息能帮助用户理解这三个函数的使用方法。 §§相关问题§§ 1. 如何将屏幕坐标转换为客户区坐标? 2. 如何使用SetWindowRgn设置窗口形状? 3. 如何获取窗口的尺寸(包括边框标题栏)?</think>### Windows API: GetWindowRect, GetClientRect, GetWindowRgn 使用方法 在Windows编程中,获取窗口几何信息是常见需求。下面详细说明三个关键API的使用方法: #### 1. **GetWindowRect - 获取窗口矩形** ```c BOOL GetWindowRect( HWND hWnd, // 目标窗口句柄 LPRECT lpRect // 接收矩形数据的结构体指针 ); ``` **功能**:获取窗口在**屏幕坐标**中的位置尺寸(包括标题栏、边框等非客户区)[^1] **使用示例**: ```c RECT rect; if (GetWindowRect(hWnd, &rect)) { int width = rect.right - rect.left; // 窗口总宽度 int height = rect.bottom - rect.top; // 窗口总高度 int left = rect.left; // 屏幕左边界坐标 int top = rect.top; // 屏幕上边界坐标 } ``` #### 2. **GetClientRect - 获取客户区矩形** ```c BOOL GetClientRect( HWND hWnd, // 目标窗口句柄 LPRECT lpRect // 接收矩形数据的结构体指针 ); ``` **功能**:获取窗口**客户区**尺寸(仅包含可绘制区域,不包括标题栏/边框)[^2] **坐标特点**:左上角始终为`(0,0)`,右下角为`(width,height)` **使用示例**: ```c RECT clientRect; if (GetClientRect(hWnd, &clientRect)) { int clientWidth = clientRect.right; // 客户区宽度 int clientHeight = clientRect.bottom; // 客户区高度 } ``` #### 3. **GetWindowRgn - 获取窗口区域** ```c int GetWindowRgn( HWND hWnd, // 目标窗口句柄 HRGN hrgn // 预创建的区域句柄 ); ``` **功能**:获取窗口的自定义形状区域(用于非矩形窗口)[^3] **返回值类型**: - `NULLREGION`:空区域 - `SIMPLEREGION`:单个矩形区域 - `COMPLEXREGION`:复杂形状区域 - `ERROR`:操作失败 **使用步骤**: ```c // 1. 创建空区域 HRGN hRgn = CreateRectRgn(0, 0, 0, 0); // 2. 获取窗口区域 int regionType = GetWindowRgn(hWnd, hRgn); // 3. 判断区域类型并处理 if (regionType == COMPLEXREGION) { // 处理复杂形状(如圆形/多边形窗口) POINT ptTest = {50, 50}; if (PtInRegion(hRgn, ptTest.x, ptTest.y)) { // 判断点是否在区域内 } } // 4. 释放资源 DeleteObject(hRgn); ``` #### 坐标转换技巧 ```c // 屏幕坐标 → 客户区坐标 POINT screenPt = {100, 100}; ScreenToClient(hWnd, &screenPt); // 转换后screenPt变为客户区坐标 // 客户区坐标 → 屏幕坐标 POINT clientPt = {50, 50}; ClientToScreen(hWnd, &clientPt); // 转换后clientPt变为屏幕坐标 ``` > **关键区别**: > - `GetWindowRect`:返回**屏幕坐标系**中的绝对位置 > - `GetClientRect`:返回**窗口客户区坐标系**中的相对尺寸 > - `GetWindowRgn`:获取窗口的**非矩形形状**信息(需配合区域函数使用)[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值