Treeview控件是我的老伙伴了,但直到前几天,一个希望设定treenode背景色的问题才令我发现,我一直没有深入到它的核心。不过一切都会好转,我决定重新复习windows的基础控件的内部动作。有部电影的对白令我记忆深刻:面包会有的,牛奶会有的。一个能自描绘Treenode的Treeview也会有的8)
根据网上得到的部分代码资料及MSDN的帮助,我先在完整版中继承了一个Treeview对象,并试图处理TreeView的OCM_NOTIFY消息,所有的一切令人愉快,我轻易地截获了TreeNode对象在重绘前发出OCM_NOTIFY消息,消息中的lParam参数代表的NMTVCUSTOMDRAW结构的子结构NMTVCUSTOMDRAW中包含的dwDrawStage成员代表CDDS_ITEMPREPAINT/CDDS_ITEMPOSTPAINT标志,而且这个结构还包含有Treenode重绘时的DC,这意意味着我可以在Treenode的重绘前/后处理这个消息,并不再使用默认的Treeview消息处理过程而达到自描绘的效果。代码片段如下:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Select Case m.Msg
Case OCM_NOTIFY
Dim NMTVCD As NMTVCUSTOMDRAW
NMTVCD = Marshal.PtrToStructure(m.LParam, GetType(NMTVCUSTOMDRAW))
Select Case NMTVCD.NMCD.hdr.code
Case NM_CUSTOMDRAW
Select Case NMTVCD.NMCD.dwDrawStage
Case CDDS_PREPAINT
' 我要得到Treenode中绘图处理前后的通知
m.Result = CDRF_NOTIFYITEMDRAW
Case CDDS_ITEMPREPAINT //这个通知将在Treenode绘图处理前收到
Dim ptr As New IntPtr
Dim rcItem As New Rectangle
Dim g As Graphics = Graphics.FromHdc(NMTVCD.NMCD.hdc)
Dim b As New SolidBrush(Color.Blue)
rcItem.X = NMTVCD.NMCD.dwItemSpec
SendMessageItemRect(m.HWnd, TVM_GETITEMRECT, 1, rcItem)
//你可以利用Graphics对象g来进行TreeNode的绘图,例如:
g.FillRectangle(New SolidBrush(Color.Yellow), New RectangleF(rcItem.X, rcItem.Y, rcItem.Width, rcItem.Height))
g.Dispose()
b.Dispose()
m.Result = CDRF_SKIPDEFAULT //这个返回值将通知Treeview控件不需再进行这个Treenode的绘图操作
Case CDDS_ITEMPOSTPAINT 这个通知将在Treenode处理后收到
//和你可以以这儿进行和CDDS_ITEMPREPAINT过程相似的绘图操作
//这代表在Treeview进行treenode描绘后你还可以为treenode再多画一些东西8)
//代码略…
m.Result = CDRF_SKIPDEFAULT
End Select
End Select
Case Else
MyBase.WndProc(m)
End Select
End Sub
但在精简版中,我挂接了TreeView控件的消息处理过程,却始终无法收到OCM_NOTIFY消息,查阅了PPC2003的SDK也没有发现该消息的说明,讨厌的WinCE系统没有提供这个通知。然后我在文档中迷醉了7~80分钟才发现,系统提供的Treeview只向父窗体发送WM_NOTIFY消息,消息的lParam代表的意义和OCM_NOTIFY通知完全一致。与是我不再继承,而是采取了使用一个自定义控件上放一个Treeview的方法。然后我在这个自定义控件中截到了WM_NOTIFY通知消息,接下来的Treenode绘图处理就和上面的示例大同小异了。
//本控件的消息处理过程
private int ThisWindowProc(IntPtr hwnd, uint uMsg, uint wParam, uint lParam)
{
bool tblnUseOldWinProc = true;
int tintFuncReturn = 0;
switch (uMsg)
{
case WM_NOTIFY:
tagNMHDR tudtNMHDR;
tagNMCUSTOMDRAWINFO tudtTreeViewCustomDrawInfo;
tagNMTVCUSTOMDRAW tudtTreeViewCustomDraw;
tudtNMHDR = (tagNMHDR)Marshal.PtrToStructure(new IntPtr(lParam), typeof(tagNMHDR));
if (tudtNMHDR.hwndFrom == treMain.Handle)
{
switch (tudtNMHDR.code)
{
case NM_CUSTOMDRAW:
tudtTreeViewCustomDraw = (tagNMTVCUSTOMDRAW)Marshal.PtrToStructure((IntPtr)lParam,
typeof(tagNMTVCUSTOMDRAW));
tudtTreeViewCustomDrawInfo = tudtTreeViewCustomDraw.nmcd;
switch (tudtTreeViewCustomDrawInfo.dwDrawStage)
{
case CDDS_PREPAINT:
//本控件在Item重画前后都将获得Treeview控件的 NM_CUSTOMDRAW消息
tintFuncReturn = (int)CDRF_NOTIFYITEMDRAW;
tblnUseOldWinProc = false;//不再使用原始窗体的消息处理过程
break;
case CDDS_ITEMPREPAINT:
{
tintFuncReturn = fnCDDS_ITEMPREPAINT_NotifyMessage(hwnd, uMsg, wParam, lParam,
ref tudtTreeViewCustomDraw, ref tblnUseOldWinProc);
Marshal.StructureToPtr(tudtTreeViewCustomDraw, (IntPtr)lParam, false);//这是重绘TreeNode的处理过程
break;
}
default:
break;
}//end switch
break;
}//end switch
}//end if
break;
default:
//对不处理的消息使用treeview的默认消息处理过程
break;
}//end switch
if (tblnUseOldWinProc == true)
return clsCECoreAPI.CallWindowProc(m_intThisPreviousWinProc, hwnd, uMsg, wParam, lParam);
else
return tintFuncReturn;
//end if
}//end function
得到的运行效果如下:
如移动设备开发感兴趣可以加入以下群24123368
入群需知:
1、不是编程开发人员请勿加入。
2、没时间讨论问题或喜欢潜水的请勿加入。
3、只想获得源程序的请勿加入
本blog文档,未经作者同意,谢绝转载。谢谢你对我的blog的访问.
联系方式: missilecat@163.com QQ:85403578