澳门金莎娱乐网站-金沙最新娱乐网址

澳门金莎娱乐网站是一家上市十年之久的老品牌娱乐网站,金沙最新娱乐网址为玩家带来丰厚的利益的良好的服务,澳门金莎娱乐网站通过多年的摸索和总结,创建了一个充满激情和欢乐的游戏平台,精挑细选数百种经典老虎机游戏,选。

转载几篇别人写的皮肤类控件的技术文章,文本

回答:

    return 0;

HDC cDC =...;//子窗口的画布句柄

  • 通过WM_PAINT消息获取DC

问题:如何用易语言自绘gdi?

 

 

Windows下要绘图和输出文本,都是通过GDI(Graphics Device Interface,图形设备接口)完成的,GDI是windows在绘制图文时的设备上下文环境,包括画笔、画刷、字体、位图等多种与绘制有关的对象。设备环境(DC)在绘制中起至关重要的作用。几乎所有的绘制(包括图形和文本)都与设备环境相关,注意“环境”的意义,就跟我们在画布上绘画和写字一样,绘制时的画布是哪个,用的什么笔,什么颜色,填充整个画布时用的什么刷子等等,这就是我们的绘制时的环境,而Windows绘图的DC设备上下文就是一样的道理。设备环境句柄(HDC)就是用来描述DC的句柄,可以说,只要有了这个句柄,就具备了在窗口上输出图形和文本的条件。你获得了窗口客户区的HDC,就可以在窗口客户区上画;你获得了窗口的非客户区HDC,就可以在它上面画;你获得了桌面HDC,就可以直接在桌面上画……

以下两种自绘都可使用GDI或是GDI 完成1.控件自绘通过API 在子类化中截获WM_PAINT消息, BeginPaint()开始控件自绘,以EndPaint()结束其位图设备为各控件和窗口,所以通过此方法绘制的窗口存在控件句柄。优点:不需要手动描述控件消息,只需要关注控件的绘制缺点:窗口无法拥有不同透明度2.完全自绘窗口具有WS_EX_LAYERED属性,WM_PAINT消息失效,只能通过API UpdateLayeredWindow进行窗口更新,所有控件均由自绘完成,包括自绘组件的消息也需要手动完成。优点:窗口可拥有不同透明度,这是控件自绘所达不到的缺点:所有控件均由自绘完成,包括自绘组件的消息也需要手动完成。需要了解WINDOWS消息机制,并进行自绘组件的消息模拟。窗口上的普通空间将无法显示,不过可通过获取WM_DRAWITEM 和WM_PAINT消息进行绘制,好处就是空间的所有事件都课使用,不需要自创模拟。

{

//此处可以调用SetClipRect()等函数来限制绘制范围

关注微信公众平台:程序员互动联盟(coder_online),你可以第一时间获取原创技术文章,和(java/C/C /Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序员互动联盟,开发人员自己的家。

}

HBITMAPpBitmap = ...;父窗口的背景位图

获取设备环境句柄的方法有两种:一是处理WM_PAINT消息时,通过BeginPaint函数返回。另外一种就是通过GetDC、GetWindowDC的API函数获取。

上面这些步骤是我个人的理解,不一定对哦!仅供参考。

 

  • 创建特定字体

......

 

 

 

其二: 如果程序不支持桌面主题服务,则不能使用上面的方法,比如程序运行在Windows2000上。这时我们可以向父窗口发送WM_PAINT消息,不过此消息所附带的wParam参数是一个画布句柄:

我们还可以通过GetDC、GetWindowDC函数来获取HDC,但是要注意,通过这个来获取的HDC,可以保存起来在其它时候使用,但是要记住一旦窗口有更新,必须想办法重新绘制,否则就会消失了。最后在使用完毕后需要调用ReleaseDC来释放,否则会造成资源泄露。

    LRESULT lReturn = ::CallWindowProc(gOldListViewProc, hListView, WM_WINDOWPOSCHANGED,

//-------------------父窗口的背景是位图的情况

有了上面的基础,我们就可以通过Windows的API来完成文本输出了,常用的文本输出函数有TextOut、DrawText、DrawTextExt、ExtTextOut等,这些函数基本都有相似的参数,比如hdc,坐标位置,字符串。下面TextOut、DrawText、ExtTextOut为例来说明如何在Windows窗口中如何输出文本,其它请查看MSDN的用法。

LRESULT CALLBACK MyListViewProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

上面的办法当然有限制,因为不是所有的父窗口都可以接受这种特殊的WM_PAINT消息功能,不过MSDN中提到大多数控件都有这个功能,大家要注意读它的文档内容。

  • GDI基本知识

    }

HDC cDC =...;//子窗口的画布句柄

我们可以在该消息中完成图文绘制,该消息的处理具有特定的格式,必须在实际绘制前调用BeginPaint,在绘制完成后调用EndPaint函数,也就是说我们需要把所有绘制的功能都放到这两个函数之间,并且HDC也只能在这之间使用,不能保存起来在其它地方使用。使用WM_PAINT有一个好处,就是windows会自己计算哪些区域需要更新,也就是说只有真正变化的地方才会更新,这样更新的代价会降低到最小。

}

这个函数就是专门用来绘制父窗口的背景的,其中的hwnd参数是子窗口的句柄,hdc也时子窗口的画布句柄,prc是子窗口需绘制的区域,这个函数实际是把父窗口的背景拷贝到子窗口上来,以这种方法达到透明。

 

我建议大家去微软的网站下载ControlSpy 2.0这个小工具,它用来监视控件的消息,这东西很有用。

其三: 如果上面的办法都不行的话,就剩下最笨的办法了,用GDI函数涂刷子窗口的背景,但你事先就要知道父窗口的背景颜色、背景位图等信息。比如拿父窗口的颜色来填充子窗口的背景,可以调用FillRect()等:

另外ExtTextOut函数还可以设置字符的间距,运行结果的第三行就是这种自己设置间距不一样的结果。

对于Windows系统中各种控件换肤功能,要数滚动条的换肤最难实现了,尤其是控件自带的系统滚动条,如Edit、ListBox、ListView、TreeView等自带的系统滚动条,要想实现其自定义的皮肤功能,用常规办法似乎都无法实现。

DeleteObject(brush);

澳门金莎娱乐网站 1

case WM_LBUTTONDOWN:

RECTcRect = ...;//子窗口需刷新的区域 

本程序还用CreateFont函数创建了一个斜体、右上排列的文本串。通过上例,我们把常用的文本输出作为实例展示给大家,只要好好对照实例代码,在结合MSDN的说明,再加上本系列的第一篇的Windows编程基本框架,一定可以掌握好Windows编程的基本文本输出。

    }

