ListViewEx.cs
Upload User: nnpulika
Upload Date: 2013-02-15
Package Size: 597k
Code Size: 45k
Category:

StatusBar

Development Platform:

C#

  1. using System;
  2. using System.Windows.Forms;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.Runtime.InteropServices;
  6. using System.Drawing.Drawing2D;
  7. using System.Resources;
  8. using System.Drawing.Text;
  9. using System.Reflection;
  10. using System.Collections;
  11. using System.ComponentModel;
  12. using UtilityLibrary.Win32;
  13. using UtilityLibrary.General;
  14. using UtilityLibrary.Collections;
  15. namespace UtilityLibrary.WinControls
  16. {
  17. #region Enumerations
  18. public enum SortedListViewFormatType
  19. {
  20. String,
  21. Numeric,
  22. Date,
  23. Custom
  24. }
  25. public enum SortedListViewSortDirection
  26. {
  27. Ascending,
  28. Descending
  29. }
  30. #endregion
  31. #region Interfaces
  32. public interface IListViewEmbeddedControl
  33. {
  34. void FastRender(Graphics g, Rectangle bounds);
  35. }
  36. #endregion
  37. #region Delegates
  38. public delegate int ListSortEvent(ListViewItem item1, ListViewItem item2);
  39. #endregion
  40. #region Helper Classes
  41. internal class HeaderIconHelper
  42. {
  43. #region Class Variables
  44. int headerIndex;
  45. int iconIndex;
  46. #endregion
  47. #region Constructor
  48. public HeaderIconHelper(int HeaderIndex, int IconIndex)
  49. {
  50. headerIndex = HeaderIndex;
  51. iconIndex = IconIndex;
  52. }
  53. #endregion
  54. #region Properties
  55. public int HeaderIndex
  56. {
  57. set { headerIndex = value; }
  58. get { return headerIndex; }
  59. }
  60. public int IconIndex
  61. {
  62. set { iconIndex = value; }
  63. get { return iconIndex; }
  64. }
  65. #endregion
  66. }
  67. internal class RowSorterHelper
  68. {
  69. #region Class Variables
  70. int columnIndex;
  71. SortedListViewFormatType format;
  72. ListSortEvent sortEvent = null;
  73. #endregion
  74. #region Constructors
  75. public RowSorterHelper(int columnIndex, SortedListViewFormatType format)
  76. {
  77. this.columnIndex = columnIndex;
  78. this.format = format;
  79. }
  80. public RowSorterHelper(int columnIndex, SortedListViewFormatType format, ListSortEvent sortEvent)
  81. {
  82. this.columnIndex = columnIndex;
  83. this.format = format;
  84. this.sortEvent = sortEvent;
  85. }
  86. #endregion
  87. #region Properties
  88. public int ColumnIndex
  89. {
  90. set { columnIndex = value; }
  91. get { return columnIndex; }
  92. }
  93. public SortedListViewFormatType Format
  94. {
  95. set { format = value; }
  96. get { return format; }
  97. }
  98. public ListSortEvent SortEvent
  99. {
  100. set { sortEvent = value; }
  101. get { return sortEvent; }
  102. }
  103. #endregion
  104. }
  105. internal class HeaderHook : System.Windows.Forms.NativeWindow
  106. {
  107. #region Class Variables
  108. ListViewEx listView = null;
  109. Point mouseDownPos = Point.Empty;
  110. bool gotLeftButtonDoubleClick = false;
  111. #endregion
  112. #region Constructors
  113. public HeaderHook(ListViewEx lv)
  114. {
  115. listView = lv;
  116. }
  117. #endregion
  118. #region Overrides
  119. protected override  void WndProc(ref Message m)
  120. {
  121. int message = m.Msg;
  122. Point mousePos = new Point(0,0);
  123. if ( message == (int)Msg.WM_LBUTTONDBLCLK )
  124. {
  125. gotLeftButtonDoubleClick = true;
  126. }
  127. if ( message == (int)Msg.WM_LBUTTONDOWN || 
  128. message == (int)Msg.WM_LBUTTONUP) 
  129. {
  130. mousePos = WindowsAPI.GetPointFromLPARAM((int)m.LParam);
  131. listView.LastMousePosition = mousePos;
  132. }
  133. if ( listView.SortingEnabled && 
  134. message == (int)Msg.WM_LBUTTONDOWN && listView.Tracking == false ) 
  135. {
  136. mouseDownPos = mousePos; 
  137. for (int i = 0; i < listView.Columns.Count; i++ )
  138. {
  139. Rectangle rc = listView.GetHeaderItemRect(i);
  140. if ( rc.Contains(mousePos))
  141. {
  142. listView.PressedHeaderItem = i;
  143. WindowsAPI.InvalidateRect(m.HWnd, IntPtr.Zero, 0);
  144. }
  145. }
  146. }
  147. if ( listView.SortingEnabled && 
  148. message == (int)Msg.WM_LBUTTONUP && listView.Tracking == false 
  149. && gotLeftButtonDoubleClick == false )
  150. {
  151. listView.PressedHeaderItem = -1;
  152. // Don't do any sorting if the mouse is being actually used
  153. // to drag the header 
  154. bool mouseMoved = (Math.Abs(mousePos.X-mouseDownPos.X))>5;
  155. if ( !mouseMoved )
  156. {
  157. for (int i = 0; i < listView.Columns.Count; i++ )
  158. {
  159. Rectangle rc = listView.GetHeaderItemRect(i);
  160. if ( rc.Contains(mousePos))
  161. {
  162. listView.LastSortedColumn = i;
  163. if ( listView.SortOrder == SortOrder.None)
  164. {
  165. // We will set the sorting to descending
  166. // because the default sorting already took place
  167. // and default sorting is ascending
  168. listView.SortOrder = SortOrder.Descending;
  169. // Make embedded controls invisible
  170.                                 // so that only the ones that need to be showing
  171. // will be made visible after the sort is done
  172. listView.MakeEmbeddedControlsInvisible();
  173. listView.Sort();
  174. }
  175. else 
  176. {
  177. if ( listView.SortOrder == SortOrder.Ascending )
  178. listView.SortOrder = SortOrder.Descending;
  179. else
  180. listView.SortOrder = SortOrder.Ascending;
  181. listView.MakeEmbeddedControlsInvisible();
  182. listView.Sort();
  183. }
  184. }
  185. }
  186. }
  187. mouseDownPos = Point.Empty;
  188. // update item
  189. WindowsAPI.InvalidateRect(m.HWnd, IntPtr.Zero, 0);
  190. }
  191. if ( message == (int)Msg.WM_LBUTTONUP )
  192. {
  193. gotLeftButtonDoubleClick = false;
  194. }
  195. base.WndProc(ref m);
  196. }
  197. #endregion
  198. }
  199.     
  200. public class CompareListItems : IComparer
  201. {
  202. #region Class Variables
  203. ListViewEx listView = null;
  204. #endregion
  205. #region Constructors
  206. public CompareListItems(ListViewEx lv)
  207. {
  208. listView = lv;
  209. }
  210. #endregion
  211. #region Methods
  212. public int Compare(object obj1, object obj2)
  213. {
  214. ListViewItem item1 = (ListViewItem)obj1;
  215. ListViewItem item2 = (ListViewItem)obj2;
  216. // Bail out if something is not quite rigth
  217. if ( item1 == null || item2 == null)
  218. return 0;
  219. RowSorterHelper rs = listView.GetRowSorterHelper();
  220. string string1 = item1.Text;
  221. string string2 = item2.Text;
  222. int result = 0;
  223. if ( listView.LastSortedColumn != 0 )
  224. {
  225. // adjust the objets if we have to sort subitems
  226. // This routine gets called the moment an item gets inserted
  227. // even if the column for which we are sorting is not there yet
  228. // To make this routing more resilient, check if the index is
  229. // valid for the subitems collecion, and to even go a step further
  230. // -- since I have seen a bug where the subitems collection "Length" gets
  231. // incremented by more than one element and the bogus element gets to be null--
  232. // check that the subitems is not null even though the collection returns it
  233. if ( listView.LastSortedColumn >= item1.SubItems.Count ||
  234. listView.LastSortedColumn >= item2.SubItems.Count )
  235. return result;
  236. // Don't trust subitems collection
  237. // Check for null objects
  238. if ( item1.SubItems[listView.LastSortedColumn] == null 
  239. || item2.SubItems[listView.LastSortedColumn] == null )
  240. return result;
  241. string1 = item1.SubItems[listView.LastSortedColumn].Text;
  242. string2 = item2.SubItems[listView.LastSortedColumn].Text;
  243. Debug.Assert(string1 != null && string2 != null);
  244. }
  245. if ( rs != null )
  246. {
  247. if ( rs.Format == SortedListViewFormatType.String)
  248. result = CompareStrings(string1, string2, listView.SortOrder);
  249. else if ( rs.Format == SortedListViewFormatType.Numeric )
  250. result = CompareNumbers(string1, string2, listView.SortOrder);
  251. else if ( rs.Format == SortedListViewFormatType.Date )
  252. result = CompareDates(string1, string2, listView.SortOrder);
  253. else if ( rs.Format == SortedListViewFormatType.Custom)
  254. {
  255. if ( rs.SortEvent != null )
  256. {
  257. result = rs.SortEvent((ListViewItem)obj1, (ListViewItem)obj2);
  258. if ( listView.SortOrder == SortOrder.Descending )
  259. result *= -1;
  260. }
  261. }
  262. }
  263. else if ( rs == null )
  264. {
  265. // Consider column as strings
  266. result = CompareStrings(string1, string2, listView.SortOrder);
  267. }
  268. return result;
  269. }
  270. #endregion
  271. #region Implementation
  272. int CompareStrings(string string1, string string2, SortOrder sortOrder)
  273. {
  274. int result = string.Compare(string1, string2);
  275. if ( sortOrder == SortOrder.Descending)
  276. result *= -1;
  277. return result;
  278. }
  279. int CompareNumbers(string string1, string string2, SortOrder sortOrder)
  280. {
  281. // Parse the object as if the were floating number that will take
  282. // care of both cases: integers and floats
  283. // -- exceptions will be thrown if they cannot be parsed
  284. float float1 = float.Parse(string1);
  285. float float2 = float.Parse(string2);
  286. int result = float1.CompareTo(float2);
  287. if ( sortOrder == SortOrder.Descending)
  288. result *= -1;
  289. return result;
  290. }
  291. int CompareDates(string string1, string string2, SortOrder sortOrder)
  292. {
  293. // Parse the object as if the were floating number that will take
  294. // care of both cases: integers and floats
  295. // -- exceptions will be thrown if they cannot be parsed
  296. DateTime date1 = DateTime.Parse(string1);
  297. DateTime date2 = DateTime.Parse(string2);
  298. int result = DateTime.Compare(date1, date2);
  299. if ( sortOrder == SortOrder.Descending)
  300. result *= -1;
  301. return result;
  302. }
  303. #endregion
  304. }
  305. public class ListViewEmbeddedControl
  306. {
  307. #region Class Variables
  308. int row = -1;
  309. int col = -1;
  310. Control control = null;
  311. #endregion
  312. #region Constructor
  313. public ListViewEmbeddedControl(Control control)
  314. {
  315. // Row a Col will be dynamically updated
  316. // to be able to support sorting in combination
  317. // with embedded controls
  318. this.control = control;
  319. }
  320. #endregion
  321. #region Properties
  322. public Control Control
  323. {
  324. get { return control; }
  325. }
  326. public int Row
  327. {
  328. set { row = value; }
  329. get { return row; }
  330. }
  331. public int Col
  332. {
  333. set { col = value; }
  334. get { return col; }
  335. }
  336. #endregion
  337. }
  338.     
  339. #endregion
  340. /// <summary>
  341. /// Summary description for SortableListView.
  342. /// </summary>
  343. [ToolboxItem(false)]
  344. public class ListViewEx : ListView
  345. {
  346. #region Class Variables
  347. // Keeps track of the header control handle
  348. // so that we can distinguish between notification and
  349. // reflected messages
  350. IntPtr hHeader = IntPtr.Zero;
  351. // Keep track of what column was sorted last
  352. int lastSortedColumn = 0;
  353. bool setInitialSortColumn = false;
  354. HeaderHook headerHook = null;
  355. bool tracking = false;
  356. int pressedHeaderItem = -1;
  357. // To keep track if the cursor
  358. // hit a header divider
  359. Point lastMousePosition;
  360. // ImageList for the check boxes
  361. // in case user decide to use checkboxes
  362. ImageList checkBoxesImageList;
  363. // Header Icons
  364. ImageList headerImageList;
  365. ArrayList headerIconsList = new ArrayList();
  366. // Sorting helper 
  367. ArrayList rowSorterList = new ArrayList();
  368. // We only support small 16x16 icons
  369. const int IMAGE_WIDTH = 16;
  370. const int TEXT_TO_ARROW_GAP = 15;
  371. const int ARROW_WIDTH = 12;
  372. const int BUFFER_SIZE = 1024;
  373. // Support to use a custom color for the header
  374. Color headerColor = Color.Empty;
  375. // Don't use the Sorting property of the ListView class
  376. // it has the nasty side effect of causing the header control
  377. // to be destroyed
  378. // Keep track of the what sorting order on this variable
  379. SortOrder sortOrder = SortOrder.Ascending;
  380. bool sortingEnabled = true;
  381. bool paintSortedColumnBackground = true;
  382. // Checkbook look
  383. bool checkBookLookEnabled = false;
  384. Color checkBookOddRowBackColor = Color.Empty;
  385. Color checkBookOddRowForeColor = Color.Empty;
  386. Color checkBookEvenRowBackColor = Color.Empty;
  387. Color checkBookEvenRowForeColor = Color.Empty;
  388. // Embedded Controls
  389. Hashtable embeddedControlsHashTable = null;
  390.         
  391. #endregion
  392. #region Constructors
  393. public ListViewEx()
  394. {
  395. SetStyle(ControlStyles.UserPaint, false);
  396. // Control needs to have full row select and detail
  397. // view enable otherwise it won't behave as intended
  398. FullRowSelect = true;
  399. View = View.Details;
  400. HeaderStyle = ColumnHeaderStyle.Nonclickable;
  401. InitializeCheckBoxesImageList();
  402. // Initialize embedded cell controls
  403. embeddedControlsHashTable = new Hashtable();
  404. }
  405. private void InitializeCheckBoxesImageList()
  406. {
  407. checkBoxesImageList = new ImageList();
  408. checkBoxesImageList.ImageSize = new Size(16, 16);
  409. Assembly thisAssembly = Assembly.GetAssembly(Type.GetType("UtilityLibrary.WinControls.ListViewEx"));
  410. ResourceManager rm = new ResourceManager("UtilityLibrary.Resources.SortedListView", thisAssembly);
  411. Bitmap checkBox = (Bitmap)rm.GetObject("CheckBox");
  412. checkBox.MakeTransparent(Color.FromArgb(0, 128, 128));
  413. checkBoxesImageList.Images.AddStrip(checkBox);
  414. }
  415. #endregion
  416. #region Overrides
  417. protected override void OnHandleCreated(EventArgs e)
  418. {
  419. base.OnHandleCreated(e);
  420. if ( !RecreatingHandle && sortingEnabled )
  421. {
  422. ListViewItemSorter = new CompareListItems(this);
  423. }
  424. // Now that the list control has been created
  425. // get a hold of the header control so that we can
  426. // subclass it
  427. // -- Header control always has a control ID equal zero
  428. hHeader = WindowsAPI.GetDlgItem(Handle, 0);
  429. Debug.Assert(hHeader != IntPtr.Zero, "Fail to get Header Control Windows Handle...");
  430. headerHook = new HeaderHook(this);
  431. headerHook.AssignHandle(hHeader);
  432. // Check if we have any ListViewItemEx instance
  433. // so that we can add the embedded control to the
  434. // ListView controls collection
  435.             UpdateEmbeddedControls();
  436. }
  437. protected override  void WndProc(ref Message message)
  438. {
  439. base.WndProc(ref message);
  440. switch (message.Msg)
  441. {
  442. case (int)Msg.WM_ERASEBKGND:
  443. IntPtr hDC = (IntPtr)message.WParam;
  444. PaintBackground(hDC);
  445. break;
  446. case (int)Msg.WM_VSCROLL:
  447. case (int)Msg.WM_KEYDOWN:
  448. case (int)Msg.WM_MOUSEWHEEL:
  449. OnVerticalScroll(ref message);
  450. break;
  451. // Notification messages come from the header
  452. case (int)Msg.WM_NOTIFY:
  453. NMHDR nm1 = (NMHDR) message.GetLParam(typeof(NMHDR));
  454. switch(nm1.code)
  455. {
  456. case (int)NotificationMessages.NM_CUSTOMDRAW:
  457. NotifyHeaderCustomDraw(ref message); 
  458. break;
  459. case (int)HeaderControlNotifications.HDN_BEGINTRACKW:
  460. Tracking = true;
  461. break;
  462. case (int)HeaderControlNotifications.HDN_ENDTRACKW:
  463. Tracking = false;
  464. if ( embeddedControlsHashTable.Count > 0 )
  465. Invalidate();
  466. // In case we don't received the mouse up message
  467. // because it is up outside the header client area
  468. PressedHeaderItem = -1;
  469. break;
  470. default:
  471. break;
  472. }
  473. break;
  474. // Reflected Messages come from the list itself
  475. case (int)ReflectedMessages.OCM_NOTIFY:
  476. NMHDR nm2 = (NMHDR) message.GetLParam(typeof(NMHDR));
  477. switch (nm2.code)
  478. {
  479. case (int)NotificationMessages.NM_CUSTOMDRAW:
  480. NotifyListCustomDraw(ref message); 
  481. break;
  482. case (int)ListViewNotifications.LVN_GETDISPINFOW:
  483. break;
  484. default:
  485. break;
  486. }
  487. break;
  488. // Default 
  489. default:
  490. break;
  491. }
  492. }
  493. protected override void OnLostFocus(EventArgs e)
  494. {
  495. base.OnLostFocus(e);
  496. if ( HideSelection == false )
  497.                 Invalidate();
  498. }
  499. #endregion
  500. #region Properties
  501. public int InitialSortedColumn
  502. {
  503. set 
  504. if ( setInitialSortColumn == false )
  505. {
  506. setInitialSortColumn = true;
  507. lastSortedColumn = value; 
  508. }
  509. }
  510. }
  511. public ImageList HeaderImageList
  512. {
  513. set { headerImageList = value; }
  514. get { return headerImageList; }
  515. }
  516. public Color HeaderColor
  517. {
  518. set { headerColor = value; }
  519. get { return headerColor; }
  520. }
  521. public SortOrder SortOrder
  522. {
  523. set { sortOrder = value; }
  524. get { return sortOrder; }
  525. }
  526. public bool SortingEnabled
  527. {
  528. set { sortingEnabled = value; }
  529. get { return sortingEnabled; }
  530. }
  531. public bool PaintSortedColumnBackground
  532. {
  533. set { 
  534. if ( paintSortedColumnBackground != value )
  535. {
  536. paintSortedColumnBackground = value;
  537. Invalidate(); 
  538. }
  539. }
  540. get { return paintSortedColumnBackground; }
  541. }
  542. public bool CheckBookLookEnabled
  543. {
  544. set { checkBookLookEnabled = value; }
  545. get { return checkBookLookEnabled; }
  546. }
  547. public Color CheckBookOddRowBackColor
  548. {
  549. set { checkBookOddRowBackColor = value; }
  550. get { return checkBookOddRowBackColor; }
  551. }
  552. public Color CheckBookOddRowForeColor
  553. {
  554. set { checkBookOddRowForeColor = value; }
  555. get { return checkBookOddRowForeColor; }
  556. }
  557. public Color CheckBookEvenRowBackColor
  558. {
  559. set { checkBookEvenRowBackColor = value; }
  560. get { return checkBookEvenRowBackColor; }
  561. }
  562. public Color CheckBookEvenRowForeColor
  563. {
  564. set { checkBookEvenRowForeColor = value; }
  565. get { return checkBookEvenRowForeColor; }
  566. }
  567. #endregion
  568. #region Methods
  569. public void AddEmbeddedControl(ListViewItem.ListViewSubItem subItem, Control control)
  570. {
  571. if ( subItem == null || control == null )
  572. throw new Exception("Invalid parameter");
  573. // Add control and associate the control with the ListViewItem hash code
  574. embeddedControlsHashTable.Add(subItem.GetHashCode(), new ListViewEmbeddedControl(control));
  575.             
  576. }
  577. public void RemoveEmbeddedControl(ListViewItem.ListViewSubItem subItem, Control control)
  578. {
  579. // Check for a valid parameter
  580. if ( subItem == null || control == null )
  581. throw new Exception("Invalid parameter");
  582. // Remove the control associate with this subitem
  583. embeddedControlsHashTable.Remove(subItem.GetHashCode());
  584. }
  585. public void SetHeaderIcon(int headerIndex, int iconIndex)
  586. {
  587. // Associate an specific header with an specific image index
  588. // in the headerImageList
  589. headerIconsList.Add(new HeaderIconHelper(headerIndex, iconIndex));
  590. }
  591. public void SetColumnSortFormat(int columnIndex, SortedListViewFormatType format)
  592. {
  593. rowSorterList.Add(new RowSorterHelper(columnIndex, format));
  594. }
  595. public void SetColumnSortFormat(int columnIndex, SortedListViewFormatType format, ListSortEvent callBack)
  596. {
  597. rowSorterList.Add(new RowSorterHelper(columnIndex, format, callBack));
  598. }
  599. public Rectangle GetHeaderItemRect(int index)
  600. {
  601. RECT rc = new RECT();
  602. WindowsAPI.SendMessage(hHeader, (int)HeaderControlMessages.HDM_GETITEMRECT, index, ref rc);
  603. return new Rectangle(rc.left, rc.top, rc.right-rc.left,rc.bottom-rc.top);
  604. }
  605. public void EnabledResizeRedraw(bool enable)
  606. {
  607. SetStyle(ControlStyles.ResizeRedraw, enable);
  608. }
  609. public void UpdateSubItemBackColor(int row, int col, Color background)
  610. {
  611. // Get the container item
  612. ListViewItem item = Items[row];
  613. Debug.Assert(item != null);
  614. Debug.Assert(col < item.SubItems.Count);
  615. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  616. Debug.Assert(subItem != null);
  617. // Call the function that does the actual work
  618. UpdateSubItem(row, col, background, subItem.ForeColor, subItem.Text, subItem.Font);
  619. }
  620. public void UpdateSubItemForeColor(int row, int col, Color foreground)
  621. {
  622. // Get the container item
  623. ListViewItem item = Items[row];
  624. Debug.Assert(item != null);
  625. Debug.Assert(col < item.SubItems.Count);
  626. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  627. Debug.Assert(subItem != null);
  628. // Call the function that does the actual work
  629. UpdateSubItem(row, col, subItem.BackColor, foreground, subItem.Text, subItem.Font);
  630. }
  631. public void UpdateSubItem(int row, int col, Color background, Color foreground)
  632. {
  633. // Get the container item
  634. ListViewItem item = Items[row];
  635. Debug.Assert(item != null);
  636. Debug.Assert(col < item.SubItems.Count);
  637. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  638. Debug.Assert(subItem != null);
  639. // Call the function that does the actual work
  640. UpdateSubItem(row, col, background, foreground, subItem.Text, subItem.Font);
  641.             
  642. }
  643. public void UpdateSubItem(int row, int col, string text, Font font)
  644. {
  645. // Get the container item
  646. ListViewItem item = Items[row];
  647. Debug.Assert(item != null);
  648. Debug.Assert(col < item.SubItems.Count);
  649. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  650. Debug.Assert(subItem != null);
  651. // Call the function that does the actual work
  652. UpdateSubItem(row, col, subItem.BackColor, subItem.ForeColor, text, font);
  653. }
  654. public void UpdateSubItem(int row, int col, string text)
  655. {
  656. // Get the container item
  657. ListViewItem item = Items[row];
  658. Debug.Assert(item != null);
  659. Debug.Assert(col < item.SubItems.Count);
  660. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  661. Debug.Assert(subItem != null);
  662. // Call the function that does the actual work
  663. UpdateSubItem(row, col, subItem.BackColor, subItem.ForeColor, text, subItem.Font);
  664. }
  665. public void UpdateSubItem(int row, int col, Font font)
  666. {
  667. // Get the container item
  668. ListViewItem item = Items[row];
  669. Debug.Assert(item != null);
  670. Debug.Assert(col < item.SubItems.Count);
  671. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  672. Debug.Assert(subItem != null);
  673. // Call the function that does the actual work
  674. UpdateSubItem(row, col, subItem.BackColor, subItem.ForeColor, subItem.Text, font);
  675. }
  676. #endregion
  677. #region Implementation
  678. #region Properties
  679. internal int LastSortedColumn
  680. {
  681. set { lastSortedColumn = value; Invalidate(); }
  682. get { return lastSortedColumn; }
  683. }
  684. internal bool Tracking
  685. {
  686. set { tracking = value; }
  687. get { return tracking; }
  688. }
  689. internal int PressedHeaderItem
  690. {
  691. set { pressedHeaderItem = value; }
  692. get { return pressedHeaderItem; }
  693. }
  694.         
  695. internal Point LastMousePosition
  696. {
  697. set { lastMousePosition = value; }
  698. get { return lastMousePosition; }
  699. }
  700. #endregion
  701. #region Methods
  702. void OnVerticalScroll(ref Message m)
  703. {
  704.             // Scrolling support for embedded controls
  705. Rectangle viewPort = GetListViewPort();
  706. foreach ( DictionaryEntry de in embeddedControlsHashTable )
  707. {
  708. ListViewEmbeddedControl ec = (ListViewEmbeddedControl)de.Value;
  709. Rectangle rc = GetSubItemRect(ec.Row, ec.Col);
  710. if ( rc.Bottom <= viewPort.Top || rc.Top >= viewPort.Bottom )
  711. // Hide the control if it is out of sight
  712. ec.Control.Visible = false;
  713. }
  714. }
  715. }
  716. Rectangle GetListViewPort()
  717. {
  718. // Calculate the list rectangle only
  719. // including the part where items are actually drawn
  720. // That is excluding the Header control area and the 
  721. // vertical and horizontal scrollbars
  722. Rectangle rc = ClientRectangle;
  723. Rectangle headerRect = GetHeaderCtrlRect();
  724. rc = new Rectangle(rc.Left, rc.Top+headerRect.Height, rc.Width, rc.Height-headerRect.Height);
  725. // Add the difference between the width of the element and the width
  726. // of the header control so that we hide item only when fully hidden
  727. ListViewItem item = TopItem;
  728. if ( item != null )
  729. {
  730. Rectangle firstRow = item.Bounds;
  731. int gap = Math.Abs(firstRow.Height-firstRow.Top);
  732. rc = new Rectangle(rc.Left, rc.Top+gap, rc.Width, rc.Height-gap);
  733. }
  734. return rc;
  735. }
  736. void UpdateEmbeddedControls()
  737. {
  738. foreach (DictionaryEntry de in embeddedControlsHashTable )
  739. {
  740. ListViewEmbeddedControl item = (ListViewEmbeddedControl)de.Value;
  741. Control currentControl = item.Control;
  742. Debug.Assert(currentControl != null);
  743.                 Controls.Add(currentControl);
  744. }
  745. }
  746. void UpdateSubItem(int row, int col, Color background, Color foreground, string text, Font font)
  747. {
  748. // This function smartly update the sub item properties without
  749. // producing the flickering problem that the original ListView displays
  750. // Lock painting
  751. WindowsAPI.SendMessage(Handle, Msg.WM_SETREDRAW, 0, 0);
  752.             
  753. // Get the container item
  754. ListViewItem item = Items[row];
  755. Debug.Assert(item != null);
  756. Debug.Assert(col < item.SubItems.Count);
  757. ListViewItem.ListViewSubItem subItem = item.SubItems[col];
  758. Debug.Assert(subItem != null);
  759. // Change the new value of the subitem properties
  760. subItem.BackColor = background;
  761. subItem.ForeColor = foreground;
  762. subItem.Text = text;
  763. subItem.Font = font;
  764. // Update the invalid region 
  765. WindowsAPI.ValidateRect(Handle, IntPtr.Zero);
  766. Rectangle rc = GetSubItemRect(row,col);
  767. RECT rect = ConversionUtil.RECTFromRectangle(rc);
  768. // Make the invalid region only the area of the sub item
  769. WindowsAPI.InvalidateRect(Handle, ref rect, 0);
  770. // Paint it
  771. WindowsAPI.SendMessage(Handle, Msg.WM_SETREDRAW, 1, 0);
  772. }
  773. void PaintBackground(IntPtr hDC)
  774. {
  775. if ( checkBookLookEnabled || SortingEnabled == false || paintSortedColumnBackground == false )
  776. return;
  777. Graphics g = Graphics.FromHdc(hDC);
  778. if ( lastSortedColumn == -1 )
  779. return;
  780. // If we don't have the column set yet, don't paint it
  781. if ( lastSortedColumn > Columns.Count )
  782. return;
  783. // If we don't have any items, don't paint background yet
  784. // since we won't be able to get the right rectangle for
  785. // the column we are going to paint if there are not items yet
  786. if ( Items.Count == 0 )
  787. return;
  788. Rectangle rc;
  789. if ( lastSortedColumn != 0 )
  790. {
  791. rc = GetSubItemRect(0, lastSortedColumn);
  792. }
  793. else
  794. {
  795. rc = GetSubItemRect(0, lastSortedColumn);
  796. Rectangle headerRect = GetHeaderItemRect(lastSortedColumn);
  797. rc = new Rectangle(headerRect.Left, rc.Top, headerRect.Width, Height);
  798.             }
  799. using ( Brush b = new SolidBrush(Color.FromArgb(247,247,247)) )
  800. {
  801. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, Height);
  802. }
  803. }
  804. bool NotifyListCustomDraw(ref Message m)
  805. {
  806. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_DODEFAULT;
  807. NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW));
  808. IntPtr thisHandle = Handle;
  809. if ( nmcd.hdr.hwndFrom != Handle)
  810. return false;
  811. switch (nmcd.dwDrawStage)
  812. {
  813. case (int)CustomDrawDrawStateFlags.CDDS_PREPAINT:
  814. // Ask for Item painting notifications
  815. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYITEMDRAW;
  816. break;
  817. case (int)CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT:
  818. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYSUBITEMDRAW;
  819. break;
  820. case (int)(CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT | CustomDrawDrawStateFlags.CDDS_SUBITEM):
  821. // Draw background
  822. DoListCustomDrawing(ref m);
  823. break;
  824. default:
  825. break;
  826. }
  827. return false;
  828. }
  829. void DoListCustomDrawing(ref Message m)
  830. {
  831. NMLVCUSTOMDRAW lvcd = (NMLVCUSTOMDRAW)m.GetLParam(typeof(NMLVCUSTOMDRAW));
  832. int row = (int)lvcd.nmcd.dwItemSpec;
  833. int physicalCol = lvcd.iSubItem;
  834. // Get actual item index
  835. int logicalCol = OrderToIndex(physicalCol);
  836.                        
  837. // If we don't have any items we must be doing something wrong
  838. // because the list is only going to request custom drawing of items
  839. // in the list, if we have items in the list, the Items cannot possibly
  840. // be zero -- probably the user is using a different thread to update the items
  841. // instead of serializing the update through the UI Thread
  842. Debug.Assert(Items.Count != 0);
  843. ListViewItem lvi = Items[row];
  844. Rectangle rc;
  845. if ( logicalCol == 0 && physicalCol != logicalCol )
  846. {
  847. rc = GetSubItemRect(row, physicalCol);
  848. Rectangle headerRect = GetHeaderItemRect(logicalCol);
  849. rc = new Rectangle(headerRect.Left, rc.Top, headerRect.Width, rc.Height);
  850. }
  851. else
  852. rc = GetSubItemRect(row, logicalCol);
  853. // Draw the item
  854. // We did not need to actually paint the items that are not selected
  855. // but doing all the painting ourselves eliminates some random bugs where
  856. // the list sometimes did not update a subitem  that was not selected anymore
  857. // leaving the subitem with a different background color 
  858. // than the rest of the row
  859. Graphics g = Graphics.FromHdc(lvcd.nmcd.hdc);
  860. ListViewItem.ListViewSubItem subItem = null;
  861. subItem = Items[row].SubItems[logicalCol];
  862. Debug.Assert(subItem != null);
  863. Font  subItemFont = subItem.Font;
  864. Color subItemBackColor = subItem.BackColor;
  865. Color subItemForeColor = subItem.ForeColor;
  866.           ListViewEmbeddedControl ec = GetEmbeddedControl(subItem);
  867. if ( ec != null )
  868. {
  869. // just paint the background to cleanup any previous selection
  870. // Paint this subitem background
  871. PaintSubItemBackground(g, row, logicalCol, physicalCol, rc, subItemBackColor);
  872. // We don't need to do anything with the control
  873. // when it comes to painting as it will paint itself
  874.                 // all we need to do is to resize it and exit
  875. // Put structure back in the message
  876. Control control = ec.Control;
  877. control.Bounds = new Rectangle(rc.Left+1, rc.Top+1, rc.Width-2, rc.Height-2);
  878. IListViewEmbeddedControl iec = control as IListViewEmbeddedControl;
  879. if ( iec != null && Tracking == true)
  880. {
  881. // If this control implement fast rendering
  882. // and we are actually tracking column resizing
  883. // paint the control using the FastRender method
  884. control.Visible = false;
  885. iec.FastRender(g, control.Bounds);
  886. }
  887. else
  888. {
  889. if ( control.Visible == false )
  890. control.Visible = true;
  891. }
  892. // Update row and col
  893. ec.Row = row;
  894. ec.Col = physicalCol;
  895. Marshal.StructureToPtr(lvcd, m.LParam, true);
  896. m.Result =  (IntPtr)CustomDrawReturnFlags.CDRF_SKIPDEFAULT;
  897. return;
  898. }
  899. // Change foreground color if we need to
  900. if ( checkBookLookEnabled )
  901. {
  902. bool even = (row % 2) == 0;
  903. Color textColor = Color.Empty;
  904. if ( even )
  905. textColor = checkBookEvenRowForeColor;
  906. else
  907. textColor = checkBookOddRowForeColor;
  908. if ( textColor == Color.Empty || IsRowSelected(row))
  909. // Use System Color for odd rows
  910. textColor = SystemColors.ControlText;
  911. subItemForeColor = textColor;
  912. }
  913. // Paint this subitem background
  914. PaintSubItemBackground(g, row, logicalCol, physicalCol, rc, subItemBackColor);
  915. // Draw Text
  916. string text = GetSubItemText(row, logicalCol);
  917. Size textSize = TextUtil.GetTextSize(g, text, Font);
  918. int gap = 4;
  919. Point pos = new Point(rc.Left+gap ,rc.Top + ((rc.Height - textSize.Height) / 2));
  920. // I use the Windows API instead of the Graphics object to draw the string
  921. // because the Graphics object draws ellipes without living blank spaces in between
  922. // the DrawText API adds those blank spaces in between 
  923. int ellipsingTringgering = 8;
  924. if ( CheckBoxes && logicalCol == 0 )
  925. {
  926. // draw checkbox
  927. int checkIndex = 0;
  928. if ( lvi.Checked)
  929. checkIndex = 1;
  930. g.DrawImage(checkBoxesImageList.Images[checkIndex], rc.Left + gap, rc.Top);
  931. pos.X += IMAGE_WIDTH;
  932. rc.Width = rc.Width - IMAGE_WIDTH; 
  933. }
  934. else if ( logicalCol == 0 && CheckBoxes == false && lvi.ImageIndex != -1 && lvi.ImageList != null )
  935. {
  936. ImageList imageList = lvi.ImageList;
  937. Image image = imageList.Images[lvi.ImageIndex];
  938. if ( image != null )
  939. {
  940. g.DrawImage(imageList.Images[lvi.ImageIndex], rc.Left + gap, rc.Top);
  941. pos.X += IMAGE_WIDTH;
  942. rc.Width = rc.Width - IMAGE_WIDTH; 
  943. }
  944. }
  945. Rectangle drawRect = new Rectangle(pos.X+2, pos.Y, rc.Width-gap-ellipsingTringgering, rc.Height);
  946. if ( subItemForeColor != Color.Empty )
  947. {
  948. TextUtil.DrawText(g, text, subItemFont, drawRect, subItemForeColor);
  949. }
  950. else
  951. {
  952. TextUtil.DrawText(g, text, subItemFont, drawRect);
  953. }
  954. g.Dispose();
  955. // Put structure back in the message
  956. Marshal.StructureToPtr(lvcd, m.LParam, true);
  957. m.Result =  (IntPtr)CustomDrawReturnFlags.CDRF_SKIPDEFAULT;
  958. }
  959. ListViewEmbeddedControl GetEmbeddedControl(ListViewItem.ListViewSubItem subItem)
  960. {
  961. int hashCode = subItem.GetHashCode();
  962. return (ListViewEmbeddedControl)embeddedControlsHashTable[hashCode];
  963.             
  964. }
  965. void PaintSubItemBackground(Graphics g, int row, int logicalCol, int physicalCol, Rectangle rc, Color subItemBackColor)
  966. {
  967. int lastColumn = Columns.Count;
  968. int subItemOffset = 2;
  969. if ( GridLines )
  970. {
  971. subItemOffset = 3;
  972. }
  973.             
  974. // Draw Fill Rectangle
  975. if ( IsRowSelected(row) )
  976. {
  977. if ( Focused || checkBookLookEnabled )
  978. {
  979. // Control has focus
  980. Debug.WriteLine("Control has focus...");
  981. int width = rc.Width;
  982. if ( physicalCol == lastColumn-1 )
  983. width = width - 1;
  984. if ( subItemBackColor != SystemColors.Window && checkBookLookEnabled == false )
  985. {
  986. using ( Brush b = new SolidBrush(subItemBackColor) )
  987. {
  988. g.FillRectangle(b, rc.Left, rc.Top+1, width, rc.Height-subItemOffset);
  989. }
  990. }
  991. else
  992. {
  993. using ( Brush b = new SolidBrush(ColorUtil.VSNetSelectionColor) )
  994. {
  995. g.FillRectangle(b, rc.Left, rc.Top+1, width, rc.Height-subItemOffset);
  996. }
  997. }
  998. // Draw Border
  999. if ( physicalCol == 0 )
  1000. {
  1001. Color borderColor = ColorUtil.VSNetBorderColor;
  1002. Rectangle rcBorder = GetRowRect(row);
  1003. int heightOffset = 1;
  1004. if ( GridLines )
  1005. heightOffset = 2;
  1006. using ( Pen p = new Pen(borderColor) )
  1007. {
  1008. g.DrawRectangle(p, rcBorder.Left+1, rcBorder.Top, rcBorder.Width-2, rcBorder.Height-heightOffset);
  1009. }
  1010. }
  1011. }
  1012. else if ( !HideSelection)
  1013. {
  1014. // If the list does not has the focus
  1015. // but we don't hide the selection
  1016. if ( subItemBackColor != SystemColors.Window && checkBookLookEnabled == false)
  1017. {
  1018. using ( Brush b = new SolidBrush(subItemBackColor) )
  1019. {
  1020. g.FillRectangle(b, rc.Left, rc.Top+1, rc.Width, rc.Height-subItemOffset);
  1021. }
  1022. }
  1023. else
  1024. {
  1025. using ( Brush b = new SolidBrush(ColorUtil.VSNetControlColor) )
  1026. {
  1027. g.FillRectangle(b, rc.Left, rc.Top+1, rc.Width, rc.Height-subItemOffset);
  1028. }
  1029. }
  1030. }
  1031. else 
  1032. {
  1033. // Hide selection but paint subitem if it has a color set
  1034. if ( subItemBackColor != SystemColors.Window)
  1035. {
  1036. using ( Brush b = new SolidBrush(subItemBackColor) )
  1037. {
  1038. g.FillRectangle(b, rc.Left, rc.Top+1, rc.Width, rc.Height-subItemOffset);
  1039. }
  1040. }
  1041. }
  1042. }
  1043. else 
  1044. {
  1045. if ( checkBookLookEnabled )
  1046. {
  1047. bool even = (row % 2) == 0;
  1048. Color rowColor = Color.Empty;
  1049. if ( even )
  1050. {
  1051. rowColor = checkBookEvenRowBackColor;
  1052. if ( rowColor == Color.Empty )
  1053. {
  1054. // Use control color for even rows
  1055. rowColor = ColorUtil.VSNetControlColor;
  1056. }
  1057. }
  1058. else
  1059. {
  1060. rowColor = checkBookOddRowBackColor;
  1061. if ( rowColor == Color.Empty )
  1062. {
  1063. // Use System Color for odd rows
  1064. rowColor = SystemColors.Window;
  1065. }
  1066. }
  1067. using ( Brush b = new SolidBrush(rowColor) )
  1068. {
  1069. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1070. }
  1071. }
  1072. else
  1073. {
  1074. if ( logicalCol == lastSortedColumn )
  1075. {
  1076. if ( subItemBackColor != SystemColors.Window)
  1077. {
  1078. using ( Brush b = new SolidBrush(subItemBackColor) )
  1079. {
  1080. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1081. }
  1082. }
  1083. else
  1084. {
  1085. if ( sortingEnabled && paintSortedColumnBackground )
  1086. {
  1087. using ( Brush b = new SolidBrush(Color.FromArgb(247,247,247)) )
  1088. {
  1089. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1090. }
  1091. }
  1092. else
  1093. {
  1094. using ( Brush b = new SolidBrush(SystemColors.Window) )
  1095. {
  1096. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1097. }
  1098. }
  1099. }
  1100. }
  1101. else 
  1102. {
  1103. if ( subItemBackColor != SystemColors.Window)
  1104. {
  1105. using ( Brush b = new SolidBrush(subItemBackColor) )
  1106. {
  1107. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1108. }
  1109. }
  1110. else
  1111. {
  1112. using ( Brush b = new SolidBrush(SystemColors.Window) )
  1113. {
  1114. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1115. }
  1116. }
  1117. }
  1118. }
  1119. }
  1120. }
  1121. bool NotifyHeaderCustomDraw(ref Message m)
  1122. {
  1123. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_DODEFAULT;
  1124. NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW));
  1125. if ( nmcd.hdr.hwndFrom != hHeader)
  1126. return false;
  1127. switch (nmcd.dwDrawStage)
  1128. {
  1129. case (int)CustomDrawDrawStateFlags.CDDS_PREPAINT:
  1130. // Ask for Item painting notifications
  1131. m.Result = (IntPtr)CustomDrawReturnFlags.CDRF_NOTIFYITEMDRAW;
  1132. break;
  1133. case (int)CustomDrawDrawStateFlags.CDDS_ITEMPREPAINT:
  1134. DoHeaderCustomDrawing(ref m);
  1135. break;
  1136. case (int)NotificationMessages.NM_NCHITTEST:
  1137. break;
  1138. default:
  1139. break;
  1140. }
  1141. return false;
  1142. }
  1143. void DoHeaderCustomDrawing(ref Message m)
  1144. {
  1145. NMCUSTOMDRAW nmcd = (NMCUSTOMDRAW)m.GetLParam(typeof(NMCUSTOMDRAW));
  1146. Graphics g = Graphics.FromHdc(nmcd.hdc);
  1147. Rectangle rc = GetHeaderCtrlRect();
  1148. int col = (int)nmcd.dwItemSpec;
  1149. rc = GetHeaderItemRect(col);
  1150. int itemRight = rc.Left + rc.Width;
  1151. using ( Brush b = new SolidBrush(SystemColors.ScrollBar) )
  1152. {
  1153. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1154. }
  1155. if ( col == PressedHeaderItem && !IsCursorOnDivider() && Tracking == false )
  1156. {
  1157. PressedHeaderItem = -1;
  1158. rc.Inflate(-1, -1);
  1159. using ( Brush b = new SolidBrush(ColorUtil.VSNetPressedColor) )
  1160. {
  1161. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1162. }
  1163. }
  1164. else
  1165. {
  1166. if ( headerColor == Color.Empty )
  1167. {
  1168. ControlPaint.DrawBorder3D(g, rc.Left, rc.Top, rc.Width, 
  1169. rc.Height-1, Border3DStyle.RaisedInner, Border3DSide.All);
  1170. }
  1171. else 
  1172. {
  1173. using ( Pen p = new Pen(ColorUtil.VSNetBorderColor))
  1174. {
  1175. g.DrawRectangle(p, rc.Left, rc.Top, rc.Width, rc.Height);
  1176. }
  1177. using ( Brush b = new SolidBrush(ColorUtil.VSNetControlColor) )
  1178. {
  1179. rc.Inflate(-1,-1);
  1180. g.FillRectangle(b, rc.Left, rc.Top, rc.Width, rc.Height);
  1181. }
  1182. }
  1183. }
  1184. string text = GetHeaderItemText(col);
  1185. Size textSize = TextUtil.GetTextSize(g, text, Font);
  1186. int gap = 4;
  1187. Point pos = new Point(rc.Left+gap ,rc.Top + ((rc.Height - textSize.Height) / 2));
  1188. int headerImageIndex;
  1189. if ( headerIconsList != null && HasHeaderImage(col, out headerImageIndex) )
  1190. {
  1191. if ( headerImageIndex != -1 )
  1192. {
  1193. Image image = headerImageList.Images[headerImageIndex];
  1194. if ( image != null )
  1195. {
  1196. int top = (rc.Height - image.Height)/2;
  1197. g.DrawImage(headerImageList.Images[headerImageIndex], rc.Left + gap, top);
  1198. pos.X += IMAGE_WIDTH;
  1199. rc.Width = rc.Width - IMAGE_WIDTH;
  1200. }
  1201. }
  1202. }
  1203. // Draw arrow glyph
  1204. if ( sortingEnabled && col == lastSortedColumn)
  1205. {
  1206. int Left = pos.X+2;
  1207. Left += textSize.Width + TEXT_TO_ARROW_GAP;
  1208. Rectangle arrowRect = new Rectangle(Left, rc.Top, ARROW_WIDTH, rc.Height); 
  1209. if ( itemRight >= (Left + ARROW_WIDTH + 4) ) 
  1210. {
  1211. if ( SortOrder == SortOrder.Ascending  || SortOrder == SortOrder.None )
  1212. DrawUpArrow(g, arrowRect);
  1213. else
  1214. DrawDownArrow(g, arrowRect);
  1215. }
  1216. }
  1217. // I use the Windows API instead of the Graphics object to draw the string
  1218. // because the Graphics object draws ellipes without living blank spaces in between
  1219. // the DrawText API adds those blank spaces in between 
  1220. int ellipsingTringgering = 8;
  1221. Rectangle drawRect = new Rectangle(pos.X+2, pos.Y, rc.Width-gap-ellipsingTringgering, rc.Height);
  1222. TextUtil.DrawText(g, text, Font, drawRect);
  1223. g.Dispose();
  1224.           
  1225. m.Result =  (IntPtr)CustomDrawReturnFlags.CDRF_SKIPDEFAULT;
  1226.           
  1227. }
  1228. bool HasHeaderImage(int headerIndex, out int imageIndex)
  1229. {
  1230. imageIndex = -1;
  1231. for ( int i = 0; i < headerIconsList.Count; i++ )
  1232. {
  1233. HeaderIconHelper hih = (HeaderIconHelper)headerIconsList[i];
  1234. if ( hih != null && hih.HeaderIndex == headerIndex )
  1235. {
  1236. imageIndex = hih.IconIndex;
  1237. return true;
  1238. }
  1239. }
  1240. return false;
  1241. }
  1242. void DrawUpArrow(Graphics g, Rectangle rc)
  1243. {
  1244. int xTop = rc.Left + rc.Width/2;
  1245. int yTop = (rc.Height - 6)/2;
  1246.             
  1247. int xLeft = xTop - 6;
  1248. int yLeft = yTop + 6;
  1249.             
  1250. int xRight = xTop + 6;
  1251. int yRight = yTop + 6;
  1252. using ( Pen p = new Pen(SystemColors.ControlDarkDark))
  1253. {
  1254. g.DrawLine(p, xLeft, yLeft, xTop, yTop);
  1255. }
  1256. using ( Pen p = new Pen(Color.White) )
  1257. {
  1258. g.DrawLine(p, xRight, yRight, xTop, yTop);
  1259. }
  1260. using ( Pen p = new Pen(Color.White) )
  1261. {
  1262. g.DrawLine(p, xLeft, yLeft, xRight, yRight);
  1263. }
  1264. }
  1265. void DrawDownArrow(Graphics g, Rectangle rc)
  1266. {
  1267. int xBottom = rc.Left + rc.Width/2;
  1268.             
  1269. int xLeft = xBottom - 6;
  1270. int yLeft = (rc.Height - 6)/2;;
  1271.             
  1272. int xRight = xBottom + 6;
  1273. int yRight = (rc.Height - 6)/2;
  1274. int yBottom = yRight + 6;
  1275. using ( Pen p = new Pen(SystemColors.ControlDarkDark) )
  1276. {
  1277. g.DrawLine(p, xLeft, yLeft, xBottom, yBottom);
  1278. }
  1279. using ( Pen p = new Pen(Color.White) )
  1280. {
  1281. g.DrawLine(p, xRight, yRight, xBottom, yBottom);
  1282. }
  1283. using ( Pen p = new Pen(SystemColors.ControlDarkDark) )
  1284. {
  1285. g.DrawLine(p, xLeft, yLeft, xRight, yRight);
  1286. }
  1287. }
  1288.        
  1289. string GetSubItemText(int row, int col)
  1290. {
  1291. // I am going to use the Windows API since using the .NET
  1292. // ListViewSubItem.Text property is causing the nasty side
  1293. // effect of changing the text when I draw the string using TextUtil.DrawText,
  1294. // even though that is not my intention at all.
  1295. // I am not sure about why this is happening but using the API solves the problem
  1296. LVITEM lvi = new LVITEM();
  1297. lvi.iItem = row;
  1298. lvi.mask = ListViewItemFlags.LVIF_TEXT;
  1299. lvi.iSubItem = col;
  1300. lvi.cchTextMax = BUFFER_SIZE;
  1301. lvi.pszText = Marshal.AllocHGlobal(BUFFER_SIZE);
  1302. WindowsAPI.SendMessage(Handle, ListViewMessages.LVM_GETITEMTEXTW, row, ref lvi);
  1303. string text = Marshal.PtrToStringAuto(lvi.pszText);
  1304. return text;
  1305. }
  1306. Rectangle GetSubItemRect(int row, int col)
  1307. {
  1308. RECT rc = new RECT();
  1309. rc.top = col;
  1310. rc.left = (int)SubItemPortion.LVIR_BOUNDS;
  1311. WindowsAPI.SendMessage(Handle, (int)ListViewMessages.LVM_GETSUBITEMRECT,  row, ref rc);
  1312. if ( col == 0 )
  1313. {
  1314. // The LVM_GETSUBITEMRECT message does not give us the rectangle for the first subitem
  1315. // since it is not considered a subitem
  1316. // obtain the rectangle for the header control and calculate from there
  1317. Rectangle headerRect = GetHeaderItemRect(col);
  1318. return new Rectangle(rc.left, rc.top, headerRect.Width, rc.bottom-rc.top);
  1319. }
  1320. return new Rectangle(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
  1321. }
  1322. Rectangle GetRowRect(int row)
  1323. {
  1324. RECT rc = new RECT();
  1325. rc.top = 0;
  1326. rc.left = (int)SubItemPortion.LVIR_BOUNDS;
  1327. WindowsAPI.SendMessage(Handle, (int)ListViewMessages.LVM_GETSUBITEMRECT,  row, ref rc);
  1328. return new Rectangle(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
  1329. }
  1330. bool IsRowSelected(int row)
  1331. {
  1332. Debug.Assert(row >= 0 && row < Items.Count);
  1333. ListViewItem lvi = Items[row];
  1334. return lvi.Selected;
  1335. }
  1336. Rectangle GetHeaderCtrlRect()
  1337. {
  1338. RECT rc = new RECT();
  1339. WindowsAPI.GetClientRect(hHeader, ref rc);
  1340. return new Rectangle(rc.left, rc.top, rc.right-rc.left,rc.bottom-rc.top);
  1341. }
  1342. protected bool IsCursorOnDivider()
  1343. {
  1344. HD_HITTESTINFO hti = new HD_HITTESTINFO();
  1345. hti.pt.x = LastMousePosition.X;
  1346. hti.pt.y = LastMousePosition.Y;
  1347. WindowsAPI.SendMessage(hHeader, HeaderControlMessages.HDM_HITTEST, 0, ref hti); 
  1348. bool hit = (hti.flags == HeaderControlHitTestFlags.HHT_ONDIVIDER);
  1349. return hit;
  1350. }
  1351. protected string GetHeaderItemText(int index)
  1352. {
  1353. // I get the bug that I get on the ListView if 
  1354. // I use the columns collection to retreive the text
  1355. // That's why I prefer to use the Windows API
  1356. HDITEM hdi = new HDITEM();
  1357. hdi.mask = HeaderItemFlags.HDI_TEXT;
  1358. hdi.cchTextMax =  BUFFER_SIZE;
  1359. hdi.pszText = Marshal.AllocHGlobal(BUFFER_SIZE);
  1360. WindowsAPI.SendMessage(hHeader, HeaderControlMessages.HDM_GETITEMW, index, ref hdi);
  1361. string text = Marshal.PtrToStringAuto(hdi.pszText);
  1362. return text;
  1363. }
  1364. int IndexToOrder(int index)
  1365. {
  1366. for ( int i = 0; i < Columns.Count; i++ )
  1367. {
  1368. int currentIndex = OrderToIndex(i);
  1369. if ( index == currentIndex )
  1370. return i;
  1371. }
  1372. // Should not get here
  1373. Debug.Assert(false);
  1374. return -1;
  1375. }
  1376. internal RowSorterHelper GetRowSorterHelper()
  1377. {
  1378. for ( int i = 0; i < rowSorterList.Count; i++ )
  1379. {
  1380. RowSorterHelper rs = (RowSorterHelper)rowSorterList[i];
  1381. if ( rs != null && rs.ColumnIndex == LastSortedColumn )
  1382. {
  1383. return rs;
  1384. }
  1385. }
  1386. return null;
  1387. }
  1388. internal int OrderToIndex(int order)
  1389. {
  1390. int result = WindowsAPI.SendMessage(hHeader, HeaderControlMessages.HDM_ORDERTOINDEX, order, 0);
  1391. return result;
  1392. }
  1393. internal void MakeEmbeddedControlsInvisible()
  1394. {
  1395. foreach (DictionaryEntry de in embeddedControlsHashTable )
  1396. {
  1397. ListViewEmbeddedControl item = (ListViewEmbeddedControl)de.Value;
  1398. Control currentControl = item.Control;
  1399. Debug.Assert(currentControl != null);
  1400. currentControl.Visible = false;
  1401. }
  1402. }
  1403. #endregion 
  1404. #endregion
  1405. }
  1406. }