当前位置:C++技术网 > 精选软件 > Windows零基础入门:2.22 窗口类之窗口类风格CS_PARENTDC

Windows零基础入门:2.22 窗口类之窗口类风格CS_PARENTDC

更新时间:2015-09-18 22:24:33浏览次数:1+次

    前两节课《Windows零基础入门:2.20 窗口类结构体之窗口类绘图风格CS_OWNDC》和《Windows零基础入门:2.21 窗口类结构体之窗口类风格CS_CLASSDC》我们分别讲了CS_OWNDC和CS_CLASSDC两种类风格。前者的窗口独有DC由窗口私有的,后者窗口类DC由窗口类创建的所有窗口共同拥有。
    那么今天要讲的绘图窗口类CS_PARENTDC,和前面两个都不一样。我在网上查阅了一些文章,说是什么这个DC是窗口和子窗口共同拥有。但是通过仔细的代码验证,事实上并非如此,而且MSDN上也不是这样解释的。一切通过实践来验证。这节内容花了大量的时间做调查和验证,所以,更新出来比较晚,请谅解。不过也是为了不能够误导,必须一个个弄清楚才行。
    MSDN解释说,CS_PARENTDC窗口类风格会设置子窗口的剪切矩形到父窗口上,这样子窗口就可以在父窗口上绘图了。有CS_PARENTDC窗口类风格的窗口,它是从系统的DC缓存中获取到普通的DC的,而不是得到父窗口的DC。指定这种窗口类风格可以提高应用程序的性能。
    而网上一些文章说的子窗口和父窗口共享一个DC,就有问题了。咱谁都不信,用程序来验证。不过,我验证过了,就先把这个窗口类风格先说明白,后面在用代码验证即可,免得分散注意力了。
    通过MSDN的解释,我们大概知道一点,就是子窗口可以在父窗口上画画。这是窗口部分的知识,父子窗口是如何互动的,这些在后面课程会详细讲。这里就重点来看看这个窗口类风格。前面两个窗口类风格只是共享DC,窗口之间是不干涉“内政”的,指的是绘画不会画到别人的窗口上,都是限于自己的客户区的范围。但是在多窗口的时候,有时候需要进行干涉,比如,要在子窗口中输入一句话,然后显示在主窗口中,这个就涉及到这个问题了。
    刚看到这个CS_PARENTDC窗口类风格的时候,我不知道它应该放在哪里,是子窗口的窗口类还是父窗口的窗口类,还是空气中。还有,它是要用在一个窗口类创建的父窗口和子窗口中还是可以是多个窗口类创建的父子窗口中。那么我们来探讨这些疑问。
    为了提供干涉“内政”的解决方式,才提供了这个窗口类风格,至于内部如何实现,不是我们现在来研究的事情。既然它是窗口类的一个风格,那么必然这个风格是要作用于由着窗口类创建的窗口中。而这个作用又巧妙的通过父窗口句柄来关联。父窗口创建时,并没有子窗口的存在。而要建立父子关系,这个要由子窗口认“干爹”才行。那么就是在创建子窗口的时候,调用CreateWindow函数指定了父窗口,这样就确立了父子关系。那么这个窗口类风格CS_PARENTDC也就可以发挥作用了。所以,这个窗口类风格需要放在创建子窗口的窗口类中。而对于父窗口,不做任何要求。父窗口也可以是用创建子窗口的窗口类创建的,也可以不是。也就是说,父子窗口可以是同一个窗口类创建的,也可以不是同一个窗口类创建的。但是你要保证,子窗口的窗口类必须要有这个窗口类风格,并且创建子窗口时要指定父窗口,这样就可以将这个窗口类风格的作用关联到父窗口中,才可以实现子窗口在父窗口上作画。至于前面提到的剪切矩形这个暂时不用管。后面的课程会有介绍。
    CS_PARENTDC产生的DC,只是一个普通的DC,就和临时创建的DC一样,并不和谁共享,创建时DC的状态就是空白的默认值,与父窗口的DC没有任何关系。至于MSDN说的提高性能,应该是指这个DC是在系统的DC缓冲区里分配的,不需要全新创建,是已经创建好的。所以自然也会快,也节省内存。
    而子窗口在父窗口作画,具体如何,这个要在后面详细讲。我们来演示代码,来验证上面的理论。为了验证两个窗口,我们来创建和注册两个窗口类,然后创建两个窗口,然后将第二个窗口作为第一个窗口的子窗口。那么也就有两个窗口过程。我们只在第二个作为子窗口的窗口类中添加窗口类风格CS_PARENTDC。
    为了验证父子窗口并不共享一个DC,我在父子窗口的窗口类中都适用CS_PARENTDC,然后在主窗口绘制一串文本,然后修改文字的颜色为蓝色,如果父子窗口共享父窗口的DC,那么子窗口输出的文字,必然就是蓝色的。这个在前面两节中已经验证过,是有改变的。
    然后在创建子窗口时,指定窗口的左上角坐标为(100,100),就是CreateWindow的第四和第五个参数,然后指定窗口的宽为400,高为300,就是第六和第七个参数指定的。在子窗口的绘图消息WM_PAINT中划线,让线段的超出子窗口的范围。如果不能在父窗口上面作画,那么超出的线就不会画出来。
    那么我观察,如果子窗口文字为蓝色,说明子窗口共享父窗口DC,如果为默认黑色,那么就不共享。如果线条超越了子窗口画到了父窗口那就表示这个窗口类风格确实可以让子窗口在父窗口上作画。否则就不能在父窗口上作画。

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc2(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, PSTR szCmdLine, int iCmdShow)
{
    HWND hPa,hChild;
    MSG  msg;
    // - 第一个窗口的窗口类
    WNDCLASS wndclass;
    wndclass.style =  CS_VREDRAW | CS_HREDRAW | CS_PARENTDC;
    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 = L"C1";

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, L"注册窗口类失败,此程序需要运行在Windows NT平台下。", L"C1", MB_ICONERROR);
        return 0;
    }
    // - 第二个窗口的窗口类
    WNDCLASS wndclass2;
    wndclass2.style = CS_VREDRAW | CS_HREDRAW | CS_PARENTDC;
    wndclass2.lpfnWndProc = WndProc2;
    wndclass2.cbClsExtra = 0;
    wndclass2.cbWndExtra = 0;
    wndclass2.hInstance = hInstance;
    wndclass2.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass2.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass2.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass2.lpszMenuName = NULL;
    wndclass2.lpszClassName = L"C2";

    if (!RegisterClass(&wndclass2))
    {
        MessageBox(NULL, L"注册窗口类失败,此程序需要运行在Windows NT平台下。", L"C2", MB_ICONERROR);
        return 0;
    }

    hPa = CreateWindow(L"C1", L"主窗口", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    ShowWindow(hPa, iCmdShow);
    UpdateWindow(hPa);
    
    hChild = CreateWindow(L"C2", L"子窗口",  WS_CHILD | WS_OVERLAPPEDWINDOW ,100, 100, 400, 300, hPa,NULL, hInstance, NULL);
    ShowWindow(hChild, iCmdShow);
    UpdateWindow(hChild);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        GetClientRect(hwnd, &rect);
        DrawText(hdc, L"主窗口", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        SetTextColor(hdc, RGB(0, 0, 255));// - 设置
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc2(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    HWND hPa;
    RECT rect;
    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        LineTo(hdc, 100, 200);// - 画第一根线
        LineTo(hdc, 600, 300);// - 画第二根线
        GetClientRect(hwnd, &rect);
        DrawText(hdc, L"主窗口", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 
        EndPaint(hwnd, &ps);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}


  运行的截图如下:

    子窗口在父窗口上作画

  运行结果如图所示,子窗口的文字为黑色,线条从子窗口客户区的左上角穿越了子窗口,一直画到了父窗口上。这个窗口类风格确实是能够实现这个效果的。如果你将父窗口的CS_PARENTDC去掉,结果依然如此。
    但是如果你将子窗口的这个风格去掉,不管父窗口有没有这个风格,这个效果也不起作用。当然,如果你将子窗口的父窗口参数设置为NULL,且将CreateWindow的风格中的WS_CHILD窗口风格去掉,那么,就是两个独立的窗口,这样更不会有这个效果,而且都没有父子关系了。都不知道啥关系,更谈不上能在父窗口上作画。
    请将代码运行一下看看效果,然后自己改动一些试试看,这样学习的效果会更好。

    最后说一下,这个窗口类风格的名字意思是父窗口DC,并不是只共享父窗口DC,而是使子窗口的DC的效果达到和拥有哦父窗口DC差不多的样子,可以在父窗口上作画而已。表面上很像是共享父窗口DC,窗口类风格的名字也很像,但是实际上并不是如此,请注意哈。