GetClientRect(cWnd,&cRect);

程序中的DrawText、ExtTextOut能设置文本输出的矩形范围,超出部分是看不见的,从运行结果我们也可以看出有两行显示不全,就是由于设置的显示范围小的缘故。

    return lReturn;

SetBurshOrgEx(cDC, x, y,&rp);如果是位图刷子,则还需要调整画布的刷子原点偏移确保无缝

  • 实现文本绘制

    HRGN wRgn = NULL; 

SetBurshOrgEx(cDC, rp.x, rp.y,NULL);还原画布的刷子偏移

上一篇我们展示了如何使用Windows SDK创建基本控件,本篇来讨论如何输出文本字符。

if(msg == WM_NCPAINT)

//-------------------父窗口的背景是颜色的情况

  1. 用户移动一个窗口,导致原来被盖住的部分窗口显示出来。
  2. 用户调整窗口的大小,并且窗口风格类型设置为CS_HREDRAW和CS_VREDRAW。
  3. 程序调用ScrollWindow或者ScrollDC函数滚动客户区。
  4. 程序调用InvalidateRect或者InvalidateRgn函数,该函数显示生产一条WM_PAINT消息。

知道了这些步骤,大概我们心中已经了然,知道如何定制控件的背景颜色和背景位图了。一般情况下我们定制第一步、第三步实现自己的特殊背景,定制第四部实现控件本身的特殊绘制。甚至我们可以整个重载控件第一步的WM_ERASEBKGND消息和第二步的WM_PAINT消息,控件背景和控件绘制全部自己搞定,没人说这样做不行。不过要注意,当自己实现WM_PAINT消息的重载处理时,后面3个步骤就都不发生了。

RECTcRect = ...;//子窗口需刷新的区域 

  • 通过API函数获取HDC

    if(::GetNextWindow(hBuddy, GW_HWNDNEXT) != hListView)

 

澳门金莎娱乐网站 2

}

HBRUSHbrush = CreatePatternBrush(pBitmap);

转载请注明出处,谢谢合作!

    //UnhookWindowsHookEx(...);

COLORREFpColor = ...;//父窗口的颜色

我们平时最常见的文本输出是不需要自己创建字体的,因为常见的对象都有系统预定义好的。如果想输出点特殊(非系统预定义的)字体,就需要我们创建并自动选入设备环境。创建字体主要有CreateFont和CreateFontIndirect,这两个函数的参数都很多,基本一样,具体用法看后面的实例。

        ::GetWindowRect(hListView, &wRect);

DeleteObject(bitmap);

#include <windows.h>
#include <tchar.h>

static TCHAR szAppName[] = TEXT("Textout");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
     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,            // window class name
                          szAppName,           // window caption
                          WS_OVERLAPPEDWINDOW, // window style
                          CW_USEDEFAULT,       // initial x position
                          CW_USEDEFAULT,       // initial y position
                          400,                 // initial x size
                          300,                 // initial y size
                          NULL,                // parent window handle
                          NULL,                // window menu handle
                          hInstance,           // program instance handle
                          NULL);               // creation parameters

     ShowWindow(hWnd, iCmdShow);
     UpdateWindow(hWnd);

     while (GetMessage(&msg, NULL, 0, 0))
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }

     return msg.wParam;
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hDC;
    PAINTSTRUCT ps;

    switch (message)
    {
    case WM_CREATE:
        return 0;
    case WM_PAINT:
        {
            RECT rect = {10, 30, 100, 50};
            TCHAR str[] = TEXT("English and 中文");

            hDC = BeginPaint(hWnd, &ps);
            TextOut(hDC, 10, 10, str, _tcslen(str));

            SetTextColor(hDC, RGB(255,0,0));
            DrawText(hDC, str, -1, &rect, DT_LEFT|DT_VCENTER);

            SetTextColor(hDC, RGB(0,255,0));
            INT dx[] = {8,8,8,8,16,8,8,8,16,8,8,8,10};
            ExtTextOut(hDC, 10, 50, 0, &rect, str, _tcslen(str), dx);

            SetTextColor(hDC, RGB(0,0,255));
            rect.right = 110;
            rect.top = 70;
            rect.bottom = 82;
            ExtTextOut(hDC, 10, rect.top, ETO_CLIPPED, &rect, str, _tcslen(str), dx);
            HFONT hFont = CreateFont(96,         // nHeight, 所创建字体的字符高度
                        0,           // nWidth,       字体的字符平均宽度
                        200,          // nEscapement,  字符输出方向与水平向右的方向所成角度,以0.1度为单位
                        0,             // nOrientation, 字符与基线的角度,以0.1度为单位
                        FW_BOLD,        // nWeight,      字符颜色的深浅度
                        TRUE,            // bItalic,      斜体属性标志(FALSE:正常字体,TRUE:斜体)
                        FALSE,            // bUnderline,   下划线属性标志(FALSE:无下划线,TRUE:有下划线)
                        FALSE,             // cStrikeOut,   删除线属性标志(FALSE:无删除线,TRUE:有删除线)
                        ANSI_CHARSET,       // nCharSet,        字符集标识0:ANSI字符集,1:系统缺省字符集
                        OUT_DEFAULT_PRECIS,  // nOutPrecision,   输出精度
                        CLIP_DEFAULT_PRECIS, // nClipPrecision,  剪切精度
                        DEFAULT_QUALITY,      // nQuality,        输出品质
                        DEFAULT_PITCH|FF_SWISS, // nPitchAndFamily, 字符间距
                        TEXT("Arial"));          // lpszFacename,    现有系统TrueType字体名称
            HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);
            SetBkMode(hDC, TRANSPARENT);
            SetTextColor(hDC, RGB(0x00, 0xFF, 0xFF));
            TextOut(hDC, 0, 150, TEXT("创建Font"), 6);
            DeleteObject(hFont);
            EndPaint(hWnd, &ps);
        }
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0 ;
    }
    return DefWindowProc (hWnd, message, wParam, lParam);
}

2  定制控件的背景颜色和背景位图的技巧。

HGDIOBJoldBitmap = SelectObject(memDC, bitmap);

Windows在检测到需要重新绘制或者刷新窗口时,会主动要求处理WM_PAINT消息。比如在如下情况下就会主动求处理:

Buddy_Window是你注册的模拟窗口类。注意,窗口一定要有WS_DISABLED风格,这是个关键,这个风格能够确保鼠标操作能够透过模拟窗口,而直接操控到所覆盖的滚动条,模拟窗口本身不接受任何鼠标键盘的输入。另外读取滚动条的矩形以及它的各个元素的可视、可用、压下等状态可通过GetScrollBarInfo()这个API来完成,不过要说明一点,这个API有些Bug,大家可去下载FreeCL 2.03版源码,里头修正了这个问题。

