TreeViewEx.cs
上传用户:nnpulika
上传日期:2013-02-15
资源大小:597k
文件大小:9k
源码类别:

状态条

开发平台:

C#

  1. using System;
  2. using System.Collections;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Data;
  6. using System.Windows.Forms;
  7. using System.Runtime.InteropServices;
  8. using System.Diagnostics;
  9. using System.Text;
  10. using UtilityLibrary.Win32;
  11. using UtilityLibrary.General;
  12. namespace UtilityLibrary.WinControls
  13. {
  14. /// <summary>
  15. /// Summary description for TreeViewEx.
  16. /// </summary>
  17. [ToolboxItem(false)]
  18. public class TreeViewEx : System.Windows.Forms.TreeView
  19. {
  20. #region Class Variables
  21. // Helper to draw item state
  22. bool itemHasFocus = false;
  23. bool itemSelected = false;
  24. bool itemIsHot = false;
  25. bool itemClicked = false;
  26. IntPtr hClickedItem = IntPtr.Zero;
  27. #endregion
  28. #region Constructor
  29. public TreeViewEx()
  30. {
  31. }
  32. #endregion
  33. #region Overrides
  34. protected override  void WndProc(ref Message message)
  35. {
  36. base.WndProc(ref message);
  37. switch (message.Msg)
  38. {
  39. // Reflected Messages come from the treeview control itself
  40. case (int)ReflectedMessages.OCM_NOTIFY:
  41. NMHDR nm = (NMHDR) message.GetLParam(typeof(NMHDR));
  42. switch (nm.code)
  43. {
  44. case (int)NotificationMessages.NM_CUSTOMDRAW:
  45. NotifyTreeCustomDraw(ref message); 
  46. break;
  47. case (int)TreeViewNotifications.TVN_ITEMEXPANDEDW:
  48. OnTreeViewItemExpanded(ref message);
  49. break;
  50. case (int)TreeViewNotifications.TVN_ITEMEXPANDINGW:
  51. OnTreeViewItemExpanding(ref message);
  52. break;
  53. case (int)TreeViewNotifications.TVN_SELCHANGINGW:
  54.                             OnTreeViewSelectionChanging(ref message);
  55. break; 
  56.                         default:
  57. break;
  58. }
  59. break;
  60. default:
  61. break;
  62. }
  63. }
  64. protected override void OnMouseDown(MouseEventArgs e)
  65. {
  66. base.OnMouseDown(e);
  67.             TreeViewHitTestFlags flags; 
  68. hClickedItem = HitTest(new Point(e.X, e.Y), out flags);
  69. if ( (flags & TreeViewHitTestFlags.TVHT_ONITEMBUTTON) != 0 && e.Button != MouseButtons.Right )
  70. hClickedItem = IntPtr.Zero;
  71.             
  72. }
  73. #endregion
  74. #region Virtuals
  75. // Let derive class implement whatever functionality they wish
  76. protected virtual void OnTreeViewItemExpanded(ref Message m)
  77. {
  78. // This value is ignore
  79. m.Result = IntPtr.Zero;
  80. }
  81. protected virtual void OnTreeViewItemExpanding(ref Message m)
  82. {
  83. // Return zero to allow tree to send expand message
  84. m.Result = IntPtr.Zero;
  85. }
  86. protected virtual void OnTreeViewSelectionChanging(ref Message m)
  87. {
  88. m.Result = IntPtr.Zero;
  89. }
  90. #endregion
  91. #region Implementation
  92. bool NotifyTreeCustomDraw(ref Message m)
  93. {
  94. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_DODEFAULT;
  95. NMTVCUSTOMDRAW tvcd = (NMTVCUSTOMDRAW)m.GetLParam(typeof(NMTVCUSTOMDRAW));
  96. IntPtr thisHandle = Handle;
  97. if ( tvcd.nmcd.hdr.hwndFrom != Handle)
  98. return false;
  99. switch (tvcd.nmcd.dwDrawStage)
  100. {
  101. case (int)CustomDrawDrawStateFlags.CDDS_PREPAINT:
  102. // Ask for Item painting notifications
  103. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYITEMDRAW;
  104. break;
  105. case (int)CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT:
  106. PrePaintCustomDrawing(ref m);
  107. break;
  108. case (int)CustomDrawDrawStateFlags.CDDS_ITEMPOSTPAINT:
  109. PostPaintCustomDrawing(ref m);
  110. break;
  111. default:
  112. break;
  113. }
  114. return false;
  115. }
  116. void PrePaintCustomDrawing(ref Message m)
  117. {
  118. NMTVCUSTOMDRAW tvcd = (NMTVCUSTOMDRAW)m.GetLParam(typeof(NMTVCUSTOMDRAW));
  119. itemHasFocus = false;
  120. itemSelected = false;
  121. itemIsHot = false;
  122. if( (tvcd.nmcd.uItemState & (uint)CustomDrawItemStateFlags.CDIS_FOCUS) != 0 )
  123. itemHasFocus = true;
  124. if( (tvcd.nmcd.uItemState & (uint)CustomDrawItemStateFlags.CDIS_SELECTED) != 0 )
  125. itemSelected = true;
  126. if( (tvcd.nmcd.uItemState & (uint)CustomDrawItemStateFlags.CDIS_HOT) != 0 )
  127. itemIsHot = true;
  128. IntPtr hNode = (IntPtr)tvcd.nmcd.dwItemSpec;
  129. itemClicked = hClickedItem != IntPtr.Zero && hClickedItem == hNode;
  130.             
  131. if ( itemHasFocus || itemSelected || itemIsHot || itemClicked )
  132. {
  133. Color backColor = Color.Empty;
  134. Color foreColor = Color.Empty;
  135. if ( itemIsHot )
  136. {
  137. // Paint text and background in SystemColors.Window color
  138. // to make the painting less noticable, we will paint
  139. // this item in the postpaint notification
  140. foreColor = SystemColors.Window;
  141. backColor = SystemColors.Window;
  142. }
  143. else if ( itemHasFocus || itemSelected || itemClicked )
  144. {
  145. // We are just going to paint the border
  146. // in the postpaint notification
  147. // -- this helps a little bit to avoid flickering
  148. foreColor = SystemColors.ControlText;
  149. if ( itemSelected && !Focused && !HideSelection )
  150. {
  151. backColor = SystemColors.Control;
  152. }
  153. else
  154. {
  155. backColor = ColorUtil.VSNetSelectionColor;
  156. }
  157. }
  158. tvcd.clrText = ColorUtil.RGB(foreColor.R, foreColor.G, foreColor.B);
  159. tvcd.clrTextBk = ColorUtil.RGB(backColor.R, backColor.G, backColor.B);
  160. // Put structure back in the message
  161. Marshal.StructureToPtr(tvcd, m.LParam, true);
  162. // Signal that we want item post paint notification
  163. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYPOSTPAINT;
  164. }
  165. }
  166. void PostPaintCustomDrawing(ref Message m)
  167. {
  168. NMTVCUSTOMDRAW tvcd = (NMTVCUSTOMDRAW)m.GetLParam(typeof(NMTVCUSTOMDRAW));
  169. IntPtr hNode = (IntPtr)tvcd.nmcd.dwItemSpec;
  170. Rectangle rect = GetItemRect(hNode);
  171. // Create a graphic object from the Device context in the message
  172. Graphics g = Graphics.FromHdc(tvcd.nmcd.hdc);
  173. if ( itemIsHot )
  174. {
  175. // Draw the whole item, background, text and border
  176. using ( Brush brush = new SolidBrush(ColorUtil.VSNetPressedColor))
  177. {
  178. g.FillRectangle(brush, rect.Left, rect.Top, rect.Width-1, rect.Height-1);
  179. g.DrawRectangle(SystemPens.Highlight, rect.Left, rect.Top, rect.Width-1, rect.Height-1);
  180. }
  181. // Draw Text
  182. string itemText = GetItemText(hNode);
  183. Size textSize = TextUtil.GetTextSize(g, itemText, Font);
  184. Point pos = new Point(rect.Left+2, rect.Top + (rect.Height - textSize.Height)/2);
  185. TextUtil.DrawText(g, itemText, Font, new Rectangle(pos, textSize));
  186. }
  187. else if ( itemHasFocus || itemSelected || itemClicked )
  188. {
  189. // Draw just the border
  190. g.DrawRectangle(SystemPens.Highlight, rect.Left, rect.Top, rect.Width-1, rect.Height-1);
  191. if ( itemClicked )
  192. {
  193. // Reset item clicked
  194. hClickedItem = IntPtr.Zero;
  195. }
  196. }
  197. // Put structure back in the message
  198. Marshal.StructureToPtr(tvcd, m.LParam, true);
  199. m.Result =  (IntPtr)CustomDrawReturnFlags.CDRF_SKIPDEFAULT;
  200. }
  201. protected Rectangle GetItemRect(IntPtr hTreeItem)
  202. {
  203. RECT rc = new RECT();
  204. // This is how Microsoft recommends to shovel the handle to the tree Node into
  205. // the rectangle structure that will be used to send a message to retrieve
  206. // the bounds of the tree item. Any wonders why Java became a huge success?
  207. unsafe 
  208. *(IntPtr*)&rc = hTreeItem;
  209. }
  210. // --I wanted to use the TreeView NET control itself to get the bounds of the current
  211. // tree node, but a quick inspection through the documentation made me realize that I would 
  212. // have to loop through the Nodes collection and all the subnodes collections that
  213. // each node have which would be terribly expensive just to retrieve such information,
  214. // specially when I need to do this really quick
  215. // instead let's use the horrible but efficient way--
  216. WindowsAPI.SendMessage(Handle, (int)TreeViewMessages.TVM_GETITEMRECT, 1, ref rc);
  217. return new Rectangle(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
  218. }
  219. protected string GetItemText(IntPtr hTreeItem)
  220. {
  221. string text;
  222. TVITEM tvi = new TVITEM();
  223. tvi.hItem = hTreeItem;
  224. tvi.mask = (uint)TreeViewItemFlags.TVIF_TEXT;
  225. tvi.cchTextMax = 1024;
  226. tvi.pszText = Marshal.AllocHGlobal(1024);
  227. WindowsAPI.SendMessage(Handle, TreeViewMessages.TVM_GETITEMW, 0, ref tvi);
  228. text = Marshal.PtrToStringAuto(tvi.pszText);
  229. Marshal.FreeHGlobal(tvi.pszText);
  230. return text;
  231. }
  232. protected IntPtr HitTest(Point point, out TreeViewHitTestFlags flags)
  233. {
  234. // Initialize flags to nothing hit
  235. flags = TreeViewHitTestFlags.TVHT_NOWHERE;
  236. // Setup hittest structure
  237. TVHITTESTINFO hti = new TVHITTESTINFO();
  238.             hti.pt.x = point.X;
  239. hti.pt.y = point.Y;
  240.             
  241. // Send message
  242. IntPtr handle = (IntPtr)WindowsAPI.SendMessage(Handle, TreeViewMessages.TVM_HITTEST, 0, ref hti);
  243. if ( handle != IntPtr.Zero )
  244.                 flags = hti.flags;
  245. return handle;
  246. }
  247. #endregion
  248. }
  249. }