4010 lines
		
	
	
		
			161 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			4010 lines
		
	
	
		
			161 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | ||
|  * Renderers - A collection of useful renderers that are used to owner draw a cell in an ObjectListView
 | ||
|  *
 | ||
|  * Author: Phillip Piper
 | ||
|  * Date: 27/09/2008 9:15 AM
 | ||
|  *
 | ||
|  * Change log: 
 | ||
|  * v2.9
 | ||
|  * 2015-08-22   JPP  - Allow selected row back/fore colours to be specified for each row
 | ||
|  * 2015-06-23   JPP  - Added ColumnButtonRenderer plus general support for Buttons
 | ||
|  * 2015-06-22   JPP  - Added BaseRenderer.ConfigureItem() and ConfigureSubItem() to easily allow
 | ||
|  *                     other renderers to be chained for use within a primary renderer.
 | ||
|  *                   - Lots of tightening of hit tests and edit rectangles
 | ||
|  * 2015-05-15   JPP  - Handle renderering an Image when that Image is returned as an aspect.
 | ||
|  * v2.8
 | ||
|  * 2014-09-26   JPP  - Dispose of animation timer in a more robust fashion.
 | ||
|  * 2014-05-20   JPP  - Handle rendering disabled rows
 | ||
|  * v2.7
 | ||
|  * 2013-04-29   JPP  - Fixed bug where Images were not vertically aligned
 | ||
|  * v2.6
 | ||
|  * 2012-10-26   JPP  - Hit detection will no longer report check box hits on columns without checkboxes.
 | ||
|  * 2012-07-13   JPP  - [Breaking change] Added preferedSize parameter to IRenderer.GetEditRectangle().
 | ||
|  * v2.5.1
 | ||
|  * 2012-07-14   JPP  - Added CellPadding to various places. Replaced DescribedTaskRenderer.CellPadding.
 | ||
|  * 2012-07-11   JPP  - Added CellVerticalAlignment to various places allow cell contents to be vertically
 | ||
|  *                     aligned (rather than always being centered).
 | ||
|  * v2.5
 | ||
|  * 2010-08-24   JPP  - CheckBoxRenderer handles hot boxes and correctly vertically centers the box.
 | ||
|  * 2010-06-23   JPP  - Major rework of HighlightTextRenderer. Now uses TextMatchFilter directly.
 | ||
|  *                     Draw highlighting underneath text to improve legibility. Works with new
 | ||
|  *                     TextMatchFilter capabilities.
 | ||
|  * v2.4
 | ||
|  * 2009-10-30   JPP  - Plugged possible resource leak by using using() with CreateGraphics()
 | ||
|  * v2.3
 | ||
|  * 2009-09-28   JPP  - Added DescribedTaskRenderer
 | ||
|  * 2009-09-01   JPP  - Correctly handle an ImageRenderer's handling of an aspect that holds
 | ||
|  *                     the image to be displayed at Byte[].
 | ||
|  * 2009-08-29   JPP  - Fixed bug where some of a cell's background was not erased. 
 | ||
|  * 2009-08-15   JPP  - Correctly MeasureText() using the appropriate graphic context
 | ||
|  *                   - Handle translucent selection setting
 | ||
|  * v2.2.1
 | ||
|  * 2009-07-24   JPP  - Try to honour CanWrap setting when GDI rendering text.
 | ||
|  * 2009-07-11   JPP  - Correctly calculate edit rectangle for subitems of a tree view
 | ||
|  *                     (previously subitems were indented in the same way as the primary column)
 | ||
|  * v2.2
 | ||
|  * 2009-06-06   JPP  - Tweaked text rendering so that column 0 isn't ellipsed unnecessarily.
 | ||
|  * 2009-05-05   JPP  - Added Unfocused foreground and background colors 
 | ||
|  *                     (thanks to Christophe Hosten)
 | ||
|  * 2009-04-21   JPP  - Fixed off-by-1 error when calculating text widths. This caused
 | ||
|  *                     middle and right aligned columns to always wrap one character
 | ||