转载几篇别人写的皮肤类控件的技术文章

程序运行,点击鼠标左键后效果如下:

1 实现系统滚动条换肤功能

SendMessage(pWnd, WM_PAINT, (WPARAM)memDC,0);

在使用Win32编程时,我们常常要输出文本到窗口上,Windows所有的文本字符或者图形输出都是通过图形设备接口(GDI)进行的,Windows的三大核心组件之一的GDI32.dll封装了所有的文本和图像输出。

if(msg == WM_HSCROLL)

揭秘换肤技术:实现系统滚动条换肤功能

对于Windows系统中各种控件换肤功能,要数滚动条的换肤最难实现了,尤其是控件自带的系统滚动条,如Edit、ListBox、ListView、TreeView等自带的系统滚动条,要想实现其自定义的皮肤功能,用常规办法似乎都无法实现。

对于常规的皮肤定制一般都是通过定制WM_PAINT、WM_ERASEBKGND、 WM_CTLCOLORxxx、NM_CUSTOMDRAW来实现。然而系统滚动条的绘制,常规的、很阳光的方法行不通,微软把一条康庄大道堵死了。根据 我的观察测试,系统滚动条有许多的消息都对其执行了绘制,这包括WM_NCPAINT、WM_NCMOUSEMOVE、WM_NCMOUSELEAVE、 WM_HSCROLL、WM_VSCROLL、WM_KEYDOWN、WM_MOUSEWHEEL等等,这些消息中有些可以定制,但有些没法定制。比如 WM_HSCROLL就无法定制,如果我们处理这个消息,就必须自己控制滚动条的范围、位置、翻页值,而这些值我们通常无法得到,微软根本没有告诉我们, 对于不同的控件它操控滚动条到底采用何种策略,不晓得。假若我们不去定制这个WM_HSCROLL,而默认的处理它又执行滚动条的绘制,这真是进退无路, 叫人束手无策。当然不是完全没有办法,死路一条。为了克服障碍,翻越障碍,每个人有不同的策略,有攀岩翻越的,安全性不高;也有走小路绕行的,比较费力。

在网上反复搜索,自己也仔细研究,基本有两种办法来实现系统滚动条换肤,一种方法是HOOKAPI,也就是拦截API的办法,还有一种是模拟法。

拦截API,实际上是修改操作系统的API入口。因为系统绘制滚动条是通过各种绘制函数来实 现的,比如SetScrollInfo(),基本系统通过这一类函数实现滚动条的绘制,对这个API进行拦截,也就是我们自己写一个 SetScrollInfo(),来亲自实现滚动条的绘制,以便由此实现滚动条的自定义绘制,实现我们想要的各种风格的皮肤外观。API的拦截有两种:一 种是修改系统所装入内存可执行模块的导入地址,替换成我们所写的伪API的地址,使API调用能够自动跳转到这个伪API上;还有一种是直接修改API函 数首地址处的若干机器指令,保存现场,写入跳转指令,使系统在执行到这个API时能自动跳转到我们所写的伪API上。我要说的是,这个拦截办法还真是有些 邪门,有安全隐患,病毒就常干这种事情。对操作系统进行这类暴力破解式的修改,很容易引起系统防火墙或反病毒软件报错,Windows原则上不允许干这种 事;其二,一旦使用此法的程序因异常而崩溃,原本对操作系统的修改没有还原,这可能会伤害到系统里的所有进程的稳定性,导致死机或运行异常。对于商业性的 工程开发,往往很忌讳这一点,都倾向于追求稳定,通常是宁用拙法,不玩巧技;其三,此法有线程同步问题:因为正当你修改程序指令同时,若其他进程里的线程 恰好执行到这里时候问题就会爆发出来,单CPU可能没啥问题,系统内存和CPU缓冲可以做到同步,但多核系统就难说了;另外这个方法还有移植性问题:在一 种版本的系统中,通过拦截API有效,在另一种版本的系统中,就不见得还有效,毕竟微软实现滚动条的绘制,它没规定一定就用哪个函数来实现,也许新系统中 它另有高招,API拦截也就不灵了。

下面我要详细讲的是模拟法了,这个办法不用拦截API,所有的技术实现都约束在系统所允许的范围内,咱一丝不苟的当遵纪守法的良民。

所谓模拟法就是在系统滚动条的区域放置一个模拟窗口,这个窗口专门用于绘制系统滚动条。当然我们也要拦截带滚动条控件的若干消息,以确保模拟窗口的绘制正确。下面只以ListView控件的水平滚动条为例,进行说明,垂直滚动条换肤可以列推。

澳门金莎娱乐网站 3

首先我们要在滚动条的区域上创建一个模拟窗口,恰好覆盖滚动条:

HWND hListView =...;//ListView窗口的句柄

HWND pWnd = ::GetParent(hListView);

HWND hBuddy =::CreateWindowEx(WS_EX_NOACTIVATE, "Buddy_Window", "",WS_CLIPSIBLINGS|WS_DISABLED|WS_CHILD, 0, 0, 0, 0, pWnd, NULL,gModule, NULL);

Buddy_Window是你注册的模拟窗口类。注意,窗口一定要有WS_DISABLED 风格,这是个关键,这个风格能够确保鼠标操作能够透过模拟窗口,而直接操控到所覆盖的滚动条,模拟窗口本身不接受任何鼠标键盘的输入。另外读取滚动条的矩 形以及它的各个元素的可视、可用、压下等状态可通过GetScrollBarInfo()这个API来完成,不过要说明一点,这个API有些Bug,大家 可去下载FreeCL2.03 版源码,里头修正了这个问题。

完成创建模拟窗口之后,你要给ListView安装一个你写的窗口过程,以拦截各种导致滚动条属性改变的种种消息:

LRESULT CALLBACK MyListViewProc(HWNDhwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

澳门金莎娱乐网站,case WM_LBUTTONDOWN:

    ......

case WM_LBUTTONDBLCLK:

    ......

case WM_NCMOUSEMOVE:

    ......

case WM_NCMOUSELEAVE:

   ...... 

}

......

gOldListViewProc =(WNNPROC)::GetWindowLong(hListView, GWL_WNDPROC);

::SetWindowLong(hListView, GWL_WNDPROC,LONG(&MyListViewProc));//安装窗口过程

 

消息处理 :

