using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Windows.Forms.VisualStyles;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
namespace Guide.Controls
{
public enum GCellAlignment
{
Left,
Center,
Right
}
/// <summary>
/// Represents a GListView's column.
/// </summary>
public abstract class GListViewColumn
{
public const int MinimumColumnWidth = 30;
public const int MaximumColumnWidth = Int16.MaxValue;
private int minWidth;
private int maxWidth;
private int initialWidth;
public GListViewColumn() : this("", GCellAlignment.Center) {}
public GListViewColumn(string name, GCellAlignment alignment)
{
this.Name = name;
this.Alignment = alignment;
this.Resizable = true;
this.minWidth = GListViewColumn.MinimumColumnWidth;
this.maxWidth = GListViewColumn.MaximumColumnWidth;
this.initialWidth = GListViewColumn.MinimumColumnWidth;
}
/// <summary>
/// Gets or sets name of the column
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets alignment of the column
/// </summary>
public GCellAlignment Alignment { get; set; }
/// <summary>
/// Determines whether the column can be resized.
/// </summary>
public bool Resizable { get; set; }
/// <summary>
/// Gets or sets minimum width of the column
/// </summary>
public int MinWidth
{
get
{
return minWidth;
}
set
{
if (value < GListViewColumn.MinimumColumnWidth)
value = GListViewColumn.MinimumColumnWidth;
if (value > this.MaxWidth)
throw new ArgumentOutOfRangeException("value must not be greater than MaxWidth");
this.minWidth = value;
}
}
/// <summary>
/// Gets or sets maximum width of the column
/// </summary>
public int MaxWidth
{
get
{
return maxWidth;
}
set
{
if (value > GListViewColumn.MaximumColumnWidth)
value = GListViewColumn.MaximumColumnWidth;
if (value < this.MinWidth)
throw new ArgumentOutOfRangeException("value must not be less than MinWidth");
this.maxWidth = value;
}
}
/// <summary>
/// Gets or sets initial width of the column
/// </summary>
public int InitialWidth
{
get
{
return this.initialWidth;
}
set
{
if (value < this.MinWidth)
throw new ArgumentOutOfRangeException("value must not be less than MinWidth");
if (value > this.MaxWidth)
throw new ArgumentOutOfRangeException("value must not be greater than MaxWidth");
this.initialWidth = value;
}
}
/// <summary>
/// Overridden by subclasses to supply default field value when a new item is created
/// </summary>
public abstract object DefaultFieldValue { get; }
/// <summary>
/// Asserts if field's data type is correct.
/// </summary>
/// <param name="field">Field</param>
/// <returns>true if field's data type is correct</returns>
public virtual bool AssertField(object field)
{
return true;
}
/// <summary>
/// Overridden by subclasses to calculate the height needed by a cell.
/// </summary>
/// <param name="owner">The owner ListView</param>
/// <param name="g">Graphics context</param>
/// <param name="field">Corresponding field</param>
/// <returns></returns>
public abstract int MeasureCellHeight(Control owner, Graphics g, object field);
/// <summary>
/// Overridden by subclasses to calculate the desired width needed by a cell.
/// </summary>
/// <param name="owner">The owner ListView</param>
/// <param name="g">Graphics context</param>
/// <param name="field">Corresponding field</param>
/// <returns></returns>
public abstract int MeasureDesiredCellWidth(Control owner, Graphics g, object field);
/// <summary>
/// Overridden by subclasses to draw a cell.
/// </summary>
/// <param name="owner">The owner ListView</param>
/// <param name="g">Graphics context</param>
/// <param name="bounds">Bounds of the cell</param>
/// <param name="field">Corresponding field</param>
/// <param name="state">Item state</param>
public abstract void DrawCell(Control owner, Graphics g, Rectangle bounds, object field, DrawItemState state);
/// <summary>
/// Overridden (if necessary) by subclasses to handle MouseClick event on a cell
/// </summary>
/// <param name="owner">The owner ListView</param>
/// <param name="field">The corresponding field</param>
/// <param name="e">Event arguments</param>
public virtual void OnCellMouseClick(Control owner, ref object field, MouseEventArgs e) { }
/// <summary>
/// Overridden (if necessary) by subclasses to handle MouseDown event on a cell
/// </summary>
/// <param name="owner">The owner ListView</param>
/// <param name="field">The corresponding field</param>
/// <param name="e">Event arguments</param>
public virtual void OnCellMouseDown(Control owner, ref object field, MouseEventArgs e) { }
/// <summary>
/// Overridden (if necessary) by subclasses to handle MouseUp event on a cell
/// </summary>
/// <param name="owner">The owner ListView</param>
/// <param name="field">The corresponding field</param>
/// <param name="e">Event arguments</param>
public virtual void OnCellMouseUp(Control owner, ref object field, MouseEventArgs e) { }
/// <summary>
/// Copies this object's data into another object. Used by subclasses to clone themselves.
/// </summary>
/// <param name="obj">The object whose data is filled</param>
public virtual void CloneInPlace(GListViewColumn obj)
{
obj.Name = this.Name;
obj.Alignment = this.Alignment;
obj.Resizable = this.Resizable;
obj.MinWidth = this.MinWidth;
obj.MaxWidth = this.MaxWidth;
obj.InitialWidth = this.InitialWidth;
}
/// <summary>
/// Overridden by subclasses to clone themselves.
/// </summary>
/// <returns>The copy of this object</returns>
public abstract GListViewColumn Clone();
}
public class GListViewColumnSchema : List<GListViewColumn>
{
public GListViewColumnSchema() { }
public GListViewColumnSchema Clone()
{
GListViewColumnSchema o = new GListViewColumnSchema();
foreach (GListViewColumn obj in this)
o.Add(obj.Clone());
return o;
}
}
/// <summary>
/// Represents a column displaying CheckBoxes
/// </summary>
public sealed class GCheckBoxColumn : GListViewColumn
{
public const int SuggestedWidth = 30;
public GCheckBoxColumn() : this("") {}
public GCheckBoxColumn(string name)
: base(name, GCellAlignment.Center)
{
this.Resizable = false;
this.InitialWidth = GCheckBoxColumn.SuggestedWidth;
}
public override object DefaultFieldValue
{
get
{
return false;
}
}
public override bool AssertField(object field)
{
return field != null && field.GetType().Equals(typeof(bool));
}
public override int MeasureCellHeight(Control owner, Graphics g, object data)
{
return CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.CheckedNormal).Height;
}
public override int MeasureDesiredCellWidth(Control owner, Graphics g, object field)
{
return CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.CheckedNormal).Width;
}
public override void DrawCell(Control owner, Graphics g, Rectangle bounds, object field, DrawItemState state)
{
CheckBoxState checkState = (bool)field ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal;
Size checkBoxGlyph = CheckBoxRenderer.GetGlyphSize(g, checkState);
if (bounds.Width < checkBoxGlyph.Width)
return;
CheckBoxRenderer.DrawCheckBox(g, GListViewUtility.AlignInside(bounds, checkBoxGlyph, this.Alignment), checkState);
}
public override void OnCellMouseClick(Control owner, ref object field, MouseEventArgs e)
{
base.OnCellMouseClick(owner, ref field, e);
bool v = (bool)field;
field = !v;
}
public override GListViewColumn Clone()
{
GCheckBoxColumn obj = new GCheckBoxColumn();
CloneInPlace(obj);
return obj;
}
}
/// <summary>
/// Represents a column displaying images.
/// </summary>
public sealed class GImageColumn : GListViewColumn
{
public const int SuggestedMinWidth = 90;
public const int SuggestedWidth = 150;
public const string DefaultMissingMessage = "...";
public static readonly Font DefaultMissingMessageFont = SystemFonts.DefaultFont;
public static readonly Color DefaultMissingMessageForeColor = SystemColors.WindowText;
private GImageColumn() {}
public GImageColumn(string name) : this(name, GCellAlignment.Center) {}
public GImageColumn(string name, GCellAlignment alignment)
: base(name, alignment)
{
this.MinWidth = GImageColumn.SuggestedMinWidth;
this.InitialWidth = GImageColumn.SuggestedWidth;
this.InvertSelected = true;
this.MissingMessage = GImageColumn.DefaultMissingMessage;
this.MissingMessageFont = GImageColumn.DefaultMissingMessageFont;
this.MissingMessageForeColor = GImageColumn.DefaultMissingMessageForeColor;
}
/// <summary>
/// Gets or sets a value indicating if the image's color should be inverted when the item is selected.
/// </summary>
/// <returns>true if the image's color is inverted</returns>
public bool InvertSelected { get; set; }
/// <summary>
/// Gets or sets a message displayed when there is not image (i.e. the image is null)
/// </summary>
public string MissingMessage { get; set; }
/// <summary>
/// Gets or sets the font of the missing message.
/// </summary>
public Font MissingMessageFont { get; set; }
/// <summary>
/// Gets or sets the color of the missing message.
/// </summary>
public Color MissingMessageForeColor { get; set; }
public override object DefaultFieldValue
{
get
{
return null;
}
}
public override bool AssertField(object field)
{
return field == null || field is Image;
}
public override int MeasureCellHeight(Control owner, Graphics g, object data)
{
Image image = data as Image;
if (image == null)
{
return (int)this.MissingMessageFont.GetHeight(g);
}
else
{
return image.Height;
}
}
public override int MeasureDesiredCellWidth(Control owner, Graphics g, object field)
{
Image image = field as Image;
if (image == null)
{
return TextRenderer.MeasureText(g, this.MissingMessage, this.MissingMessageFont).Height;
}
else
{
return image.Width;
}
}
public override void DrawCell(Control owner, Graphics g, Rectangle bounds, object field, DrawItemState state)
{
Image image = field as Image;
bool selected = (state & DrawItemState.Selected) == DrawItemState.Selected;
// No image
if (image == null)
return;
// Not enough space
if (bounds.Width < image.Width)
{
GListViewUtility.DrawText(g, bounds, "...", this.MissingMessageFont, this.Alignment, selected ? GListViewUtility.InvertColor(this.MissingMessageForeColor) : this.MissingMessageForeColor, true);
return;
}
// Image
if (selected && this.InvertSelected)
{
// Invert image's color
ImageAttributes attrs = new ImageAttributes();
attrs.SetColorMatrix(new ColorMatrix(new float[][] {
new float[] {-1, 0, 0, 0, 0},
new float[] {0, -1, 0, 0, 0},
new float[] {0, 0, -1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {1, 1, 1, 0, +1}
}), ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(image, new Rectangle(GListViewUtility.AlignInside(bounds, image.Size, this.Alignment), image.Size), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attrs);
}
else
{
g.DrawImage(image, GListViewUtility.AlignInside(bounds, image.Size, this.Alignment));
}
}
public override GListViewColumn Clone()
{
GImageColumn obj = new GImageColumn();
CloneInPlace(obj);
obj.InvertSelected = this.InvertSelected;
obj.MissingMessage = this.MissingMessage;
obj.MissingMessageFont = this.MissingMessageFont;
obj.MissingMessageForeColor = this.MissingMessageForeColor;
return obj;
}
}
public sealed class GTextColumn : GListViewColumn
{
public const int SuggestedWidth = 75;
public static readonly Font DefaultFont = SystemFonts.DefaultFont;
public static readonly Color DefaultForeColor = SystemColors.WindowText;
private GTextColumn() {}
public GTextColumn(string name) : this(name, GCellAlignment.Center) {}
public GTextColumn(string name, GCellAlignment alignment)
: base(name, alignment)
{
this.InitialWidth = GTextColumn.SuggestedWidth;
this.InvertSelected = true;
this.Font = GTextColumn.DefaultFont;
this.ForeColor = GTextColumn.DefaultForeColor;
this.SingleLine = false;
}
/// <summary>
/// Gets or sets a value indicating if the text's color should be inverted when the item is selected.
/// </summary>
/// <returns>true if color is inverted</returns>
public bool InvertSelected { get; set; }
/// <summary>
/// Gets or sets the font used for drawing the text.
/// </summary>
public Font Font { get; set; }
/// <summary>
/// Gets or sets the text's color.
/// </summary>
public Color ForeColor { get; set; }
/// <summary>
/// Gets or sets a value indicating if the text should be drawn in a single line.
/// </summary>
public bool SingleLine { get; set; }
public override object DefaultFieldValue
{
get
{
return "";
}
}
public override bool AssertField(object field)
{
return field != null && field is String;
}
public override int MeasureCellHeight(Control owner, Graphics g, object data)
{
return (int)this.Font.GetHeight(g);
}
public override int MeasureDesiredCellWidth(Control owner, Graphics g, object field)
{
string text = (string)field;
return TextRenderer.MeasureText(g, text, this.Font).Width;
}
public override void DrawCell(Control owner, Graphics g, Rectangle bounds, object field, DrawItemState state)
{
string text = (string)field;
bool selected = (state & DrawItemState.Selected) == DrawItemState.Selected;
GListViewUtility.DrawText(g, bounds, text, this.Font, this.Alignment, selected ? GListViewUtility.InvertColor(this.ForeColor) : this.ForeColor, this.SingleLine);
}
public override GListViewColumn Clone()
{
GTextColumn obj = new GTextColumn();
CloneInPlace(obj);
obj.InvertSelected = this.InvertSelected;
obj.Font = this.Font;
obj.ForeColor = this.ForeColor;
obj.SingleLine = this.SingleLine;
return obj;
}
}
public class GListViewUtility
{
static public void DrawText(Graphics g, Rectangle bounds, string text, Font font, GCellAlignment alignment, Color color, bool singleline)
{
TextFormatFlags flags = TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis | TextFormatFlags.PreserveGraphicsTranslateTransform;
if (singleline)
flags |= TextFormatFlags.SingleLine;
switch (alignment)
{
case GCellAlignment.Left:
flags |= TextFormatFlags.Left;
break;
case GCellAlignment.Center:
flags |= TextFormatFlags.HorizontalCenter;
break;
case GCellAlignment.Right:
flags |= TextFormatFlags.Right;
break;
}
TextRenderer.DrawText(g, text, font, bounds, color, flags);
}
static public Point AlignInside(Rectangle outer, Size inner, GCellAlignment alignment)
{
switch (alignment)
{
case GCellAlignment.Left:
return new Point(outer.Left, outer.Top + (outer.Height - inner.Height) / 2);
case GCellAlignment.Center:
return new Point(outer.Left + (outer.Width - inner.Width) / 2, outer.Top + (outer.Height - inner.Height) / 2);
case GCellAlignment.Right:
return new Point(outer.Right - inner.Width, outer.Top + (outer.Height - inner.Height) / 2);
default:
throw new Exception("Error!");
}
}
static public Color InvertColor(Color color)
{
return Color.FromArgb(255 - color.R, 255 - color.G, 255 - color.B);
}
}
class GListViewSettings
{
public const string HeaderBackColor = "Control";
public const string HeaderHighlightBackColor = "ControlLight";
public const string HeaderPressedBackColor = "Control";
public const string HeaderForeColor = "ControlText";
public static readonly Font HeaderFont = SystemFonts.DefaultFont;
public const int HeaderHeight = 25;
public const string BorderColor = "ControlDark";
public const string DraggedSplitterColor = "Black";
public const int SplitterSensitivity = 2;
public const string SelectionBackColor = "Highlight";
public const string GridLineColor = "ControlLight";
public const int CellPadding = 3;
public const int MinRowHeight = 20;
}
public class GHeaderClickEventArgs : EventArgs
{
private int iHeader;
internal GHeaderClickEventArgs(int iHeader)
{
this.iHeader = iHeader;
}
public int HeaderIndex
{
get
{
return iHeader;
}
}
}
public delegate void GHeaderClickEventHandler(object sender, GHeaderClickEventArgs e);
public abstract class GListView<T> : UserControl where T : class
{
class FlickerFreePanel : Panel
{
private bool cursorInside = false;
public FlickerFreePanel()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
cursorInside = true;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
cursorInside = false;
}
public bool CursorInside
{
get
{
return cursorInside;
}
}
}
/// <summary>
/// Represents a ListItem used by GListView
/// </summary>
class GListItem
{
public GListItem(int fieldsCount)
{
this.Fields = new object[fieldsCount];
}
public object[] Fields { get; set; }
public object Object { get; set; }
}
// Controls
private FlickerFreePanel pnHeaders = new FlickerFreePanel();
private ProperListBox lstItems = new ProperListBox();
// Columns
private GListViewColumnSchema columns;
private int[] splitterPositions;
private int iCheckBoxColumn = -1;
private int iHotHeader = -1;
private bool headerPressed = false;
// Objects
private List<T> objects = new List<T>();
// State variables
private int iDraggedSplitter = -1;
private int xDraggedSplitter = 0;
private bool splitterBeingDoubleClicked = false;
/// <summary>
/// Creates a new GListView
/// </summary>
public GListView()
{
this.BorderStyle = BorderStyle.Fixed3D;
InitializeElements();
this.HeaderBackColor = Color.FromName(GListViewSettings.HeaderBackColor);
this.HeaderHighlightBackColor = Color.FromName(GListViewSettings.HeaderHighlightBackColor);
this.HeaderPressedBackColor = Color.FromName(GListViewSettings.HeaderPressedBackColor);
this.HeaderForeColor = Color.FromName(GListViewSettings.HeaderForeColor);
this.HeaderFont = GListViewSettings.HeaderFont;
this.HeaderHeight = GListViewSettings.HeaderHeight;
this.BorderColor = Color.FromName(GListViewSettings.BorderColor);
this.DraggedSplitterColor = Color.FromName(GListViewSettings.DraggedSplitterColor);
this.SelectionBackgroundColor = Color.FromName(GListViewSettings.SelectionBackColor);
this.GridLineColor = Color.FromName(GListViewSettings.GridLineColor);
this.CellPadding = GListViewSettings.CellPadding;
this.MinRowHeight = GListViewSettings.MinRowHeight;
this.GridLines = true;
this.SelectionMode = SelectionMode.MultiExtended;
this.AssertsFields = true;
}
private void InitializeElements()
{
// Header Panel
pnHeaders.Dock = DockStyle.Top;
pnHeaders.Height = 25;
pnHeaders.Paint += new PaintEventHandler(pnHeaders_Paint);
pnHeaders.MouseDoubleClick += new MouseEventHandler(pnHeaders_MouseDoubleClick);
pnHeaders.MouseMove += new MouseEventHandler(pnHeaders_MouseMove);
pnHeaders.MouseDown += new MouseEventHandler(pnHeaders_MouseDown);
pnHeaders.MouseUp += new MouseEventHandler(pnHeaders_MouseUp);
pnHeaders.MouseLeave += new EventHandler(pnHeaders_MouseLeave);
this.Controls.Add(pnHeaders);
// ListBox
lstItems.BorderStyle = BorderStyle.None;
lstItems.IntegralHeight = false;
lstItems.Left = 0;
lstItems.Top = pnHeaders.Bottom;
lstItems.Width = this.Width;
lstItems.Height = this.Height - lstItems.Top;
lstItems.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
lstItems.DrawMode = DrawMode.OwnerDrawVariable;
lstItems.SelectedIndexChanged += new EventHandler(lstItems_SelectedIndexChanged);
lstItems.DrawItem += new DrawItemEventHandler(lstItems_DrawItem);
lstItems.MeasureItem += new MeasureItemEventHandler(lstItems_MeasureItem);
lstItems.Paint += new PaintEventHandler(lstItems_Paint);
lstItems.MouseClick += new MouseEventHandler(lstItems_MouseClick);
lstItems.MouseUp += new MouseEventHandler(lstItems_MouseUp);
lstItems.MouseDown += new MouseEventHandler(lstItems_MouseDown);
lstItems.MouseWheel += new MouseEventHandler(lstItems_MouseWheel);
lstItems.HorizontalScrolled += new EventHandler(lstItems_HorizontalScrolled);
lstItems.Click += new EventHandler(lstItems_Click);
lstItems.DoubleClick += new EventHandler(lstItems_DoubleClick);
this.Controls.Add(lstItems);
}
// Properties
/// <summary>
/// Gets the column schema of this ListView.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public GListViewColumnSchema Columns
{
get
{
return columns;
}
}
/// <summary>
/// Sets the column schema of this ListView. This clears current columns and items.
/// This method is designed to be called immediately after creation of the ListView.
/// </summary>
/// <param name="columns"></param>
public void SetColumns(GListViewColumnSchema columns)
{
this.columns = columns.Clone();
this.splitterPositions = new int[this.Columns.Count];
ReconstructColumns();
}
/// <summary>
/// Gets a value indicating if CheckBoxes are supported by current column schema.
/// (It is supported if at least one column is of type GCheckBoxColumn)
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool CheckBoxes
{
get
{
return iCheckBoxColumn >= 0;
}
}
/// <summary>
/// Gets or sets splitter's color.
/// </summary>
[Category("ListView")]
[DefaultValue(typeof(Color), GListViewSettings.BorderColor)]
public Color BorderColor { get; set; }
/// <summary>
/// Gets or sets header's background color.
/// </summary>
[Category("ListView.Headers")]
[DefaultValue(typeof(Color), GListViewSettings.HeaderBackColor)]
public Color HeaderBackColor { get; set; }
/// <summary>
/// Gets or sets header's highlight background color.
/// </summary>
[Category("ListView.Headers")]
[DefaultValue(typeof(Color), GListViewSettings.HeaderHighlightBackColor)]
public Color HeaderHighlightBackColor { get; set; }
/// <summary>
/// Gets or sets header's pressed background color.
/// </summary>
[Category("ListView.Headers")]
[DefaultValue(typeof(Color), GListViewSettings.HeaderPressedBackColor)]
public Color HeaderPressedBackColor { get; set; }
/// <summary>
/// Gets or sets the header's text color.
/// </summary>
[Category("ListView.Headers")]
[DefaultValue(typeof(Color), GListViewSettings.HeaderForeColor)]
public Color HeaderForeColor { get; set; }
/// <summary>
/// Gets or sets the font used for headers.
/// </summary>
[Category("ListView.Headers")]
public Font HeaderFont { get; set; }
/// <summary>
/// Gets or sets headers' height.
/// </summary>
[Category("ListView.Headers")]
[DefaultValue(GListViewSettings.HeaderHeight)]
public int HeaderHeight
{
get
{
return pnHeaders.Height;
}
set
{
pnHeaders.Height = value;
}
}
/// <summary>
/// Gets or sets dragged splitter's color.
/// </summary>
[Category("ListView.Headers")]
[DefaultValue(typeof(Color), GListViewSettings.DraggedSplitterColor)]
public Color DraggedSplitterColor { get; set; }
/// <summary>
/// Gets or sets the ListView's background color of selected items.
/// </summary>
[Category("ListView.Items")]
[DefaultValue(typeof(Color), GListViewSettings.SelectionBackColor)]
public Color SelectionBackgroundColor { get; set; }
/// <summary>
/// Gets or sets color of grid lines.
/// </summary>
[Category("ListView.Items")]
[DefaultValue(typeof(Color), GListViewSettings.GridLineColor)]
public Color GridLineColor { get; set; }
/// <summary>
/// Gets or sets a value indicating if grid lines are displayed
/// </summary>
/// <returns>
/// true if grid lines are shown
/// </returns>
[Category("ListView.Items")]
[DefaultValue(true)]
public bool GridLines { get; set; }
/// <summary>
/// Gets or sets how much space should be padded for each cell.
/// </summary>
[Category("ListView.Items")]
[DefaultValue(GListViewSettings.CellPadding)]
public int CellPadding { get; set; }
/// <summary>
/// Gets or sets minimum row height.
/// </summary>
[Category("ListView.Items")]
[DefaultValue(GListViewSettings.MinRowHeight)]
public int MinRowHeight { get; set; }
/// <summary>
/// Gets or sets the method in which items are selected in the ListView.
/// </summary>
/// <returns>
/// One of the System.Windows.Forms.SelectionMode values. The default is SelectionMode.One.
/// </returns>
[Category("ListView.Items")]
[DefaultValue(SelectionMode.MultiExtended)]
public SelectionMode SelectionMode
{
get
{
return lstItems.SelectionMode;
}
set
{
lstItems.SelectionMode = value;
}
}
/// <summary>
/// Gets or sets a value indicating if fields should be asserted by Columns when objects are translated
/// </summary>
/// <returns>true if fields should be asserted. False otherwise.</returns>
/// <remarks>Disabling this will improve performance but invalid data will be allowed, whose sources will be hard to trace out</remarks>
[Category("ListView.Behavior")]
[DefaultValue(true)]
public bool AssertsFields { get; set; }
// Column Operations
private int GetColumnWidth(int index)
{
if (splitterPositions.Length == 0)
return pnHeaders.Width;
if (index == 0)
return splitterPositions[0];
return splitterPositions[index] - splitterPositions[index - 1];
}
private int GetColumnStart(int index)
{
if (index == 0)
return 0;
else
return splitterPositions[index - 1];
}
private int GetColumnEnd(int index)
{
return splitterPositions[index];
}
private void SetColumnSizes(int[] widths)
{
int pos = 0;
for (int i = 0; i < this.Columns.Count; i++)
{
pos += widths[i];
splitterPositions[i] = pos;
}
}
/// <summary>
/// Gets the index of column which is used as Check column (i.e. it determines which items are checked).
/// </summary>
[Browsable(false)]
public int CheckColumnIndex
{
get
{
return iCheckBoxColumn;
}
}
private void ReconstructColumns()
{
InitializeColumnSizes();
FindCheckColumn();
ValidateHScrollBar();
}
private void InitializeColumnSizes()
{
int[] widths = new int[this.Columns.Count];
for (int i = 0; i < this.Columns.Count; i++)
widths[i] = this.Columns[i].InitialWidth;
SetColumnSizes(widths);
}
private void FindCheckColumn()
{
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
if (this.Columns[iColumn] is GCheckBoxColumn)
{
iCheckBoxColumn = iColumn;
break;
}
}
// Item Operations
private GListItem CreateListItem()
{
GListItem item = new GListItem(this.Columns.Count);
item.Fields = new object[this.Columns.Count];
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
{
item.Fields[iColumn] = this.Columns[iColumn].DefaultFieldValue;
}
return item;
}
/// <summary>
/// Adds a new object to the ListView.
/// </summary>
/// <param name="obj">The object to be added</param>
public void AddItem(T obj)
{
if (obj == null)
throw new ArgumentNullException();
objects.Add(obj);
lstItems.Items.Add(Translate(obj));
lstItems.Invalidate();
}
/// <summary>
/// Adds multiple objects into the ListView.
/// </summary>
/// <param name="obj">The object to be added</param>
public void AddItems(ICollection<T> objects)
{
foreach (T obj in objects)
AddItem(obj);
}
/// <summary>
/// Replace an existing object in the ListView.
/// </summary>
/// <param name="oldObj">The object to be replaced for</param>
/// <param name="newObj">The new object</param>
public void ReplaceItem(T oldObj, T newObj)
{
for (int iItem = 0; iItem < lstItems.Items.Count; iItem++)
{
GListItem item = lstItems.Items[iItem] as GListItem;
if (item.Object.Equals(oldObj))
{
ReplaceItem(iItem, newObj);
break;
}
}
}
/// <summary>
/// Replace an existing object by specifying its index in the ListView.
/// </summary>
/// <param name="index">Index of the object</param>
/// <param name="obj">The new object</param>
public void ReplaceItem(int index, T newObj)
{
try
{
GListItem item = lstItems.Items[index] as GListItem;
item.Object = newObj;
Translate(newObj, item.Fields);
lstItems.Invalidate();
}
catch (ArgumentOutOfRangeException ex)
{
throw ex;
}
}
/// <summary>
/// Removes an object from the ListView.
/// </summary>
/// <param name="obj">The object to be removed</param>
public void RemoveItem(T obj)
{
if (objects.Contains(obj))
{
objects.Remove(obj);
foreach (GListItem item in lstItems.Items)
{
if (item.Object.Equals(obj))
{
lstItems.Items.Remove(item);
break;
}
}
}
lstItems.Invalidate();
}
/// <summary>
/// Removes some objects from the ListView.
/// </summary>
/// <param name="objects">The objects to be removed</param>
public void RemoveItems(IList<T> objects)
{
foreach (T obj in objects)
RemoveItem(obj);
}
/// <summary>
/// Removes an object from the ListView by specifying its index.
/// </summary>
/// <param name="index">The index of the object which will be removed</param>
public void RemoveItemAt(int index)
{
try
{
objects.Clear();
lstItems.Items.RemoveAt(index);
}
catch (IndexOutOfRangeException ex)
{
throw ex;
}
}
/// <summary>
/// Removes all objects from the ListView.
/// </summary>
public void ClearItems()
{
objects.Clear();
lstItems.Items.Clear();
}
/// <summary>
/// Update all objects in the ListView.
/// </summary>
public void UpdateItems()
{
foreach (GListItem item in lstItems.Items)
Translate(item.Object as T, item.Fields);
lstItems.Invalidate();
}
/// <summary>
/// Gets an object at specified index
/// </summary>
/// <param name="index">Where to get</param>
/// <returns>The desired item</returns>
public T GetItem(int index)
{
try
{
GListItem item = lstItems.Items[index] as GListItem;
return item.Object as T;
}
catch (ArgumentOutOfRangeException ex)
{
throw ex;
}
}
/// <summary>
/// Get all items of the ListView
/// </summary>
/// <returns></returns>
public List<T> GetItems()
{
List<T> objs = new List<T>();
foreach (GListItem item in this.lstItems.Items)
objs.Add(item.Object as T);
return objs;
}
/// <summary>
/// Gets the number of items in the ListView
/// </summary>
/// <returns>Number of items</returns>
public int CountItems()
{
return lstItems.Items.Count;
}
/// <summary>
/// Selects or deselects an object
/// </summary>
/// <param name="obj">The object to be selected</param>
public void SelectItem(T obj, bool selected)
{
List<T> selection = this.SelectedItems;
if (!selected && selection.Contains(obj))
selection.Remove(obj);
if (selected && !selection.Contains(obj))
selection.Add(obj);
this.SelectedItems = selection;
}
/// <summary>
/// Checks or unchecks an object
/// </summary>
/// <param name="obj">The object to be selected</param>
public void CheckItem(T obj, bool value)
{
List<T> checkedItems = this.CheckedItems;
if (!value && checkedItems.Contains(obj))
checkedItems.Remove(obj);
if (value && !checkedItems.Contains(obj))
checkedItems.Add(obj);
this.CheckedItems = checkedItems;
}
/// <summary>
/// Gets or sets selected items from the ListView.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<T> SelectedItems
{
get
{
List<T> objs = new List<T>();
foreach (GListItem item in lstItems.SelectedItems)
objs.Add(item.Object as T);
return objs;
}
set
{
// Must de-select items before selecting items
List<int> nonSelection = new List<int>();
List<int> selection = new List<int>();
for (int iItem = 0; iItem < lstItems.Items.Count; iItem++)
{
bool selected = false;
GListItem item = lstItems.Items[iItem] as GListItem;
foreach (T obj in value)
{
if (obj == null)
throw new ArgumentNullException();
if (obj.Equals(item.Object))
{
selected = true;
break;
}
}
(selected ? selection : nonSelection).Add(iItem);
}
foreach (int iItem in nonSelection)
lstItems.SetSelected(iItem, false);
foreach (int iItem in selection)
lstItems.SetSelected(iItem, true);
}
}
/// <summary>
/// Gets or sets selected indices of the ListView
/// </summary>
/// <remarks>The returned collected is sorted</remarks>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<int> SelectedIndices
{
get
{
List<int> selected = new List<int>();
foreach (int iItem in lstItems.SelectedIndices)
selected.Add(iItem);
selected.Sort();
return selected;
}
set
{
List<int> notSelected = new List<int>();
for (int i = 0; i < CountItems(); i++)
if (!value.Contains(i))
notSelected.Add(i);
foreach (int iItem in notSelected)
lstItems.SetSelected(iItem, false);
foreach (int iItem in value)
lstItems.SetSelected(iItem, true);
}
}
/// <summary>
/// Gets or sets checked items from the ListView.
/// </summary>
/// <remarks>
/// This property is useful only if the ListView's column schema supports CheckBoxes.
/// </remarks>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<T> CheckedItems
{
get
{
if (iCheckBoxColumn >= 0)
{
List<T> objs = new List<T>();
foreach (GListItem item in lstItems.Items)
if ((bool)item.Fields[iCheckBoxColumn])
objs.Add(item.Object as T);
return objs;
}
else
{
return new List<T>();
}
}
set
{
if (iCheckBoxColumn >= 0)
{
for (int iItem = 0; iItem < CountItems(); iItem++)
{
GListItem item = lstItems.Items[iItem] as GListItem;
item.Fields[iCheckBoxColumn] = value.Contains(item.Object as T);
}
lstItems.Invalidate();
}
}
}
private GListItem Translate(T obj)
{
GListItem item = CreateListItem();
item.Object = obj;
Translate(obj, item.Fields);
AssertFields(item.Fields);
return item;
}
private void AssertFields(object[] fields)
{
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
if (!this.Columns[iColumn].AssertField(fields[iColumn]))
throw new Exception("Field's data type is not correct.");
}
/// <summary>
/// Overridden by subclasses to translate from an object to represented form.
/// </summary>
/// <param name="obj">The object to be translated</param>
/// <param name="fields">The array of fields to be filled by displayed data</param>
protected abstract void Translate(T obj, object[] fields);
// Events
/// <summary>
/// Occurs when some items are selected or deselected.
/// </summary>
[Category("ListView")]
public event EventHandler SelectedItemsChanged;
protected virtual void OnSelectedItemsChanged(EventArgs e)
{
if (SelectedItemsChanged != null)
SelectedItemsChanged(this, e);
}
/// <summary>
/// Occurs when some items are checked or unchecked.
/// </summary>
[Category("ListView")]
public event EventHandler CheckedItemsChanged;
protected virtual void OnCheckedItemsChanged(EventArgs e)
{
if (CheckedItemsChanged != null)
CheckedItemsChanged(this, e);
}
/// <summary>
/// Occurs when a header is clicked.
/// </summary>
[Category("ListView")]
public event GHeaderClickEventHandler HeaderClick;
protected virtual void OnHeaderClick(GHeaderClickEventArgs e)
{
if (this.HeaderClick != null)
this.HeaderClick(this, e);
}
/// <summary>
/// Occurs when an item is clicked.
/// </summary>
/// <remarks>The clicked item can be acquired using SelectedItems property</remarks>
public event EventHandler ItemClick;
protected virtual void OnItemClick(EventArgs e)
{
if (ItemClick != null)
ItemClick(this, e);
}
/// <summary>
/// Occurs when an item is double clicked.
/// </summary>
/// <remarks>The clicked item can be acquired using SelectedItems property</remarks>
public event EventHandler ItemDoubleClick;
protected virtual void OnItemDoubleClick(EventArgs e)
{
if (ItemDoubleClick != null)
ItemDoubleClick(this, e);
}
/// <summary>
/// Occurs when a key is down
/// </summary>
public new event KeyEventHandler KeyDown
{
add
{
lstItems.KeyDown += value;
}
remove
{
lstItems.KeyDown -= value;
}
}
/// <summary>
/// Occurs when a key is up
/// </summary>
public new event KeyEventHandler KeyUp
{
add
{
lstItems.KeyUp += value;
}
remove
{
lstItems.KeyUp -= value;
}
}
/// <summary>
/// Occurs when a key is pressed.
/// </summary>
public new event KeyPressEventHandler KeyPress
{
add
{
lstItems.KeyPress += value;
}
remove
{
lstItems.KeyPress -= value;
}
}
// Serialization
/// <summary>
/// Serializes column sizes into a byte array.
/// </summary>
/// <returns>Serialized column size information</returns>
/// <remarks>This method is designed for easily storing ListView settings in persistent memory</remarks>
public byte[] SerializeColumnSizes()
{
MemoryStream bytes = new MemoryStream();
BinaryWriter w = new BinaryWriter(bytes);
w.Write(this.Columns.Count);
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
{
w.Write(GetColumnWidth(iColumn));
}
w.Close();
return bytes.ToArray();
}
/// <summary>
/// Deserializes column sizes from a byte array.
/// </summary>
/// <param name="bytes">The serialized column sizes</param>
/// <remarks>This method is designed for easily storing ListView settings in persistent memory</remarks>
public void DeserializeColumnSizes(byte[] bytes)
{
if (bytes == null)
return;
MemoryStream mem = new MemoryStream(bytes);
BinaryReader r = new BinaryReader(mem);
int n = r.ReadInt32();
if (n != this.Columns.Count)
{
Debug.WriteLine("[GListView] DeserializeColumnSizes() failed because number of columns doesn't match.");
return;
}
int[] widths = new int[n];
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
{
widths[iColumn] = r.ReadInt32();
}
SetColumnSizes(widths);
r.Close();
}
// Headers
private void pnHeaders_MouseDoubleClick(object sender, MouseEventArgs e)
{
int iColumn = FindSplitterAt(lstItems.HScrollPosition + e.X);
if (iColumn >= 0)
{
// Ensure that MouseUp doesn't intervene this...
splitterBeingDoubleClicked = true;
GListViewColumn column = this.Columns[iColumn];
Graphics g = lstItems.CreateGraphics();
int w = column.MinWidth;
foreach (GListItem item in lstItems.Items)
{
int desired = column.MeasureDesiredCellWidth(this, g, item.Fields[iColumn]);
if (w < desired)
w = desired;
}
w += 2 * this.CellPadding;
int dx = w - GetColumnWidth(iColumn);
int x = splitterPositions[iColumn] + dx;
RestrictSplitterPosition(iColumn, ref x);
MoveSplitter(iColumn, x - splitterPositions[iColumn]);
}
}
private void pnHeaders_MouseDown(object sender, MouseEventArgs e)
{
int x = e.X + lstItems.HScrollPosition;
if (e.Button == MouseButtons.Left)
{
// Splitter
iDraggedSplitter = FindSplitterAt(x);
if (iDraggedSplitter >= 0)
{
xDraggedSplitter = x;
}
else
{
if (iHotHeader != -1)
{
if (!headerPressed)
{
headerPressed = true;
pnHeaders.Invalidate();
}
}
}
}
}
private void pnHeaders_MouseUp(object sender, MouseEventArgs e)
{
if (splitterBeingDoubleClicked)
{
splitterBeingDoubleClicked = false;
iDraggedSplitter = -1;
return;
}
if (iDraggedSplitter >= 0)
{
int dx = xDraggedSplitter - splitterPositions[iDraggedSplitter];
MoveSplitter(iDraggedSplitter, dx);
iDraggedSplitter = -1;
}
else
{
if (iHotHeader != -1)
{
if (headerPressed)
{
headerPressed = false;
pnHeaders.Invalidate();
OnHeaderClick(new GHeaderClickEventArgs(iHotHeader));
}
}
}
}
private void pnHeaders_MouseMove(object sender, MouseEventArgs e)
{
// Actual position due to scrolling
int x = e.X + lstItems.HScrollPosition;
// Highlight header
int iOldHoveredHeader = iHotHeader;
iHotHeader = FindHeaderAt(x);
if (iHotHeader != iOldHoveredHeader)
pnHeaders.Invalidate();
// Change Cursor
if ((!headerPressed) && (iDraggedSplitter >= 0 || FindSplitterAt(x) >= 0))
pnHeaders.Cursor = Cursors.VSplit;
else
pnHeaders.Cursor = this.DefaultCursor;
if (iDraggedSplitter >= 0)
{
xDraggedSplitter = x;
RestrictSplitterPosition(iDraggedSplitter, ref xDraggedSplitter);
pnHeaders.Invalidate();
lstItems.Invalidate();
}
}
private void pnHeaders_MouseLeave(object sender, EventArgs e)
{
this.Cursor = this.DefaultCursor;
if (!headerPressed)
{
if (iHotHeader != -1)
{
iHotHeader = -1;
pnHeaders.Invalidate();
}
}
}
private void RestrictSplitterPosition(int iSplitter, ref int x)
{
GListViewColumn column = Columns[iSplitter];
if (column.Resizable)
{
int xColumn = GetColumnStart(iSplitter);
int min = xColumn + column.MinWidth;
int max = xColumn + column.MaxWidth;
if (x < min)
x = min;
if (x > max)
x = max;
}
else
{
x = GetColumnEnd(iSplitter);
}
}
private void ValidateHScrollBar()
{
int edge = (int)splitterPositions[splitterPositions.Length - 1];
if (edge > this.Width)
{
lstItems.HorizontalScrollbar = true;
lstItems.HorizontalExtent = edge;
}
else
{
lstItems.HorizontalScrollbar = false;
lstItems.HorizontalExtent = 0;
}
}
private int FindSplitterAt(int x)
{
for (int i = 0; i < splitterPositions.Length; i++)
{
if (Math.Abs(x - splitterPositions[i]) <= GListViewSettings.SplitterSensitivity)
{
return i;
}
}
return -1;
}
private int FindHeaderAt(int x)
{
for (int i = 0; i < splitterPositions.Length; i++)
{
if (x < splitterPositions[i])
{
return i;
}
}
return -1;
}
private void MoveSplitter(int iSplitter, int dx)
{
for (int i = iSplitter; i < this.Columns.Count; i++)
splitterPositions[i] += dx;
ValidateHScrollBar();
pnHeaders.Invalidate();
lstItems.Invalidate();
}
private void pnHeaders_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.TranslateTransform(-lstItems.HScrollPosition, 0);
// Draw Splitters
for (int i = 0; i < splitterPositions.Length; i++)
{
int x = splitterPositions[i];
// g.DrawLine(new Pen(this.BorderColor), x, 0, x, pnHeaders.Height);
}
// Draw Headers
for (int i = 0; i < this.Columns.Count; i++)
{
string text = Columns[i].Name;
Rectangle rect = new Rectangle((int)GetColumnStart(i), 0, (int)GetColumnWidth(i), pnHeaders.Height);
// Fill
if (i == iHotHeader && pnHeaders.CursorInside)
if (headerPressed)
g.FillRectangle(new SolidBrush(this.HeaderPressedBackColor), rect);
else
g.FillRectangle(new SolidBrush(this.HeaderHighlightBackColor), rect);
else
g.FillRectangle(new SolidBrush(this.HeaderBackColor), rect);
// Border
if (i == iHotHeader && headerPressed && pnHeaders.CursorInside)
ControlPaint.DrawBorder3D(e.Graphics, rect, Border3DStyle.SunkenInner);
else
ControlPaint.DrawBorder3D(e.Graphics, rect, Border3DStyle.RaisedInner);
// Text
rect.Inflate(-this.CellPadding, -this.CellPadding);
GListViewUtility.DrawText(g, rect, text, this.HeaderFont, GCellAlignment.Center, this.ForeColor, true);
}
// Draw Dragged Splitter
if (iDraggedSplitter >= 0)
g.DrawLine(new Pen(this.DraggedSplitterColor), xDraggedSplitter, 0, xDraggedSplitter, pnHeaders.Height);
g.ResetTransform();
// Draw Border
if (this.BorderStyle == BorderStyle.None)
{
Pen pen = new Pen(this.BorderColor);
g.DrawLine(pen, 0, 0, pnHeaders.Width - 1, 0);
g.DrawLine(pen, 0, 0, 0, pnHeaders.Height - 1);
g.DrawLine(pen, pnHeaders.Width - 1, 0, pnHeaders.Width - 1, pnHeaders.Height - 1);
}
}
// Items
private Rectangle GetCellRect(int index, int column)
{
Rectangle itemRect = lstItems.GetItemRectangle(index);
return new Rectangle(itemRect.X + GetColumnStart(column), itemRect.Y, GetColumnWidth(column), itemRect.Height);
}
private GListItem FindListItem(T obj)
{
// lstItems.Items.Cast<GListItem>().First(item => item.Object.Equals(obj));
foreach (GListItem item in lstItems.Items)
if (item.Object.Equals(obj))
return item;
return null;
}
private void lstItems_SelectedIndexChanged(object sender, EventArgs e)
{
lstItems.Invalidate();
OnSelectedItemsChanged(e);
}
private void lstItems_MeasureItem(object sender, MeasureItemEventArgs e)
{
e.ItemWidth = lstItems.Width + 100;
e.ItemHeight = CalculateRowHeight(e.Graphics, e.Index);
}
private void lstItems_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0)
return;
GListItem item = lstItems.Items[e.Index] as GListItem;
bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
// It seems sometimes the Graphics is already translated
// This might be an unknown bug of FlickFreeListBox
// Anyway this hack works fine
if (e.Graphics.VisibleClipBounds.Left == 0)
e.Graphics.TranslateTransform(-lstItems.HScrollPosition, 0);
// Background
if (selected)
e.Graphics.FillRectangle(new SolidBrush(this.SelectionBackgroundColor), new Rectangle(0, e.Bounds.Y, GetColumnEnd(this.Columns.Count - 1), e.Bounds.Height));
int h = CalculateRowHeight(e.Graphics, e.Index);
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
{
GListViewColumn column = this.Columns[iColumn];
Rectangle rect = GetCellRect(e.Index, iColumn);
Rectangle inner = rect;
inner.Inflate(-CellPadding, -CellPadding);
// Draw cell itself
Columns[iColumn].DrawCell(this, e.Graphics, inner, item.Fields[iColumn], e.State);
// Grid lines
Pen gridPen = new Pen(GridLineColor);
e.Graphics.DrawLine(gridPen, rect.Left, rect.Top, rect.Right, rect.Top);
e.Graphics.DrawLine(gridPen, rect.Left, rect.Top, rect.Left, rect.Bottom);
// Finalizing grid lines
if (iColumn == this.Columns.Count - 1)
e.Graphics.DrawLine(gridPen, rect.Right, rect.Top, rect.Right, rect.Bottom);
if (e.Index == lstItems.Items.Count - 1)
e.Graphics.DrawLine(gridPen, rect.Left, rect.Bottom, rect.Right, rect.Bottom);
}
e.Graphics.ResetTransform();
}
private int CalculateRowHeight(Graphics g, int index)
{
GListItem item = lstItems.Items[index] as GListItem;
int h = this.MinRowHeight;
for (int iColumn = 0; iColumn < this.Columns.Count; iColumn++)
{
int cellHeight = this.columns[iColumn].MeasureCellHeight(this, g, item.Fields[iColumn]);
h = Math.Max(h, cellHeight);
}
return h + 2 * CellPadding;
}
private void lstItems_Paint(object sender, PaintEventArgs e)
{
if (this.BorderStyle == BorderStyle.None)
e.Graphics.DrawRectangle(SystemPens.ControlDark, new Rectangle(0, 0, lstItems.Width - 1, lstItems.Height - 1));
// Dragged Splitter
if (iDraggedSplitter >= 0)
{
e.Graphics.TranslateTransform(-lstItems.HScrollPosition, 0);
e.Graphics.DrawLine(new Pen(DraggedSplitterColor), xDraggedSplitter, 0, xDraggedSplitter, lstItems.Height);
e.Graphics.ResetTransform();
}
}
private void lstItems_MouseClick(object sender, MouseEventArgs e)
{
int iItem, iColumn;
GetCellFromPoint(e.X, e.Y, out iItem, out iColumn);
if (iItem >= 0 && iColumn >= 0)
{
GListItem item = lstItems.Items[iItem] as GListItem;
object data = item.Fields[iColumn];
this.Columns[iColumn].OnCellMouseClick(this, ref data, e);
item.Fields[iColumn] = data;
if (iColumn == this.CheckColumnIndex)
OnCheckedItemsChanged(EventArgs.Empty);
}
}
private void lstItems_MouseUp(object sender, MouseEventArgs e)
{
int iItem, iColumn;
GetCellFromPoint(e.X, e.Y, out iItem, out iColumn);
if (iItem >= 0 && iColumn >= 0)
{
GListItem item = lstItems.Items[iItem] as GListItem;
object data = item.Fields[iColumn];
this.Columns[iColumn].OnCellMouseUp(this, ref data, e);
item.Fields[iColumn] = data;
}
}
private void lstItems_MouseDown(object sender, MouseEventArgs e)
{
int iItem, iColumn;
GetCellFromPoint(e.X, e.Y, out iItem, out iColumn);
if (iItem >= 0 && iColumn >= 0)
{
GListItem item = lstItems.Items[iItem] as GListItem;
object data = item.Fields[iColumn];
this.Columns[iColumn].OnCellMouseDown(this, ref data, e);
item.Fields[iColumn] = data;
}
}
private void GetCellFromPoint(int x, int y, out int iItem, out int iColumn)
{
iItem = IndexFromPoint(x, y);
if (iItem == ListBox.NoMatches)
{
iColumn = -1;
return;
}
for (int i = 0; i < this.Columns.Count; i++)
{
Rectangle rect = GetCellRect(iItem, i);
if (rect.Contains(x, y))
{
iColumn = i;
return;
}
}
// This is possible
iColumn = -1;
}
private int IndexFromPoint(int x, int y)
{
for (int iItem = 0; iItem < CountItems(); iItem++)
if (lstItems.GetItemRectangle(iItem).Contains(x, y))
return iItem;
return -1;
}
private void lstItems_MouseWheel(object sender, MouseEventArgs e)
{
pnHeaders.Invalidate();
lstItems.Invalidate();
}
private void lstItems_HorizontalScrolled(object sender, EventArgs e)
{
pnHeaders.Invalidate();
lstItems.Invalidate();
}
void lstItems_Click(object sender, EventArgs e)
{
if (lstItems.SelectedIndices.Count > 0)
OnItemClick(e);
}
void lstItems_DoubleClick(object sender, EventArgs e)
{
if (lstItems.SelectedIndices.Count > 0)
OnItemDoubleClick(e);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
ValidateHScrollBar();
}
}
}