/GDFlacToolVS2008/ObjectListView2010/OLVColumn.cs

http://gdflactool.codeplex.com · C# · 1494 lines · 764 code · 139 blank · 591 comment · 162 complexity · c3e0817cce2c29b7e709062b87b75a1b MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * OLVColumn - A column in an ObjectListView
  3. *
  4. * Author: Phillip Piper
  5. * Date: 31-March-2011 5:53 pm
  6. *
  7. * Change log:
  8. * 2011-05-27 JPP - Added Sortable, Hideable, Groupable, Searchable, ShowTextInHeader properties
  9. * 2011-04-12 JPP - Added HasFilterIndicator
  10. * 2011-03-31 JPP - Split into its own file
  11. *
  12. * Copyright (C) 2011-2012 Phillip Piper
  13. *
  14. * This program is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU General Public License as published by
  16. * the Free Software Foundation, either version 3 of the License, or
  17. * (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. *
  27. * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
  28. */
  29. using System;
  30. using System.ComponentModel;
  31. using System.Windows.Forms;
  32. using System.Drawing;
  33. using System.Collections;
  34. using System.Diagnostics;
  35. namespace BrightIdeasSoftware {
  36. /// <summary>
  37. /// An OLVColumn knows which aspect of an object it should present.
  38. /// </summary>
  39. /// <remarks>
  40. /// The column knows how to:
  41. /// <list type="bullet">
  42. /// <item><description>extract its aspect from the row object</description></item>
  43. /// <item><description>convert an aspect to a string</description></item>
  44. /// <item><description>calculate the image for the row object</description></item>
  45. /// <item><description>extract a group "key" from the row object</description></item>
  46. /// <item><description>convert a group "key" into a title for the group</description></item>
  47. /// </list>
  48. /// <para>For sorting to work correctly, aspects from the same column
  49. /// must be of the same type, that is, the same aspect cannot sometimes
  50. /// return strings and other times integers.</para>
  51. /// </remarks>
  52. [Browsable(false)]
  53. public partial class OLVColumn : ColumnHeader {
  54. #region Life and death
  55. /// <summary>
  56. /// Create an OLVColumn
  57. /// </summary>
  58. public OLVColumn() {
  59. }
  60. /// <summary>
  61. /// Initialize a column to have the given title, and show the given aspect
  62. /// </summary>
  63. /// <param name="title">The title of the column</param>
  64. /// <param name="aspect">The aspect to be shown in the column</param>
  65. public OLVColumn(string title, string aspect)
  66. : this() {
  67. this.Text = title;
  68. this.AspectName = aspect;
  69. }
  70. #endregion
  71. #region Public Properties
  72. /// <summary>
  73. /// This delegate will be used to extract a value to be displayed in this column.
  74. /// </summary>
  75. /// <remarks>
  76. /// If this is set, AspectName is ignored.
  77. /// </remarks>
  78. [Browsable(false),
  79. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  80. public AspectGetterDelegate AspectGetter {
  81. get { return aspectGetter; }
  82. set { aspectGetter = value; }
  83. }
  84. private AspectGetterDelegate aspectGetter;
  85. /// <summary>
  86. /// Remember if this aspect getter for this column was generated internally, and can therefore
  87. /// be regenerated at will
  88. /// </summary>
  89. [Obsolete("This property is no longer maintained", true),
  90. Browsable(false),
  91. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  92. public bool AspectGetterAutoGenerated {
  93. get { return aspectGetterAutoGenerated; }
  94. set { aspectGetterAutoGenerated = value; }
  95. }
  96. private bool aspectGetterAutoGenerated;
  97. /// <summary>
  98. /// The name of the property or method that should be called to get the value to display in this column.
  99. /// This is only used if a ValueGetterDelegate has not been given.
  100. /// </summary>
  101. /// <remarks>This name can be dotted to chain references to properties or parameter-less methods.</remarks>
  102. /// <example>"DateOfBirth"</example>
  103. /// <example>"Owner.HomeAddress.Postcode"</example>
  104. [Category("ObjectListView"),
  105. Description("The name of the property or method that should be called to get the aspect to display in this column"),
  106. DefaultValue(null)]
  107. public string AspectName {
  108. get { return aspectName; }
  109. set {
  110. aspectName = value;
  111. this.aspectMunger = null;
  112. }
  113. }
  114. private string aspectName;
  115. /// <summary>
  116. /// This delegate will be used to put an edited value back into the model object.
  117. /// </summary>
  118. /// <remarks>
  119. /// This does nothing if IsEditable == false.
  120. /// </remarks>
  121. [Browsable(false),
  122. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  123. public AspectPutterDelegate AspectPutter {
  124. get { return aspectPutter; }
  125. set { aspectPutter = value; }
  126. }
  127. private AspectPutterDelegate aspectPutter;
  128. /// <summary>
  129. /// The delegate that will be used to translate the aspect to display in this column into a string.
  130. /// </summary>
  131. /// <remarks>If this value is set, AspectToStringFormat will be ignored.</remarks>
  132. [Browsable(false),
  133. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  134. public AspectToStringConverterDelegate AspectToStringConverter {
  135. get { return aspectToStringConverter; }
  136. set { aspectToStringConverter = value; }
  137. }
  138. private AspectToStringConverterDelegate aspectToStringConverter;
  139. /// <summary>
  140. /// This format string will be used to convert an aspect to its string representation.
  141. /// </summary>
  142. /// <remarks>
  143. /// This string is passed as the first parameter to the String.Format() method.
  144. /// This is only used if AspectToStringConverter has not been set.</remarks>
  145. /// <example>"{0:C}" to convert a number to currency</example>
  146. [Category("ObjectListView"),
  147. Description("The format string that will be used to convert an aspect to its string representation"),
  148. DefaultValue(null)]
  149. public string AspectToStringFormat {
  150. get { return aspectToStringFormat; }
  151. set { aspectToStringFormat = value; }
  152. }
  153. private string aspectToStringFormat;
  154. /// <summary>
  155. /// Gets or sets whether the cell editor should use AutoComplete
  156. /// </summary>
  157. [Category("ObjectListView"),
  158. Description("Should the editor for cells of this column use AutoComplete"),
  159. DefaultValue(true)]
  160. public bool AutoCompleteEditor {
  161. get { return this.AutoCompleteEditorMode != AutoCompleteMode.None; }
  162. set {
  163. if (value) {
  164. if (this.AutoCompleteEditorMode == AutoCompleteMode.None)
  165. this.AutoCompleteEditorMode = AutoCompleteMode.Append;
  166. } else
  167. this.AutoCompleteEditorMode = AutoCompleteMode.None;
  168. }
  169. }
  170. /// <summary>
  171. /// Gets or sets whether the cell editor should use AutoComplete
  172. /// </summary>
  173. [Category("ObjectListView"),
  174. Description("Should the editor for cells of this column use AutoComplete"),
  175. DefaultValue(AutoCompleteMode.Append)]
  176. public AutoCompleteMode AutoCompleteEditorMode {
  177. get { return autoCompleteEditorMode; }
  178. set { autoCompleteEditorMode = value; }
  179. }
  180. private AutoCompleteMode autoCompleteEditorMode = AutoCompleteMode.Append;
  181. /// <summary>
  182. /// Gets whether this column can be hidden by user actions
  183. /// </summary>
  184. /// <remarks>This take into account both the Hideable property and whether this column
  185. /// is the primary column of the listview (column 0).</remarks>
  186. [Browsable(false)]
  187. public bool CanBeHidden {
  188. get {
  189. return this.Hideable && (this.Index != 0);
  190. }
  191. }
  192. /// <summary>
  193. /// Gets or sets whether this column will show a checkbox.
  194. /// </summary>
  195. /// <remarks>
  196. /// Setting this on column 0 has no effect. Column 0 check box is controlled
  197. /// by the CheckBoxes property on the ObjectListView itself.
  198. /// </remarks>
  199. [Category("ObjectListView"),
  200. Description("Should values in this column be treated as a checkbox, rather than a string?"),
  201. DefaultValue(false)]
  202. public virtual bool CheckBoxes {
  203. get { return checkBoxes; }
  204. set {
  205. if (this.checkBoxes == value)
  206. return;
  207. this.checkBoxes = value;
  208. if (this.checkBoxes) {
  209. if (this.Renderer == null)
  210. this.Renderer = new CheckStateRenderer();
  211. } else {
  212. if (this.Renderer is CheckStateRenderer)
  213. this.Renderer = null;
  214. }
  215. }
  216. }
  217. private bool checkBoxes;
  218. /// <summary>
  219. /// Gets or sets the clustering strategy used for this column.
  220. /// </summary>
  221. /// <remarks>
  222. /// <para>
  223. /// The clustering strategy is used to build a Filtering menu for this item.
  224. /// If this is null, a useful default will be chosen.
  225. /// </para>
  226. /// <para>
  227. /// To disable filtering on this colummn, set UseFiltering to false.
  228. /// </para>
  229. /// <para>
  230. /// Cluster strategies belong to a particular column. The same instance
  231. /// cannot be shared between multiple columns.
  232. /// </para>
  233. /// </remarks>
  234. [Browsable(false),
  235. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  236. public IClusteringStrategy ClusteringStrategy {
  237. get {
  238. if (this.clusteringStrategy == null)
  239. this.ClusteringStrategy = this.DecideDefaultClusteringStrategy();
  240. return clusteringStrategy;
  241. }
  242. set {
  243. this.clusteringStrategy = value;
  244. if (this.clusteringStrategy != null)
  245. this.clusteringStrategy.Column = this;
  246. }
  247. }
  248. private IClusteringStrategy clusteringStrategy;
  249. /// <summary>
  250. /// Should this column resize to fill the free space in the listview?
  251. /// </summary>
  252. /// <remarks>
  253. /// <para>
  254. /// If you want two (or more) columns to equally share the available free space, set this property to True.
  255. /// If you want this column to have a larger or smaller share of the free space, you must
  256. /// set the FreeSpaceProportion property explicitly.
  257. /// </para>
  258. /// <para>
  259. /// Space filling columns are still governed by the MinimumWidth and MaximumWidth properties.
  260. /// </para>
  261. /// /// </remarks>
  262. [Category("ObjectListView"),
  263. Description("Will this column resize to fill unoccupied horizontal space in the listview?"),
  264. DefaultValue(false)]
  265. public bool FillsFreeSpace {
  266. get { return this.FreeSpaceProportion > 0; }
  267. set { this.freeSpaceProportion = value ? 1 : 0; }
  268. }
  269. /// <summary>
  270. /// What proportion of the unoccupied horizontal space in the control should be given to this column?
  271. /// </summary>
  272. /// <remarks>
  273. /// <para>
  274. /// There are situations where it would be nice if a column (normally the rightmost one) would expand as
  275. /// the list view expands, so that as much of the column was visible as possible without having to scroll
  276. /// horizontally (you should never, ever make your users have to scroll anything horizontally!).
  277. /// </para>
  278. /// <para>
  279. /// A space filling column is resized to occupy a proportion of the unoccupied width of the listview (the
  280. /// unoccupied width is the width left over once all the the non-filling columns have been given their space).
  281. /// This property indicates the relative proportion of that unoccupied space that will be given to this column.
  282. /// The actual value of this property is not important -- only its value relative to the value in other columns.
  283. /// For example:
  284. /// <list type="bullet">
  285. /// <item><description>
  286. /// If there is only one space filling column, it will be given all the free space, regardless of the value in FreeSpaceProportion.
  287. /// </description></item>
  288. /// <item><description>
  289. /// If there are two or more space filling columns and they all have the same value for FreeSpaceProportion,
  290. /// they will share the free space equally.
  291. /// </description></item>
  292. /// <item><description>
  293. /// If there are three space filling columns with values of 3, 2, and 1
  294. /// for FreeSpaceProportion, then the first column with occupy half the free space, the second will
  295. /// occupy one-third of the free space, and the third column one-sixth of the free space.
  296. /// </description></item>
  297. /// </list>
  298. /// </para>
  299. /// </remarks>
  300. [Browsable(false),
  301. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  302. public int FreeSpaceProportion {
  303. get { return freeSpaceProportion; }
  304. set { freeSpaceProportion = Math.Max(0, value); }
  305. }
  306. private int freeSpaceProportion;
  307. /// <summary>
  308. /// Gets or sets whether groups will be rebuild on this columns values when this column's header is clicked.
  309. /// </summary>
  310. /// <remarks>
  311. /// <para>This setting is only used when ShowGroups is true.</para>
  312. /// <para>
  313. /// If this is false, clicking the header will not rebuild groups. It will not provide
  314. /// any feedback as to why the list is not being regrouped. It is the programmers responsibility to
  315. /// provide appropriate feedback.
  316. /// </para>
  317. /// <para>When this is false, BeforeCreatingGroups events are still fired, which can be used to allow grouping
  318. /// or give feedback, on a case by case basis.</para>
  319. /// </remarks>
  320. [Category("ObjectListView"),
  321. Description("Will the list create groups when this header is clicked?"),
  322. DefaultValue(true)]
  323. public bool Groupable {
  324. get { return groupable; }
  325. set { groupable = value; }
  326. }
  327. private bool groupable = true;
  328. /// <summary>
  329. /// This delegate is called when a group has been created but not yet made
  330. /// into a real ListViewGroup. The user can take this opportunity to fill
  331. /// in lots of other details about the group.
  332. /// </summary>
  333. [Browsable(false),
  334. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  335. public GroupFormatterDelegate GroupFormatter {
  336. get { return groupFormatter; }
  337. set { groupFormatter = value; }
  338. }
  339. private GroupFormatterDelegate groupFormatter;
  340. /// <summary>
  341. /// This delegate is called to get the object that is the key for the group
  342. /// to which the given row belongs.
  343. /// </summary>
  344. [Browsable(false),
  345. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  346. public GroupKeyGetterDelegate GroupKeyGetter {
  347. get { return groupKeyGetter; }
  348. set { groupKeyGetter = value; }
  349. }
  350. private GroupKeyGetterDelegate groupKeyGetter;
  351. /// <summary>
  352. /// This delegate is called to convert a group key into a title for that group.
  353. /// </summary>
  354. [Browsable(false),
  355. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  356. public GroupKeyToTitleConverterDelegate GroupKeyToTitleConverter {
  357. get { return groupKeyToTitleConverter; }
  358. set { groupKeyToTitleConverter = value; }
  359. }
  360. private GroupKeyToTitleConverterDelegate groupKeyToTitleConverter;
  361. /// <summary>
  362. /// When the listview is grouped by this column and group title has an item count,
  363. /// how should the lable be formatted?
  364. /// </summary>
  365. /// <remarks>
  366. /// The given format string can/should have two placeholders:
  367. /// <list type="bullet">
  368. /// <item><description>{0} - the original group title</description></item>
  369. /// <item><description>{1} - the number of items in the group</description></item>
  370. /// </list>
  371. /// </remarks>
  372. /// <example>"{0} [{1} items]"</example>
  373. [Category("ObjectListView"),
  374. Description("The format to use when suffixing item counts to group titles"),
  375. DefaultValue(null),
  376. Localizable(true)]
  377. public string GroupWithItemCountFormat {
  378. get { return groupWithItemCountFormat; }
  379. set { groupWithItemCountFormat = value; }
  380. }
  381. private string groupWithItemCountFormat;
  382. /// <summary>
  383. /// Gets this.GroupWithItemCountFormat or a reasonable default
  384. /// </summary>
  385. /// <remarks>
  386. /// If GroupWithItemCountFormat is not set, its value will be taken from the ObjectListView if possible.
  387. /// </remarks>
  388. [Browsable(false)]
  389. public string GroupWithItemCountFormatOrDefault {
  390. get {
  391. if (!String.IsNullOrEmpty(this.GroupWithItemCountFormat))
  392. return this.GroupWithItemCountFormat;
  393. if (this.ListView != null) {
  394. cachedGroupWithItemCountFormat = ((ObjectListView)this.ListView).GroupWithItemCountFormatOrDefault;
  395. return cachedGroupWithItemCountFormat;
  396. }
  397. // There is one rare but pathelogically possible case where the ListView can
  398. // be null (if the column is grouping a ListView, but is not one of the columns
  399. // for that ListView) so we have to provide a workable default for that rare case.
  400. return cachedGroupWithItemCountFormat ?? "{0} [{1} items]";
  401. }
  402. }
  403. private string cachedGroupWithItemCountFormat;
  404. /// <summary>
  405. /// When the listview is grouped by this column and a group title has an item count,
  406. /// how should the lable be formatted if there is only one item in the group?
  407. /// </summary>
  408. /// <remarks>
  409. /// The given format string can/should have two placeholders:
  410. /// <list type="bullet">
  411. /// <item><description>{0} - the original group title</description></item>
  412. /// <item><description>{1} - the number of items in the group (always 1)</description></item>
  413. /// </list>
  414. /// </remarks>
  415. /// <example>"{0} [{1} item]"</example>
  416. [Category("ObjectListView"),
  417. Description("The format to use when suffixing item counts to group titles"),
  418. DefaultValue(null),
  419. Localizable(true)]
  420. public string GroupWithItemCountSingularFormat {
  421. get { return groupWithItemCountSingularFormat; }
  422. set { groupWithItemCountSingularFormat = value; }
  423. }
  424. private string groupWithItemCountSingularFormat;
  425. /// <summary>
  426. /// Get this.GroupWithItemCountSingularFormat or a reasonable default
  427. /// </summary>
  428. /// <remarks>
  429. /// <para>If this value is not set, the values from the list view will be used</para>
  430. /// </remarks>
  431. [Browsable(false)]
  432. public string GroupWithItemCountSingularFormatOrDefault {
  433. get {
  434. if (!String.IsNullOrEmpty(this.GroupWithItemCountSingularFormat))
  435. return this.GroupWithItemCountSingularFormat;
  436. if (this.ListView != null) {
  437. cachedGroupWithItemCountSingularFormat = ((ObjectListView)this.ListView).GroupWithItemCountSingularFormatOrDefault;
  438. return cachedGroupWithItemCountSingularFormat;
  439. }
  440. // There is one rare but pathelogically possible case where the ListView can
  441. // be null (if the column is grouping a ListView, but is not one of the columns
  442. // for that ListView) so we have to provide a workable default for that rare case.
  443. return cachedGroupWithItemCountSingularFormat ?? "{0} [{1} item]";
  444. }
  445. }
  446. private string cachedGroupWithItemCountSingularFormat;
  447. /// <summary>
  448. /// Gets whether this column should be drawn with a filter indicator in the column header.
  449. /// </summary>
  450. [Browsable(false)]
  451. public bool HasFilterIndicator {
  452. get {
  453. return this.UseFiltering && this.ValuesChosenForFiltering != null && this.ValuesChosenForFiltering.Count > 0;
  454. }
  455. }
  456. /// <summary>
  457. /// Gets or sets a delegate that will be used to own draw header column.
  458. /// </summary>
  459. [Browsable(false),
  460. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  461. public HeaderDrawingDelegate HeaderDrawing {
  462. get { return headerDrawing; }
  463. set { headerDrawing = value; }
  464. }
  465. private HeaderDrawingDelegate headerDrawing;
  466. /// <summary>
  467. /// Gets or sets the style that will be used to draw the header for this column
  468. /// </summary>
  469. /// <remarks>This is only uses when the owning ObjectListView has HeaderUsesThemes set to false.</remarks>
  470. [Category("ObjectListView"),
  471. Description("What style will be used to draw the header of this column"),
  472. DefaultValue(null)]
  473. public HeaderFormatStyle HeaderFormatStyle {
  474. get { return this.headerFormatStyle; }
  475. set { this.headerFormatStyle = value; }
  476. }
  477. private HeaderFormatStyle headerFormatStyle;
  478. /// <summary>
  479. /// Gets or sets the font in which the header for this column will be drawn
  480. /// </summary>
  481. /// <remarks>This property will be made obsolete in v2.5. Use HeaderFormatStyle instead</remarks>
  482. /// <remarks>This is only uses when HeaderUsesThemes is false.</remarks>
  483. [Browsable(false)]
  484. [DefaultValue(null)]
  485. public Font HeaderFont {
  486. get { return this.HeaderFormatStyle == null ? null : this.HeaderFormatStyle.Normal.Font; }
  487. set {
  488. if (value == null && this.HeaderFormatStyle == null)
  489. return;
  490. if (this.HeaderFormatStyle == null)
  491. this.HeaderFormatStyle = new HeaderFormatStyle();
  492. this.HeaderFormatStyle.SetFont(value);
  493. }
  494. }
  495. /// <summary>
  496. /// Gets or sets the color in which the text of the header for this column will be drawn
  497. /// </summary>
  498. /// <remarks>This property will be made obsolete in v2.5. Use HeaderFormatStyle instead</remarks>
  499. /// <remarks>This is only uses when HeaderUsesThemes is false.</remarks>
  500. [Browsable(false)]
  501. [DefaultValue(typeof(Color), "")]
  502. public Color HeaderForeColor {
  503. get { return this.HeaderFormatStyle == null ? Color.Empty : this.HeaderFormatStyle.Normal.ForeColor; }
  504. set {
  505. if (value.IsEmpty && this.HeaderFormatStyle == null)
  506. return;
  507. if (this.HeaderFormatStyle == null)
  508. this.HeaderFormatStyle = new HeaderFormatStyle();
  509. this.HeaderFormatStyle.SetForeColor(value);
  510. }
  511. }
  512. /// <summary>
  513. /// Gets or sets whether the text values in this column will act like hyperlinks
  514. /// </summary>
  515. /// <remarks>This is only taken into account when HeaderUsesThemes is false.</remarks>
  516. [Category("ObjectListView"),
  517. Description("Name of the image that will be shown in the column header."),
  518. DefaultValue(null),
  519. TypeConverter(typeof(ImageKeyConverter)),
  520. Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(System.Drawing.Design.UITypeEditor)),
  521. RefreshProperties(RefreshProperties.Repaint)]
  522. public string HeaderImageKey {
  523. get { return headerImageKey; }
  524. set { headerImageKey = value; }
  525. }
  526. private string headerImageKey;
  527. /// <summary>
  528. /// Gets or sets how the text of the header will be drawn?
  529. /// </summary>
  530. [Category("ObjectListView"),
  531. Description("How will the header text be aligned?"),
  532. DefaultValue(HorizontalAlignment.Left)]
  533. public HorizontalAlignment HeaderTextAlign {
  534. get { return headerTextAlign.HasValue ? headerTextAlign.Value : this.TextAlign; }
  535. set { headerTextAlign = value; }
  536. }
  537. private HorizontalAlignment? headerTextAlign;
  538. /// <summary>
  539. /// Gets the header alignment converted to a StringAlignment
  540. /// </summary>
  541. [Browsable(false)]
  542. public StringAlignment HeaderTextAlignAsStringAlignment {
  543. get {
  544. switch (this.HeaderTextAlign) {
  545. case HorizontalAlignment.Left: return StringAlignment.Near;
  546. case HorizontalAlignment.Center: return StringAlignment.Center;
  547. case HorizontalAlignment.Right: return StringAlignment.Far;
  548. default: return StringAlignment.Near;
  549. }
  550. }
  551. }
  552. /// <summary>
  553. /// Gets whether or not this column has an image in the header
  554. /// </summary>
  555. [Browsable(false)]
  556. public bool HasHeaderImage {
  557. get {
  558. return (this.ListView != null &&
  559. this.ListView.SmallImageList != null &&
  560. this.ListView.SmallImageList.Images.ContainsKey(this.HeaderImageKey));
  561. }
  562. }
  563. /// <summary>
  564. /// Gets or sets whether this column can be hidden by the user.
  565. /// </summary>
  566. /// <remarks>
  567. /// <para>Column 0 can never be hidden, regardless of this setting.</para>
  568. /// </remarks>
  569. [Category("ObjectListView"),
  570. Description("Will the user be able to choose to hide this column?"),
  571. DefaultValue(true)]
  572. public bool Hideable {
  573. get { return hideable; }
  574. set { hideable = value; }
  575. }
  576. private bool hideable = true;
  577. /// <summary>
  578. /// Gets or sets whether the text values in this column will act like hyperlinks
  579. /// </summary>
  580. [Category("ObjectListView"),
  581. Description("Will the text values of this column act like hyperlinks?"),
  582. DefaultValue(false)]
  583. public bool Hyperlink {
  584. get { return hyperlink; }
  585. set { hyperlink = value; }
  586. }
  587. private bool hyperlink;
  588. /// <summary>
  589. /// This is the name of property that will be invoked to get the image selector of the
  590. /// image that should be shown in this column.
  591. /// It can return an int, string, Image or null.
  592. /// </summary>
  593. /// <remarks>
  594. /// <para>This is ignored if ImageGetter is not null.</para>
  595. /// <para>The property can use these return value to identify the image:</para>
  596. /// <list type="bullet">
  597. /// <item><description>null or -1 -- indicates no image</description></item>
  598. /// <item><description>an int -- the int value will be used as an index into the image list</description></item>
  599. /// <item><description>a String -- the string value will be used as a key into the image list</description></item>
  600. /// <item><description>an Image -- the Image will be drawn directly (only in OwnerDrawn mode)</description></item>
  601. /// </list>
  602. /// </remarks>
  603. [Category("ObjectListView"),
  604. Description("The name of the property that holds the image selector"),
  605. DefaultValue(null)]
  606. public string ImageAspectName {
  607. get { return imageAspectName; }
  608. set { imageAspectName = value; }
  609. }
  610. private string imageAspectName;
  611. /// <summary>
  612. /// This delegate is called to get the image selector of the image that should be shown in this column.
  613. /// It can return an int, string, Image or null.
  614. /// </summary>
  615. /// <remarks><para>This delegate can use these return value to identify the image:</para>
  616. /// <list type="bullet">
  617. /// <item><description>null or -1 -- indicates no image</description></item>
  618. /// <item><description>an int -- the int value will be used as an index into the image list</description></item>
  619. /// <item><description>a String -- the string value will be used as a key into the image list</description></item>
  620. /// <item><description>an Image -- the Image will be drawn directly (only in OwnerDrawn mode)</description></item>
  621. /// </list>
  622. /// </remarks>
  623. [Browsable(false),
  624. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  625. public ImageGetterDelegate ImageGetter {
  626. get { return imageGetter; }
  627. set { imageGetter = value; }
  628. }
  629. private ImageGetterDelegate imageGetter;
  630. /// <summary>
  631. /// Can the values shown in this column be edited?
  632. /// </summary>
  633. /// <remarks>This defaults to true, since the primary means to control the editability of a listview
  634. /// is on the listview itself. Once a listview is editable, all the columns are too, unless the
  635. /// programmer explicitly marks them as not editable</remarks>
  636. [Category("ObjectListView"),
  637. Description("Can the value in this column be edited?"),
  638. DefaultValue(true)]
  639. public bool IsEditable {
  640. get { return isEditable; }
  641. set { isEditable = value; }
  642. }
  643. private bool isEditable = true;
  644. /// <summary>
  645. /// Is this column a fixed width column?
  646. /// </summary>
  647. [Browsable(false)]
  648. public bool IsFixedWidth {
  649. get {
  650. return (this.MinimumWidth != -1 && this.MaximumWidth != -1 && this.MinimumWidth >= this.MaximumWidth);
  651. }
  652. }
  653. /// <summary>
  654. /// Get/set whether this column should be used when the view is switched to tile view.
  655. /// </summary>
  656. /// <remarks>Column 0 is always included in tileview regardless of this setting.
  657. /// Tile views do not work well with many "columns" of information.
  658. /// Two or three works best.</remarks>
  659. [Category("ObjectListView"),
  660. Description("Will this column be used when the view is switched to tile view"),
  661. DefaultValue(false)]
  662. public bool IsTileViewColumn {
  663. get { return isTileViewColumn; }
  664. set { isTileViewColumn = value; }
  665. }
  666. private bool isTileViewColumn;
  667. /// <summary>
  668. /// Gets or sets whether the text of this header should be
  669. /// rendered vertically.
  670. /// </summary>
  671. /// <remarks>
  672. /// <para>If this is true, it is a good idea to set ToolTipText to the name of the column so it's easy to read.</para>
  673. /// <para>Currently (2010-08), vertical headers are text only. They do not draw their image.</para>
  674. /// </remarks>
  675. [Category("ObjectListView"),
  676. Description("Will the header for this column be drawn vertically?"),
  677. DefaultValue(false)]
  678. public bool IsHeaderVertical {
  679. get { return isHeaderVertical; }
  680. set { isHeaderVertical = value; }
  681. }
  682. private bool isHeaderVertical;
  683. /// <summary>
  684. /// Can this column be seen by the user?
  685. /// </summary>
  686. /// <remarks>After changing this value, you must call RebuildColumns() before the changes will be effected.</remarks>
  687. [Category("ObjectListView"),
  688. Description("Can this column be seen by the user?"),
  689. DefaultValue(true)]
  690. public bool IsVisible {
  691. get { return isVisible; }
  692. set { isVisible = value; }
  693. }
  694. private bool isVisible = true;
  695. /// <summary>
  696. /// Where was this column last positioned within the Detail view columns
  697. /// </summary>
  698. /// <remarks>DisplayIndex is volatile. Once a column is removed from the control,
  699. /// there is no way to discover where it was in the display order. This property
  700. /// guards that information even when the column is not in the listview's active columns.</remarks>
  701. [Browsable(false),
  702. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  703. public int LastDisplayIndex {
  704. get { return this.lastDisplayIndex; }
  705. set { this.lastDisplayIndex = value; }
  706. }
  707. private int lastDisplayIndex = -1;
  708. /// <summary>
  709. /// What is the maximum width that the user can give to this column?
  710. /// </summary>
  711. /// <remarks>-1 means there is no maximum width. Give this the same value as MinimumWidth to make a fixed width column.</remarks>
  712. [Category("ObjectListView"),
  713. Description("What is the maximum width to which the user can resize this column?"),
  714. DefaultValue(-1)]
  715. public int MaximumWidth {
  716. get { return maxWidth; }
  717. set {
  718. maxWidth = value;
  719. if (maxWidth != -1 && this.Width > maxWidth)
  720. this.Width = maxWidth;
  721. }
  722. }
  723. private int maxWidth = -1;
  724. /// <summary>
  725. /// What is the minimum width that the user can give to this column?
  726. /// </summary>
  727. /// <remarks>-1 means there is no minimum width. Give this the same value as MaximumWidth to make a fixed width column.</remarks>
  728. [Category("ObjectListView"),
  729. Description("What is the minimum width to which the user can resize this column?"),
  730. DefaultValue(-1)]
  731. public int MinimumWidth {
  732. get { return minWidth; }
  733. set {
  734. minWidth = value;
  735. if (this.Width < minWidth)
  736. this.Width = minWidth;
  737. }
  738. }
  739. private int minWidth = -1;
  740. /// <summary>
  741. /// Get/set the renderer that will be invoked when a cell needs to be redrawn
  742. /// </summary>
  743. [Category("ObjectListView"),
  744. Description("The renderer will draw this column when the ListView is owner drawn"),
  745. DefaultValue(null)]
  746. public IRenderer Renderer {
  747. get { return renderer; }
  748. set { renderer = value; }
  749. }
  750. private IRenderer renderer;
  751. /// <summary>
  752. /// This delegate is called when a cell needs to be drawn in OwnerDrawn mode.
  753. /// </summary>
  754. /// <remarks>This method is kept primarily for backwards compatibility.
  755. /// New code should implement an IRenderer, though this property will be maintained.</remarks>
  756. [Browsable(false),
  757. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  758. public RenderDelegate RendererDelegate {
  759. get {
  760. if (this.Renderer is Version1Renderer)
  761. return ((Version1Renderer)this.Renderer).RenderDelegate;
  762. else
  763. return null;
  764. }
  765. set {
  766. this.Renderer = value == null ? null : new Version1Renderer(value);
  767. }
  768. }
  769. /// <summary>
  770. /// Gets or sets whether the text in this column's cell will be used when doing text searching.
  771. /// </summary>
  772. /// <remarks>
  773. /// <para>
  774. /// If this is false, text filters will not trying searching this columns cells when looking for matches.
  775. /// </para>
  776. /// </remarks>
  777. [Category("ObjectListView"),
  778. Description("Will the text of the cells in this column be considered when searching?"),
  779. DefaultValue(true)]
  780. public bool Searchable {
  781. get { return searchable; }
  782. set { searchable = value; }
  783. }
  784. private bool searchable = true;
  785. /// <summary>
  786. /// Gets or sets whether the header for this column will include the column's Text.
  787. /// </summary>
  788. /// <remarks>
  789. /// <para>
  790. /// If this is false, the only thing rendered in the column header will be the image from <see cref="HeaderImageKey"/>.
  791. /// </para>
  792. /// <para>This setting is only considered when <see cref="ObjectListView.HeaderUsesThemes"/> is false on the owning ObjectListView.</para>
  793. /// </remarks>
  794. [Category("ObjectListView"),
  795. Description("Will the header for this column include text?"),
  796. DefaultValue(true)]
  797. public bool ShowTextInHeader {
  798. get { return showTextInHeader; }
  799. set { showTextInHeader = value; }
  800. }
  801. private bool showTextInHeader = true;
  802. /// <summary>
  803. /// Gets or sets whether the contents of the list will be resorted when the user clicks the
  804. /// header of this column.
  805. /// </summary>
  806. /// <remarks>
  807. /// <para>
  808. /// If this is false, clicking the header will not sort the list, but will not provide
  809. /// any feedback as to why the list is not being sorted. It is the programmers responsibility to
  810. /// provide appropriate feedback.
  811. /// </para>
  812. /// <para>When this is false, BeforeSorting events are still fired, which can be used to allow sorting
  813. /// or give feedback, on a case by case basis.</para>
  814. /// </remarks>
  815. [Category("ObjectListView"),
  816. Description("Will clicking this columns header resort the list?"),
  817. DefaultValue(true)]
  818. public bool Sortable {
  819. get { return sortable; }
  820. set { sortable = value; }
  821. }
  822. private bool sortable = true;
  823. /// <summary>
  824. /// Gets or sets the horizontal alignment of the contents of the column.
  825. /// </summary>
  826. /// <remarks>.NET will not allow column 0 to have any alignment except
  827. /// to the left. We can't change the basic behaviour of the listview,
  828. /// but when owner drawn, column 0 can now have other alignments.</remarks>
  829. new public HorizontalAlignment TextAlign {
  830. get {
  831. return this.textAlign.HasValue ? this.textAlign.Value : base.TextAlign;
  832. }
  833. set {
  834. this.textAlign = value;
  835. base.TextAlign = value;
  836. }
  837. }
  838. private HorizontalAlignment? textAlign;
  839. /// <summary>
  840. /// Gets the StringAlignment equivilent of the column text alignment
  841. /// </summary>
  842. [Browsable(false)]
  843. public StringAlignment TextStringAlign {
  844. get {
  845. switch (this.TextAlign) {
  846. case HorizontalAlignment.Center:
  847. return StringAlignment.Center;
  848. case HorizontalAlignment.Left:
  849. return StringAlignment.Near;
  850. case HorizontalAlignment.Right:
  851. return StringAlignment.Far;
  852. default:
  853. return StringAlignment.Near;
  854. }
  855. }
  856. }
  857. /// <summary>
  858. /// What string should be displayed when the mouse is hovered over the header of this column?
  859. /// </summary>
  860. /// <remarks>If a HeaderToolTipGetter is installed on the owning ObjectListView, this
  861. /// value will be ignored.</remarks>
  862. [Category("ObjectListView"),
  863. Description("The tooltip to show when the mouse is hovered over the header of this column"),
  864. DefaultValue((String)null),
  865. Localizable(true)]
  866. public String ToolTipText {
  867. get { return toolTipText; }
  868. set { toolTipText = value; }
  869. }
  870. private String toolTipText;
  871. /// <summary>
  872. /// Should this column have a tri-state checkbox?
  873. /// </summary>
  874. /// <remarks>
  875. /// If this is true, the user can choose the third state (normally Indeterminate).
  876. /// </remarks>
  877. [Category("ObjectListView"),
  878. Description("Should values in this column be treated as a tri-state checkbox?"),
  879. DefaultValue(false)]
  880. public virtual bool TriStateCheckBoxes {
  881. get { return triStateCheckBoxes; }
  882. set {
  883. triStateCheckBoxes = value;
  884. if (value && !this.CheckBoxes)
  885. this.CheckBoxes = true;
  886. }
  887. }
  888. private bool triStateCheckBoxes;
  889. /// <summary>
  890. /// Group objects by the initial letter of the aspect of the column
  891. /// </summary>
  892. /// <remarks>
  893. /// One common pattern is to group column by the initial letter of the value for that group.
  894. /// The aspect must be a string (obviously).
  895. /// </remarks>
  896. [Category("ObjectListView"),
  897. Description("The name of the property or method that should be called to get the aspect to display in this column"),
  898. DefaultValue(false)]
  899. public bool UseInitialLetterForGroup {
  900. get { return useInitialLetterForGroup; }
  901. set { useInitialLetterForGroup = value; }
  902. }
  903. private bool useInitialLetterForGroup;
  904. /// <summary>
  905. /// Gets or sets whether or not this column should be user filterable
  906. /// </summary>
  907. [Category("ObjectListView"),
  908. Description("Does this column want to show a Filter menu item when its header is right clicked"),
  909. DefaultValue(true)]
  910. public bool UseFiltering {
  911. get { return useFiltering; }
  912. set { useFiltering = value; }
  913. }
  914. private bool useFiltering = true;
  915. /// <summary>
  916. /// Gets or sets a filter that will only include models where the model's value
  917. /// for this column is one of the values in ValuesChosenForFiltering
  918. /// </summary>
  919. [Browsable(false),
  920. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  921. public IModelFilter ValueBasedFilter {
  922. get {
  923. if (!this.UseFiltering)
  924. return null;
  925. if (valueBasedFilter != null)
  926. return valueBasedFilter;
  927. if (this.ClusteringStrategy == null)
  928. return null;
  929. if (this.ValuesChosenForFiltering == null || this.ValuesChosenForFiltering.Count == 0)
  930. return null;
  931. return new OneOfFilter(this.ClusteringStrategy.GetClusterKey, this.ValuesChosenForFiltering);
  932. }
  933. set { valueBasedFilter = value; }
  934. }
  935. private IModelFilter valueBasedFilter;
  936. /// <summary>
  937. /// Gets or sets the values that will be used to generate a filter for this
  938. /// column. For a model to be included by the generated filter, its value for this column
  939. /// must be in this list. If the list is null or empty, this column will
  940. /// not be used for filtering.
  941. /// </summary>
  942. [Browsable(false),
  943. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  944. public IList ValuesChosenForFiltering {
  945. get { return this.valuesChosenForFiltering; }
  946. set { this.valuesChosenForFiltering = value; }
  947. }
  948. private IList valuesChosenForFiltering = new ArrayList();
  949. /// <summary>
  950. /// What is the width of this column?
  951. /// </summary>
  952. [Category("ObjectListView"),
  953. Description("The width in pixels of this column"),
  954. DefaultValue(60)]
  955. new public int Width {
  956. get { return base.Width; }
  957. set {
  958. if (this.MaximumWidth != -1 && value > this.MaximumWidth)
  959. base.Width = this.MaximumWidth;
  960. else
  961. base.Width = Math.Max(this.MinimumWidth, value);
  962. }
  963. }
  964. /// <summary>
  965. /// Gets or set whether the contents of this column's cells should be word wrapped
  966. /// </summary>
  967. /// <remarks>If this column uses a custom IRenderer (that is, one that is not descended
  968. /// from BaseRenderer), then that renderer is responsible for implementing word wrapping.</remarks>
  969. [Category("ObjectListView"),
  970. Description("Draw this column cell's word wrapped"),
  971. DefaultValue(false)]
  972. public bool WordWrap {
  973. get { return wordWrap; }
  974. set {
  975. wordWrap = value;
  976. // If there isn't a renderer and they are turning word wrap off, we don't need to do anything
  977. if (this.Renderer == null && !wordWrap)
  978. return;
  979. // All other cases require a renderer of some sort
  980. if (this.Renderer == null)
  981. this.Renderer = new HighlightTextRenderer();
  982. BaseRenderer baseRenderer = this.Renderer as BaseRenderer;
  983. // If there is a custom renderer (not descended from BaseRenderer),
  984. // we leave it up to them to implement wrapping
  985. if (baseRenderer == null)
  986. return;
  987. baseRenderer.CanWrap = wordWrap;
  988. }
  989. }
  990. private bool wordWrap;
  991. #endregion
  992. #region Object commands
  993. /// <summary>
  994. /// For a given group value, return the string that should be used as the groups title.
  995. /// </summary>
  996. /// <param name="value">The group key that is being converted to a title</param>
  997. /// <returns>string</returns>
  998. public string ConvertGroupKeyToTitle(object value) {
  999. if (this.groupKeyToTitleConverter != null)
  1000. return this.groupKeyToTitleConverter(value);
  1001. return value == null ? "{null}" : this.ValueToString(value);
  1002. }
  1003. /// <summary>
  1004. /// Get the checkedness of the given object for this column
  1005. /// </summary>
  1006. /// <param name="rowObject">The row object that is being displayed</param>
  1007. /// <returns>The checkedness of the object</returns>
  1008. public CheckState GetCheckState(object rowObject) {
  1009. if (!this.CheckBoxes)
  1010. return CheckState.Unchecked;
  1011. bool? aspectAsBool = this.GetValue(rowObject) as bool?;
  1012. if (aspectAsBool.HasValue) {
  1013. if (aspectAsBool.Value)
  1014. return CheckState.Checked;
  1015. else
  1016. return CheckState.Unchecked;
  1017. } else
  1018. return CheckState.Indeterminate;
  1019. }
  1020. /// <summary>
  1021. /// Put the checkedness of the given object for this column
  1022. /// </summary>
  1023. /// <param name="rowObject">The row object that is being displayed</param>
  1024. /// <param name="newState"></param>
  1025. /// <returns>The checkedness of the object</returns>
  1026. public void PutCheckState(object rowObject, CheckState newState) {
  1027. if (newState == CheckState.Checked)
  1028. this.PutValue(rowObject, true);
  1029. else
  1030. if (newState == CheckState.Unchecked)
  1031. this.PutValue(rowObject, false);
  1032. else
  1033. this.PutValue(rowObject, null);
  1034. }
  1035. /// <summary>
  1036. /// For a given row object, extract the value indicated by the AspectName property of this column.
  1037. /// </summary>
  1038. /// <param name="rowObject">The row object that is being displayed</param>
  1039. /// <returns>An object, which is the aspect named by AspectName</returns>
  1040. public object GetAspectByName(object rowObject) {
  1041. if (this.aspectMunger == null)