首先我们要拦截WM_NCLBUTTONDOWN 和WM_NCLBUTTONDBLCLK这两个消息,当你在滚动条上按下鼠标时,就立刻触发WM_NCLBUTTONDOWN,如果连续快速按两下鼠标就 还有WM_NCLBUTTONDBLCLK。注意双击时只有一个WM_NCLBUTTONDOWN消息,而不是两个。第二次按鼠标会出现一个 WM_NCLBUTTONDBLCLK。实际上这两个消息我们完全可以等而视之,做相同的处理。系统在处理这个两个消息时,都会在其内部处理中触发许多其 他消息,其中有若干个WM_HSCROLL、WM_CAPTURECHANGED、WM_NCMOUSELEAVE。我们主要是要处理 WM_HSCROLL,因为它最有价值。在你松开鼠标后,系统发送并处理完最后一个WM_HSCROLL之后,这才从WM_NCLBUTTONDOWN或 WM_NCLBUTTONDBLCLK中返回:

if(msg == WM_NCLBUTTONDOWN || msg== WM_NCLBUTTONDBLCLK)

{

   //注意默认处理会有N个WM_HSCROLL消息出现,要等你的按下拖拽操作完成后,这个调用才返回

   //在这个调用内部,我估计系统会进入一种消息循环,因为按住左键之后,WM_NCMOUSEMOVE

    //和WM_NCLBUTTONUP都不再触发了。其内部估计是捕捉了WM_NCMOUSEMOVE消息,因之反复刷新滚动

   //盒的位置,若有必要你可安装鼠标钩子,以捕捉鼠标移动消息,以及时刷新模拟窗口中滚动盒的

   //的位置。若只是响应WM_HSCROLL消息,你可能觉得滚动盒的拖拽比较滞,不平滑。

 

   //SetWindowsHookEx(...);

    LRESUTL code= ::CallWindowProc(gOldListViewProc, hListView, msg, wParam,lParam);

    //UnhookWindowsHookEx(...);

 

    returncode;

}

 

我建议大家去微软的网站下载ControlSpy2.0 这个小工具,它用来监视控件的消息,这东西很有用。

下面我们再看WM_HSCROLL消息,这个消息一般是系统处理WM_NCLBUTTONDOWN或者WM_NCLBUTTONDBLCLK时产生的,但有时也可能是程序刻意发送的,和滚动条操作没有关系。

if(msg == WM_HSCROLL)

   ::CallWindowProc(gOldListViewProc, hListView, msg, wParam,lParam);

   //1)读取滚动条的数据

   //2)再在模拟窗口上绘制滚动条

    //......

    return0;

}

另外还有许多其他的消息可能导致滚动条属性的变化,如用户的键盘操作、鼠标滚轮操作可能导致 滚动条的Thumb位置发生改变,这些操作分别导致WM_KEYDOWN和WM_MOUSEWHEEL,而系统又在这两个消息中执行滚动条的绘制,因此你 必须截获它们,但处理方法同WM_HSCROLL,这里不再啰嗦了。

当我们没有按下鼠标左键时,只是简单地在滚动条移动鼠标时,我们会发现滚动条的按钮和 Thumb都会自动高亮,其实这些变化都是系统在WM_NCMOUSEMOVE和WM_NCMOUSELEAVE中绘制完成的。比如当鼠标进入到 Thumb上时,它就高亮了,当鼠标离开它,它又变灰色了,你要分别处理WM_NCMOUSEMOVE和WM_NCMOUSELEAVE这两个消息。注意 只有ListView应用主题风格之后才可能有WM_NCMOUSELEAVE消息,传统风格的ListView就没有这个消息,只有 WM_NCMOUSEMOVE消息,因此处理高亮还真有些麻烦。正是因为主题风格和传统风格的差异,因此我建议你也处理WM_THEMECHANGED消 息,以识别不同的主题模式,实现不同高亮处理。看上面那个图,TreeView的滚动条是主题风格的,但ListView的滚动条是传统风格的,但我加了 换肤功能。

另外,我们还应当拦截处理ListView的WM_NCPAINT,将滚动条的区域抠掉,不让它绘制滚动条。尽管滚动条被模拟窗口遮住了,但还是有必要这样做,以防止出现意外情况:

if(msg == WM_NCPAINT)

{

    HRGN wRgn =NULL; 

    RECTsbRect = GetScrollBarRect();//读取滚动条矩形的一个函数

    HRGNsRgn = ::CreateRectRgn(sbRect.left, sbRect.top, sbRect.right,sbRect.bottom

    if(wParam ==1)

    {

       RECT wRect;

       ::GetWindowRect(hListView, &wRect);

       wRgn = ::CreateRectRgn(wRect.left, wRect.top, wRect.right,wRect.bottom););

       wRgn = ::CombineRgn(wRng, wRgn, sRgn, RGN_DIFF);

    }

    else

    {

        wRgn= (HRGN)wParam; 

       wRgn = ::CombineRgn(wRng, wRgn, sRgn, RGN_DIFF);

    }

    ::DeleteObject(sRgn);

   ::CallWindowProc(gOldListViewProc, hListView, WM_NCPAINT,WPARAM(wRgn), 0);

    if(wParam ==1)

       ::DeleteObject(wRgn);

    return0;

}

另外,还要拦截WM_WINDOWPOSCHANGING消息,因为当ListView的位 置、尺寸、Z秩序发生改变时要及时调整模拟窗口的位置、尺寸、Z秩序。为何要在WM_WINDOWPOSCHANGING中进行,而不选择在 WM_WINDOWPOSCHANGED或WM_MOVE或WM_SIZE中进行呢?因为在WM_WINDOWPOSCHANGING中处理,可让模拟窗 口先于ListView调整自己的位置、尺寸和Z,可避免一些绘制问题。实际我在FreeCL中既用了WM_WINDOWPOSCHANGING,也用到 WM_WINDOWPOSCHANGED两个消息。在处理WM_WINDOWPOSCHANGING时调整位置、尺寸、Z秩序,在处理 WM_WINDOWPOSCHANGED时,强制重绘模拟窗口。

末了还要说明一点的是,系统可能因为用户改变控件尺寸导致其滚动条自动消失或自动显示,或者 调整滚动条的可用状态,如你拉宽支持多行显示的Edit控件,会导致滚动条箭头按钮变灰,Thumb消失。这些动作通常都是在 WM_WINDOWPOSCHANGED中完成的,因此这个消息也需要拦截处理:

if(msg == WM_WINDOWPOSCHANGED)

