win32通用控件TreeView滚动条自绘

《标题》win32通用控件TreeView滚动条自绘


     直接使用windows sdk 进行开发自绘滚动条是很让人蛋疼的,嫌消息HOOK 麻烦,又不了解第三方控件,别担心,你还有一条小路可走:使用子窗口模拟滚动条。

效果图如下,正常状态下的滚动条

win32通用控件TreeView滚动条自绘


鼠标进入滚动条时候滚动条的颜色:

win32通用控件TreeView滚动条自绘


左键按下拖拉滚动条时滚动条的颜色:

win32通用控件TreeView滚动条自绘


其实思路很简单,要注意的地方和大致过程如下:

1:创建 treeview 控件的时候记得加入属性 TVS_NOSCROLL  禁止使用滚动条

      代码示例:

HWND htree = CreateWindow(
		"SysTreeView32", nullptr,
		WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TVS_HASLINES | TVS_HASBUTTONS | TVS_NOSCROLL,
		petreeViewarea.left, petreeViewarea.top, petreeViewarea.right, petreeViewarea.bottom,
		hparent, (HMENU)id, appInst, NULL
		);


2:记得为新创建的 treeview 控件指定一个消息处理函数

3:创建 滚动条子窗口之后需要去去掉 caption 属性

      示例代码:

SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION);
SetWindowPos(hwnd, NULL, 0, 0, (int)FRAME_WIDTH, (int)FRAME_HEIGHT, SWP_DRAWFRAME | SWP_NOMOVE);

4:使用3个变量  min,pos,max  来薄记滚动条的状态,并且使用 pagesize 来保存页面的大小
,变量之间的关系可以使用下面两条函数来计算:

/*
* 计算垂直滚动条的高度
* @pagesize: 当前页面大小
* @pomax: pos最大
* @返回值: 滚动条的高度
*/
int calScrollbarLen(const int& pagesize, const int& posmax)
{
	if (pagesize >= posmax)
		return 0;

	return (int)((float)(pagesize * pagesize) / (float)posmax);
}

/*
* 计算垂直滚动条的起始位置
* 
*/ 
int calScrollbarTop(const int& pagesize, const int& pos, const int& posmax)
{
	if (pagesize >= posmax)
		return 0;

	return (int)((float)(pagesize * pos) / (float)posmax);
}

5:为了实现滚动条三态效果需要在滚动条的消息函数中处理 WM_MOUSEMOVE,WM_MOUSEHOVER,WM_MOUSELEAVE,追踪鼠标事件消息并设着相应状态颜色

6:分别在 滚动条 和 treeview 控件中处理消息 WM_MOUSEHEEL 更改 pos 的值响应用户滚动滚动条操作

7:最后也是最重要的一点,当 treeview 某个节点展开或收缩的时候 max 的值会发生变化,treeview控件消息以WM_NOTIFY形式发送给父窗口,为了响应这个消息我们应该在 treeview 的父窗口中使用下面的语句来处理 展开或收缩消息(TVN_ITEMEXPANDING比TVN_ITEMEXPANDED来的快一步,具体用哪个按喜好)

switch (message)
    {

。。。。。。。。。

    case WM_NOTIFY:            // 通知消息
        tvinfo = (LPNMTREEVIEW)lparam;
        switch (tvinfo->hdr.code)
        {
        case TVN_ITEMEXPANDING:
            // pnm->action=2 表示打开
            // pnm->action=1 表示关闭
            pnm = (NMTREEVIEW*)lparam;
            if (pnm)
            {
        
            }
            break;

。。。。。。。

MSDN没有明确说明但需要注意的是 NMTREEVIEW 结构体成员 action 为1表示 子项关闭/收缩,2表示打开。但是为了精确的计算 max 还得必须知道打开的子项有多少个子节点或者收缩子项有多少个子节点, treeview 已经为我们提供大量“宏”可用,例如你可以通过 BOOL TreeView_GetItem( HWND hwndTV, LPTVITEMEX pitem )  来取得 hendTV 某个节点的信息,只要把上面代码中的 pnm->itemNew.hItem传入pitem ,并且设置好 pitem 其它几项就可以得到诸如 节点名称,节点图像,节点是否有子节点的信息(注意pitem->int cChildren;只会返回1或0来表示是否有子节点),然后为了统计到底有多少个子节点,假如有100个子节点的话又得使用 TreeView_GetNextItem 宏101次来遍历统计节点数量,要知道这是个灾难,因为这些宏是通过向 treeview 发送消息来取得数据的,况且还是调用的 sendmessage 函数。

废话这么多因此明白了要想知道当前节点有多少子节点是绝对使用其他方法来实现的,还记得添加节点时候使用的宏 TreeView_InsertItem,如果你设置了 TVINSERTSTRUCT.item.lParam的值,那么 接受

TVN_ITEMEXPANDING 或 TVN_ITEMEXPANDED 消息的时候就可以通过 LPNMTREEVIEW(lparam)->itemNew.lParam 来重新取得这个值,然后轻松读取预先设置的节点数量,标准而不失个性化的设计,很好。接下来该怎么做就显而易见了,

使用 calScrollbarTop 和 calScrollbarLen重新计算滚动条的起始位置和长度,并使用 setWindowPos 重新设置 滚动条 和 treeview控件的位置和大小。


本篇结束。

win32通用控件TreeView滚动条自绘,布布扣,bubuko.com

win32通用控件TreeView滚动条自绘

上一篇:C#编写Windows服务程序图文教程


下一篇:Windows编程之基本窗口控件小结