|  *                     when printed using ListViewPrinter (SF#2776634).
 | ||
|  * 2009-04-11   JPP  - Correctly renderer checkboxes when RowHeight is non-standard
 | ||
|  * 2009-04-06   JPP  - Allow for item indent when calculating edit rectangle
 | ||
|  * v2.1
 | ||
|  * 2009-02-24   JPP  - Work properly with ListViewPrinter again
 | ||
|  * 2009-01-26   JPP  - AUSTRALIA DAY (why aren't I on holidays!)
 | ||
|  *                   - Major overhaul of renderers. Now uses IRenderer interface.
 | ||
|  *                   - ImagesRenderer and FlagsRenderer<T> are now defunct.
 | ||
|  *                     The names are retained for backward compatibility.
 | ||
|  * 2009-01-23   JPP  - Align bitmap AND text according to column alignment (previously
 | ||
|  *                     only text was aligned and bitmap was always to the left).
 | ||
|  * 2009-01-21   JPP  - Changed to use TextRenderer rather than native GDI routines.
 | ||
|  * 2009-01-20   JPP  - Draw images directly from image list if possible. 30% faster!
 | ||
|  *                   - Tweaked some spacings to look more like native ListView
 | ||
|  *                   - Text highlight for non FullRowSelect is now the right color
 | ||
|  *                     when the control doesn't have focus.
 | ||
|  *                   - Commented out experimental animations. Still needs work.
 | ||
|  * 2009-01-19   JPP  - Changed to draw text using GDI routines. Looks more like
 | ||
|  *                     native control this way. Set UseGdiTextRendering to false to 
 | ||
|  *                     revert to previous behavior.
 | ||
|  * 2009-01-15   JPP  - Draw background correctly when control is disabled
 | ||
|  *                   - Render checkboxes using CheckBoxRenderer
 | ||
|  * v2.0.1
 | ||
|  * 2008-12-29   JPP  - Render text correctly when HideSelection is true.
 | ||
|  * 2008-12-26   JPP  - BaseRenderer now works correctly in all Views
 | ||
|  * 2008-12-23   JPP  - Fixed two small bugs in BarRenderer
 | ||
|  * v2.0
 | ||
|  * 2008-10-26   JPP  - Don't owner draw when in Design mode
 | ||
|  * 2008-09-27   JPP  - Separated from ObjectListView.cs
 | ||
|  * 
 | ||
|  * Copyright (C) 2006-2014 Phillip Piper
 | ||
|  * 
 | ||
|  * TO DO:
 | ||
|  * - Hit detection on renderers doesn't change the controls standard selection behavior
 | ||
|  *
 | ||
|  * This program is free software: you can redistribute it and/or modify
 | ||
|  * it under the terms of the GNU General Public License as published by
 | ||
|  * the Free Software Foundation, either version 3 of the License, or
 | ||
|  * (at your option) any later version.
 | ||
|  *
 | ||
|  * This program is distributed in the hope that it will be useful,
 | ||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||
|  * GNU General Public License for more details.
 | ||
|  *
 | ||
|  * You should have received a copy of the GNU General Public License
 | ||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||
|  *
 | ||
|  * If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
 | ||
|  */
 | ||
| 
 | ||
| using System;
 | ||
| using System.Collections;
 | ||
| using System.Collections.Generic;
 | ||
| using System.ComponentModel;
 | ||
| using System.Diagnostics;
 | ||
| using System.Drawing;
 | ||
| using System.Drawing.Drawing2D;
 | ||
| using System.Drawing.Imaging;
 | ||
| using System.Globalization;
 | ||
| using System.IO;
 | ||
| using System.Threading;
 | ||
| using System.Windows.Forms;
 | ||
| using System.Windows.Forms.VisualStyles;
 | ||
| using Timer = System.Threading.Timer;
 | ||
| 
 | ||
| namespace BrightIdeasSoftware {
 | ||
|     /// <summary>
 | ||
|     /// Renderers are the mechanism used for owner drawing cells. As such, they can also handle
 | ||
|     /// hit detection and positioning of cell editing rectangles.
 | ||
|     /// </summary>
 | ||
|     public interface IRenderer {
 | ||
|         /// <summary>
 | ||
|         /// Render the whole item within an ObjectListView. This is only used in non-Details views.
 | ||
|         /// </summary>
 | ||
|         /// <param name="e">The event</param>
 | ||
|         /// <param name="g">A Graphics for rendering</param>
 | ||
|         /// <param name="itemBounds">The bounds of the item</param>
 | ||
|         /// <param name="rowObject">The model object to be drawn</param>
 | ||
|         /// <returns>Return true to indicate that the event was handled and no further processing is needed.</returns>
 | ||
|         bool RenderItem(DrawListViewItemEventArgs e, Graphics g, Rectangle itemBounds, Object rowObject);
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Render one cell within an ObjectListView when it is in Details mode.
 | ||
|         /// </summary>
 | ||
|         /// <param name="e">The event</param>
 | ||
|         /// <param name="g">A Graphics for rendering</param>
 | ||
|         /// <param name="cellBounds">The bounds of the cell</param>
 | ||
|         /// <param name="rowObject">The model object to be drawn</param>
 | ||
|         /// <returns>Return true to indicate that the event was handled and no further processing is needed.</returns>
 | ||
|         bool RenderSubItem(DrawListViewSubItemEventArgs e, Graphics g, Rectangle cellBounds, Object rowObject);
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// What is under the given point?
 | ||
|         /// </summary>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x">x co-ordinate</param>
 | ||
|         /// <param name="y">y co-ordinate</param>
 | ||
|         /// <remarks>This method should only alter HitTestLocation and/or UserData.</remarks>
 | ||
|         void HitTest(OlvListViewHitTestInfo hti, int x, int y);
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// When the value in the given cell is to be edited, where should the edit rectangle be placed?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         Rectangle GetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize);
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// Renderers that implement this interface will have the filter property updated,
 | ||
|     /// each time the filter on the ObjectListView is updated.
 | ||
|     /// </summary>
 | ||
|     public interface IFilterAwareRenderer
 | ||
|     {
 | ||
|         /// <summary>
 | ||
|         /// 
 | ||
|         /// </summary>
 | ||
|         IModelFilter Filter { get; set; }
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// An AbstractRenderer is a do-nothing implementation of the IRenderer interface.
 | ||
|     /// </summary>
 | ||
|     [Browsable(true),
 | ||
|      ToolboxItem(false)]
 | ||
|     public class AbstractRenderer : Component, IRenderer {
 | ||
|         #region IRenderer Members
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Render the whole item within an ObjectListView. This is only used in non-Details views.
 | ||
|         /// </summary>
 | ||
|         /// <param name="e">The event</param>
 | ||
|         /// <param name="g">A Graphics for rendering</param>
 | ||
|         /// <param name="itemBounds">The bounds of the item</param>
 | ||
|         /// <param name="rowObject">The model object to be drawn</param>
 | ||
|         /// <returns>Return true to indicate that the event was handled and no further processing is needed.</returns>
 | ||
|         public virtual bool RenderItem(DrawListViewItemEventArgs e, Graphics g, Rectangle itemBounds, object rowObject) {
 | ||
|             return true;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Render one cell within an ObjectListView when it is in Details mode.
 | ||
|         /// </summary>
 | ||
|         /// <param name="e">The event</param>
 | ||
|         /// <param name="g">A Graphics for rendering</param>
 | ||
|         /// <param name="cellBounds">The bounds of the cell</param>
 | ||
|         /// <param name="rowObject">The model object to be drawn</param>
 | ||
|         /// <returns>Return true to indicate that the event was handled and no further processing is needed.</returns>
 | ||
|         public virtual bool RenderSubItem(DrawListViewSubItemEventArgs e, Graphics g, Rectangle cellBounds, object rowObject) {
 | ||
|             return false;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// What is under the given point?
 | ||
|         /// </summary>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x">x co-ordinate</param>
 | ||
|         /// <param name="y">y co-ordinate</param>
 | ||
|         /// <remarks>This method should only alter HitTestLocation and/or UserData.</remarks>
 | ||
|         public virtual void HitTest(OlvListViewHitTestInfo hti, int x, int y) {}
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// When the value in the given cell is to be edited, where should the edit rectangle be placed?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         public virtual Rectangle GetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) {
 | ||
|             return cellBounds;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// This class provides compatibility for v1 RendererDelegates
 | ||
|     /// </summary>
 | ||
|     [ToolboxItem(false)]
 | ||
|     internal class Version1Renderer : AbstractRenderer {
 | ||
|         public Version1Renderer(RenderDelegate renderDelegate) {
 | ||
|             this.RenderDelegate = renderDelegate;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The renderer delegate that this renderer wraps
 | ||
|         /// </summary>
 | ||
|         public RenderDelegate RenderDelegate;
 | ||
| 
 | ||
|         #region IRenderer Members
 | ||
| 
 | ||
|         public override bool RenderSubItem(DrawListViewSubItemEventArgs e, Graphics g, Rectangle cellBounds, object rowObject) {
 | ||
|             if (this.RenderDelegate == null)
 | ||
|                 return base.RenderSubItem(e, g, cellBounds, rowObject);
 | ||
|             else
 | ||
|                 return this.RenderDelegate(e, g, cellBounds, rowObject);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// A BaseRenderer provides useful base level functionality for any custom renderer.
 | ||
|     /// </summary>
 | ||
|     /// <remarks>
 | ||
|     /// <para>Subclasses will normally override the Render or OptionalRender method, and use the other
 | ||
|     /// methods as helper functions.</para>
 | ||
|     /// </remarks>
 | ||
|     [Browsable(true),
 | ||
|      ToolboxItem(true)]
 | ||
|     public class BaseRenderer : AbstractRenderer {
 | ||
|         internal const TextFormatFlags NormalTextFormatFlags = TextFormatFlags.NoPrefix |
 | ||
|                                                                TextFormatFlags.EndEllipsis |
 | ||
|                                                                TextFormatFlags.PreserveGraphicsTranslateTransform;
 | ||
| 
 | ||
|         #region Configuration Properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Can the renderer wrap lines that do not fit completely within the cell?
 | ||
|         /// </summary>
 | ||
|         /// <remarks>Wrapping text doesn't work with the GDI renderer.</remarks>
 | ||
|         [Category("Appearance"),
 | ||
|          Description("Can the renderer wrap text that does not fit completely within the cell"),
 | ||
|          DefaultValue(false)]
 | ||
|         public bool CanWrap {
 | ||
|             get { return canWrap; }
 | ||
|             set {
 | ||
|                 canWrap = value;
 | ||
|                 if (canWrap)
 | ||
|                     this.UseGdiTextRendering = false;
 | ||
|             }
 | ||
|         }
 | ||
|         private bool canWrap;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets how many pixels will be left blank around this cell
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// <para>
 | ||
|         /// This setting only takes effect when the control is owner drawn.
 | ||
|         /// </para>
 | ||
|         /// <para><see cref="ObjectListView.CellPadding"/> for more details.</para>
 | ||
|         /// </remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The number of pixels that renderer will leave empty around the edge of the cell"),
 | ||
|          DefaultValue(null)]
 | ||
|         public Rectangle? CellPadding {
 | ||
|             get { return this.cellPadding; }
 | ||
|             set { this.cellPadding = value; }
 | ||
|         }
 | ||
|         private Rectangle? cellPadding;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets the horiztonal alignment of the column
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         public HorizontalAlignment CellHorizontalAlignment
 | ||
|         {
 | ||
|             get { return this.Column == null ? HorizontalAlignment.Left : this.Column.TextAlign; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets how cells drawn by this renderer will be vertically aligned.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// <para>
 | ||
|         /// If this is not set, the value from the column or control itself will be used.
 | ||
|         /// </para>
 | ||
|         /// </remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("How will cell values be vertically aligned?"),
 | ||
|          DefaultValue(null)]
 | ||
|         public virtual StringAlignment? CellVerticalAlignment {
 | ||
|             get { return this.cellVerticalAlignment; }
 | ||
|             set { this.cellVerticalAlignment = value; }
 | ||
|         }
 | ||
|         private StringAlignment? cellVerticalAlignment;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets the optional padding that this renderer should apply before drawing.
 | ||
|         /// This property considers all possible sources of padding
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         protected virtual Rectangle? EffectiveCellPadding {
 | ||
|             get {
 | ||
|                 if (this.cellPadding.HasValue)
 | ||
|                     return this.cellPadding.Value;
 | ||
| 
 | ||
|                 if (this.OLVSubItem != null && this.OLVSubItem.CellPadding.HasValue)
 | ||
|                     return this.OLVSubItem.CellPadding.Value;
 | ||
| 
 | ||
|                 if (this.ListItem != null && this.ListItem.CellPadding.HasValue)
 | ||
|                     return this.ListItem.CellPadding.Value;
 | ||
| 
 | ||
|                 if (this.Column != null && this.Column.CellPadding.HasValue)
 | ||
|                     return this.Column.CellPadding.Value;
 | ||
| 
 | ||
|                 if (this.ListView != null && this.ListView.CellPadding.HasValue)
 | ||
|                     return this.ListView.CellPadding.Value;
 | ||
| 
 | ||
|                 return null;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets the vertical cell alignment that should govern the rendering.
 | ||
|         /// This property considers all possible sources.
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         protected virtual StringAlignment EffectiveCellVerticalAlignment {
 | ||
|             get {
 | ||
|                 if (this.cellVerticalAlignment.HasValue)
 | ||
|                     return this.cellVerticalAlignment.Value;
 | ||
| 
 | ||
|                 if (this.OLVSubItem != null && this.OLVSubItem.CellVerticalAlignment.HasValue)
 | ||
|                     return this.OLVSubItem.CellVerticalAlignment.Value;
 | ||
| 
 | ||
|                 if (this.ListItem != null && this.ListItem.CellVerticalAlignment.HasValue)
 | ||
|                     return this.ListItem.CellVerticalAlignment.Value;
 | ||
| 
 | ||
|                 if (this.Column != null && this.Column.CellVerticalAlignment.HasValue)
 | ||
|                     return this.Column.CellVerticalAlignment.Value;
 | ||
| 
 | ||
|                 if (this.ListView != null)
 | ||
|                     return this.ListView.CellVerticalAlignment;
 | ||
| 
 | ||
|                 return StringAlignment.Center;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the image list from which keyed images will be fetched
 | ||
|         /// </summary>
 | ||
|         [Category("Appearance"),
 | ||
|          Description("The image list from which keyed images will be fetched for drawing. If this is not given, the small ImageList from the ObjectListView will be used"),
 | ||
|          DefaultValue(null)]
 | ||
|         public ImageList ImageList {
 | ||
|             get { return imageList; }
 | ||
|             set { imageList = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private ImageList imageList;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// When rendering multiple images, how many pixels should be between each image?
 | ||
|         /// </summary>
 | ||
|         [Category("Appearance"),
 | ||
|          Description("When rendering multiple images, how many pixels should be between each image?"),
 | ||
|          DefaultValue(1)]
 | ||
|         public int Spacing {
 | ||
|             get { return spacing; }
 | ||
|             set { spacing = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int spacing = 1;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Should text be rendered using GDI routines? This makes the text look more
 | ||
|         /// like a native List view control.
 | ||
|         /// </summary>
 | ||
|         [Category("Appearance"),
 | ||
|          Description("Should text be rendered using GDI routines?"),
 | ||
|          DefaultValue(true)]
 | ||
|         public virtual bool UseGdiTextRendering {
 | ||
|             get {
 | ||
|                 // Can't use GDI routines on a GDI+ printer context
 | ||
|                 return !this.IsPrinting && useGdiTextRendering;
 | ||
|             }
 | ||
|             set { useGdiTextRendering = value; }
 | ||
|         }
 | ||
|         private bool useGdiTextRendering = true;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region State Properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get or set the aspect of the model object that this renderer should draw
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Object Aspect {
 | ||
|             get {
 | ||
|                 if (aspect == null)
 | ||
|                     aspect = column.GetValue(this.rowObject);
 | ||
|                 return aspect;
 | ||
|             }
 | ||
|             set { aspect = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Object aspect;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// What are the bounds of the cell that is being drawn?
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Rectangle Bounds {
 | ||
|             get { return bounds; }
 | ||
|             set { bounds = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Rectangle bounds;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get or set the OLVColumn that this renderer will draw
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public OLVColumn Column {
 | ||
|             get { return column; }
 | ||
|             set { column = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private OLVColumn column;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get/set the event that caused this renderer to be called
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public DrawListViewItemEventArgs DrawItemEvent {
 | ||
|             get { return drawItemEventArgs; }
 | ||
|             set { drawItemEventArgs = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private DrawListViewItemEventArgs drawItemEventArgs;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get/set the event that caused this renderer to be called
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public DrawListViewSubItemEventArgs Event {
 | ||
|             get { return eventArgs; }
 | ||
|             set { eventArgs = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private DrawListViewSubItemEventArgs eventArgs;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or  sets the font to be used for text in this cell
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Font Font {
 | ||
|             get {
 | ||
|                 if (this.font != null || this.ListItem == null)
 | ||
|                     return this.font;
 | ||
| 
 | ||
|                 if (this.SubItem == null || this.ListItem.UseItemStyleForSubItems)
 | ||
|                     return this.ListItem.Font;
 | ||
| 
 | ||
|                 return this.SubItem.Font;
 | ||
|             }
 | ||
|             set { this.font = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Font font;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets the image list from which keyed images will be fetched
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public ImageList ImageListOrDefault {
 | ||
|             get { return this.ImageList ?? this.ListView.SmallImageList; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Should this renderer fill in the background before drawing?
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public bool IsDrawBackground {
 | ||
|             get { return !this.IsPrinting; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Cache whether or not our item is selected
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public bool IsItemSelected {
 | ||
|             get { return isItemSelected; }
 | ||
|             set { isItemSelected = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private bool isItemSelected;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Is this renderer being used on a printer context?
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public bool IsPrinting {
 | ||
|             get { return isPrinting; }
 | ||
|             set { isPrinting = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private bool isPrinting;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get or set the listitem that this renderer will be drawing
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public OLVListItem ListItem {
 | ||
|             get { return listItem; }
 | ||
|             set { listItem = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private OLVListItem listItem;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get/set the listview for which the drawing is to be done
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public ObjectListView ListView {
 | ||
|             get { return objectListView; }
 | ||
|             set { objectListView = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private ObjectListView objectListView;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get the specialized OLVSubItem that this renderer is drawing
 | ||
|         /// </summary>
 | ||
|         /// <remarks>This returns null for column 0.</remarks>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public OLVListSubItem OLVSubItem {
 | ||
|             get { return listSubItem as OLVListSubItem; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get or set the model object that this renderer should draw
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Object RowObject {
 | ||
|             get { return rowObject; }
 | ||
|             set { rowObject = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Object rowObject;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Get or set the list subitem that this renderer will be drawing
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public OLVListSubItem SubItem {
 | ||
|             get { return listSubItem; }
 | ||
|             set { listSubItem = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private OLVListSubItem listSubItem;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The brush that will be used to paint the text
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Brush TextBrush {
 | ||
|             get {
 | ||
|                 if (textBrush == null)
 | ||
|                     return new SolidBrush(this.GetForegroundColor());
 | ||
|                 else
 | ||
|                     return this.textBrush;
 | ||
|             }
 | ||
|             set { textBrush = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Brush textBrush;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Will this renderer use the custom images from the parent ObjectListView
 | ||
|         /// to draw the checkbox images.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// <para>
 | ||
|         /// If this is true, the renderer will use the images from the 
 | ||
|         /// StateImageList to represent checkboxes. 0 - unchecked, 1 - checked, 2 - indeterminate.
 | ||
|         /// </para>
 | ||
|         /// <para>If this is false (the default), then the renderer will use .NET's standard
 | ||
|         /// CheckBoxRenderer.</para>
 | ||
|         /// </remarks>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public bool UseCustomCheckboxImages {
 | ||
|             get { return useCustomCheckboxImages; }
 | ||
|             set { useCustomCheckboxImages = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private bool useCustomCheckboxImages;
 | ||
| 
 | ||
|         private void ClearState() {
 | ||
|             this.Event = null;
 | ||
|             this.DrawItemEvent = null;
 | ||
|             this.Aspect = null;
 | ||
|             this.Font = null;
 | ||
|             this.TextBrush = null;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Utilities
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Align the second rectangle with the first rectangle,
 | ||
|         /// according to the alignment of the column
 | ||
|         /// </summary>
 | ||
|         /// <param name="outer">The cell's bounds</param>
 | ||
|         /// <param name="inner">The rectangle to be aligned within the bounds</param>
 | ||
|         /// <returns>An aligned rectangle</returns>
 | ||
|         protected virtual Rectangle AlignRectangle(Rectangle outer, Rectangle inner) {
 | ||
|             Rectangle r = new Rectangle(outer.Location, inner.Size);
 | ||
| 
 | ||
|             // Align horizontally depending on the column alignment
 | ||
|             if (inner.Width < outer.Width) {
 | ||
|                 r.X = AlignHorizontally(outer, inner);
 | ||
|             }
 | ||
| 
 | ||
|             // Align vertically too
 | ||
|             if (inner.Height < outer.Height) {
 | ||
|                 r.Y = AlignVertically(outer, inner);
 | ||
|             }
 | ||
| 
 | ||
|             return r;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the left edge of the rectangle that aligns the outer rectangle with the inner one 
 | ||
|         /// according to this renderer's horizontal alignment
 | ||
|         /// </summary>
 | ||
|         /// <param name="outer"></param>
 | ||
|         /// <param name="inner"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected int AlignHorizontally(Rectangle outer, Rectangle inner) {
 | ||
|             HorizontalAlignment alignment = this.CellHorizontalAlignment;
 | ||
|             switch (alignment) {
 | ||
|                 case HorizontalAlignment.Left:
 | ||
|                     return outer.Left + 1;
 | ||
|                 case HorizontalAlignment.Center:
 | ||
|                     return outer.Left + ((outer.Width - inner.Width) / 2);
 | ||
|                 case HorizontalAlignment.Right:
 | ||
|                     return outer.Right - inner.Width - 1;
 | ||
|                 default:
 | ||
|                     throw new ArgumentOutOfRangeException();
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the top of the rectangle that aligns the outer rectangle with the inner rectangle
 | ||
|         /// according to this renders vertical alignment
 | ||
|         /// </summary>
 | ||
|         /// <param name="outer"></param>
 | ||
|         /// <param name="inner"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected int AlignVertically(Rectangle outer, Rectangle inner) {
 | ||
|             return AlignVertically(outer, inner.Height);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the top of the rectangle that aligns the outer rectangle with a rectangle of the given height
 | ||
|         /// according to this renderer's vertical alignment
 | ||
|         /// </summary>
 | ||
|         /// <param name="outer"></param>
 | ||
|         /// <param name="innerHeight"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected int AlignVertically(Rectangle outer, int innerHeight) {
 | ||
|             switch (this.EffectiveCellVerticalAlignment) {
 | ||
|                 case StringAlignment.Near:
 | ||
|                     return outer.Top + 1;
 | ||
|                 case StringAlignment.Center:
 | ||
|                     return outer.Top + ((outer.Height - innerHeight) / 2);
 | ||
|                 case StringAlignment.Far:
 | ||
|                     return outer.Bottom - innerHeight - 1;
 | ||
|                 default:
 | ||
|                     throw new ArgumentOutOfRangeException();
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the space that our rendering will occupy and then align that space
 | ||
|         /// with the given rectangle, according to the Column alignment
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r">Pre-padded bounds of the cell</param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Rectangle CalculateAlignedRectangle(Graphics g, Rectangle r) {
 | ||
|             if (this.Column == null)
 | ||
|                 return r;
 | ||
| 
 | ||
|             Rectangle contentRectangle = new Rectangle(Point.Empty, this.CalculateContentSize(g, r));
 | ||
|             return this.AlignRectangle(r, contentRectangle);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the size of the content of this cell.
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r">Pre-padded bounds of the cell</param>
 | ||
|         /// <returns>The width and height of the content</returns>
 | ||
|         protected virtual Size CalculateContentSize(Graphics g, Rectangle r)
 | ||
|         {
 | ||
|             Size checkBoxSize = this.CalculatePrimaryCheckBoxSize(g);
 | ||
|             Size imageSize = this.CalculateImageSize(g, this.GetImageSelector());
 | ||
|             Size textSize = this.CalculateTextSize(g, this.GetText(), r.Width - (checkBoxSize.Width + imageSize.Width));
 | ||
| 
 | ||
|             // If the combined width is greater than the whole cell,  we just use the cell itself
 | ||
| 
 | ||
|             int width = Math.Min(r.Width, checkBoxSize.Width + imageSize.Width + textSize.Width);
 | ||
|             int componentMaxHeight = Math.Max(checkBoxSize.Height, Math.Max(imageSize.Height, textSize.Height));
 | ||
|             int height = Math.Min(r.Height, componentMaxHeight);
 | ||
| 
 | ||
|             return new Size(width, height);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the bounds of a checkbox given the (pre-padded) cell bounds
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds">Pre-padded cell bounds</param>
 | ||
|         /// <returns></returns>
 | ||
|         protected Rectangle CalculateCheckBoxBounds(Graphics g, Rectangle cellBounds) {
 | ||
|             Size checkBoxSize = this.CalculateCheckBoxSize(g);
 | ||
|             return this.AlignRectangle(cellBounds, new Rectangle(0, 0, checkBoxSize.Width, checkBoxSize.Height));
 | ||
|         }
 | ||
|         
 | ||
|         
 | ||
|         /// <summary>
 | ||
|         /// How much space will the check box for this cell occupy?
 | ||
|         /// </summary>
 | ||
|         /// <remarks>Only column 0 can have check boxes. Sub item checkboxes are
 | ||
|         /// treated as images</remarks>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Size CalculateCheckBoxSize(Graphics g)
 | ||
|         {
 | ||
|             if (UseCustomCheckboxImages && this.ListView.StateImageList != null)
 | ||
|                 return this.ListView.StateImageList.ImageSize;
 | ||
| 
 | ||
|             return CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.UncheckedNormal);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// How much space will the check box for this row occupy? 
 | ||
|         /// If the list doesn't have checkboxes, or this isn't the primary column,
 | ||
|         /// this returns an empty size.
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Size CalculatePrimaryCheckBoxSize(Graphics g) {
 | ||
|             if (!this.ListView.CheckBoxes || !this.ColumnIsPrimary)
 | ||
|                 return Size.Empty;
 | ||
|             
 | ||
|             Size size = this.CalculateCheckBoxSize(g);
 | ||
|             size.Width += 6;
 | ||
|             return size;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// How much horizontal space will the image of this cell occupy?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="imageSelector"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual int CalculateImageWidth(Graphics g, object imageSelector)
 | ||
|         {
 | ||
|             return this.CalculateImageSize(g, imageSelector).Width + 2;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// How much vertical space will the image of this cell occupy?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="imageSelector"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual int CalculateImageHeight(Graphics g, object imageSelector)
 | ||
|         {
 | ||
|             return this.CalculateImageSize(g, imageSelector).Height;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// How much space will the image of this cell occupy?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="imageSelector"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Size CalculateImageSize(Graphics g, object imageSelector)
 | ||
|         {
 | ||
|             if (imageSelector == null || imageSelector == DBNull.Value)
 | ||
|                 return Size.Empty;
 | ||
| 
 | ||
|             // Check for the image in the image list (most common case)
 | ||
|             ImageList il = this.ImageListOrDefault;
 | ||
|             if (il != null)
 | ||
|             {
 | ||
|                 int selectorAsInt = -1;
 | ||
| 
 | ||
|                 if (imageSelector is Int32)
 | ||
|                     selectorAsInt = (Int32)imageSelector;
 | ||
|                 else
 | ||
|                 {
 | ||
|                     String selectorAsString = imageSelector as String;
 | ||
|                     if (selectorAsString != null)
 | ||
|                         selectorAsInt = il.Images.IndexOfKey(selectorAsString);
 | ||
|                 }
 | ||
|                 if (selectorAsInt >= 0)
 | ||
|                     return il.ImageSize;
 | ||
|             }
 | ||
| 
 | ||
|             // Is the selector actually an image?
 | ||
|             Image image = imageSelector as Image;
 | ||
|             if (image != null)
 | ||
|                 return image.Size;
 | ||
| 
 | ||
|             return Size.Empty;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// How much horizontal space will the text of this cell occupy?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         /// <param name="width"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual int CalculateTextWidth(Graphics g, string txt, int width)
 | ||
|         {
 | ||
|             if (String.IsNullOrEmpty(txt))
 | ||
|                 return 0;
 | ||
| 
 | ||
|             return CalculateTextSize(g, txt, width).Width;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// How much space will the text of this cell occupy?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         /// <param name="width"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Size CalculateTextSize(Graphics g, string txt, int width)
 | ||
|         {
 | ||
|             if (String.IsNullOrEmpty(txt))
 | ||
|                 return Size.Empty;
 | ||
| 
 | ||
|             if (this.UseGdiTextRendering)
 | ||
|             {
 | ||
|                 Size proposedSize = new Size(width, Int32.MaxValue);
 | ||
|                 return TextRenderer.MeasureText(g, txt, this.Font, proposedSize, NormalTextFormatFlags);
 | ||
|             }
 | ||
|             
 | ||
|             // Using GDI+ renderering
 | ||
|             using (StringFormat fmt = new StringFormat()) {
 | ||
|                 fmt.Trimming = StringTrimming.EllipsisCharacter;
 | ||
|                 SizeF sizeF = g.MeasureString(txt, this.Font, width, fmt);
 | ||
|                 return new Size(1 + (int)sizeF.Width, 1 + (int)sizeF.Height);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the Color that is the background color for this item's cell
 | ||
|         /// </summary>
 | ||
|         /// <returns>The background color of the subitem</returns>
 | ||
|         public virtual Color GetBackgroundColor() {
 | ||
|             if (!this.ListView.Enabled)
 | ||
|                 return SystemColors.Control;
 | ||
| 
 | ||
|             if (this.IsItemSelected && !this.ListView.UseTranslucentSelection && this.ListView.FullRowSelect)
 | ||
|                 return this.GetSelectedBackgroundColor();
 | ||
| 
 | ||
|             if (this.SubItem == null || this.ListItem.UseItemStyleForSubItems)
 | ||
|                 return this.ListItem.BackColor;
 | ||
| 
 | ||
|             return this.SubItem.BackColor;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the color of the background color when the item is selected
 | ||
|         /// </summary>
 | ||
|         /// <returns>The background color of the subitem</returns>
 | ||
|         public virtual Color GetSelectedBackgroundColor() {
 | ||
|             if (this.ListView.Focused) 
 | ||
|                 return this.ListItem.SelectedBackColor ?? this.ListView.SelectedBackColorOrDefault;
 | ||
| 
 | ||
|             if (!this.ListView.HideSelection)
 | ||
|                 return this.ListView.UnfocusedSelectedBackColorOrDefault;
 | ||
| 
 | ||
|             return this.ListItem.BackColor;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the color to be used for text in this cell
 | ||
|         /// </summary>
 | ||
|         /// <returns>The text color of the subitem</returns>
 | ||
|         public virtual Color GetForegroundColor() {
 | ||
|             if (this.IsItemSelected && 
 | ||
|                 !this.ListView.UseTranslucentSelection &&
 | ||
|                 (this.ColumnIsPrimary || this.ListView.FullRowSelect)) 
 | ||
|                 return this.GetSelectedForegroundColor();
 | ||
| 
 | ||
|             return this.SubItem == null || this.ListItem.UseItemStyleForSubItems ? this.ListItem.ForeColor : this.SubItem.ForeColor;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the color of the foreground color when the item is selected
 | ||
|         /// </summary>
 | ||
|         /// <returns>The foreground color of the subitem</returns>
 | ||
|         public virtual Color GetSelectedForegroundColor()
 | ||
|         {
 | ||
|             if (this.ListView.Focused)
 | ||
|                 return this.ListItem.SelectedForeColor ?? this.ListView.SelectedForeColorOrDefault;
 | ||
| 
 | ||
|             if (!this.ListView.HideSelection)
 | ||
|                 return this.ListView.UnfocusedSelectedForeColorOrDefault;
 | ||
| 
 | ||
|             return this.SubItem == null || this.ListItem.UseItemStyleForSubItems ? this.ListItem.ForeColor : this.SubItem.ForeColor;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the image that should be drawn against this subitem
 | ||
|         /// </summary>
 | ||
|         /// <returns>An Image or null if no image should be drawn.</returns>
 | ||
|         protected virtual Image GetImage() {
 | ||
|             return this.GetImage(this.GetImageSelector());
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the actual image that should be drawn when keyed by the given image selector.
 | ||
|         /// An image selector can be: <list type="bullet">
 | ||
|         /// <item><description>an int, giving the index into the image list</description></item>
 | ||
|         /// <item><description>a string, giving the image key into the image list</description></item>
 | ||
|         /// <item><description>an Image, being the image itself</description></item>
 | ||
|         /// </list>
 | ||
|         /// </summary>
 | ||
|         /// <param name="imageSelector">The value that indicates the image to be used</param>
 | ||
|         /// <returns>An Image or null</returns>
 | ||
|         protected virtual Image GetImage(Object imageSelector) {
 | ||
|             if (imageSelector == null || imageSelector == DBNull.Value)
 | ||
|                 return null;
 | ||
| 
 | ||
|             ImageList il = this.ImageListOrDefault;
 | ||
|             if (il != null) {
 | ||
|                 if (imageSelector is Int32) {
 | ||
|                     Int32 index = (Int32) imageSelector;
 | ||
|                     if (index < 0 || index >= il.Images.Count)
 | ||
|                         return null;
 | ||
| 
 | ||
|                     return il.Images[index];
 | ||
|                 }
 | ||
| 
 | ||
|                 String str = imageSelector as String;
 | ||
|                 if (str != null) {
 | ||
|                     if (il.Images.ContainsKey(str))
 | ||
|                         return il.Images[str];
 | ||
| 
 | ||
|                     return null;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             return imageSelector as Image;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// </summary>
 | ||
|         protected virtual Object GetImageSelector() {
 | ||
|             return this.ColumnIsPrimary ? this.ListItem.ImageSelector : this.OLVSubItem.ImageSelector;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the string that should be drawn within this
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual string GetText() {
 | ||
|             return this.SubItem == null ? this.ListItem.Text : this.SubItem.Text;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the Color that is the background color for this item's text
 | ||
|         /// </summary>
 | ||
|         /// <returns>The background color of the subitem's text</returns>
 | ||
|         [Obsolete("Use GetBackgroundColor() instead")]
 | ||
|         protected virtual Color GetTextBackgroundColor() {
 | ||
|             return Color.Red; // just so it shows up if it is used
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region IRenderer members
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Render the whole item in a non-details view.
 | ||
|         /// </summary>
 | ||
|         /// <param name="e"></param>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="itemBounds"></param>
 | ||
|         /// <param name="model"></param>
 | ||
|         /// <returns></returns>
 | ||
|         public override bool RenderItem(DrawListViewItemEventArgs e, Graphics g, Rectangle itemBounds, object model) {
 | ||
|             this.ConfigureItem(e, itemBounds, model);
 | ||
|             return this.OptionalRender(g, itemBounds);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Prepare this renderer to draw in response to the given event
 | ||
|         /// </summary>
 | ||
|         /// <param name="e"></param>
 | ||
|         /// <param name="itemBounds"></param>
 | ||
|         /// <param name="model"></param>
 | ||
|         /// <remarks>Use this if you want to chain a second renderer within a primary renderer.</remarks>
 | ||
|         public virtual void ConfigureItem(DrawListViewItemEventArgs e, Rectangle itemBounds, object model)
 | ||
|         {
 | ||
|             this.ClearState();
 | ||
| 
 | ||
|             this.DrawItemEvent = e;
 | ||
|             this.ListItem = (OLVListItem)e.Item;
 | ||
|             this.SubItem = null;
 | ||
|             this.ListView = (ObjectListView)this.ListItem.ListView;
 | ||
|             this.Column = this.ListView.GetColumn(0);
 | ||
|             this.RowObject = model;
 | ||
|             this.Bounds = itemBounds;
 | ||
|             this.IsItemSelected = this.ListItem.Selected && this.ListItem.Enabled;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Render one cell
 | ||
|         /// </summary>
 | ||
|         /// <param name="e"></param>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="model"></param>
 | ||
|         /// <returns></returns>
 | ||
|         public override bool RenderSubItem(DrawListViewSubItemEventArgs e, Graphics g, Rectangle cellBounds, object model) {
 | ||
|             this.ConfigureSubItem(e, cellBounds, model);
 | ||
|             return this.OptionalRender(g, cellBounds);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Prepare this renderer to draw in response to the given event
 | ||
|         /// </summary>
 | ||
|         /// <param name="e"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="model"></param>
 | ||
|         /// <remarks>Use this if you want to chain a second renderer within a primary renderer.</remarks>
 | ||
|         public virtual void ConfigureSubItem(DrawListViewSubItemEventArgs e, Rectangle cellBounds, object model) {
 | ||
|             this.ClearState();
 | ||
| 
 | ||
|             this.Event = e;
 | ||
|             this.ListItem = (OLVListItem)e.Item;
 | ||
|             this.SubItem = (OLVListSubItem)e.SubItem;
 | ||
|             this.ListView = (ObjectListView)this.ListItem.ListView;
 | ||
|             this.Column = (OLVColumn)e.Header;
 | ||
|             this.RowObject = model;
 | ||
|             this.Bounds = cellBounds;
 | ||
|             this.IsItemSelected = this.ListItem.Selected && this.ListItem.Enabled;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate which part of this cell was hit
 | ||
|         /// </summary>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         public override void HitTest(OlvListViewHitTestInfo hti, int x, int y) {
 | ||
|             this.ClearState();
 | ||
| 
 | ||
|             this.ListView = hti.ListView;
 | ||
|             this.ListItem = hti.Item;
 | ||
|             this.SubItem = hti.SubItem;
 | ||
|             this.Column = hti.Column;
 | ||
|             this.RowObject = hti.RowObject;
 | ||
|             this.IsItemSelected = this.ListItem.Selected && this.ListItem.Enabled;
 | ||
|             if (this.SubItem == null)
 | ||
|                 this.Bounds = this.ListItem.Bounds;
 | ||
|             else
 | ||
|                 this.Bounds = this.ListItem.GetSubItemBounds(this.Column.Index);
 | ||
| 
 | ||
|             using (Graphics g = this.ListView.CreateGraphics()) {
 | ||
|                 this.HandleHitTest(g, hti, x, y);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the edit rectangle
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         public override Rectangle GetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) {
 | ||
|             this.ClearState();
 | ||
| 
 | ||
|             this.ListView = (ObjectListView) item.ListView;
 | ||
|             this.ListItem = item;
 | ||
|             this.SubItem = item.GetSubItem(subItemIndex);
 | ||
|             this.Column = this.ListView.GetColumn(subItemIndex);
 | ||
|             this.RowObject = item.RowObject;
 | ||
|             this.IsItemSelected = this.ListItem.Selected && this.ListItem.Enabled;
 | ||
|             this.Bounds = cellBounds;
 | ||
| 
 | ||
|             return this.HandleGetEditRectangle(g, cellBounds, item, subItemIndex, preferredSize);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region IRenderer implementation
 | ||
| 
 | ||
|         // Subclasses will probably want to override these methods rather than the IRenderer
 | ||
|         // interface methods.
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our data into the given rectangle using the given graphics context.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// <para>Subclasses should override this method.</para></remarks>
 | ||
|         /// <param name="g">The graphics context that should be used for drawing</param>
 | ||
|         /// <param name="r">The bounds of the subitem cell</param>
 | ||
|         /// <returns>Returns whether the rendering has already taken place.
 | ||
|         /// If this returns false, the default processing will take over.
 | ||
|         /// </returns>
 | ||
|         public virtual bool OptionalRender(Graphics g, Rectangle r) {
 | ||
|             if (this.ListView.View != View.Details)
 | ||
|                 return false;
 | ||
| 
 | ||
|             this.Render(g, r);
 | ||
|             return true;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our data into the given rectangle using the given graphics context.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// <para>Subclasses should override this method if they never want
 | ||
|         /// to fall back on the default processing</para></remarks>
 | ||
|         /// <param name="g">The graphics context that should be used for drawing</param>
 | ||
|         /// <param name="r">The bounds of the subitem cell</param>
 | ||
|         public virtual void Render(Graphics g, Rectangle r) {
 | ||
|             this.StandardRender(g, r);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Do the actual work of hit testing. Subclasses should override this rather than HitTest()
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         protected virtual void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y) {
 | ||
|             Rectangle r = this.CalculateAlignedRectangle(g, ApplyCellPadding(this.Bounds));
 | ||
|             this.StandardHitTest(g, hti, r, x, y);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Handle a HitTest request after all state information has been initialized
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) {
 | ||
|             // MAINTAINER NOTE: This type testing is wrong (design-wise). The base class should return cell bounds,
 | ||
|             // and a more specialized class should return StandardGetEditRectangle(). But BaseRenderer is used directly
 | ||
|             // to draw most normal cells, as well as being directly subclassed for user implemented renderers. And this
 | ||
|             // method needs to return different bounds in each of those cases. We should have a StandardRenderer and make
 | ||
|             // BaseRenderer into an ABC -- but that would break too much existing code. And so we have this hack :(
 | ||
| 
 | ||
|             // If we are a standard renderer, return the position of the text, otherwise, use the whole cell.
 | ||
|             if (this.GetType() == typeof (BaseRenderer))
 | ||
|                 return this.StandardGetEditRectangle(g, cellBounds, preferredSize);
 | ||
| 
 | ||
|             // Center the editor vertically
 | ||
|             if (cellBounds.Height != preferredSize.Height)
 | ||
|                 cellBounds.Y += (cellBounds.Height - preferredSize.Height) / 2;
 | ||
| 
 | ||
|             return cellBounds;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Standard IRenderer implementations
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the standard "[checkbox] [image] [text]" cell after the state properties have been initialized.
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         protected void StandardRender(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
| 
 | ||
|             // Adjust the first columns rectangle to match the padding used by the native mode of the ListView
 | ||
|             if (this.ColumnIsPrimary && this.CellHorizontalAlignment == HorizontalAlignment.Left ) {
 | ||
|                 r.X += 3;
 | ||
|                 r.Width -= 1;
 | ||
|             }
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
|             this.DrawAlignedImageAndText(g, r);
 | ||
| 
 | ||
|             // Show where the bounds of the cell padding are (debugging)
 | ||
|             if (ObjectListView.ShowCellPaddingBounds)
 | ||
|                 g.DrawRectangle(Pens.Purple, r);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Change the bounds of the given rectangle to take any cell padding into account
 | ||
|         /// </summary>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <returns></returns>
 | ||
|         public virtual Rectangle ApplyCellPadding(Rectangle r) {
 | ||
|             Rectangle? padding = this.EffectiveCellPadding;
 | ||
|             if (!padding.HasValue)
 | ||
|                 return r;
 | ||
|             // The two subtractions below look wrong, but are correct!
 | ||
|             Rectangle paddingRectangle = padding.Value;
 | ||
|             r.Width -= paddingRectangle.Right;
 | ||
|             r.Height -= paddingRectangle.Bottom;
 | ||
|             r.Offset(paddingRectangle.Location);
 | ||
|             return r;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Perform normal hit testing relative to the given aligned content bounds
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="alignedContentRectangle"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         protected virtual void StandardHitTest(Graphics g, OlvListViewHitTestInfo hti, Rectangle alignedContentRectangle, int x, int y) {
 | ||
|             Rectangle r = alignedContentRectangle;
 | ||
| 
 | ||
|             // Match tweaking from renderer
 | ||
|             if (this.ColumnIsPrimary && this.CellHorizontalAlignment == HorizontalAlignment.Left && !(this is TreeListView.TreeRenderer)) {
 | ||
|                 r.X += 3;
 | ||
|                 r.Width -= 1;
 | ||
|             }
 | ||
|             int width = 0;
 | ||
| 
 | ||
|             // Did they hit a check box on the primary column?
 | ||
|             if (this.ColumnIsPrimary && this.ListView.CheckBoxes) {
 | ||
|                 Size checkBoxSize = this.CalculateCheckBoxSize(g);
 | ||
|                 int checkBoxTop = this.AlignVertically(r, checkBoxSize.Height);
 | ||
|                 Rectangle r3 = new Rectangle(r.X, checkBoxTop, checkBoxSize.Width, checkBoxSize.Height);
 | ||
|                 width = r3.Width + 6;
 | ||
|                 // g.DrawRectangle(Pens.DarkGreen, r3);
 | ||
|                 if (r3.Contains(x, y)) {
 | ||
|                     hti.HitTestLocation = HitTestLocation.CheckBox;
 | ||
|                     return;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             // Did they hit the image? If they hit the image of a 
 | ||
|             // non-primary column that has a checkbox, it counts as a 
 | ||
|             // checkbox hit
 | ||
|             r.X += width;
 | ||
|             r.Width -= width;
 | ||
|             width = this.CalculateImageWidth(g, this.GetImageSelector());
 | ||
|             Rectangle rTwo = r;
 | ||
|             rTwo.Width = width;
 | ||
|             // g.DrawRectangle(Pens.Red, rTwo);
 | ||
|             if (rTwo.Contains(x, y)) {
 | ||
|                 if (this.Column != null && (this.Column.Index > 0 && this.Column.CheckBoxes))
 | ||
|                     hti.HitTestLocation = HitTestLocation.CheckBox;
 | ||
|                 else
 | ||
|                     hti.HitTestLocation = HitTestLocation.Image;
 | ||
|                 return;
 | ||
|             }
 | ||
| 
 | ||
|             // Did they hit the text?
 | ||
|             r.X += width;
 | ||
|             r.Width -= width;
 | ||
|             width = this.CalculateTextWidth(g, this.GetText(), r.Width);
 | ||
|             rTwo = r;
 | ||
|             rTwo.Width = width;
 | ||
|             // g.DrawRectangle(Pens.Blue, rTwo);
 | ||
|             if (rTwo.Contains(x, y)) {
 | ||
|                 hti.HitTestLocation = HitTestLocation.Text;
 | ||
|                 return;
 | ||
|             }
 | ||
| 
 | ||
|             hti.HitTestLocation = HitTestLocation.InCell;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// This method calculates the bounds of the text within a standard layout
 | ||
|         /// (i.e. optional checkbox, optional image, text)
 | ||
|         /// </summary>
 | ||
|         /// <remarks>This method only works correctly if the state of the renderer
 | ||
|         /// has been fully initialized (see BaseRenderer.GetEditRectangle)</remarks>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual Rectangle StandardGetEditRectangle(Graphics g, Rectangle cellBounds, Size preferredSize) {
 | ||
| 
 | ||
|             Size contentSize = this.CalculateContentSize(g, cellBounds);
 | ||
|             int contentWidth = this.Column.CellEditUseWholeCellEffective ? cellBounds.Width : contentSize.Width;
 | ||
|             Rectangle editControlBounds = this.CalculatePaddedAlignedBounds(g, cellBounds, new Size(contentWidth, preferredSize.Height));
 | ||
| 
 | ||
|             Size checkBoxSize = this.CalculatePrimaryCheckBoxSize(g);
 | ||
|             int imageWidth = this.CalculateImageWidth(g, this.GetImageSelector());
 | ||
| 
 | ||
|             int width = checkBoxSize.Width + imageWidth;
 | ||
| 
 | ||
|             // Indent the primary column by the required amount
 | ||
|             if (this.ListItem.IndentCount > 0) {
 | ||
|                 int indentWidth = this.ListView.SmallImageSize.Width * this.ListItem.IndentCount;
 | ||
|                 width += indentWidth;
 | ||
|             }
 | ||
| 
 | ||
|             editControlBounds.X += width;
 | ||
|             editControlBounds.Width -= width;
 | ||
| 
 | ||
|             if (editControlBounds.Width < 50)
 | ||
|                 editControlBounds.Width = 50;
 | ||
|             if (editControlBounds.Right > cellBounds.Right)
 | ||
|                 editControlBounds.Width = cellBounds.Right - editControlBounds.Left;
 | ||
| 
 | ||
|             return editControlBounds;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Apply any padding to the given bounds, and then align a rectangle of the given
 | ||
|         /// size within that padded area.
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="preferredSize"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected Rectangle CalculatePaddedAlignedBounds(Graphics g, Rectangle cellBounds, Size preferredSize) {
 | ||
|             Rectangle r = ApplyCellPadding(cellBounds);
 | ||
|             r = this.AlignRectangle(r, new Rectangle(Point.Empty, preferredSize));
 | ||
|             return r;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Drawing routines
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the given image aligned horizontally within the column.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// Over tall images are scaled to fit. Over-wide images are
 | ||
|         /// truncated. This is by design!
 | ||
|         /// </remarks>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">Bounds of the cell</param>
 | ||
|         /// <param name="image">The image to be drawn</param>
 | ||
|         protected virtual void DrawAlignedImage(Graphics g, Rectangle r, Image image) {
 | ||
|             if (image == null)
 | ||
|                 return;
 | ||
| 
 | ||
|             // By default, the image goes in the top left of the rectangle
 | ||
|             Rectangle imageBounds = new Rectangle(r.Location, image.Size);
 | ||
| 
 | ||
|             // If the image is too tall to be drawn in the space provided, proportionally scale it down.
 | ||
|             // Too wide images are not scaled.
 | ||
|             if (image.Height > r.Height) {
 | ||
|                 float scaleRatio = (float) r.Height / (float) image.Height;
 | ||
|                 imageBounds.Width = (int) ((float) image.Width * scaleRatio);
 | ||
|                 imageBounds.Height = r.Height - 1;
 | ||
|             }
 | ||
| 
 | ||
|             // Align and draw our (possibly scaled) image
 | ||
|             Rectangle alignRectangle = this.AlignRectangle(r, imageBounds);
 | ||
|             if (this.ListItem.Enabled)
 | ||
|                 g.DrawImage(image, alignRectangle);
 | ||
|             else
 | ||
|                 ControlPaint.DrawImageDisabled(g, image, alignRectangle.X, alignRectangle.Y, GetBackgroundColor());
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our subitems image and text
 | ||
|         /// </summary>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">Pre-padded bounds of the cell</param>
 | ||
|         protected virtual void DrawAlignedImageAndText(Graphics g, Rectangle r) {
 | ||
|             this.DrawImageAndText(g, this.CalculateAlignedRectangle(g, r));
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Fill in the background of this cell
 | ||
|         /// </summary>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">Bounds of the cell</param>
 | ||
|         protected virtual void DrawBackground(Graphics g, Rectangle r) {
 | ||
|             if (!this.IsDrawBackground)
 | ||
|                 return;
 | ||
| 
 | ||
|             Color backgroundColor = this.GetBackgroundColor();
 | ||
| 
 | ||
|             using (Brush brush = new SolidBrush(backgroundColor)) {
 | ||
|                 g.FillRectangle(brush, r.X - 1, r.Y - 1, r.Width + 2, r.Height + 2);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the primary check box of this row (checkboxes in other sub items use a different method)
 | ||
|         /// </summary>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">The pre-aligned and padded target rectangle</param>
 | ||
|         protected virtual int DrawCheckBox(Graphics g, Rectangle r) {
 | ||
|             // The odd constants are to match checkbox placement in native mode (on XP at least)
 | ||
|             // TODO: Unify this with CheckStateRenderer
 | ||
| 
 | ||
|             // The rectangle r is already horizontally aligned. We still need to align it vertically.
 | ||
|             Size checkBoxSize = this.CalculateCheckBoxSize(g);
 | ||
|             Point checkBoxLocation = new Point(r.X, this.AlignVertically(r, checkBoxSize.Height));
 | ||
| 
 | ||
|             if (this.IsPrinting || this.UseCustomCheckboxImages) {
 | ||
|                 int imageIndex = this.ListItem.StateImageIndex;
 | ||
|                 if (this.ListView.StateImageList == null || imageIndex < 0 || imageIndex >= this.ListView.StateImageList.Images.Count)
 | ||
|                     return 0;
 | ||
| 
 | ||
|                 return this.DrawImage(g, new Rectangle(checkBoxLocation, checkBoxSize), this.ListView.StateImageList.Images[imageIndex]) + 4;
 | ||
|             }
 | ||
| 
 | ||
|             CheckBoxState boxState = this.GetCheckBoxState(this.ListItem.CheckState);
 | ||
|             CheckBoxRenderer.DrawCheckBox(g, checkBoxLocation, boxState);
 | ||
|             return checkBoxSize.Width;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the CheckBoxState we need to correctly draw the given state
 | ||
|         /// </summary>
 | ||
|         /// <param name="checkState"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected virtual CheckBoxState GetCheckBoxState(CheckState checkState) {
 | ||
| 
 | ||
|             // Should the checkbox be drawn as disabled?
 | ||
|             if (this.IsCheckBoxDisabled) {
 | ||
|                 switch (checkState) {
 | ||
|                     case CheckState.Checked:
 | ||
|                         return CheckBoxState.CheckedDisabled;
 | ||
|                     case CheckState.Unchecked:
 | ||
|                         return CheckBoxState.UncheckedDisabled;
 | ||
|                     default:
 | ||
|                         return CheckBoxState.MixedDisabled;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             // Is the cursor currently over this checkbox?
 | ||
|             if (this.IsCheckboxHot) {
 | ||
|                 switch (checkState) {
 | ||
|                     case CheckState.Checked:
 | ||
|                         return CheckBoxState.CheckedHot;
 | ||
|                     case CheckState.Unchecked:
 | ||
|                         return CheckBoxState.UncheckedHot;
 | ||
|                     default:
 | ||
|                         return CheckBoxState.MixedHot;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             // Not hot and not disabled -- just draw it normally
 | ||
|             switch (checkState) {
 | ||
|                 case CheckState.Checked:
 | ||
|                     return CheckBoxState.CheckedNormal;
 | ||
|                 case CheckState.Unchecked:
 | ||
|                     return CheckBoxState.UncheckedNormal;
 | ||
|                 default:
 | ||
|                     return CheckBoxState.MixedNormal;
 | ||
|             }
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Should this checkbox be drawn as disabled?
 | ||
|         /// </summary>
 | ||
|         protected virtual bool IsCheckBoxDisabled {
 | ||
|             get {
 | ||
|                 if (this.ListItem != null && !this.ListItem.Enabled)
 | ||
|                     return true;
 | ||
| 
 | ||
|                 if (!this.ListView.RenderNonEditableCheckboxesAsDisabled)
 | ||
|                     return false;
 | ||
| 
 | ||
|                 return (this.ListView.CellEditActivation == ObjectListView.CellEditActivateMode.None ||
 | ||
|                         (this.Column != null && !this.Column.IsEditable));
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Is the current item hot (i.e. under the mouse)?
 | ||
|         /// </summary>
 | ||
|         protected bool IsCellHot {
 | ||
|             get {
 | ||
|                 return this.ListView != null &&
 | ||
|                        this.ListView.HotRowIndex == this.ListItem.Index &&
 | ||
|                        this.ListView.HotColumnIndex == (this.Column == null ? 0 : this.Column.Index);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Is the mouse over a checkbox in this cell?
 | ||
|         /// </summary>
 | ||
|         protected bool IsCheckboxHot {
 | ||
|             get {
 | ||
|                 return this.IsCellHot && this.ListView.HotCellHitLocation == HitTestLocation.CheckBox;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the given text and optional image in the "normal" fashion
 | ||
|         /// </summary>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">Bounds of the cell</param>
 | ||
|         /// <param name="imageSelector">The optional image to be drawn</param>
 | ||
|         protected virtual int DrawImage(Graphics g, Rectangle r, Object imageSelector) {
 | ||
|             if (imageSelector == null || imageSelector == DBNull.Value)
 | ||
|                 return 0;
 | ||
| 
 | ||
|             // Draw from the image list (most common case)
 | ||
|             ImageList il = this.ImageListOrDefault;
 | ||
|             if (il != null) {
 | ||
| 
 | ||
|                 // Try to translate our imageSelector into a valid ImageList index
 | ||
|                 int selectorAsInt = -1;
 | ||
|                 if (imageSelector is Int32) {
 | ||
|                     selectorAsInt = (Int32) imageSelector;
 | ||
|                     if (selectorAsInt >= il.Images.Count)
 | ||
|                         selectorAsInt = -1;
 | ||
|                 } else {
 | ||
|                     String selectorAsString = imageSelector as String;
 | ||
|                     if (selectorAsString != null)
 | ||
|                         selectorAsInt = il.Images.IndexOfKey(selectorAsString);
 | ||
|                 }
 | ||
| 
 | ||
|                 // If we found a valid index into the ImageList, draw it.
 | ||
|                 // We want to draw using the native DrawImageList calls, since that let's us do some nice effects
 | ||
|                 // But the native call does not work on PrinterDCs, so if we're printing we have to skip this bit.
 | ||
|                 if (selectorAsInt >= 0) {
 | ||
|                     if (!this.IsPrinting) {
 | ||
|                         if (il.ImageSize.Height < r.Height)
 | ||
|                             r.Y = this.AlignVertically(r, new Rectangle(Point.Empty, il.ImageSize));
 | ||
| 
 | ||
|                         // If we are not printing, it's probable that the given Graphics object is double buffered using a BufferedGraphics object.
 | ||
|                         // But the ImageList.Draw method doesn't honor the Translation matrix that's probably in effect on the buffered
 | ||
|                         // graphics. So we have to calculate our drawing rectangle, relative to the cells natural boundaries.
 | ||
|                         // This effectively simulates the Translation matrix.
 | ||
| 
 | ||
|                         Rectangle r2 = new Rectangle(r.X - this.Bounds.X, r.Y - this.Bounds.Y, r.Width, r.Height);
 | ||
|                         NativeMethods.DrawImageList(g, il, selectorAsInt, r2.X, r2.Y, this.IsItemSelected, !this.ListItem.Enabled);
 | ||
|                         return il.ImageSize.Width;
 | ||
|                     }
 | ||
| 
 | ||
|                     // For some reason, printing from an image list doesn't work onto a printer context
 | ||
|                     // So get the image from the list and FALL THROUGH to the "print an image" case
 | ||
|                     imageSelector = il.Images[selectorAsInt];
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             // Is the selector actually an image?
 | ||
|             Image image = imageSelector as Image;
 | ||
|             if (image == null)
 | ||
|                 return 0; // no, give up
 | ||
| 
 | ||
|             if (image.Size.Height < r.Height)
 | ||
|                 r.Y = this.AlignVertically(r, new Rectangle(Point.Empty, image.Size));
 | ||
| 
 | ||
|             if (this.ListItem.Enabled)
 | ||
|                 g.DrawImageUnscaled(image, r.X, r.Y);
 | ||
|             else
 | ||
|                 ControlPaint.DrawImageDisabled(g, image, r.X, r.Y, GetBackgroundColor());
 | ||
| 
 | ||
|             return image.Width;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our subitems image and text
 | ||
|         /// </summary>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">Bounds of the cell</param>
 | ||
|         protected virtual void DrawImageAndText(Graphics g, Rectangle r) {
 | ||
|             int offset = 0;
 | ||
|             if (this.ListView.CheckBoxes && this.ColumnIsPrimary) {
 | ||
|                 offset = this.DrawCheckBox(g, r) + 6;
 | ||
|                 r.X += offset;
 | ||
|                 r.Width -= offset;
 | ||
|             }
 | ||
| 
 | ||
|             offset = this.DrawImage(g, r, this.GetImageSelector());
 | ||
|             r.X += offset;
 | ||
|             r.Width -= offset;
 | ||
| 
 | ||
|             this.DrawText(g, r, this.GetText());
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the given collection of image selectors
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="imageSelectors"></param>
 | ||
|         protected virtual int DrawImages(Graphics g, Rectangle r, ICollection imageSelectors) {
 | ||
|             // Collect the non-null images
 | ||
|             List<Image> images = new List<Image>();
 | ||
|             foreach (Object selector in imageSelectors) {
 | ||
|                 Image image = this.GetImage(selector);
 | ||
|                 if (image != null)
 | ||
|                     images.Add(image);
 | ||
|             }
 | ||
| 
 | ||
|             // Figure out how much space they will occupy
 | ||
|             int width = 0;
 | ||
|             int height = 0;
 | ||
|             foreach (Image image in images) {
 | ||
|                 width += (image.Width + this.Spacing);
 | ||
|                 height = Math.Max(height, image.Height);
 | ||
|             }
 | ||
| 
 | ||
|             // Align the collection of images within the cell
 | ||
|             Rectangle r2 = this.AlignRectangle(r, new Rectangle(0, 0, width, height));
 | ||
| 
 | ||
|             // Finally, draw all the images in their correct location
 | ||
|             Color backgroundColor = GetBackgroundColor();
 | ||
|             Point pt = r2.Location;
 | ||
|             foreach (Image image in images) {
 | ||
|                 if (this.ListItem.Enabled)
 | ||
|                     g.DrawImage(image, pt);
 | ||
|                 else
 | ||
|                     ControlPaint.DrawImageDisabled(g, image, pt.X, pt.Y, backgroundColor);
 | ||
|                 pt.X += (image.Width + this.Spacing);
 | ||
|             }
 | ||
| 
 | ||
|             // Return the width that the images occupy
 | ||
|             return width;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the given text and optional image in the "normal" fashion
 | ||
|         /// </summary>
 | ||
|         /// <param name="g">Graphics context to use for drawing</param>
 | ||
|         /// <param name="r">Bounds of the cell</param>
 | ||
|         /// <param name="txt">The string to be drawn</param>
 | ||
|         public virtual void DrawText(Graphics g, Rectangle r, String txt) {
 | ||
|             if (String.IsNullOrEmpty(txt))
 | ||
|                 return;
 | ||
| 
 | ||
|             if (this.UseGdiTextRendering)
 | ||
|                 this.DrawTextGdi(g, r, txt);
 | ||
|             else
 | ||
|                 this.DrawTextGdiPlus(g, r, txt);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Print the given text in the given rectangle using only GDI routines
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         /// <remarks>
 | ||
|         /// The native list control uses GDI routines to do its drawing, so using them
 | ||
|         /// here makes the owner drawn mode looks more natural.
 | ||
|         /// <para>This method doesn't honour the CanWrap setting on the renderer. All
 | ||
|         /// text is single line</para>
 | ||
|         /// </remarks>
 | ||
|         protected virtual void DrawTextGdi(Graphics g, Rectangle r, String txt) {
 | ||
|             Color backColor = Color.Transparent;
 | ||
|             if (this.IsDrawBackground && this.IsItemSelected && ColumnIsPrimary && !this.ListView.FullRowSelect)
 | ||
|                 backColor = this.GetSelectedBackgroundColor();
 | ||
| 
 | ||
|             TextFormatFlags flags = NormalTextFormatFlags | this.CellVerticalAlignmentAsTextFormatFlag;
 | ||
| 
 | ||
|             // I think there is a bug in the TextRenderer. Setting or not setting SingleLine doesn't make 
 | ||
|             // any difference -- it is always single line.
 | ||
|             if (!this.CanWrap)
 | ||
|                 flags |= TextFormatFlags.SingleLine;
 | ||
|             TextRenderer.DrawText(g, txt, this.Font, r, this.GetForegroundColor(), backColor, flags);
 | ||
|         }
 | ||
| 
 | ||
|         private bool ColumnIsPrimary {
 | ||
|             get { return this.Column != null && this.Column.Index == 0; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets the cell's vertical alignment as a TextFormatFlag
 | ||
|         /// </summary>
 | ||
|         /// <exception cref="ArgumentOutOfRangeException"></exception>
 | ||
|         protected TextFormatFlags CellVerticalAlignmentAsTextFormatFlag {
 | ||
|             get {
 | ||
|                 switch (this.EffectiveCellVerticalAlignment) {
 | ||
|                     case StringAlignment.Near:
 | ||
|                         return TextFormatFlags.Top;
 | ||
|                     case StringAlignment.Center:
 | ||
|                         return TextFormatFlags.VerticalCenter;
 | ||
|                     case StringAlignment.Far:
 | ||
|                         return TextFormatFlags.Bottom;
 | ||
|                     default:
 | ||
|                         throw new ArgumentOutOfRangeException();
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets the StringFormat needed when drawing text using GDI+
 | ||
|         /// </summary>
 | ||
|         protected virtual StringFormat StringFormatForGdiPlus {
 | ||
|             get {
 | ||
|                 StringFormat fmt = new StringFormat();
 | ||
|                 fmt.LineAlignment = this.EffectiveCellVerticalAlignment;
 | ||
|                 fmt.Trimming = StringTrimming.EllipsisCharacter;
 | ||
|                 fmt.Alignment = this.Column == null ? StringAlignment.Near : this.Column.TextStringAlign;
 | ||
|                 if (!this.CanWrap)
 | ||
|                     fmt.FormatFlags = StringFormatFlags.NoWrap;
 | ||
|                 return fmt;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Print the given text in the given rectangle using normal GDI+ .NET methods
 | ||
|         /// </summary>
 | ||
|         /// <remarks>Printing to a printer dc has to be done using this method.</remarks>
 | ||
|         protected virtual void DrawTextGdiPlus(Graphics g, Rectangle r, String txt) {
 | ||
|             using (StringFormat fmt = this.StringFormatForGdiPlus) {
 | ||
|                 // Draw the background of the text as selected, if it's the primary column
 | ||
|                 // and it's selected and it's not in FullRowSelect mode.
 | ||
|                 Font f = this.Font;
 | ||
|                 if (this.IsDrawBackground && this.IsItemSelected && this.ColumnIsPrimary && !this.ListView.FullRowSelect) {
 | ||
|                     SizeF size = g.MeasureString(txt, f, r.Width, fmt);
 | ||
|                     Rectangle r2 = r;
 | ||
|                     r2.Width = (int) size.Width + 1;
 | ||
|                     using (Brush brush = new SolidBrush(this.GetSelectedBackgroundColor())) {
 | ||
|                         g.FillRectangle(brush, r2);
 | ||
|                     }
 | ||
|                 }
 | ||
|                 RectangleF rf = r;
 | ||
|                 g.DrawString(txt, f, this.TextBrush, rf, fmt);
 | ||
|             }
 | ||
| 
 | ||
|             // We should put a focus rectangle around the column 0 text if it's selected --
 | ||
|             // but we don't because:
 | ||
|             // - I really dislike this UI convention
 | ||
|             // - we are using buffered graphics, so the DrawFocusRecatangle method of the event doesn't work
 | ||
| 
 | ||
|             //if (this.ColumnIsPrimary) {
 | ||
|             //    Size size = TextRenderer.MeasureText(this.SubItem.Text, this.ListView.ListFont);
 | ||
|             //    if (r.Width > size.Width)
 | ||
|             //        r.Width = size.Width;
 | ||
|             //    this.Event.DrawFocusRectangle(r);
 | ||
|             //}
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// This renderer highlights substrings that match a given text filter. 
 | ||
|     /// </summary>
 | ||
|     public class HighlightTextRenderer : BaseRenderer, IFilterAwareRenderer {
 | ||
|         #region Life and death
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Create a HighlightTextRenderer
 | ||
|         /// </summary>
 | ||
|         public HighlightTextRenderer() {
 | ||
|             this.FramePen = Pens.DarkGreen;
 | ||
|             this.FillBrush = Brushes.Yellow;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Create a HighlightTextRenderer
 | ||
|         /// </summary>
 | ||
|         /// <param name="filter"></param>
 | ||
|         public HighlightTextRenderer(TextMatchFilter filter)
 | ||
|             : this() {
 | ||
|             this.Filter = filter;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Create a HighlightTextRenderer
 | ||
|         /// </summary>
 | ||
|         /// <param name="text"></param>
 | ||
|         [Obsolete("Use HighlightTextRenderer(TextMatchFilter) instead", true)]
 | ||
|         public HighlightTextRenderer(string text) {}
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Configuration properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set how rounded will be the corners of the text match frame
 | ||
|         /// </summary>
 | ||
|         [Category("Appearance"),
 | ||
|          DefaultValue(3.0f),
 | ||
|          Description("How rounded will be the corners of the text match frame?")]
 | ||
|         public float CornerRoundness {
 | ||
|             get { return cornerRoundness; }
 | ||
|             set { cornerRoundness = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private float cornerRoundness = 3.0f;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the brush will be used to paint behind the matched substrings.
 | ||
|         /// Set this to null to not fill the frame.
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Brush FillBrush {
 | ||
|             get { return fillBrush; }
 | ||
|             set { fillBrush = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Brush fillBrush;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the filter that is filtering the ObjectListView and for
 | ||
|         /// which this renderer should highlight text
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public TextMatchFilter Filter {
 | ||
|             get { return filter; }
 | ||
|             set { filter = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private TextMatchFilter filter;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// When a filter changes, keep track of the text matching filters
 | ||
|         /// </summary>
 | ||
|         IModelFilter IFilterAwareRenderer.Filter
 | ||
|         {
 | ||
|             get { return filter; }
 | ||
|             set { RegisterNewFilter(value); }
 | ||
|         }
 | ||
| 
 | ||
|         internal void RegisterNewFilter(IModelFilter newFilter) {
 | ||
|             TextMatchFilter textFilter = newFilter as TextMatchFilter;
 | ||
|             if (textFilter != null)
 | ||
|             {
 | ||
|                 Filter = textFilter;
 | ||
|                 return;
 | ||
|             }
 | ||
|             CompositeFilter composite = newFilter as CompositeFilter;
 | ||
|             if (composite != null)
 | ||
|             {
 | ||
|                 foreach (TextMatchFilter textSubFilter in composite.TextFilters)
 | ||
|                 {
 | ||
|                     Filter = textSubFilter;
 | ||
|                     return;
 | ||
|                 }
 | ||
|             }
 | ||
|             Filter = null; 
 | ||
|         } 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the pen will be used to frame the matched substrings.
 | ||
|         /// Set this to null to not draw a frame.
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Pen FramePen {
 | ||
|             get { return framePen; }
 | ||
|             set { framePen = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Pen framePen;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets whether the frame around a text match will have rounded corners
 | ||
|         /// </summary>
 | ||
|         [Category("Appearance"),
 | ||
|          DefaultValue(true),
 | ||
|          Description("Will the frame around a text match will have rounded corners?")]
 | ||
|         public bool UseRoundedRectangle {
 | ||
|             get { return useRoundedRectangle; }
 | ||
|             set { useRoundedRectangle = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private bool useRoundedRectangle = true;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Compatibility properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the text that will be highlighted
 | ||
|         /// </summary>
 | ||
|         [Obsolete("Set the Filter directly rather than just the text", true)]
 | ||
|         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public string TextToHighlight {
 | ||
|             get { return String.Empty; }
 | ||
|             set { }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the manner in which substring will be compared.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>
 | ||
|         /// Use this to control if substring matches are case sensitive or insensitive.</remarks>
 | ||
|         [Obsolete("Set the Filter directly rather than just this setting", true)]
 | ||
|         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public StringComparison StringComparison {
 | ||
|             get { return StringComparison.CurrentCultureIgnoreCase; }
 | ||
|             set { }
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region IRenderer interface overrides
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Handle a HitTest request after all state information has been initialized
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         protected override Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) {
 | ||
|             return this.StandardGetEditRectangle(g, cellBounds, preferredSize);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Rendering
 | ||
| 
 | ||
|         // This class has two implement two highlighting schemes: one for GDI, another for GDI+.
 | ||
|         // Naturally, GDI+ makes the task easier, but we have to provide something for GDI
 | ||
|         // since that it is what is normally used.
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw text using GDI
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         protected override void DrawTextGdi(Graphics g, Rectangle r, string txt) {
 | ||
|             if (this.ShouldDrawHighlighting)
 | ||
|                 this.DrawGdiTextHighlighting(g, r, txt);
 | ||
| 
 | ||
|             base.DrawTextGdi(g, r, txt);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the highlighted text using GDI
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         protected virtual void DrawGdiTextHighlighting(Graphics g, Rectangle r, string txt) {
 | ||
| 
 | ||
|             // TextRenderer puts horizontal padding around the strings, so we need to take
 | ||
|             // that into account when measuring strings
 | ||
|             const int paddingAdjustment = 6;
 | ||
| 
 | ||
|             // Cache the font
 | ||
|             Font f = this.Font;
 | ||
| 
 | ||
|             foreach (CharacterRange range in this.Filter.FindAllMatchedRanges(txt)) {
 | ||
|                 // Measure the text that comes before our substring
 | ||
|                 Size precedingTextSize = Size.Empty;
 | ||
|                 if (range.First > 0) {
 | ||
|                     string precedingText = txt.Substring(0, range.First);
 | ||
|                     precedingTextSize = TextRenderer.MeasureText(g, precedingText, f, r.Size, NormalTextFormatFlags);
 | ||
|                     precedingTextSize.Width -= paddingAdjustment;
 | ||
|                 }
 | ||
| 
 | ||
|                 // Measure the length of our substring (may be different each time due to case differences)
 | ||
|                 string highlightText = txt.Substring(range.First, range.Length);
 | ||
|                 Size textToHighlightSize = TextRenderer.MeasureText(g, highlightText, f, r.Size, NormalTextFormatFlags);
 | ||
|                 textToHighlightSize.Width -= paddingAdjustment;
 | ||
| 
 | ||
|                 float textToHighlightLeft = r.X + precedingTextSize.Width + 1;
 | ||
|                 float textToHighlightTop = this.AlignVertically(r, textToHighlightSize.Height);
 | ||
| 
 | ||
|                 // Draw a filled frame around our substring
 | ||
|                 this.DrawSubstringFrame(g, textToHighlightLeft, textToHighlightTop, textToHighlightSize.Width, textToHighlightSize.Height);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw an indication around the given frame that shows a text match
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         /// <param name="width"></param>
 | ||
|         /// <param name="height"></param>
 | ||
|         protected virtual void DrawSubstringFrame(Graphics g, float x, float y, float width, float height) {
 | ||
|             if (this.UseRoundedRectangle) {
 | ||
|                 using (GraphicsPath path = this.GetRoundedRect(x, y, width, height, 3.0f)) {
 | ||
|                     if (this.FillBrush != null)
 | ||
|                     {
 | ||
|                         if (this.IsItemSelected)
 | ||
|                         { g.FillPath(Brushes.Red, path); }
 | ||
|                         else
 | ||
|                         {
 | ||
|                             g.FillPath(this.FillBrush, path);
 | ||
|                         }
 | ||
|                     }
 | ||
|                     if (this.FramePen != null)
 | ||
|                         g.DrawPath(this.FramePen, path);
 | ||
|                 }
 | ||
|             } else {
 | ||
|                 if (this.FillBrush != null)
 | ||
|                     if (this.IsItemSelected)
 | ||
|                     { g.FillRectangle(Brushes.Red, x, y, width, height); }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         g.FillRectangle(this.FillBrush, x, y, width, height);
 | ||
|                     }
 | ||
|                 
 | ||
|                 if (this.FramePen != null)
 | ||
|                     g.DrawRectangle(this.FramePen, x, y, width, height);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the text using GDI+
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         protected override void DrawTextGdiPlus(Graphics g, Rectangle r, string txt) {
 | ||
|             if (this.ShouldDrawHighlighting)
 | ||
|                 this.DrawGdiPlusTextHighlighting(g, r, txt);
 | ||
| 
 | ||
|             base.DrawTextGdiPlus(g, r, txt);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the highlighted text using GDI+
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="txt"></param>
 | ||
|         protected virtual void DrawGdiPlusTextHighlighting(Graphics g, Rectangle r, string txt) {
 | ||
|             // Find the substrings we want to highlight
 | ||
|             List<CharacterRange> ranges = new List<CharacterRange>(this.Filter.FindAllMatchedRanges(txt));
 | ||
| 
 | ||
|             if (ranges.Count == 0)
 | ||
|                 return;
 | ||
| 
 | ||
|             using (StringFormat fmt = this.StringFormatForGdiPlus) {
 | ||
|                 RectangleF rf = r;
 | ||
|                 fmt.SetMeasurableCharacterRanges(ranges.ToArray());
 | ||
|                 Region[] stringRegions = g.MeasureCharacterRanges(txt, this.Font, rf, fmt);
 | ||
| 
 | ||
|                 foreach (Region region in stringRegions) {
 | ||
|                     RectangleF bounds = region.GetBounds(g);
 | ||
|                     this.DrawSubstringFrame(g, bounds.X - 1, bounds.Y - 1, bounds.Width + 2, bounds.Height);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Utilities
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets whether the renderer should actually draw highlighting
 | ||
|         /// </summary>
 | ||
|         protected bool ShouldDrawHighlighting {
 | ||
|             get { return this.Column == null || (this.Column.Searchable && this.Filter != null && this.Filter.HasComponents); }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return a GraphicPath that is a round cornered rectangle
 | ||
|         /// </summary>
 | ||
|         /// <returns>A round cornered rectangle path</returns>
 | ||
|         /// <remarks>If I could rely on people using C# 3.0+, this should be
 | ||
|         /// an extension method of GraphicsPath.</remarks>        
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         /// <param name="width"></param>
 | ||
|         /// <param name="height"></param>
 | ||
|         /// <param name="diameter"></param>
 | ||
|         protected GraphicsPath GetRoundedRect(float x, float y, float width, float height, float diameter) {
 | ||
|             return GetRoundedRect(new RectangleF(x, y, width, height), diameter);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return a GraphicPath that is a round cornered rectangle
 | ||
|         /// </summary>
 | ||
|         /// <param name="rect">The rectangle</param>
 | ||
|         /// <param name="diameter">The diameter of the corners</param>
 | ||
|         /// <returns>A round cornered rectangle path</returns>
 | ||
|         /// <remarks>If I could rely on people using C# 3.0+, this should be
 | ||
|         /// an extension method of GraphicsPath.</remarks>
 | ||
|         protected GraphicsPath GetRoundedRect(RectangleF rect, float diameter) {
 | ||
|             GraphicsPath path = new GraphicsPath();
 | ||
| 
 | ||
|             if (diameter > 0) {
 | ||
|                 RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter);
 | ||
|                 path.AddArc(arc, 180, 90);
 | ||
|                 arc.X = rect.Right - diameter;
 | ||
|                 path.AddArc(arc, 270, 90);
 | ||
|                 arc.Y = rect.Bottom - diameter;
 | ||
|                 path.AddArc(arc, 0, 90);
 | ||
|                 arc.X = rect.Left;
 | ||
|                 path.AddArc(arc, 90, 90);
 | ||
|                 path.CloseFigure();
 | ||
|             } else {
 | ||
|                 path.AddRectangle(rect);
 | ||
|             }
 | ||
| 
 | ||
|             return path;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// This class maps a data value to an image that should be drawn for that value.
 | ||
|     /// </summary>
 | ||
|     /// <remarks><para>It is useful for drawing data that is represented as an enum or boolean.</para></remarks>
 | ||
|     public class MappedImageRenderer : BaseRenderer {
 | ||
|         /// <summary>
 | ||
|         /// Return a renderer that draw boolean values using the given images
 | ||
|         /// </summary>
 | ||
|         /// <param name="trueImage">Draw this when our data value is true</param>
 | ||
|         /// <param name="falseImage">Draw this when our data value is false</param>
 | ||
|         /// <returns>A Renderer</returns>
 | ||
|         public static MappedImageRenderer Boolean(Object trueImage, Object falseImage) {
 | ||
|             return new MappedImageRenderer(true, trueImage, false, falseImage);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return a renderer that draw tristate boolean values using the given images
 | ||
|         /// </summary>
 | ||
|         /// <param name="trueImage">Draw this when our data value is true</param>
 | ||
|         /// <param name="falseImage">Draw this when our data value is false</param>
 | ||
|         /// <param name="nullImage">Draw this when our data value is null</param>
 | ||
|         /// <returns>A Renderer</returns>
 | ||
|         public static MappedImageRenderer TriState(Object trueImage, Object falseImage, Object nullImage) {
 | ||
|             return new MappedImageRenderer(new Object[] {true, trueImage, false, falseImage, null, nullImage});
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a new empty renderer
 | ||
|         /// </summary>
 | ||
|         public MappedImageRenderer() {
 | ||
|             map = new System.Collections.Hashtable();
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a new renderer that will show the given image when the given key is the aspect value
 | ||
|         /// </summary>
 | ||
|         /// <param name="key">The data value to be matched</param>
 | ||
|         /// <param name="image">The image to be shown when the key is matched</param>
 | ||
|         public MappedImageRenderer(Object key, Object image)
 | ||
|             : this() {
 | ||
|             this.Add(key, image);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a new renderer that will show the given images when it receives the given keys
 | ||
|         /// </summary>
 | ||
|         /// <param name="key1"></param>
 | ||
|         /// <param name="image1"></param>
 | ||
|         /// <param name="key2"></param>
 | ||
|         /// <param name="image2"></param>
 | ||
|         public MappedImageRenderer(Object key1, Object image1, Object key2, Object image2)
 | ||
|             : this() {
 | ||
|             this.Add(key1, image1);
 | ||
|             this.Add(key2, image2);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Build a renderer from the given array of keys and their matching images
 | ||
|         /// </summary>
 | ||
|         /// <param name="keysAndImages">An array of key/image pairs</param>
 | ||
|         public MappedImageRenderer(Object[] keysAndImages)
 | ||
|             : this() {
 | ||
|             if ((keysAndImages.GetLength(0) % 2) != 0)
 | ||
|                 throw new ArgumentException("Array must have key/image pairs");
 | ||
| 
 | ||
|             for (int i = 0; i < keysAndImages.GetLength(0); i += 2)
 | ||
|                 this.Add(keysAndImages[i], keysAndImages[i + 1]);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Register the image that should be drawn when our Aspect has the data value.
 | ||
|         /// </summary>
 | ||
|         /// <param name="value">Value that the Aspect must match</param>
 | ||
|         /// <param name="image">An ImageSelector -- an int, string or image</param>
 | ||
|         public void Add(Object value, Object image) {
 | ||
|             if (value == null)
 | ||
|                 this.nullImage = image;
 | ||
|             else
 | ||
|                 map[value] = image;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Render our value
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
| 
 | ||
|             ICollection aspectAsCollection = this.Aspect as ICollection;
 | ||
|             if (aspectAsCollection == null)
 | ||
|                 this.RenderOne(g, r, this.Aspect);
 | ||
|             else
 | ||
|                 this.RenderCollection(g, r, aspectAsCollection);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw a collection of images
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="imageSelectors"></param>
 | ||
|         protected void RenderCollection(Graphics g, Rectangle r, ICollection imageSelectors) {
 | ||
|             ArrayList images = new ArrayList();
 | ||
|             Image image = null;
 | ||
|             foreach (Object selector in imageSelectors) {
 | ||
|                 if (selector == null)
 | ||
|                     image = this.GetImage(this.nullImage);
 | ||
|                 else if (map.ContainsKey(selector))
 | ||
|                     image = this.GetImage(map[selector]);
 | ||
|                 else
 | ||
|                     image = null;
 | ||
| 
 | ||
|                 if (image != null)
 | ||
|                     images.Add(image);
 | ||
|             }
 | ||
| 
 | ||
|             this.DrawImages(g, r, images);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw one image
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="selector"></param>
 | ||
|         protected void RenderOne(Graphics g, Rectangle r, Object selector) {
 | ||
|             Image image = null;
 | ||
|             if (selector == null)
 | ||
|                 image = this.GetImage(this.nullImage);
 | ||
|             else if (map.ContainsKey(selector))
 | ||
|                 image = this.GetImage(map[selector]);
 | ||
| 
 | ||
|             if (image != null)
 | ||
|                 this.DrawAlignedImage(g, r, image);
 | ||
|         }
 | ||
| 
 | ||
|         #region Private variables
 | ||
| 
 | ||
|         private Hashtable map; // Track the association between values and images
 | ||
|         private Object nullImage; // image to be drawn for null values (since null can't be a key)
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// This renderer draws just a checkbox to match the check state of our model object.
 | ||
|     /// </summary>
 | ||
|     public class CheckStateRenderer : BaseRenderer {
 | ||
|         /// <summary>
 | ||
|         /// Draw our cell
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
|             if (this.Column == null)
 | ||
|                 return;
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
|             CheckState state = this.Column.GetCheckState(this.RowObject);
 | ||
|             if (this.IsPrinting) {
 | ||
|                 // Renderers don't work onto printer DCs, so we have to draw the image ourselves
 | ||
|                 string key = ObjectListView.CHECKED_KEY;
 | ||
|                 if (state == CheckState.Unchecked)
 | ||
|                     key = ObjectListView.UNCHECKED_KEY;
 | ||
|                 if (state == CheckState.Indeterminate)
 | ||
|                     key = ObjectListView.INDETERMINATE_KEY;
 | ||
|                 this.DrawAlignedImage(g, r, this.ImageListOrDefault.Images[key]);
 | ||
|             } else {
 | ||
|                 r = this.CalculateCheckBoxBounds(g, r);
 | ||
|                 CheckBoxRenderer.DrawCheckBox(g, r.Location, this.GetCheckBoxState(state));
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Handle the GetEditRectangle request
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         protected override Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) {
 | ||
|             return this.CalculatePaddedAlignedBounds(g, cellBounds, preferredSize);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Handle the HitTest request
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         protected override void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y) {
 | ||
|             Rectangle r = this.CalculateCheckBoxBounds(g, this.Bounds);
 | ||
|             if (r.Contains(x, y))
 | ||
|                 hti.HitTestLocation = HitTestLocation.CheckBox;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// Render an image that comes from our data source.
 | ||
|     /// </summary>
 | ||
|     /// <remarks>The image can be sourced from:
 | ||
|     /// <list type="bullet">
 | ||
|     /// <item><description>a byte-array (normally when the image to be shown is
 | ||
|     /// stored as a value in a database)</description></item>
 | ||
|     /// <item><description>an int, which is treated as an index into the image list</description></item>
 | ||
|     /// <item><description>a string, which is treated first as a file name, and failing that as an index into the image list</description></item>
 | ||
|     /// <item><description>an ICollection of ints or strings, which will be drawn as consecutive images</description></item>
 | ||
|     /// </list>
 | ||
|     /// <para>If an image is an animated GIF, it's state is stored in the SubItem object.</para>
 | ||
|     /// <para>By default, the image renderer does not render animations (it begins life with animations paused).
 | ||
|     /// To enable animations, you must call Unpause().</para>
 | ||
|     /// <para>In the current implementation (2009-09), each column showing animated gifs must have a 
 | ||
|     /// different instance of ImageRenderer assigned to it. You cannot share the same instance of
 | ||
|     /// an image renderer between two animated gif columns. If you do, only the last column will be
 | ||
|     /// animated.</para>
 | ||
|     /// </remarks>
 | ||
|     public class ImageRenderer : BaseRenderer {
 | ||
|         /// <summary>
 | ||
|         /// Make an empty image renderer
 | ||
|         /// </summary>
 | ||
|         public ImageRenderer() {
 | ||
|             this.stopwatch = new Stopwatch();
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make an empty image renderer that begins life ready for animations
 | ||
|         /// </summary>
 | ||
|         public ImageRenderer(bool startAnimations)
 | ||
|             : this() {
 | ||
|             this.Paused = !startAnimations;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Finalizer
 | ||
|         /// </summary>
 | ||
|         protected override void Dispose(bool disposing) {
 | ||
|             Paused = true;
 | ||
|             base.Dispose(disposing);
 | ||
|         }
 | ||
| 
 | ||
|         #region Properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Should the animations in this renderer be paused?
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public bool Paused {
 | ||
|             get { return isPaused; }
 | ||
|             set {
 | ||
|                 if (this.isPaused == value)
 | ||
|                     return;
 | ||
| 
 | ||
|                 this.isPaused = value;
 | ||
|                 if (this.isPaused) {
 | ||
|                     this.StopTickler();
 | ||
|                     this.stopwatch.Stop();
 | ||
|                 } else {
 | ||
|                     this.Tickler.Change(1, Timeout.Infinite);
 | ||
|                     this.stopwatch.Start();
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         private bool isPaused = true;
 | ||
| 
 | ||
|         private void StopTickler() {
 | ||
|             if (this.tickler == null)
 | ||
|                 return;
 | ||
| 
 | ||
|             this.tickler.Dispose();
 | ||
|             this.tickler = null;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets a timer that can be used to trigger redraws on animations
 | ||
|         /// </summary>
 | ||
|         protected Timer Tickler {
 | ||
|             get {
 | ||
|                 if (this.tickler == null)
 | ||
|                     this.tickler = new System.Threading.Timer(new TimerCallback(this.OnTimer), null, Timeout.Infinite, Timeout.Infinite);
 | ||
|                 return this.tickler;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Commands
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Pause any animations
 | ||
|         /// </summary>
 | ||
|         public void Pause() {
 | ||
|             this.Paused = true;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Unpause any animations
 | ||
|         /// </summary>
 | ||
|         public void Unpause() {
 | ||
|             this.Paused = false;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Drawing
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our image
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
| 
 | ||
|             if (this.Aspect == null || this.Aspect == System.DBNull.Value)
 | ||
|                 return;
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
| 
 | ||
|             if (this.Aspect is System.Byte[]) {
 | ||
|                 this.DrawAlignedImage(g, r, this.GetImageFromAspect());
 | ||
|             } else {
 | ||
|                 ICollection imageSelectors = this.Aspect as ICollection;
 | ||
|                 if (imageSelectors == null)
 | ||
|                     this.DrawAlignedImage(g, r, this.GetImageFromAspect());
 | ||
|                 else
 | ||
|                     this.DrawImages(g, r, imageSelectors);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Translate our Aspect into an image.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>The strategy is:<list type="bullet">
 | ||
|         /// <item><description>If its a byte array, we treat it as an in-memory image</description></item>
 | ||
|         /// <item><description>If it's an int, we use that as an index into our image list</description></item>
 | ||
|         /// <item><description>If it's a string, we try to load a file by that name. If we can't, 
 | ||
|         /// we use the string as an index into our image list.</description></item>
 | ||
|         ///</list></remarks>
 | ||
|         /// <returns>An image</returns>
 | ||
|         protected Image GetImageFromAspect() {
 | ||
|             // If we've already figured out the image, don't do it again
 | ||
|             if (this.OLVSubItem != null && this.OLVSubItem.ImageSelector is Image) {
 | ||
|                 if (this.OLVSubItem.AnimationState == null)
 | ||
|                     return (Image) this.OLVSubItem.ImageSelector;
 | ||
|                 else
 | ||
|                     return this.OLVSubItem.AnimationState.image;
 | ||
|             }
 | ||
| 
 | ||
|             // Try to convert our Aspect into an Image
 | ||
|             // If its a byte array, we treat it as an in-memory image
 | ||
|             // If it's an int, we use that as an index into our image list
 | ||
|             // If it's a string, we try to find a file by that name.
 | ||
|             //    If we can't, we use the string as an index into our image list.
 | ||
|             Image image = this.Aspect as Image;
 | ||
|             if (image != null) {
 | ||
|                 // Don't do anything else
 | ||
|             } else if (this.Aspect is System.Byte[]) {
 | ||
|                 using (MemoryStream stream = new MemoryStream((System.Byte[]) this.Aspect)) {
 | ||
|                     try {
 | ||
|                         image = Image.FromStream(stream);
 | ||
|                     }
 | ||
|                     catch (ArgumentException) {
 | ||
|                         // ignore
 | ||
|                     }
 | ||
|                 }
 | ||
|             } else if (this.Aspect is Int32) {
 | ||
|                 image = this.GetImage(this.Aspect);
 | ||
|             } else {
 | ||
|                 String str = this.Aspect as String;
 | ||
|                 if (!String.IsNullOrEmpty(str)) {
 | ||
|                     try {
 | ||
|                         image = Image.FromFile(str);
 | ||
|                     }
 | ||
|                     catch (FileNotFoundException) {
 | ||
|                         image = this.GetImage(this.Aspect);
 | ||
|                     }
 | ||
|                     catch (OutOfMemoryException) {
 | ||
|                         image = this.GetImage(this.Aspect);
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             // If this image is an animation, initialize the animation process
 | ||
|             if (this.OLVSubItem != null && AnimationState.IsAnimation(image)) {
 | ||
|                 this.OLVSubItem.AnimationState = new AnimationState(image);
 | ||
|             }
 | ||
| 
 | ||
|             // Cache the image so we don't repeat this dreary process
 | ||
|             if (this.OLVSubItem != null)
 | ||
|                 this.OLVSubItem.ImageSelector = image;
 | ||
| 
 | ||
|             return image;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Events
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// This is the method that is invoked by the timer. It basically switches control to the listview thread.
 | ||
|         /// </summary>
 | ||
|         /// <param name="state">not used</param>
 | ||
|         public void OnTimer(Object state) {
 | ||
| 
 | ||
|             if (this.IsListViewDead)
 | ||
|                 return;
 | ||
| 
 | ||
|             if (this.Paused)
 | ||
|                 return;
 | ||
| 
 | ||
|             if (this.ListView.InvokeRequired)
 | ||
|                 this.ListView.Invoke((MethodInvoker) delegate { this.OnTimer(state); });
 | ||
|             else
 | ||
|                 this.OnTimerInThread();
 | ||
|         }
 | ||
| 
 | ||
|         private bool IsListViewDead {
 | ||
|             get {
 | ||
|                 // Apply a whole heap of sanity checks, which basically ensure that the ListView is still alive
 | ||
|                 return this.ListView == null ||
 | ||
|                        this.ListView.Disposing ||
 | ||
|                        this.ListView.IsDisposed ||
 | ||
|                        !this.ListView.IsHandleCreated;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// This is the OnTimer callback, but invoked in the same thread as the creator of the ListView.
 | ||
|         /// This method can use all of ListViews methods without creating a CrossThread exception.
 | ||
|         /// </summary>
 | ||
|         protected void OnTimerInThread() {
 | ||
|             // MAINTAINER NOTE: This method must renew the tickler. If it doesn't the animations will stop.
 | ||
| 
 | ||
|             // If this listview has been destroyed, we can't do anything, so we return without
 | ||
|             // renewing the tickler, effectively killing all animations on this renderer
 | ||
| 
 | ||
|             if (this.IsListViewDead)
 | ||
|                 return;
 | ||
| 
 | ||
|             if (this.Paused)
 | ||
|                 return;
 | ||
| 
 | ||
|             // If we're not in Detail view or our column has been removed from the list,
 | ||
|             // we can't do anything at the moment, but we still renew the tickler because the view may change later.
 | ||
|             if (this.ListView.View != System.Windows.Forms.View.Details || this.Column == null || this.Column.Index < 0) {
 | ||
|                 this.Tickler.Change(1000, Timeout.Infinite);
 | ||
|                 return;
 | ||
|             }
 | ||
| 
 | ||
|             long elapsedMilliseconds = this.stopwatch.ElapsedMilliseconds;
 | ||
|             int subItemIndex = this.Column.Index;
 | ||
|             long nextCheckAt = elapsedMilliseconds + 1000; // wait at most one second before checking again
 | ||
|             Rectangle updateRect = new Rectangle(); // what part of the view must be updated to draw the changed gifs?
 | ||
| 
 | ||
|             // Run through all the subitems in the view for our column, and for each one that
 | ||
|             // has an animation attached to it, see if the frame needs updating.
 | ||
| 
 | ||
|             for (int i = 0; i < this.ListView.GetItemCount(); i++) {
 | ||
|                 OLVListItem lvi = this.ListView.GetItem(i);
 | ||
| 
 | ||
|                 // Get the animation state from the subitem. If there isn't an animation state, skip this row.
 | ||
|                 OLVListSubItem lvsi = lvi.GetSubItem(subItemIndex);
 | ||
|                 AnimationState state = lvsi.AnimationState;
 | ||
|                 if (state == null || !state.IsValid)
 | ||
|                     continue;
 | ||
| 
 | ||
|                 // Has this frame of the animation expired?
 | ||
|                 if (elapsedMilliseconds >= state.currentFrameExpiresAt) {
 | ||
|                     state.AdvanceFrame(elapsedMilliseconds);
 | ||
| 
 | ||
|                     // Track the area of the view that needs to be redrawn to show the changed images
 | ||
|                     if (updateRect.IsEmpty)
 | ||
|                         updateRect = lvsi.Bounds;
 | ||
|                     else
 | ||
|                         updateRect = Rectangle.Union(updateRect, lvsi.Bounds);
 | ||
|                 }
 | ||
| 
 | ||
|                 // Remember the minimum time at which a frame is next due to change
 | ||
|                 nextCheckAt = Math.Min(nextCheckAt, state.currentFrameExpiresAt);
 | ||
|             }
 | ||
| 
 | ||
|             // Update the part of the listview where frames have changed
 | ||
|             if (!updateRect.IsEmpty)
 | ||
|                 this.ListView.Invalidate(updateRect);
 | ||
| 
 | ||
|             // Renew the tickler in time for the next frame change
 | ||
|             this.Tickler.Change(nextCheckAt - elapsedMilliseconds, Timeout.Infinite);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Instances of this class kept track of the animation state of a single image.
 | ||
|         /// </summary>
 | ||
|         internal class AnimationState {
 | ||
|             private const int PropertyTagTypeShort = 3;
 | ||
|             private const int PropertyTagTypeLong = 4;
 | ||
|             private const int PropertyTagFrameDelay = 0x5100;
 | ||
|             private const int PropertyTagLoopCount = 0x5101;
 | ||
| 
 | ||
|             /// <summary>
 | ||
|             /// Is the given image an animation
 | ||
|             /// </summary>
 | ||
|             /// <param name="image">The image to be tested</param>
 | ||
|             /// <returns>Is the image an animation?</returns>
 | ||
|             public static bool IsAnimation(Image image) {
 | ||
|                 if (image == null)
 | ||
|                     return false;
 | ||
|                 else
 | ||
|                     return (new List<Guid>(image.FrameDimensionsList)).Contains(FrameDimension.Time.Guid);
 | ||
|             }
 | ||
| 
 | ||
|             /// <summary>
 | ||
|             /// Create an AnimationState in a quiet state
 | ||
|             /// </summary>
 | ||
|             public AnimationState() {
 | ||
|                 this.imageDuration = new List<int>();
 | ||
|             }
 | ||
| 
 | ||
|             /// <summary>
 | ||
|             /// Create an animation state for the given image, which may or may not
 | ||
|             /// be an animation
 | ||
|             /// </summary>
 | ||
|             /// <param name="image">The image to be rendered</param>
 | ||
|             public AnimationState(Image image)
 | ||
|                 : this() {
 | ||
|                 if (!AnimationState.IsAnimation(image))
 | ||
|                     return;
 | ||
| 
 | ||
|                 // How many frames in the animation?
 | ||
|                 this.image = image;
 | ||
|                 this.frameCount = this.image.GetFrameCount(FrameDimension.Time);
 | ||
| 
 | ||
|                 // Find the delay between each frame.
 | ||
|                 // The delays are stored an array of 4-byte ints. Each int is the
 | ||
|                 // number of 1/100th of a second that should elapsed before the frame expires
 | ||
|                 foreach (PropertyItem pi in this.image.PropertyItems) {
 | ||
|                     if (pi.Id == PropertyTagFrameDelay) {
 | ||
|                         for (int i = 0; i < pi.Len; i += 4) {
 | ||
|                             //TODO: There must be a better way to convert 4-bytes to an int
 | ||
|                             int delay = (pi.Value[i + 3] << 24) + (pi.Value[i + 2] << 16) + (pi.Value[i + 1] << 8) + pi.Value[i];
 | ||
|                             this.imageDuration.Add(delay * 10); // store delays as milliseconds
 | ||
|                         }
 | ||
|                         break;
 | ||
|                     }
 | ||
|                 }
 | ||
| 
 | ||
|                 // There should be as many frame durations as frames
 | ||
|                 Debug.Assert(this.imageDuration.Count == this.frameCount, "There should be as many frame durations as there are frames.");
 | ||
|             }
 | ||
| 
 | ||
|             /// <summary>
 | ||
|             /// Does this state represent a valid animation
 | ||
|             /// </summary>
 | ||
|             public bool IsValid {
 | ||
|                 get { return (this.image != null && this.frameCount > 0); }
 | ||
|             }
 | ||
| 
 | ||
|             /// <summary>
 | ||
|             /// Advance our images current frame and calculate when it will expire
 | ||
|             /// </summary>
 | ||
|             public void AdvanceFrame(long millisecondsNow) {
 | ||
|                 this.currentFrame = (this.currentFrame + 1) % this.frameCount;
 | ||
|                 this.currentFrameExpiresAt = millisecondsNow + this.imageDuration[this.currentFrame];
 | ||
|                 this.image.SelectActiveFrame(FrameDimension.Time, this.currentFrame);
 | ||
|             }
 | ||
| 
 | ||
|             internal int currentFrame;
 | ||
|             internal long currentFrameExpiresAt;
 | ||
|             internal Image image;
 | ||
|             internal List<int> imageDuration;
 | ||
|             internal int frameCount;
 | ||
|         }
 | ||
| 
 | ||
|         #region Private variables
 | ||
| 
 | ||
|         private System.Threading.Timer tickler; // timer used to tickle the animations
 | ||
|         private Stopwatch stopwatch; // clock used to time the animation frame changes
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// Render our Aspect as a progress bar
 | ||
|     /// </summary>
 | ||
|     public class BarRenderer : BaseRenderer {
 | ||
|         #region Constructors
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a BarRenderer
 | ||
|         /// </summary>
 | ||
|         public BarRenderer()
 | ||
|             : base() {}
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a BarRenderer for the given range of data values
 | ||
|         /// </summary>
 | ||
|         public BarRenderer(int minimum, int maximum)
 | ||
|             : this() {
 | ||
|             this.MinimumValue = minimum;
 | ||
|             this.MaximumValue = maximum;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a BarRenderer using a custom bar scheme
 | ||
|         /// </summary>
 | ||
|         public BarRenderer(Pen pen, Brush brush)
 | ||
|             : this() {
 | ||
|             this.Pen = pen;
 | ||
|             this.Brush = brush;
 | ||
|             this.UseStandardBar = false;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a BarRenderer using a custom bar scheme
 | ||
|         /// </summary>
 | ||
|         public BarRenderer(int minimum, int maximum, Pen pen, Brush brush)
 | ||
|             : this(minimum, maximum) {
 | ||
|             this.Pen = pen;
 | ||
|             this.Brush = brush;
 | ||
|             this.UseStandardBar = false;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a BarRenderer that uses a horizontal gradient
 | ||
|         /// </summary>
 | ||
|         public BarRenderer(Pen pen, Color start, Color end)
 | ||
|             : this() {
 | ||
|             this.Pen = pen;
 | ||
|             this.SetGradient(start, end);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make a BarRenderer that uses a horizontal gradient
 | ||
|         /// </summary>
 | ||
|         public BarRenderer(int minimum, int maximum, Pen pen, Color start, Color end)
 | ||
|             : this(minimum, maximum) {
 | ||
|             this.Pen = pen;
 | ||
|             this.SetGradient(start, end);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Configuration Properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD>Ӧ<EFBFBD><D3A6>ϵͳ<CFB5><CDB3>ʽ<EFBFBD><CABD><EFBFBD>ƣ<EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD>Ӧ<EFBFBD><D3A6>ϵͳ<CFB5><CDB3>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(true)]
 | ||
|         public bool UseStandardBar {
 | ||
|             get { return useStandardBar; }
 | ||
|             set { useStandardBar = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private bool useStandardBar = true;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǵĵ<C7B5>Ԫ<EFBFBD><D4AA><EFBFBD>߿<EFBFBD><DFBF><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD><D8B5><EFBFBD><EFBFBD><EFBFBD>ͼ
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǵĵ<C7B5>Ԫ<EFBFBD><D4AA><EFBFBD>߿<EFBFBD><DFBF><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD><D8B5><EFBFBD><EFBFBD><EFBFBD>ͼ"),
 | ||
|          DefaultValue(2)]
 | ||
|         public int Padding {
 | ||
|             get { return padding; }
 | ||
|             set { padding = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int padding = 2;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         ///<2F>ڻ<EFBFBD><DABB>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD>֮ǰ<D6AE><C7B0><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>ʲô<CAB2><C3B4>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD>ؼ<EFBFBD><D8BC>ڲ<EFBFBD><DAB2><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD>ͼ<EFBFBD>ڲ<EFBFBD><DAB2><EFBFBD><EFBFBD><EFBFBD>ɫ"),
 | ||
|          DefaultValue(typeof (Color), "AliceBlue")]
 | ||
|         public Color BackgroundColor {
 | ||
|             get { return backgroundColor; }
 | ||
|             set { backgroundColor = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Color backgroundColor = Color.AliceBlue;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><DFBF><EFBFBD>ɫ
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><DFBF><EFBFBD>ɫ"),
 | ||
|          DefaultValue(typeof (Color), "Black")]
 | ||
|         public Color FrameColor {
 | ||
|             get { return frameColor; }
 | ||
|             set { frameColor = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Color frameColor = Color.Black;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı߿<C4B1><DFBF><EFBFBD><EFBFBD>ؿ<EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ı߿<C4B1><DFBF><EFBFBD><EFBFBD>ؿ<EFBFBD>"),
 | ||
|          DefaultValue(1.0f)]
 | ||
|         public float FrameWidth {
 | ||
|             get { return frameWidth; }
 | ||
|             set { frameWidth = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private float frameWidth = 1.0f;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ġ<EFBFBD><C4A1><EFBFBD><EFBFBD>䡱<EFBFBD><E4A1B1><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD>ʲô<CAB2><C3B4>ɫ<EFBFBD><C9AB>
 | ||
|         /// </summary>
 | ||
|         /// <remarks><3E><><EFBFBD><EFBFBD>GradientStartColorΪColor.Emptyʱ<79><CAB1>ʹ<EFBFBD>ô<EFBFBD>ѡ<EFBFBD><D1A1></remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ġ<EFBFBD><C4A1><EFBFBD><EFBFBD>䡱<EFBFBD><E4A1B1><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD>ʲô<CAB2><C3B4>ɫ<EFBFBD><C9AB>"),
 | ||
|          DefaultValue(typeof (Color), "BlueViolet")]
 | ||
|         public Color FillColor {
 | ||
|             get { return fillColor; }
 | ||
|             set { fillColor = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Color fillColor = Color.BlueViolet;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(typeof (Color), "CornflowerBlue")]
 | ||
|         public Color GradientStartColor {
 | ||
|             get { return startColor; }
 | ||
|             set { startColor = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Color startColor = Color.CornflowerBlue;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>β<EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>β<EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(typeof (Color), "DarkBlue")]
 | ||
|         public Color GradientEndColor {
 | ||
|             get { return endColor; }
 | ||
|             set { endColor = value; }
 | ||
|         }
 | ||
|         private Color endColor = Color.DarkBlue;
 | ||
|         /// <summary>
 | ||
|         /// ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>ʼ<EFBFBD><CABC><EFBFBD>佥<EFBFBD><E4BDA5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>ʼ<EFBFBD><CABC><EFBFBD>佥<EFBFBD><E4BDA5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(typeof(Color), "CornflowerBlue")]
 | ||
|         public Color NearGradientStartColor
 | ||
|         {
 | ||
|             get { return near_startColor; }
 | ||
|             set { near_startColor = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Color near_startColor = Color.CornflowerBlue;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>β<EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("ʹ<>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD>ɫ<EFBFBD><C9AB>β<EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(typeof(Color), "DarkBlue")]
 | ||
|         public Color NearGradientEndColor
 | ||
|         {
 | ||
|             get { return near_endColor; }
 | ||
|             set { near_endColor = value; }
 | ||
|         }
 | ||
|         private Color near_endColor = Color.DarkBlue;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1>ö<EFBFBD><C3B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(100)]
 | ||
|         public int MaximumWidth {
 | ||
|             get { return maximumWidth; }
 | ||
|             set { maximumWidth = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int maximumWidth = 100;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD>۵<EFBFBD>Ԫ<EFBFBD><D4AA><EFBFBD>ж<EFBFBD><D0B6>ߣ<EFBFBD><DFA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(16)]
 | ||
|         public int MaximumHeight {
 | ||
|             get { return maximumHeight; }
 | ||
|             set { maximumHeight = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int maximumHeight = 16;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Ԥ<>ڵ<EFBFBD><DAB5><EFBFBD>С<EFBFBD><D0A1><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>С<EFBFBD>ڴ<EFBFBD>ֵ<EFBFBD><D6B5>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD>ʾһ<CABE><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("Ԥ<>ڵ<EFBFBD><DAB5><EFBFBD>С<EFBFBD><D0A1><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>С<EFBFBD>ڴ<EFBFBD>ֵ<EFBFBD><D6B5>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD>ʾһ<CABE><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(0.0)]
 | ||
|         public double MinimumValue {
 | ||
|             get { return minimumValue; }
 | ||
|             set { minimumValue = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private double minimumValue = 0.0;
 | ||
|         /// <summary>
 | ||
|         /// <20>ӽ<EFBFBD><D3BD><EFBFBD>ʱ<EFBFBD><CAB1>ֵ,<2C><><EFBFBD>ڸ<EFBFBD>ֵʱ<D6B5><CAB1><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>ͬ<EFBFBD><CDAC>ɫ<EFBFBD><C9AB>С<EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>MinimumValue<75><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>MaximumValueֵ<65><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾδ<CABE><CEB4><EFBFBD>ø<EFBFBD>ֵ
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("<22>ӽ<EFBFBD><D3BD><EFBFBD>ʱ<EFBFBD><CAB1>ֵ,<2C><><EFBFBD>ڸ<EFBFBD>ֵʱ<D6B5><CAB1><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>ͬ<EFBFBD><CDAC>ɫ<EFBFBD><C9AB>С<EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>MinimumValue<75><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD>MaximumValueֵ<65><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾδ<CABE><CEB4><EFBFBD>ø<EFBFBD>ֵ"),
 | ||
|          DefaultValue(0.0)]
 | ||
|         public double NearFullValue
 | ||
|         {
 | ||
|             get { return nearfullValue; }
 | ||
|             set { nearfullValue = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private double nearfullValue = 0.0;
 | ||
|         /// <summary>
 | ||
|         ///<2F><>Χ<EFBFBD><CEA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>ֵ<EFBFBD><D6B5>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ρ<EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("<22><>Χ<EFBFBD><CEA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>ֵ<EFBFBD><D6B5>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ρ<EFBFBD>"),
 | ||
|          DefaultValue(100.0)]
 | ||
|         public double MaximumValue {
 | ||
|             get { return maximumValue; }
 | ||
|             set { maximumValue = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private double maximumValue = 100.0;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Public Properties (non-IDE)
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The Pen that will draw the frame surrounding this bar
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Pen Pen {
 | ||
|             get {
 | ||
|                 if (this.pen == null && !this.FrameColor.IsEmpty)
 | ||
|                     return new Pen(this.FrameColor, this.FrameWidth);
 | ||
|                 else
 | ||
|                     return this.pen;
 | ||
|             }
 | ||
|             set { this.pen = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Pen pen;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The brush that will be used to fill the bar
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Brush Brush {
 | ||
|             get {
 | ||
|                 if (this.brush == null && !this.FillColor.IsEmpty)
 | ||
|                     return new SolidBrush(this.FillColor);
 | ||
|                 else
 | ||
|                     return this.brush;
 | ||
|             }
 | ||
|             set { this.brush = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Brush brush;
 | ||
|         /// <summary>
 | ||
|         /// The brush that will be used to fill the bar
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Brush NearBrush
 | ||
|         {
 | ||
|             get
 | ||
|             {
 | ||
|                 if (this.near_brush == null && !this.FillColor.IsEmpty)
 | ||
|                     return new SolidBrush(this.FillColor);
 | ||
|                 else
 | ||
|                     return this.near_brush;
 | ||
|             }
 | ||
|             set { this.near_brush = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Brush near_brush;
 | ||
|         /// <summary>
 | ||
|         /// The brush that will be used to fill the background of the bar
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Brush BackgroundBrush {
 | ||
|             get {
 | ||
|                 if (this.backgroundBrush == null && !this.BackgroundColor.IsEmpty)
 | ||
|                     return new SolidBrush(this.BackgroundColor);
 | ||
|                 else
 | ||
|                     return this.backgroundBrush;
 | ||
|             }
 | ||
|             set { this.backgroundBrush = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Brush backgroundBrush;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw this progress bar using a gradient
 | ||
|         /// </summary>
 | ||
|         /// <param name="start"></param>
 | ||
|         /// <param name="end"></param>
 | ||
|         public void SetGradient(Color start, Color end) {
 | ||
|             this.GradientStartColor = start;
 | ||
|             this.GradientEndColor = end;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our aspect
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
| 
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
| 
 | ||
|             Rectangle frameRect = Rectangle.Inflate(r, 0 - this.Padding, 0 - this.Padding);
 | ||
|             frameRect.Width = Math.Min(frameRect.Width, this.MaximumWidth);
 | ||
|             frameRect.Height = Math.Min(frameRect.Height, this.MaximumHeight);
 | ||
|             frameRect = this.AlignRectangle(r, frameRect);
 | ||
| 
 | ||
|             // Convert our aspect to a numeric value
 | ||
|             IConvertible convertable = this.Aspect as IConvertible;
 | ||
|             if (convertable == null)
 | ||
|                 return;
 | ||
|             double aspectValue = convertable.ToDouble(NumberFormatInfo.InvariantInfo);
 | ||
| 
 | ||
|             Rectangle fillRect = Rectangle.Inflate(frameRect, -1, -1);
 | ||
|             if (aspectValue <= this.MinimumValue)
 | ||
|                 fillRect.Width = 0;
 | ||
|             else if (aspectValue < this.MaximumValue)
 | ||
|                 fillRect.Width = (int) (fillRect.Width * (aspectValue - this.MinimumValue) / this.MaximumValue);
 | ||
| 
 | ||
|             // MS-themed progress bars don't work when printing
 | ||
|             if (this.UseStandardBar && ProgressBarRenderer.IsSupported && !this.IsPrinting) {
 | ||
|                 ProgressBarRenderer.DrawHorizontalBar(g, frameRect);
 | ||
|                 ProgressBarRenderer.DrawHorizontalChunks(g, fillRect);
 | ||
|             } else {
 | ||
|                 g.FillRectangle(this.BackgroundBrush, frameRect);
 | ||
|                 if (fillRect.Width > 0) {
 | ||
|                     // FillRectangle fills inside the given rectangle, so expand it a little
 | ||
|                     fillRect.Width++;
 | ||
|                     fillRect.Height++;
 | ||
|                     if (aspectValue >= nearfullValue && nearfullValue >= this.MinimumValue && nearfullValue<= this.MaximumValue)
 | ||
|                     {
 | ||
|                         if (this.NearGradientStartColor == Color.Empty)
 | ||
|                             g.FillRectangle(this.NearBrush, fillRect);
 | ||
|                         else
 | ||
|                         {
 | ||
|                             using (LinearGradientBrush gradient = new LinearGradientBrush(frameRect, this.NearGradientStartColor, this.NearGradientEndColor, LinearGradientMode.Horizontal))
 | ||
|                             {
 | ||
|                                 g.FillRectangle(gradient, fillRect);
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         if (this.GradientStartColor == Color.Empty)
 | ||
|                             g.FillRectangle(this.Brush, fillRect);
 | ||
|                         else
 | ||
|                         {
 | ||
|                             using (LinearGradientBrush gradient = new LinearGradientBrush(frameRect, this.GradientStartColor, this.GradientEndColor, LinearGradientMode.Horizontal))
 | ||
|                             {
 | ||
|                                 g.FillRectangle(gradient, fillRect);
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
|                 }
 | ||
|                 g.DrawRectangle(this.Pen, frameRect);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Handle the GetEditRectangle request
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="item"></param>
 | ||
|         /// <param name="subItemIndex"></param>
 | ||
|         /// <param name="preferredSize"> </param>
 | ||
|         /// <returns></returns>
 | ||
|         protected override Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) {
 | ||
|             return this.CalculatePaddedAlignedBounds(g, cellBounds, preferredSize);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// An ImagesRenderer draws zero or more images depending on the data returned by its Aspect.
 | ||
|     /// </summary>
 | ||
|     /// <remarks><para>This renderer's Aspect must return a ICollection of ints, strings or Images,
 | ||
|     /// each of which will be drawn horizontally one after the other.</para>
 | ||
|     /// <para>As of v2.1, this functionality has been absorbed into ImageRenderer and this is now an
 | ||
|     /// empty shell, solely for backwards compatibility.</para>
 | ||
|     /// </remarks>
 | ||
|     [ToolboxItem(false)]
 | ||
|     public class ImagesRenderer : ImageRenderer {}
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// A MultiImageRenderer draws the same image a number of times based on our data value
 | ||
|     /// </summary>
 | ||
|     /// <remarks><para>The stars in the Rating column of iTunes is a good example of this type of renderer.</para></remarks>
 | ||
|     public class MultiImageRenderer : BaseRenderer {
 | ||
|         /// <summary>
 | ||
|         /// Make a quiet renderer
 | ||
|         /// </summary>
 | ||
|         public MultiImageRenderer()
 | ||
|             : base() {}
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Make an image renderer that will draw the indicated image, at most maxImages times.
 | ||
|         /// </summary>
 | ||
|         /// <param name="imageSelector"></param>
 | ||
|         /// <param name="maxImages"></param>
 | ||
|         /// <param name="minValue"></param>
 | ||
|         /// <param name="maxValue"></param>
 | ||
|         public MultiImageRenderer(Object imageSelector, int maxImages, int minValue, int maxValue)
 | ||
|             : this() {
 | ||
|             this.ImageSelector = imageSelector;
 | ||
|             this.MaxNumberImages = maxImages;
 | ||
|             this.MinimumValue = minValue;
 | ||
|             this.MaximumValue = maxValue;
 | ||
|         }
 | ||
| 
 | ||
|         #region Configuration Properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The index of the image that should be drawn
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("The index of the image that should be drawn"),
 | ||
|          DefaultValue(-1)]
 | ||
|         public int ImageIndex {
 | ||
|             get {
 | ||
|                 if (imageSelector is Int32)
 | ||
|                     return (Int32) imageSelector;
 | ||
|                 else
 | ||
|                     return -1;
 | ||
|             }
 | ||
|             set { imageSelector = value; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The name of the image that should be drawn
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("The index of the image that should be drawn"),
 | ||
|          DefaultValue(null)]
 | ||
|         public string ImageName {
 | ||
|             get { return imageSelector as String; }
 | ||
|             set { imageSelector = value; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// The image selector that will give the image to be drawn
 | ||
|         /// </summary>
 | ||
|         /// <remarks>Like all image selectors, this can be an int, string or Image</remarks>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public Object ImageSelector {
 | ||
|             get { return imageSelector; }
 | ||
|             set { imageSelector = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Object imageSelector;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// What is the maximum number of images that this renderer should draw?
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("The maximum number of images that this renderer should draw"),
 | ||
|          DefaultValue(10)]
 | ||
|         public int MaxNumberImages {
 | ||
|             get { return maxNumberImages; }
 | ||
|             set { maxNumberImages = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int maxNumberImages = 10;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Values less than or equal to this will have 0 images drawn
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("Values less than or equal to this will have 0 images drawn"),
 | ||
|          DefaultValue(0)]
 | ||
|         public int MinimumValue {
 | ||
|             get { return minimumValue; }
 | ||
|             set { minimumValue = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int minimumValue = 0;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Values greater than or equal to this will have MaxNumberImages images drawn
 | ||
|         /// </summary>
 | ||
|         [Category("Behavior"),
 | ||
|          Description("Values greater than or equal to this will have MaxNumberImages images drawn"),
 | ||
|          DefaultValue(100)]
 | ||
|         public int MaximumValue {
 | ||
|             get { return maximumValue; }
 | ||
|             set { maximumValue = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private int maximumValue = 100;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our data value
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
| 
 | ||
|             Image image = this.GetImage(this.ImageSelector);
 | ||
|             if (image == null)
 | ||
|                 return;
 | ||
| 
 | ||
|             // Convert our aspect to a numeric value
 | ||
|             IConvertible convertable = this.Aspect as IConvertible;
 | ||
|             if (convertable == null)
 | ||
|                 return;
 | ||
|             double aspectValue = convertable.ToDouble(NumberFormatInfo.InvariantInfo);
 | ||
| 
 | ||
|             // Calculate how many images we need to draw to represent our aspect value
 | ||
|             int numberOfImages;
 | ||
|             if (aspectValue <= this.MinimumValue)
 | ||
|                 numberOfImages = 0;
 | ||
|             else if (aspectValue < this.MaximumValue)
 | ||
|                 numberOfImages = 1 + (int) (this.MaxNumberImages * (aspectValue - this.MinimumValue) / this.MaximumValue);
 | ||
|             else
 | ||
|                 numberOfImages = this.MaxNumberImages;
 | ||
| 
 | ||
|             // If we need to shrink the image, what will its on-screen dimensions be?
 | ||
|             int imageScaledWidth = image.Width;
 | ||
|             int imageScaledHeight = image.Height;
 | ||
|             if (r.Height < image.Height) {
 | ||
|                 imageScaledWidth = (int) ((float) image.Width * (float) r.Height / (float) image.Height);
 | ||
|                 imageScaledHeight = r.Height;
 | ||
|             }
 | ||
|             // Calculate where the images should be drawn
 | ||
|             Rectangle imageBounds = r;
 | ||
|             imageBounds.Width = (this.MaxNumberImages * (imageScaledWidth + this.Spacing)) - this.Spacing;
 | ||
|             imageBounds.Height = imageScaledHeight;
 | ||
|             imageBounds = this.AlignRectangle(r, imageBounds);
 | ||
| 
 | ||
|             // Finally, draw the images
 | ||
|             Rectangle singleImageRect = new Rectangle(imageBounds.X, imageBounds.Y, imageScaledWidth, imageScaledHeight);
 | ||
|             Color backgroundColor = GetBackgroundColor();
 | ||
|             for (int i = 0; i < numberOfImages; i++) {
 | ||
|                 if (this.ListItem.Enabled) {
 | ||
|                     this.DrawImage(g, singleImageRect, this.ImageSelector);
 | ||
|                 }  else
 | ||
|                     ControlPaint.DrawImageDisabled(g, image, singleImageRect.X, singleImageRect.Y, backgroundColor);
 | ||
|                 singleImageRect.X += (imageScaledWidth + this.Spacing);
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// A class to render a value that contains a bitwise-OR'ed collection of values.
 | ||
|     /// </summary>
 | ||
|     public class FlagRenderer : BaseRenderer {
 | ||
|         /// <summary>
 | ||
|         /// Register the given image to the given value
 | ||
|         /// </summary>
 | ||
|         /// <param name="key">When this flag is present...</param>
 | ||
|         /// <param name="imageSelector">...draw this image</param>
 | ||
|         public void Add(Object key, Object imageSelector) {
 | ||
|             Int32 k2 = ((IConvertible) key).ToInt32(NumberFormatInfo.InvariantInfo);
 | ||
| 
 | ||
|             this.imageMap[k2] = imageSelector;
 | ||
|             this.keysInOrder.Remove(k2);
 | ||
|             this.keysInOrder.Add(k2);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the flags
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
| 
 | ||
|             IConvertible convertable = this.Aspect as IConvertible;
 | ||
|             if (convertable == null)
 | ||
|                 return;
 | ||
| 
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
| 
 | ||
|             Int32 v2 = convertable.ToInt32(NumberFormatInfo.InvariantInfo);
 | ||
|             ArrayList images = new ArrayList();
 | ||
|             foreach (Int32 key in this.keysInOrder) {
 | ||
|                 if ((v2 & key) == key) {
 | ||
|                     Image image = this.GetImage(this.imageMap[key]);
 | ||
|                     if (image != null)
 | ||
|                         images.Add(image);
 | ||
|                 }
 | ||
|             }
 | ||
|             if (images.Count > 0)
 | ||
|                 this.DrawImages(g, r, images);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Do the actual work of hit testing. Subclasses should override this rather than HitTest()
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         protected override void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y) {
 | ||
|             IConvertible convertable = this.Aspect as IConvertible;
 | ||
|             if (convertable == null)
 | ||
|                 return;
 | ||
| 
 | ||
|             Int32 v2 = convertable.ToInt32(NumberFormatInfo.InvariantInfo);
 | ||
| 
 | ||
|             Point pt = this.Bounds.Location;
 | ||
|             foreach (Int32 key in this.keysInOrder) {
 | ||
|                 if ((v2 & key) == key) {
 | ||
|                     Image image = this.GetImage(this.imageMap[key]);
 | ||
|                     if (image != null) {
 | ||
|                         Rectangle imageRect = new Rectangle(pt, image.Size);
 | ||
|                         if (imageRect.Contains(x, y)) {
 | ||
|                             hti.UserData = key;
 | ||
|                             return;
 | ||
|                         }
 | ||
|                         pt.X += (image.Width + this.Spacing);
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         private List<Int32> keysInOrder = new List<Int32>();
 | ||
|         private Dictionary<Int32, Object> imageMap = new Dictionary<Int32, object>();
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// This renderer draws an image, a single line title, and then multi-line description
 | ||
|     /// under the title.
 | ||
|     /// </summary>
 | ||
|     /// <remarks>
 | ||
|     /// <para>This class works best with FullRowSelect = true.</para>
 | ||
|     /// <para>It's not designed to work with cell editing -- it will work but will look odd.</para>
 | ||
|     /// <para>
 | ||
|     /// It's not RightToLeft friendly.
 | ||
|     /// </para>
 | ||
|     /// </remarks>
 | ||
|     public class DescribedTaskRenderer : BaseRenderer, IFilterAwareRenderer
 | ||
|     {
 | ||
|         private readonly StringFormat noWrapStringFormat;
 | ||
|         private readonly HighlightTextRenderer highlightTextRenderer = new HighlightTextRenderer();
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Create a DescribedTaskRenderer
 | ||
|         /// </summary>
 | ||
|         public DescribedTaskRenderer() {
 | ||
|             this.noWrapStringFormat = new StringFormat(StringFormatFlags.NoWrap);
 | ||
|             this.noWrapStringFormat.Trimming = StringTrimming.EllipsisCharacter;
 | ||
|             this.noWrapStringFormat.Alignment = StringAlignment.Near;
 | ||
|             this.noWrapStringFormat.LineAlignment = StringAlignment.Near;
 | ||
|             this.highlightTextRenderer.CellVerticalAlignment = StringAlignment.Near;
 | ||
|         }
 | ||
| 
 | ||
|         #region Configuration properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Should text be rendered using GDI routines? This makes the text look more
 | ||
|         /// like a native List view control.
 | ||
|         /// </summary>
 | ||
|         public override bool UseGdiTextRendering
 | ||
|         {
 | ||
|             get { return base.UseGdiTextRendering; }
 | ||
|             set
 | ||
|             {
 | ||
|                 base.UseGdiTextRendering = value;
 | ||
|                 this.highlightTextRenderer.UseGdiTextRendering = value;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the font that will be used to draw the title of the task
 | ||
|         /// </summary>
 | ||
|         /// <remarks>If this is null, the ListView's font will be used</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The font that will be used to draw the title of the task"),
 | ||
|          DefaultValue(null)]
 | ||
|         public Font TitleFont {
 | ||
|             get { return titleFont; }
 | ||
|             set { titleFont = value; }
 | ||
|         }
 | ||
|         private Font titleFont;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return a font that has been set for the title or a reasonable default
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         public Font TitleFontOrDefault {
 | ||
|             get { return this.TitleFont ?? this.ListView.Font; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the color of the title of the task
 | ||
|         /// </summary>
 | ||
|         /// <remarks>This color is used when the task is not selected or when the listview
 | ||
|         /// has a translucent selection mechanism.</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The color of the title"),
 | ||
|          DefaultValue(typeof (Color), "")]
 | ||
|         public Color TitleColor {
 | ||
|             get { return titleColor; }
 | ||
|             set { titleColor = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Color titleColor;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the color of the title of the task or a reasonable default
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         public Color TitleColorOrDefault {
 | ||
|             get {
 | ||
|                 if (!this.ListItem.Enabled)
 | ||
|                     return this.SubItem.ForeColor;
 | ||
|                 if (this.IsItemSelected || this.TitleColor.IsEmpty)
 | ||
|                     return this.GetForegroundColor();
 | ||
|                 
 | ||
|                 return this.TitleColor;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the font that will be used to draw the description of the task
 | ||
|         /// </summary>
 | ||
|         /// <remarks>If this is null, the ListView's font will be used</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The font that will be used to draw the description of the task"),
 | ||
|          DefaultValue(null)]
 | ||
|         public Font DescriptionFont {
 | ||
|             get { return descriptionFont; }
 | ||
|             set { descriptionFont = value; }
 | ||
|         }
 | ||
| 
 | ||
|         private Font descriptionFont;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return a font that has been set for the title or a reasonable default
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         public Font DescriptionFontOrDefault {
 | ||
|             get { return this.DescriptionFont ?? this.ListView.Font; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or set the color of the description of the task
 | ||
|         /// </summary>
 | ||
|         /// <remarks>This color is used when the task is not selected or when the listview
 | ||
|         /// has a translucent selection mechanism.</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The color of the description"),
 | ||
|          DefaultValue(typeof (Color), "")]
 | ||
|         public Color DescriptionColor {
 | ||
|             get { return descriptionColor; }
 | ||
|             set { descriptionColor = value; }
 | ||
|         }
 | ||
|         private Color descriptionColor = Color.Empty;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Return the color of the description of the task or a reasonable default
 | ||
|         /// </summary>
 | ||
|         [Browsable(false)]
 | ||
|         public Color DescriptionColorOrDefault {
 | ||
|             get {
 | ||
|                 if (!this.ListItem.Enabled)
 | ||
|                     return this.SubItem.ForeColor;
 | ||
|                 if (this.IsItemSelected && !this.ListView.UseTranslucentSelection)
 | ||
|                     return this.GetForegroundColor();
 | ||
|                 return this.DescriptionColor.IsEmpty ? defaultDescriptionColor : this.DescriptionColor;
 | ||
|             }
 | ||
|         }
 | ||
|         private static Color defaultDescriptionColor = Color.FromArgb(45, 46, 49);
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the number of pixels that will be left between the image and the text
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The number of pixels that that will be left between the image and the text"),
 | ||
|          DefaultValue(4)]
 | ||
|         public int ImageTextSpace
 | ||
|         {
 | ||
|             get { return imageTextSpace; }
 | ||
|             set { imageTextSpace = value; }
 | ||
|         }
 | ||
|         private int imageTextSpace = 4;
 | ||
|         /// <summary>
 | ||
|         /// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
 | ||
|          DefaultValue(4)]
 | ||
|         public int TopSpace
 | ||
|         {
 | ||
|             get { return topSpace; }
 | ||
|             set { topSpace = value; }
 | ||
|         }
 | ||
|         private int topSpace = 4;
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the number of pixels that will be left between the title and the description
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The number of pixels that that will be left between the title and the description"),
 | ||
|          DefaultValue(2)]
 | ||
|         public int TitleDescriptionSpace
 | ||
|         {
 | ||
|             get { return titleDescriptionSpace; }
 | ||
|             set { titleDescriptionSpace = value; }
 | ||
|         }
 | ||
|         private int titleDescriptionSpace = 2;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the name of the aspect of the model object that contains the task description
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The name of the aspect of the model object that contains the task description"),
 | ||
|          DefaultValue(null)]
 | ||
|         public string DescriptionAspectName {
 | ||
|             get { return descriptionAspectName; }
 | ||
|             set { descriptionAspectName = value; }
 | ||
|         }
 | ||
|         private string descriptionAspectName;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Text highlighting
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the filter that is filtering the ObjectListView and for
 | ||
|         /// which this renderer should highlight text
 | ||
|         /// </summary>
 | ||
|         [Browsable(false),
 | ||
|          DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 | ||
|         public TextMatchFilter Filter
 | ||
|         {
 | ||
|             get { return this.highlightTextRenderer.Filter; }
 | ||
|             set { this.highlightTextRenderer.Filter = value; }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// When a filter changes, keep track of the text matching filters
 | ||
|         /// </summary>
 | ||
|         IModelFilter IFilterAwareRenderer.Filter {
 | ||
|             get { return this.Filter; }
 | ||
|             set { this.highlightTextRenderer.RegisterNewFilter(value); }
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Calculating
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Fetch the description from the model class
 | ||
|         /// </summary>
 | ||
|         /// <param name="model"></param>
 | ||
|         /// <returns></returns>
 | ||
|         public virtual string GetDescription(object model) {
 | ||
|             if (String.IsNullOrEmpty(this.DescriptionAspectName))
 | ||
|                 return String.Empty;
 | ||
| 
 | ||
|             if (this.descriptionGetter == null)
 | ||
|                 this.descriptionGetter = new Munger(this.DescriptionAspectName);
 | ||
| 
 | ||
|             return this.descriptionGetter.GetValue(model) as string;
 | ||
|         }
 | ||
|         private Munger descriptionGetter;
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Rendering
 | ||
|         /// <summary>
 | ||
|         /// 
 | ||
|         /// </summary>
 | ||
|         /// <param name="e"></param>
 | ||
|         /// <param name="cellBounds"></param>
 | ||
|         /// <param name="model"></param>
 | ||
|         public override void ConfigureSubItem(DrawListViewSubItemEventArgs e, Rectangle cellBounds, object model) {
 | ||
|             base.ConfigureSubItem(e, cellBounds, model);
 | ||
|             this.highlightTextRenderer.ConfigureSubItem(e, cellBounds, model);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw our item
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         public override void Render(Graphics g, Rectangle r) {
 | ||
|             this.DrawBackground(g, r);
 | ||
|             r = this.ApplyCellPadding(r);
 | ||
|             this.DrawDescribedTask(g, r, this.GetText(), this.GetDescription(this.RowObject), this.GetImageSelector());
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the task
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <param name="title"></param>
 | ||
|         /// <param name="description"></param>
 | ||
|         /// <param name="imageSelector"></param>
 | ||
|         protected virtual void DrawDescribedTask(Graphics g, Rectangle r, string title, string description, object imageSelector) {
 | ||
| 
 | ||
|             //Debug.WriteLine(String.Format("DrawDescribedTask({0}, {1}, {2}, {3})", r, title, description, imageSelector));
 | ||
| 
 | ||
|             // Draw the image if one's been given
 | ||
|             Rectangle textBounds = r;
 | ||
|             if (imageSelector != null) {
 | ||
|                 int imageWidth = this.DrawImage(g, r, imageSelector);
 | ||
|                 int gapToText = imageWidth + this.ImageTextSpace;
 | ||
|                 textBounds.Y += TopSpace;
 | ||
|                 textBounds.X += gapToText;
 | ||
|                 textBounds.Width -= gapToText;
 | ||
|             }
 | ||
| 
 | ||
|             // Draw the title
 | ||
|             if (!String.IsNullOrEmpty(title)) {
 | ||
|                 using (SolidBrush b = new SolidBrush(this.TitleColorOrDefault)) {
 | ||
|                     this.highlightTextRenderer.CanWrap = false;
 | ||
|                     this.highlightTextRenderer.Font = this.TitleFontOrDefault;
 | ||
|                     this.highlightTextRenderer.TextBrush = b;
 | ||
|                     this.highlightTextRenderer.DrawText(g, textBounds, title);
 | ||
|                 }
 | ||
| 
 | ||
|                 // How tall was the title?
 | ||
|                 SizeF size = g.MeasureString(title, this.TitleFontOrDefault, textBounds.Width, this.noWrapStringFormat);
 | ||
|                 int pixelsToDescription = this.TitleDescriptionSpace + (int)size.Height;
 | ||
|                 textBounds.Y += pixelsToDescription;
 | ||
|                 textBounds.Height -= pixelsToDescription;
 | ||
|             }
 | ||
| 
 | ||
|             // Draw the description
 | ||
|             if (!String.IsNullOrEmpty(description)) {
 | ||
|                 using (SolidBrush b = new SolidBrush(this.DescriptionColorOrDefault)) {
 | ||
|                     this.highlightTextRenderer.CanWrap = true;
 | ||
|                     this.highlightTextRenderer.Font = this.DescriptionFontOrDefault;
 | ||
|                     this.highlightTextRenderer.TextBrush = b;
 | ||
|                     this.highlightTextRenderer.DrawText(g, textBounds, description); 
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             //g.DrawRectangle(Pens.OrangeRed, r);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         #region Hit Testing
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Handle the HitTest request
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         protected override void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y) {
 | ||
|             if (this.Bounds.Contains(x, y))
 | ||
|                 hti.HitTestLocation = HitTestLocation.Text;
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// This renderer draws a functioning button in its cell
 | ||
|     /// </summary>
 | ||
|     public class ColumnButtonRenderer : BaseRenderer {
 | ||
| 
 | ||
|         #region Properties
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets how each button will be sized
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|         Description("How each button will be sized"),
 | ||
|         DefaultValue(OLVColumn.ButtonSizingMode.TextBounds)]
 | ||
|         public OLVColumn.ButtonSizingMode SizingMode
 | ||
|         {
 | ||
|             get { return this.sizingMode; }
 | ||
|             set { this.sizingMode = value; }
 | ||
|         }
 | ||
|         private OLVColumn.ButtonSizingMode sizingMode = OLVColumn.ButtonSizingMode.TextBounds;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the size of the button when the SizingMode is FixedBounds
 | ||
|         /// </summary>
 | ||
|         /// <remarks>If this is not set, the bounds of the cell will be used</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|         Description("The size of the button when the SizingMode is FixedBounds"),
 | ||
|         DefaultValue(null)]
 | ||
|         public Size? ButtonSize
 | ||
|         {
 | ||
|             get { return this.buttonSize; }
 | ||
|             set { this.buttonSize = value; }
 | ||
|         }
 | ||
|         private Size? buttonSize;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the extra space that surrounds the cell when the SizingMode is TextBounds
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|         Description("The extra space that surrounds the cell when the SizingMode is TextBounds")]
 | ||
|         public Size? ButtonPadding
 | ||
|         {
 | ||
|             get { return this.buttonPadding; }
 | ||
|             set { this.buttonPadding = value; }
 | ||
|         }
 | ||
|         private Size? buttonPadding = new Size(10, 10);
 | ||
| 
 | ||
|         private Size ButtonPaddingOrDefault {
 | ||
|             get { return this.ButtonPadding ?? new Size(10, 10); }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the maximum width that a button can occupy.
 | ||
|         /// -1 means there is no maximum width.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>This is only considered when the SizingMode is TextBounds</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|         Description("The maximum width that a button can occupy when the SizingMode is TextBounds"),
 | ||
|         DefaultValue(-1)]
 | ||
|         public int MaxButtonWidth
 | ||
|         {
 | ||
|             get { return this.maxButtonWidth; }
 | ||
|             set { this.maxButtonWidth = value; }
 | ||
|         }
 | ||
|         private int maxButtonWidth = -1;
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Gets or sets the minimum width that a button can occupy.
 | ||
|         /// -1 means there is no minimum width.
 | ||
|         /// </summary>
 | ||
|         /// <remarks>This is only considered when the SizingMode is TextBounds</remarks>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("The minimum width that a button can be when the SizingMode is TextBounds"),
 | ||
|          DefaultValue(-1)]
 | ||
|         public int MinButtonWidth {
 | ||
|             get { return this.minButtonWidth; }
 | ||
|             set { this.minButtonWidth = value; }
 | ||
|         }
 | ||
|         private int minButtonWidth = -1;
 | ||
|         /// <summary>
 | ||
|         ///<2F><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD><C5A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD><C5A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)")]
 | ||
|         public Color ButtonForeColor
 | ||
|         {
 | ||
|             get;
 | ||
|             set;
 | ||
|         } = Color.White;
 | ||
|         /// <summary>
 | ||
|         ///<2F><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)")]
 | ||
|         public Color ButtonBaseColor
 | ||
|         {
 | ||
|             get;
 | ||
|             set;
 | ||
|         } = Color.Green;
 | ||
|         /// <summary>
 | ||
|         ///<2F><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD>ı߿<C4B1><DFBF><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD>ı߿<C4B1><DFBF><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)")]
 | ||
|         public Color ButtonBorderColor
 | ||
|         {
 | ||
|             get;
 | ||
|             set;
 | ||
|         } = Color.Gray;
 | ||
|         /// <summary>
 | ||
|         ///<2F><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD><C5A5><EFBFBD>ڱ߿<DAB1><DFBF><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD><C5A5><EFBFBD>ڱ߿<DAB1><DFBF><EFBFBD>ɫ(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)")]
 | ||
|         public Color ButtonInnerBorderColor
 | ||
|         {
 | ||
|             get;
 | ||
|             set;
 | ||
|         } = Color.Gray;
 | ||
|         /// <summary>
 | ||
|         ///<2F><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD>Ƿ߿<F1BBADB1>(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)
 | ||
|         /// </summary>
 | ||
|         [Category("ObjectListView"),
 | ||
|          Description("<22><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>ô<EFBFBD><C3B4>а<EFBFBD>ť<EFBFBD>Ƿ߿<F1BBADB1>(<28><><EFBFBD><EFBFBD><EFBFBD>а<EFBFBD>ť<EFBFBD>Ļ<EFBFBD>)")]
 | ||
|         public bool ButtonDrawBorder
 | ||
|         {
 | ||
|             get;
 | ||
|             set;
 | ||
|         } =true;
 | ||
|        
 | ||
|         #endregion
 | ||
|         #region Rendering
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Calculate the size of the contents
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         /// <returns></returns>
 | ||
|         protected override Size CalculateContentSize(Graphics g, Rectangle r) {
 | ||
|             if (this.SizingMode == OLVColumn.ButtonSizingMode.CellBounds)
 | ||
|                 return r.Size;
 | ||
| 
 | ||
|             if (this.SizingMode == OLVColumn.ButtonSizingMode.FixedBounds)
 | ||
|                 return this.ButtonSize ?? r.Size;
 | ||
| 
 | ||
|             // Ok, SizingMode must be TextBounds. So figure out the size of the text
 | ||
|             Size textSize = this.CalculateTextSize(g, this.GetText(), r.Width);
 | ||
| 
 | ||
|             // Allow for padding and max width
 | ||
|             textSize.Height += this.ButtonPaddingOrDefault.Height * 2;
 | ||
|             textSize.Width += this.ButtonPaddingOrDefault.Width * 2;
 | ||
|             if (this.MaxButtonWidth != -1 && textSize.Width > this.MaxButtonWidth)
 | ||
|                 textSize.Width = this.MaxButtonWidth;
 | ||
|             if (textSize.Width < this.MinButtonWidth)
 | ||
|                 textSize.Width = this.MinButtonWidth;
 | ||
| 
 | ||
|             return textSize;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Draw the button
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="r"></param>
 | ||
|         protected override void DrawImageAndText(Graphics g, Rectangle r) {
 | ||
|             TextFormatFlags textFormatFlags = TextFormatFlags.HorizontalCenter |
 | ||
|                                               TextFormatFlags.VerticalCenter |
 | ||
|                                               TextFormatFlags.EndEllipsis |
 | ||
|                                               TextFormatFlags.NoPadding |
 | ||
|                                               TextFormatFlags.SingleLine |
 | ||
|                                               TextFormatFlags.PreserveGraphicsTranslateTransform;
 | ||
|             if (this.ListView.RightToLeftLayout)
 | ||
|                 textFormatFlags |= TextFormatFlags.RightToLeft;
 | ||
| 
 | ||
|             string buttonText = GetText();
 | ||
|             //g.DrawString(buttonText, this.Font,Brushes.Black,new PointF(r.X,r.Y));
 | ||
|             if (!String.IsNullOrEmpty(buttonText))
 | ||
|             {
 | ||
|                 var button_state = CalculatePushButtonState();
 | ||
|                 var baseColor = ButtonBaseColor;
 | ||
|                 if (button_state == PushButtonState.Normal)
 | ||
|                 {
 | ||
|                     baseColor = ButtonBaseColor;
 | ||
|                 }
 | ||
|                 else if (button_state == PushButtonState.Hot)
 | ||
|                 {
 | ||
|                   baseColor = Rendering.Util_GDI.GetColor(ButtonBaseColor, 0, -35, -24, -30);
 | ||
|                 }
 | ||
|                 else if (button_state == PushButtonState.Pressed)
 | ||
|                 {
 | ||
|                     baseColor = Rendering.Util_GDI.GetColor(ButtonBaseColor, 0, -35, -24, -9);
 | ||
|                 }
 | ||
|                 else if (button_state == PushButtonState.Disabled)
 | ||
|                 {
 | ||
|                     baseColor = SystemColors.ControlDark;
 | ||
|                 }
 | ||
|                 Rendering.Util_GDI.DrawButtonX(buttonText, g, r, this.ButtonForeColor, baseColor, ButtonBorderColor, ButtonInnerBorderColor, ryControls.RoundStyle.All, 5, ButtonDrawBorder);
 | ||
|                 //Rendering.Util_GDI.DrawRoundButton(buttonText, g, r, Rendering.buttonStyle.ButtonNormal);
 | ||
|                 //ButtonRenderer.DrawButton(g, r, buttonText, this.Font, textFormatFlags, false, CalculatePushButtonState());
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// What part of the control is under the given point?
 | ||
|         /// </summary>
 | ||
|         /// <param name="g"></param>
 | ||
|         /// <param name="hti"></param>
 | ||
|         /// <param name="bounds"></param>
 | ||
|         /// <param name="x"></param>
 | ||
|         /// <param name="y"></param>
 | ||
|         protected override void StandardHitTest(Graphics g, OlvListViewHitTestInfo hti, Rectangle bounds, int x, int y) {
 | ||
|             Rectangle r = ApplyCellPadding(bounds);
 | ||
|             if (r.Contains(x, y))
 | ||
|                 hti.HitTestLocation = HitTestLocation.Button;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// What is the state of the button?
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         protected PushButtonState CalculatePushButtonState() {
 | ||
|             if (!this.ListItem.Enabled && !this.Column.EnableButtonWhenItemIsDisabled)
 | ||
|                 return PushButtonState.Disabled;
 | ||
| 
 | ||
|             if (this.IsButtonHot)
 | ||
|                 return ObjectListView.IsLeftMouseDown ? PushButtonState.Pressed : PushButtonState.Hot;
 | ||
| 
 | ||
|             return PushButtonState.Normal;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// Is the mouse over the button?
 | ||
|         /// </summary>
 | ||
|         protected bool IsButtonHot {
 | ||
|             get {
 | ||
|                 return this.IsCellHot && this.ListView.HotCellHitLocation == HitTestLocation.Button;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| }
 |