{

    LRESULTlReturn = ::CallWindowProc(gOldListViewProc, hListView,WM_WINDOWPOSCHANGED,

       wParam, lParam);

   if(::GetNextWindow(hBuddy, GW_HWNDNEXT ) !=hListView)

   {  //调整模拟窗口的Z-Order,确保其紧贴ListView之上

       HWND hAbove = ::GetNextWindow(hListView, GW_HWNDPREV);

       UINT flag =SWP_NOACTIVATE|SWP_NOREDRAW|SWP_NOMOVE|SWP_NOSIZE|SWP_NOSENDCHANGING;
           ::SetWindowPos(hBuddy, hAbove?hAbove:HWND_TOP, 0, 0, 0, 0,flag);

    }

    //测试滚动条的显隐状态是否改变,并因之调整模拟窗口的显隐状态

   //如果滚动条仍旧显示或滚动条某些元素的显示状态有变化或控件位置、尺寸可能改变,

   //强制重绘模拟窗口

    //......

    returnlReturn;

}

最后要处理WM_SHOWWINDOW和WM_DESTROY,当ListView隐藏时让模拟窗口也随之消失;当它显示时,让模拟窗口也随之显示。当ListView销毁时会触发WM_DESTROY,这时也需要销毁模拟窗口,这很重要。 

总的来讲,模拟法是蛮麻烦的,但比较安全,可靠性高。对开发者来讲,都是对消息进行特定处 理,是常规手段,对最终用户来讲,用此法实现的滚动条自绘,完全能够满足要求,具有充分的自由度,甚至可以对滚动条添加更多特定的功能,比如可以在上面添 加其他按钮,就算是加动画、加广告也都是可以的。

澳门金莎娱乐网站 4

        wRgn = ::CombineRgn(wRng, wRgn, sRgn, RGN_DIFF);

HWND pWnd= ...;//父窗口句柄

末了还要说明一点的是,系统可能因为用户改变控件尺寸导致其滚动条自动消失或自动显示,或者调整滚动条的可用状态,如你拉宽支持多行显示的Edit控件,会导致滚动条箭头按钮变灰,Thumb消失。这些动作通常都是在WM_WINDOWPOSCHANGED中完成的,因此这个消息也需要拦截处理:

//用户可以调用BitBlt(...)等函数拷贝memDC的内容到子窗口的某个区域,这样就达到了透明效果;

第四步:对于菜单和许多标准控件,如Button、Edit、ListBox、Static、ComboBox它可能会向父窗口发送WM_MEASUREITEM和WM_DRAWITEM消息,另外通用控件Tab、StatusBar、ListView、Header也可能会有WM_DRAWITEM消息;但对于多数通用控件,如TreeView、ListView、Rebar、Trackbar、Toolbar等,它会向父窗口发送许多其ID为NM_CUSTOMDRAW的WM_NOTIFY消息。对于这两种消息,实际要求用户在已经涂刷好的背景之上再执行自己的绘制工作;

FillRect(cDC, &cRect,brush);

定制颜色倒是很简单,根据控件的类型处理WM_ERASEBKGND、WM_CTLCOLORxxx、WM_DRAWITEM、WM_NOTIFY消息了,一般情况下,定制WM_ERASEBKGND和WM_CTLCOLORxxx就可以了;对于背景位图很多控件却特别麻烦,像ListBox,你把背景位图涂刷好,结果因为用户操作滚动条或鼠标滚轮或按方向键,背景位图也发生滚动,这就不得不重绘位图,对于背景颜色就没有这个问题,不管怎么滚动,颜色还是那个颜色,位图就不行,需要自己重载发生滚动操作的各种消息以实现位图重绘。微软似乎并不假设你会修改控件的背景位图,它没有对这个情况作准备,总是毫不犹豫的对控件画布执行Scroll操作。真正实现背景位图的方法常常迫使我们要拦截那些导致窗口内容发生滚动的各种操作,因此位图背景的功能实现也总让人觉得不怎么规范、不那么可信。

SendMessage(pWnd, WM_ERASEBKGND, (WPARAM)memDC,0); 

    ::DeleteObject(sRgn);

POINTrp;

    ......

HDCmemDC = CreateCompatibleDC(NULL);

    return 0;

很多情况下,我们需要控件 的背景是透明的,就是要求直接看到控件父窗口的背景颜色、背景位图,比如标签控件、单选Radio控件、复选Check控件,通常都要求在父窗口的背景上 进行绘制。然而要求控件的画布透明,这个技术在GDI的文档中没有看到Microsoft作任何说明,当然还是有别的办法。

}

实现控件的透明背景

另外,我们还应当拦截处理ListView的WM_NCPAINT,将滚动条的区域抠掉,不让它绘制滚动条。尽管滚动条被模拟窗口遮住了,但还是有必要这样做,以防止出现意外情况:

RECTcRect;

        wRgn = ::CombineRgn(wRng, wRgn, sRgn, RGN_DIFF);

//至此memDC上已经保存了父窗口的背景内容

    {

HBITMAPbitmap = CreateCompatibleBitmap(dc, cRect.right,cRect.bottom);

        RECT wRect;

DeleteDC(memDC); 

    //的位置。若只是响应WM_HSCROLL消息,你可能觉得滚动盒的拖拽比较滞,不平滑。

FillRect(cDC, &cRect,brush);

    //盒的位置,若有必要你可安装鼠标钩子,以捕捉鼠标移动消息,以及时刷新模拟窗口中滚动盒的

 

        wParam, lParam);

SelectObject(memDC, oldBitmap);

对于常规的皮肤定制一般都是通过定制WM_PAINT、WM_ERASEBKGND、WM_CTLCOLORxxx、NM_CUSTOMDRAW来实现。然而系统滚动条的绘制,常规的、很阳光的方法行不通,微软把一条康庄大道堵死了。根据我的观察测试,系统滚动条有许多的消息都对其执行了绘制,这包括WM_NCPAINT、WM_NCMOUSEMOVE、WM_NCMOUSELEAVE、WM_HSCROLL、WM_VSCROLL、WM_KEYDOWN、WM_MOUSEWHEEL等等,这些消息中有些可以定制,但有些没法定制。比如WM_HSCROLL就无法定制,如果我们处理这个消息,就必须自己控制滚动条的范围、位置、翻页值,而这些值我们通常无法得到,微软根本没有告诉我们,对于不同的控件它操控滚动条到底采用何种策略,不晓得。假若我们不去定制这个WM_HSCROLL,而默认的处理它又执行滚动条的绘制,这真是进退无路,叫人束手无策。当然不是完全没有办法,死路一条。为了克服障碍,翻越障碍,每个人有不同的策略,有攀岩翻越的,安全性不高;也有走小路绕行的,比较费力。

HRESULT DrawThemeParentBackground(HWND hwnd , HDC hdc , RECT *prc );

    else

其一: 如果程序支持桌面主题服务的话,则可调用主题服务的API来实现背景。我们先看看这个API:

    }

