Win32解决透明字体改变时重叠的问题,GetClientRect与GetWindowRect、ClientToScreen、ScreenToClient的使用

⌚Time: 2023-01-18 23:50:40

👨‍💻Author: Jack Ge

透明字体,改变时发生文本重叠,解决办法是刷新窗体局部区域,该区域是文本或者按钮等控件的区域

Win32 API中使用InvalidateRect函数使指定区域失效,意味着要刷新该区域,再用UpdateWindow函数强迫窗体立即刷新


RECT rc;

...

InvalidateRect(hWnd,&rc,true);

UpdateWindow(hWnd)

以100x30大小的静态文本控件为例,如何获得文本控件的区域?

GetClientRect和GetWindowRect两个函数可以获取控件区域

GetClientRect

检索窗口工作区的坐标。 客户端坐标指定工作区的左上角和右下角。 由于客户端坐标相对于窗口工作区的左上角,左上角的坐标 (0,0) 。


cBOOL GetClientRect(

  [in]  HWND   hWnd,

  [out] LPRECT lpRect

);

getWindowRect

检索指定窗口的边界矩形的尺寸。 尺寸以相对于屏幕左上角的屏幕坐标提供。


BOOL GetWindowRect(

  [in]  HWND   hWnd,

  [out] LPRECT lpRect

);

两个函数得到尺寸信息储存在RECT结构体中

RECT结构体


typedef struct tagRECT {

  LONG left;

  LONG top;

  LONG right;

  LONG bottom;

} RECT, *PRECT, *NPRECT, *LPRECT;

left



指定矩形左上角的 x 坐标。



top



指定矩形左上角的 y 坐标。



right



指定矩形右下角的 x 坐标。



bottom



指定矩形右下角的 y 坐标。

使用GetClientRect获取到的只是控件的尺寸,它的左上角坐标永远是(0,0),而右下角坐标为(100,30),如果使用此区域刷新窗体,那么会刷新父窗体上(0,0,100,30)的区域,显然是不能刷新到控件的位置

而使用GetWindowRect得到的是控件区域相对于屏幕的位置(700,300,100,30),使用此区域刷新父窗体也是错误的区域

有两个函数:ClientToScreen函数和ScreenToClient 函数

ClientToScreen 函数将指定点的工作区坐标转换为屏幕坐标。


BOOL ClientToScreen(

  [in]      HWND    hWnd,

  [in, out] LPPOINT lpPoint

);

ScreenToClient 函数将屏幕上指定点的屏幕坐标转换为工作区坐标。


BOOL ScreenToClient(

  [in] HWND    hWnd,

       LPPOINT lpPoint

);

通过ScreenToClient可以将屏幕位置上的点转换为相对于客户区位置的点,首先通过GetWindowRect函数获取控件区域左上角相对屏幕坐标,之后转化为相对于客户区的坐标就可以直到控件在客户区的位置。


    RECT rc;

    //获取控件在屏幕中位置

    GetWindowRect(GetDlgItem(hWnd,controlID),&rc);

    //将左上坐标转化成点

    POINT   p;

    p.x = rc.left;

    p.y = rc.top;

    //将点坐标在屏幕位置转化成在客户区位置

    ScreenToClient(hWnd,&p);

而控件的宽度可以通过GetClientRect函数获取,RECT的right - left和bottom - top就是控件的宽和高,


    GetClientRect(GetDlgItem(hWnd,controlID),&rc);

因此控件相对于客户区的区域就可以确定了

左上角:(p.x,p.y)

右下角(左上角坐标加控件的宽和高):(p.x + rc.right - rc.left,p.y + rc.bottom - rc.top)

之后在父窗体中刷新此区域就可以刷新到控件的实际位置




    rc.right = p.x + rc.right - rc.left;

    rc.left = p.x;

    rc.bottom = p.y + rc.bottom - rc.top;

    rc.top = p.y;

    

实际代码


void refresh_control(HWND hWnd,unsigned int controlID){
    RECT rc;
    //获取控件在屏幕中位置
    GetWindowRect(GetDlgItem(hWnd,controlID),&rc);
    //将左上坐标转化成点
    POINT   p;
    p.x = rc.left;
    p.y = rc.top;
    //将点坐标在屏幕位置转化成在客户区位置
    ScreenToClient(hWnd,&p);
    //获取控件长宽
    GetClientRect(GetDlgItem(hWnd,controlID),&rc);
    //获取控件区域在客户区的相对位置
    rc.right = p.x + rc.right - rc.left;
    rc.left = p.x;
    rc.bottom = p.y + rc.bottom - rc.top;
    rc.top = p.y;
    //刷新客户区控件位置
    InvalidateRect(hWnd,&rc,true);
    UpdateWindow(hWnd);
}