揭秘换肤技术:定制控件背景颜色与背景位图

 

本文讲述如何定制控件的背景颜色和背景位图的技巧。

首先要说一下控件的绘制过程:当控件的某个区域需要重绘时,都会触发WM_ERASEBKGND和WM_PAINT消息。比如控件的某个区域被另一个程序的窗口挡住了,而后那个窗口又被移走了,这时控件被挡住的内容就需要重新绘制了。

第一步:系统向控件发送WM_ERASEBKGND消息以实现背景的擦除工作(有时不发送,比如用户可能调用InvalidateRect(),其参数却指定不擦除背景,这样就没有这个消息);

第二步:系统向控件的窗口过程发送WM_PAINT消息,控件执行处理这个WM_PAINT消息时会有选择地触发后面三个步骤的动作;

第三步:对于有些标准控件,如Button、Edit、ListBox、 ScrollBar、Static控件,它还会向父窗口发送WM_CTLCOLORxxx(WM_CTLCOLORBUTTON、 WM_CTLCOLOREDIT、WM_CTLCOLORSTATIC、WM_CTLCOLORLISTBOX、 WM_CTLCOLORSCROLLBAR等)的消息,这些消息返回一个刷子句柄,系统拿这个刷子句柄进一步涂刷自己的背景。另外还发现Trackbar 也会向父窗口发送WM_CTLCOLORSTATIC消息,TreeView在某些情况下也有,不过我没有看到微软在什么地方对这一点作说明;我常常发现 很多人处理这个消息时,喜欢给系统返回一个NULL_BRUSH的空刷子,以为这样系统就不会把前面步骤画好的背景覆盖掉,其实不一定的,有些控件不覆 盖,有些就有问题,像Trackbar就是如此,要小心。

第四步:对于菜单和许多标准控件,如Button、Edit、ListBox、 Static、ComboBox它可能会向父窗口发送WM_MEASUREITEM和WM_DRAWITEM消息,另外通用控件Tab、 StatusBar、ListView、Header也可能会有WM_DRAWITEM消息;但对于多数通用控件,如TreeView、 ListView、Rebar、Trackbar、Toolbar等,它会向父窗口发送许多其ID为NM_CUSTOMDRAW的WM_NOTIFY消 息。对于这两种消息,实际要求用户在已经涂刷好的背景之上再执行自己的绘制工作;

第五步:当控件的WM_DRAWITEM或者WM_NOTIFY消息没有被用户处理时,系统会亲自执行自己的默认绘制工作,把控件画出来,这一步没有办法重载。

知道了这些步骤,大概我们心中已经了然,知道如何定制控件的背景颜色和背景位图了。一般情况 下我们定制第一步、第三步实现自己的特殊背景,定制第四部实现控件本身的特殊绘制。甚至我们可以整个重载控件第一步的WM_ERASEBKGND消息和第 二步的WM_PAINT消息,控件背景和控件绘制全部自己搞定,没人说这样做不行。不过要注意,当自己实现WM_PAINT消息的重载处理时,后面3个步 骤就都不发生了。

定制颜色倒是很简单,根据控件的类型处理WM_ERASEBKGND、 WM_CTLCOLORxxx、WM_DRAWITEM、WM_NOTIFY消息了,一般情况下,定制WM_ERASEBKGND和 WM_CTLCOLORxxx就可以了;对于背景位图很多控件却特别麻烦,像ListBox,你把背景位图涂刷好,结果因为用户操作滚动条或鼠标滚轮或按 方向键,背景位图也发生滚动,这就不得不重绘位图,对于背景颜色就没有这个问题,不管怎么滚动,颜色还是那个颜色,位图就不行,需要自己重载发生滚动操作 的各种消息以实现位图重绘。微软似乎并不假设你会修改控件的背景位图,它没有对这个情况作准备,总是毫不犹豫的对控件画布执行Scroll操作。真正实现 背景位图的方法常常迫使我们要拦截那些导致窗口内容发生滚动的各种操作,因此位图背景的功能实现也总让人觉得不怎么规范、不那么可信。

最后要说明的是:如果父控件还包含背景透明的子控件,你应当重载父控件的WM_ERASEBKGND消息,否则那些透明背景的子控件可能就没有正确的背景内容。

上面这些步骤是我个人的理解,不一定对哦!仅供参考。

 

当我们没有按下鼠标左键时,只是简单地在滚动条移动鼠标时,我们会发现滚动条的按钮和Thumb都会自动高亮,其实这些变化都是系统在WM_NCMOUSEMOVE和WM_NCMOUSELEAVE中绘制完成的。比如当鼠标进入到Thumb上时,它就高亮了,当鼠标离开它,它又变灰色了,你要分别处理WM_NCMOUSEMOVE和WM_NCMOUSELEAVE这两个消息。注意只有ListView应用主题风格之后才可能有WM_NCMOUSELEAVE消息,传统风格的ListView就没有这个消息,只有WM_NCMOUSEMOVE消息,因此处理高亮还真有些麻烦。正是因为主题风格和传统风格的差异,因此我建议你也处理WM_THEMECHANGED消息,以识别不同的主题模式,实现不同高亮处理。看上面那个图,TreeView的滚动条是主题风格的,但ListView的滚动条是传统风格的,但我加了换肤功能。

原连接:

    //测试滚动条的显隐状态是否改变,并因之调整模拟窗口的显隐状态

 

在网上反复搜索,自己也仔细研究,基本有两种办法来实现系统滚动条换肤,一种方法是HOOK API,也就是拦截API的办法,还有一种是模拟法。

 

   

HWND cWnd= ...;//子窗口句柄

HBRUSHbrush = CreateSolidBrush(pColor);

3 实现控件的透明背景

  

很多情况下,我们需要控件的背景是透明的,就是要求直接看到控件父窗口的背景颜色、背景位图,比如标签控件、单选Radio控件、复选Check控件,通常都要求在父窗口的背景上进行绘制。然而要求控件的画布透明,这个技术在GDI的文档中没有看到Microsoft作任何说明,当然还是有别的办法。

 

其一:如果程序支持桌面主题服务的话,则可调用主题服务的API来实现背景。我们先看看这个API:

HRESULT DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc);

这个函数就是专门用来绘制父窗口的背景的,其中的hwnd参数是子窗口的句柄,hdc也时子窗口的画布句柄,prc是子窗口需绘制的区域,这个函数实际是把父窗口的背景拷贝到子窗口上来,以这种方法达到透明。

 

其二:如果程序不支持桌面主题服务,则不能使用上面的方法,比如程序运行在Windows 2000上。这时我们可以向父窗口发送WM_PAINT消息,不过此消息所附带的wParam参数是一个画布句柄:

HDC dc = GetDC(NULL);

HWND cWnd = ...;//子窗口句柄

HWND pWnd = ...;//父窗口句柄

RECT cRect;

GetClientRect(cWnd, &cRect);

HBITMAP bitmap = CreateCompatibleBitmap(dc, cRect.right, cRect.bottom);

ReleaseDC(dc);

HDC memDC = CreateCompatibleDC(NULL);

HGDIOBJ oldBitmap = SelectObject(memDC, bitmap);

//此处可以调用SetClipRect()等函数来限制绘制范围

SendMessage(pWnd, WM_ERASEBKGND, (WPARAM)memDC, 0); 

SendMessage(pWnd, WM_PAINT, (WPARAM)memDC, 0);

//至此memDC上已经保存了父窗口的背景内容

//用户可以调用BitBlt(...)等函数拷贝memDC的内容到子窗口的某个区域,这样就达到了透明效果;

SelectObject(memDC, oldBitmap);

DeleteDC(memDC); 

DeleteObject(bitmap);

上面的办法当然有限制,因为不是所有的父窗口都可以接受这种特殊的WM_PAINT消息功能,不过MSDN中提到大多数控件都有这个功能,大家要注意读它的文档内容。

 

其三:如果上面的办法都不行的话,就剩下最笨的办法了,用GDI函数涂刷子窗口的背景,但你事先就要知道父窗口的背景颜色、背景位图等信息。比如拿父窗口的颜色来填充子窗口的背景,可以调用FillRect()等:

//-------------------父窗口的背景是颜色的情况

COLORREF pColor = ...;//父窗口的颜色

HDC cDC = ...;//子窗口的画布句柄

RECT cRect = ...;//子窗口需刷新的区域 

HBRUSH brush = CreateSolidBrush(pColor);

FillRect(cDC, &cRect, brush);

DeleteObject(brush);

 

//-------------------父窗口的背景是位图的情况

HBITMAP pBitmap = ...;父窗口的背景位图

HDC cDC = ...;//子窗口的画布句柄

RECT cRect = ...;//子窗口需刷新的区域 

POINT rp;

SetBurshOrgEx(cDC, x, y, &rp);如果是位图刷子,则还需要调整画布的刷子原点偏移确保无缝

HBRUSH brush = CreatePatternBrush(pBitmap);

FillRect(cDC, &cRect, brush);

DeleteObject(brush);

SetBurshOrgEx(cDC, rp.x, rp.y, NULL);还原画布的刷子偏移

ReleaseDC(dc);

    HRGN sRgn = ::CreateRectRgn(sbRect.left, sbRect.top, sbRect.right, sbRect.bottom

HDC dc = GetDC(NULL);

DeleteObject(brush);

第五步:当控件的WM_DRAWITEM或者WM_NOTIFY消息没有被用户处理时,系统会亲自执行自己的默认绘制工作,把控件画出来,这一步没有办法重载。

下面我要详细讲的是模拟法了,这个办法不用拦截API,所有的技术实现都约束在系统所允许的范围内,咱一丝不苟的当遵纪守法的良民。

 

首先我们要在滚动条的区域上创建一个模拟窗口,恰好覆盖滚动条:

 

首先我们要拦截WM_NCLBUTTONDOWN 和 WM_NCLBUTTONDBLCLK这两个消息,当你在滚动条上按下鼠标时,就立刻触发WM_NCLBUTTONDOWN,如果连续快速按两下鼠标就还有WM_NCLBUTTONDBLCLK。注意双击时只有一个WM_NCLBUTTONDOWN消息,而不是两个。第二次按鼠标会出现一个WM_NCLBUTTONDBLCLK。实际上这两个消息我们完全可以等而视之,做相同的处理。系统在处理这个两个消息时,都会在其内部处理中触发许多其他消息,其中有若干个WM_HSCROLL、WM_CAPTURECHANGED、WM_NCMOUSELEAVE。我们主要是要处理WM_HSCROLL,因为它最有价值。在你松开鼠标后,系统发送并处理完最后一个WM_HSCROLL之后,这才从WM_NCLBUTTONDOWN或WM_NCLBUTTONDBLCLK中返回:

下面我们再看WM_HSCROLL消息,这个消息一般是系统处理WM_NCLBUTTONDOWN或者WM_NCLBUTTONDBLCLK时产生的,但有时也可能是程序刻意发送的,和滚动条操作没有关系。

HWND hListView = ...;//ListView窗口的句柄

}

    if(wParam == 1)

    //注意默认处理会有N个WM_HSCROLL消息出现,要等你的按下拖拽操作完成后,这个调用才返回

case WM_NCMOUSEMOVE:

    //强制重绘模拟窗口

HWND hBuddy = ::CreateWindowEx(WS_EX_NOACTIVATE, "Buddy_Window", "", WS_CLIPSIBLINGS|WS_DISABLED|WS_CHILD, 0, 0, 0, 0, pWnd, NULL, gModule, NULL);

第三步:对于有些标准控件,如Button、Edit、ListBox、ScrollBar、Static控件,它还会向父窗口发送WM_CTLCOLORxxx(WM_CTLCOLORBUTTON、WM_CTLCOLOREDIT、WM_CTLCOLORSTATIC、WM_CTLCOLORLISTBOX、WM_CTLCOLORSCROLLBAR等)的消息,这些消息返回一个刷子句柄,系统拿这个刷子句柄进一步涂刷自己的背景。另外还发现Trackbar也会向父窗口发送WM_CTLCOLORSTATIC消息,TreeView在某些情况下也有,不过我没有看到微软在什么地方对这一点作说明;我常常发现很多人处理这个消息时,喜欢给系统返回一个NULL_BRUSH的空刷子,以为这样系统就不会把前面步骤画好的背景覆盖掉,其实不一定的,有些控件不覆盖,有些就有问题,像Trackbar就是如此,要小心。

另外还有许多其他的消息可能导致滚动条属性的变化,如用户的键盘操作、鼠标滚轮操作可能导致滚动条的Thumb位置发生改变,这些操作分别导致WM_KEYDOWN和WM_MOUSEWHEEL,而系统又在这两个消息中执行滚动条的绘制,因此你必须截获它们,但处理方法同WM_HSCROLL,这里不再啰嗦了。

    //2)再在模拟窗口上绘制滚动条

总的来讲,模拟法是蛮麻烦的,但比较安全,可靠性高。对开发者来讲,都是对消息进行特定处理,是常规手段,对最终用户来讲,用此法实现的滚动条自绘,完全能够满足要求,具有充分的自由度,甚至可以对滚动条添加更多特定的功能,比如可以在上面添加其他按钮,就算是加动画、加广告也都是可以的。

最后要说明的是:如果父控件还包含背景透明的子控件,你应当重载父控件的WM_ERASEBKGND消息,否则那些透明背景的子控件可能就没有正确的背景内容。

        HWND hAbove = ::GetNextWindow(hListView, GW_HWNDPREV);

    //在这个调用内部,我估计系统会进入一种消息循环,因为按住左键之后,WM_NCMOUSEMOVE

    //......

所谓模拟法就是在系统滚动条的区域放置一个模拟窗口,这个窗口专门用于绘制系统滚动条。当然我们也要拦截带滚动条控件的若干消息,以确保模拟窗口的绘制正确。下面只以ListView控件的水平滚动条为例,进行说明,垂直滚动条换肤可以列推。

完成创建模拟窗口之后,你要给ListView安装一个你写的窗口过程,以拦截各种导致滚动条属性改变的种种消息:

   

 

    ......

{

HWND pWnd = ::GetParent(hListView);

::SetWindowLong(hListView, GWL_WNDPROC, LONG(&MyListViewProc));//安装窗口过程

if(msg == WM_NCLBUTTONDOWN || msg == WM_NCLBUTTONDBLCLK)

{

消息处理

    LRESUTL code = ::CallWindowProc(gOldListViewProc, hListView, msg, wParam, lParam);

{

        wRgn = (HRGN)wParam; 

    ......

第二步:系统向控件的窗口过程发送WM_PAINT消息,控件执行处理这个WM_PAINT消息时会有选择地触发后面三个步骤的动作;

case WM_LBUTTONDBLCLK:

    if(wParam == 1)

    {

    //1)读取滚动条的数据

if(msg == WM_WINDOWPOSCHANGED)

    //如果滚动条仍旧显示或滚动条某些元素的显示状态有变化或控件位置、尺寸可能改变,

最后要处理WM_SHOWWINDOW和WM_DESTROY,当ListView隐藏时让模拟窗口也随之消失;当它显示时,让模拟窗口也随之显示。当ListView销毁时会触发WM_DESTROY,这时也需要销毁模拟窗口,这很重要。 

    ::CallWindowProc(gOldListViewProc, hListView, msg, wParam, lParam);

澳门金莎娱乐网站 5

        wRgn = ::CreateRectRgn(wRect.left, wRect.top, wRect.right, wRect.bottom););

    ::CallWindowProc(gOldListViewProc, hListView, WM_NCPAINT, WPARAM(wRgn), 0);

    ...... 

    {   //调整模拟窗口的Z-Order,确保其紧贴ListView之上

    return code;

第一步:系统向控件发送WM_ERASEBKGND消息以实现背景的擦除工作(有时不发送,比如用户可能调用InvalidateRect(),其参数却指定不擦除背景,这样就没有这个消息);

    RECT sbRect = GetScrollBarRect();//读取滚动条矩形的一个函数

 

case WM_NCMOUSELEAVE:

    //......

        ::DeleteObject(wRgn);

    //SetWindowsHookEx(...);

另外,还要拦截WM_WINDOWPOSCHANGING消息,因为当ListView的位置、尺寸、Z秩序发生改变时要及时调整模拟窗口的位置、尺寸、Z秩序。为何要在WM_WINDOWPOSCHANGING中进行,而不选择在WM_WINDOWPOSCHANGED或WM_MOVE或WM_SIZE中进行呢?因为在WM_WINDOWPOSCHANGING中处理,可让模拟窗口先于ListView调整自己的位置、尺寸和Z,可避免一些绘制问题。实际我在FreeCL中既用了WM_WINDOWPOSCHANGING,也用到WM_WINDOWPOSCHANGED两个消息。在处理WM_WINDOWPOSCHANGING时调整位置、尺寸、Z秩序,在处理WM_WINDOWPOSCHANGED时,强制重绘模拟窗口。

拦截API,实际上是修改操作系统的API入口。因为系统绘制滚动条是通过各种绘制函数来实现的,比如SetScrollInfo(),基本系统通过这一类函数实现滚动条的绘制,对这个API进行拦截,也就是我们自己写一个SetScrollInfo(),来亲自实现滚动条的绘制,以便由此实现滚动条的自定义绘制,实现我们想要的各种风格的皮肤外观。API的拦截有两种:一种是修改系统所装入内存可执行模块的导入地址,替换成我们所写的伪API的地址,使API调用能够自动跳转到这个伪API上;还有一种是直接修改API函数首地址处的若干机器指令,保存现场,写入跳转指令,使系统在执行到这个API时能自动跳转到我们所写的伪API上。我要说的是,这个拦截办法还真是有些邪门,有安全隐患,病毒就常干这种事情。对操作系统进行这类暴力破解式的修改,很容易引起系统防火墙或反病毒软件报错,Windows原则上不允许干这种事;其二,一旦使用此法的程序因异常而崩溃,原本对操作系统的修改没有还原,这可能会伤害到系统里的所有进程的稳定性,导致死机或运行异常。对于商业性的工程开发,往往很忌讳这一点,都倾向于追求稳定,通常是宁用拙法,不玩巧技;其三,此法有线程同步问题:因为正当你修改程序指令同时,若其他进程里的线程恰好执行到这里时候问题就会爆发出来,单CPU可能没啥问题,系统内存和CPU缓冲可以做到同步,但多核系统就难说了;另外这个方法还有移植性问题:在一种版本的系统中,通过拦截API有效,在另一种版本的系统中,就不见得还有效,毕竟微软实现滚动条的绘制,它没规定一定就用哪个函数来实现,也许新系统中它另有高招,API拦截也就不灵了。

gOldListViewProc = (WNNPROC)::GetWindowLong(hListView, GWL_WNDPROC);

首先要说一下控件的绘制过程:当控件的某个区域需要重绘时,都会触发WM_ERASEBKGND和WM_PAINT消息。比如控件的某个区域被另一个程序的窗口挡住了,而后那个窗口又被移走了,这时控件被挡住的内容就需要重新绘制了。

    //和WM_NCLBUTTONUP都不再触发了。其内部估计是捕捉了WM_NCMOUSEMOVE消息,因之反复刷新滚动

        UINT flag = SWP_NOACTIVATE|SWP_NOREDRAW|SWP_NOMOVE|SWP_NOSIZE|SWP_NOSENDCHANGING;
            ::SetWindowPos(hBuddy, hAbove?hAbove:HWND_TOP, 0, 0, 0, 0, flag);

本文由澳门金莎娱乐网站发布于 成人娱乐,转载请注明出处:转载几篇别人写的皮肤类控件的技术文章,文本