vbAccelerator - Contents of code file: MultiColumnList_MultiColumnList.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace vbAccelerator.Components.ListBox
{
#region SListBoxFindDirection
public enum SListBoxFindDirection : int
{
up = -1,
down = 1
}
#endregion
#region SListBox
/// <summary>
/// SListBox is a flexible extended ListBox allowing for multiple columns,
/// hierarchical groups with expand/collapse, invisible rows,
/// rows which span multiple columns and icons in cells.
/// </summary>
public class SListBox : System.Windows.Forms.ListBox
{
#region ObjectCollection
/// <summary>
/// Strongly-typed collection of objects within the SListBox
/// </summary>
new public class ObjectCollection :
System.Windows.Forms.ListBox.ObjectCollection
{
#region Member variables
private SListBox owner;
#endregion
new public SListItem this[int index]
{
get
{
return (SListItem)base[index];
}
set
{
base[index] = value;
}
}
/// <summary>
/// Adds an item to the list of items in the SListBox
/// </summary>
/// <param name="item">Item to add</param>
/// <returns>Zero-based index of the item in the collection</returns>
public System.Int32 Add ( SListItem item )
{
item.Owner = this.owner;
return base.Add(item);
}
/// <summary>
/// Adds an array of items to the list of items in the SListBox
/// </summary>
/// <param name="items">An array of objects to add to the list</param>
public void AddRange ( SListItem[] items )
{
foreach (SListItem item in items)
{
item.Owner = this.owner;
}
base.AddRange(items);
}
/// <summary>
/// Adds the items of an existing SListBox.ObjectCollection to the list
/// of items in this SListBox
/// </summary>
/// <param name="value">An SlistBox.ObjectCollection to load into this
collection</param>
public void AddRange ( ObjectCollection value )
{
base.AddRange(value);
}
/// <summary>
/// Determines whether the specified item is located within the
collection
/// </summary>
/// <param name="value">An SListItem containing the item to locate in
the collection</param>
/// <returns>True if the item is located within the collection;
otherwise false.</returns>
public bool Contains(SListItem value)
{
return base.Contains(value);
}
/// <summary>
/// Copies the entire collection into an existing array of objects at
a specified location within the array
/// </summary>
/// <param name="dest">The object array in which the items from the
collection are copied to</param>
/// <param name="arrayIndex">The location within the destination array
to copy the items from the collection to</param>
public void CopyTo(SListItem[] dest , System.Int32 arrayIndex )
{
base.CopyTo(dest, arrayIndex);
}
/// <summary>
/// Returns the index within the collection of the specified item.
/// </summary>
/// <param name="value">The SListItem to locate in the
collection.</param>
/// <returns>The zero-based index where the item is located within the
collection, otherwise -1</returns>
public int IndexOf(SListItem value)
{
return base.IndexOf(value);
}
/// <summary>
/// Inserts an item into the list box at the specified index.
/// </summary>
/// <param name="index">The zero-based index location where the item
is inserted.</param>
/// <param name="item">The SListItem to insert.</param>
public void Insert(int index, SListItem item)
{
item.Owner = this.owner;
base.Insert(index, item);
}
/// <summary>
/// Initializes a new instance of the ObjectCollection with an array
of SListItem objects
/// </summary>
/// <param name="owner">SListBox that owns the collection</param>
/// <param name="value">An array of SListItem objects to add to the
collection</param>
public ObjectCollection ( SListBox owner , SListItem[] value ) :
base(owner, value)
{
foreach (SListItem item in value)
{
item.Owner = owner;
}
this.owner = owner;
}
/// <summary>
/// Initializes a new instance of the ObjectCollection based on
another ObjectCollection.
/// </summary>
/// <param name="owner">SListBox that owns the collection</param>
/// <param name="value">An ObjectCollection from which the contents
are copied to this collection</param>
public ObjectCollection ( SListBox owner , ObjectCollection value ) :
base(owner, value)
{
this.owner = owner;
}
/// <summary>
/// Initializes a new instance of the ObjectCollection.
/// </summary>
/// <param name="owner">The SListBox that owns the collection</param>
public ObjectCollection ( SListBox owner ) : base(owner)
{
this.owner = owner;
}
}
#endregion
#region Member Variables
private int lastSelectedIndex = -1;
private SListColumnCollection columns = null;
private ImageList imgList = null;
private int lastWidth = 0;
private bool showGroupingRows = false;
private int[] sortColumnArray = new int[0];
private SortOrder[] sortColumnOrder = new SortOrder[0];
#endregion
/// <summary>
/// Gets/sets whether grouping rows for each grouped
/// column are automatically added to the control
/// when the GroupOrder property of a column is
/// changed.
/// </summary>
public bool ShowGroupingRows
{
get
{
return this.showGroupingRows;
}
set
{
this.showGroupingRows = value;
if (value)
{
addGroupedRows();
}
else
{
removeGroupedRows();
}
}
}
/// <summary>
/// Gets/sets the ImageList used as source of icons
/// </summary>
public System.Windows.Forms.ImageList ImageList
{
get
{
return this.imgList;
}
set
{
this.imgList = value;
}
}
/// <summary>
/// Returns the collection of columns associated
/// with the control
/// </summary>
public SListColumnCollection Columns
{
get
{
return this.columns;
}
}
/// <summary>
/// Determines whether the specified item is expanded or not
/// </summary>
/// <param name="item">Item to check</param>
/// <returns>True if expanded, False otherwise</returns>
public virtual bool ItemIsExpanded(
SListItem item)
{
bool isExpanded = true;
int index = this.Items.IndexOf(item);
if (index > -1)
{
if (index < this.Items.Count)
{
SListItem testItem = this.Items[index + 1];
if (!testItem.Visible)
{
isExpanded = false;
}
}
}
return isExpanded;
}
/// <summary>
/// Expands or Collapses any child items under the specified
/// item.
/// </summary>
/// <param name="item">Item to expand/collapse for</param>
/// <param name="expand">True if expanding, False otherwise</param>
public virtual void ExpandCollapse(
SListItem item,
bool expand
)
{
int index = this.Items.IndexOf(item);
if (index > -1)
{
for (int i = index + 1; i < this.Items.Count; i++)
{
SListItem testItem = this.Items[i];
if (testItem.Indentation > item.Indentation)
{
if (testItem.Visible != expand)
{
testItem.Visible = expand;
}
}
else
{
break;
}
}
}
}
internal int[] SortColumnArray()
{
return this.sortColumnArray;
}
internal SortOrder[] SortColumnOrder()
{
return this.sortColumnOrder;
}
/// <summary>
/// Called when the SortOrder property of a column is changed
/// </summary>
/// <param name="columnIndex">Column index to sort by</param>
public virtual void SortByColumn(
int columnIndex
)
{
Array sortArray;
Array indexArray = Array.CreateInstance(typeof(SListItem),
this.Items.Count);
int i = 0;
int index = 0;
if (columnIndex == 0)
{
sortArray = Array.CreateInstance(typeof(SListItem),
this.Items.Count);
foreach (SListItem item in this.Items)
{
sortArray.SetValue(item, i);
indexArray.SetValue(item, i);
i++;
}
Array.Sort(sortArray, indexArray);
foreach (SListItem item in indexArray)
{
this.Items[index] = item;
index++;
}
}
else
{
// Create an array of the column items:
sortArray = Array.CreateInstance(typeof(SListColumnItem),
this.Items.Count);
foreach (SListItem item in this.Items)
{
sortArray.SetValue(item[columnIndex], i);
indexArray.SetValue(item, i);
i++;
}
Array.Sort(sortArray, indexArray);
if (this.columns[columnIndex].SortOrder == SortOrder.Descending)
{
Array.Reverse(sortArray);
Array.Reverse(indexArray);
}
// now modify the position of all the items in the box according
// to the sort order:
foreach (SListItem item in indexArray)
{
this.Items[index] = item;
index++;
}
}
}
/// <summary>
/// Gets the collection of SListItems in this ListBox
/// </summary>
new public ObjectCollection Items
{
get
{
return (ObjectCollection)base.Items;
}
}
/// <summary>
/// Finds the next visible item index in the control, if any.
/// </summary>
/// <param name="index">Zero-based index of the item to start at</param>
/// <param name="direction">Direction to search in</param>
/// <returns>The index of the next visible item if found, -1
otherwise</returns>
public int NextVisibleItemIndex(
int index,
SListBoxFindDirection direction
)
{
int nextIndex = -1;
SListItem item;
if (direction == SListBoxFindDirection.down)
{
int i = index;
bool found = false;
while (!found)
{
i++;
if (i == this.Items.Count)
{
// no next item to select.
// Select the current item if possible:
if (lastSelectedIndex != -1)
{
nextIndex = lastSelectedIndex;
found = true;
}
else
{
i = this.SelectedIndex;
while (!found)
{
i--;
if (i < 0)
{
// nothing to select
found = true;
}
else
{
item = this.Items[i];
if ((item.Height > 0) && (item.Visible))
{
nextIndex = i;
found = true;
}
}
}
}
}
else
{
item = this.Items[i];
if ((item.Height > 0) && (item.Visible))
{
nextIndex = i;
found = true;
}
}
}
}
else
{
// trying to select the prior item
int i = index;
bool found = false;
while (!found)
{
i--;
if (i < 0)
{
// no prior item to select.
// select the current item if possible
if (lastSelectedIndex != -1)
{
nextIndex = lastSelectedIndex;
found = true;
}
else
{
i = this.SelectedIndex;
while (!found)
{
i++;
if (i == this.Items.Count)
{
// nothing to select
found = true;
}
else
{
item = this.Items[i];
if ((item.Height > 0) && (item.Visible))
{
nextIndex = i;
found = true;
}
}
}
}
}
else
{
item = this.Items[i];
if ((item.Height > 0) && (item.Visible))
{
nextIndex = i;
found = true;
}
}
}
}
return nextIndex;
}
private void removeGroupedRows()
{
int i = 0;
while (i < this.Items.Count)
{
if (this.Items[i].GroupRow)
{
// it is a grouping row:
this.Items.RemoveAt(i);
}
else
{
i++;
}
}
}
private void addGroupedRows()
{
if (this.showGroupingRows)
{
if ((this.Items.Count > 0) && (this.sortColumnArray.Length > 0))
{
// add the first item:
SListItem s = null;
int i = 0;
int indent = 0;
foreach (int col in this.sortColumnArray)
{
s = new SListItem(
this.Items[i][col].Text
);
s.ShowColumns = false;
s.BackColor = Color.FromKnownColor(KnownColor.Control);
s.Indentation = indent;
s.GroupRow = true;
this.Items.Insert(i, s);
i++;
indent += 16;
}
int itemIndent = indent + 16;
int lastRow = i;
bool foundFirst = false;
while (i < this.Items.Count)
{
//this.Items[i].Indentation += itemIndent;
// check if this item matches the last one:
indent = 0;
foundFirst = false;
foreach (int col in this.sortColumnArray)
{
if (
(!this.Items[lastRow][col].Text.Equals(this.Items[i][col].
Text)) ||
foundFirst )
{
s = new SListItem(
this.Items[i][col].Text
);
s.ShowColumns = false;
s.BackColor = Color.FromKnownColor(KnownColor.Control);
s.Indentation = indent;
s.GroupRow = true;
this.Items.Insert(i, s);
i++;
lastRow = i;
foundFirst = true;
}
indent += 16;
}
i++;
}
}
}
}
internal void SetColumnGroupOrder(
SListColumn item,
int order
)
{
if (item.GroupOrder == order)
{
// nothing to do
}
else
{
this.Visible = false;
removeGroupedRows();
// Check if this item is already part of a group order:
if (item.GroupOrder > 0)
{
// it is already there.
int existingGroupOrder = item.GroupOrder;
foreach (SListColumn col in this.columns)
{
if (col.GroupOrder > 0)
{
if (existingGroupOrder < order)
{
if ((col.GroupOrder > existingGroupOrder) &&
(col.GroupOrder <= order))
{
col.SetColumnGroupOrder(col.GroupOrder - 1);
}
}
else
{
if ((col.GroupOrder >= order) && (col.GroupOrder <
existingGroupOrder))
{
col.SetColumnGroupOrder(col.GroupOrder + 1);
}
}
}
}
item.SetColumnGroupOrder(order);
}
else
{
// this is a new item.
// Determine the group order of this item:
int maxGroupOrder = 0;
foreach (SListColumn col in this.columns)
{
if (col.GroupOrder >= order)
{
col.SetColumnGroupOrder(col.GroupOrder + 1);
}
if (col.GroupOrder > maxGroupOrder)
{
maxGroupOrder = col.GroupOrder;
}
}
if (order > maxGroupOrder)
{
// we add to the end:
order = maxGroupOrder + 1;
}
item.SetColumnGroupOrder(order);
}
ArrayList sca = new ArrayList();
ArrayList sco = new ArrayList();
int index = 0;
foreach (SListColumn col in this.columns)
{
if (col.GroupOrder > 0)
{
sca.Add(index);
sco.Add(col.SortOrder);
}
index++;
}
sortColumnArray = (int [])sca.ToArray(index.GetType());
sortColumnOrder = (SortOrder
[])sco.ToArray(SortOrder.Ascending.GetType());
this.SortByColumn(0);
addGroupedRows();
this.Visible = true;
}
}
protected override void AddItemsCore ( object[] value )
{
base.AddItemsCore(value);
}
protected override void SetItemCore ( System.Int32 index , System.Object
value )
{
base.SetItemCore(index, value);
}
protected override void SetItemsCore ( System.Collections.IList value )
{
base.SetItemsCore(value);
}
/// <summary>
/// Responds to a non-height property change event of an item.
/// Forces the ListBox to redraw the item.
/// </summary>
/// <param name="item"></param>
public virtual void OnItemChanged(
SListItem item
)
{
Rectangle rc = this.GetItemRectangle(this.Items.IndexOf(item));
this.Invalidate(rc);
}
/// <summary>
/// Responds to a height changed event of an item.
/// In a ListBox, the only way to change the height of an item
/// once it has been Measured is to remove it and then add it
/// again.
/// </summary>
/// <param name="item">Item whose height has changed</param>
public virtual void OnHeightChanged(
SListItem item
)
{
int index = this.Items.IndexOf(item);
this.Items.RemoveAt(index);
this.Items.Insert(index, item);
}
public virtual void OnColumnChanged(
SListColumn column
)
{
OnResize(null);
}
/// <summary>
/// Creates a new instance of the SListBox.ObjectCollection.
/// </summary>
/// <returns>An SListBox.ObjectCollection that represents the new item
collection</returns>
protected override System.Windows.Forms.ListBox.ObjectCollection
CreateItemCollection()
{
return new ObjectCollection(this);
}
/// <summary>
/// Raises the OnResize event and makes the Horizontal scroll bar visible
/// if necessary.
/// </summary>
/// <param name="e">Event arguments</param>
protected override void OnResize( System.EventArgs e )
{
int width = 0;
foreach (SListColumn col in this.columns)
{
width += col.Width;
}
if (width > this.Width)
{
this.HorizontalScrollbar = true;
this.HorizontalExtent = width;
}
else
{
this.HorizontalScrollbar = false;
}
if (width != this.lastWidth)
{
// necessary to redraw all the items
this.Invalidate();
}
base.OnResize(e);
}
/// <summary>
/// Calls the ListBox base OnSelectedIndexChanged unless the
/// item being selected has a height less than 0, in which
/// case the appropriate next visible item is selected.
/// </summary>
/// <param name="e">Event arguments</param>
protected override void OnSelectedIndexChanged ( System.EventArgs e )
{
if ((this.SelectedIndex >= 0) && (this.SelectedIndex <
this.Items.Count))
{
SListItem item = this.Items[this.SelectedIndex];
if ((item.Height <= 0) || (!item.Visible))
{
SListBoxFindDirection direction = SListBoxFindDirection.up;
if (lastSelectedIndex < this.SelectedIndex)
{
// trying to select the next item
direction = SListBoxFindDirection.down;
}
this.lastSelectedIndex = -1;
int index = NextVisibleItemIndex(this.SelectedIndex, direction);
if (index > -1)
{
this.SelectedIndex = index;
}
}
else
{
base.OnSelectedIndexChanged(e);
this.lastSelectedIndex = this.SelectedIndex;
}
}
else
{
base.OnSelectedIndexChanged(e);
}
}
/// <summary>
/// Draws the SListBox item and raises the DrawItem
/// event.
/// </summary>
/// <param name="e">DrawItemEventArgs for the item</param>
protected override void OnDrawItem(DrawItemEventArgs e)
{
if ((e.Index >= 0) && (e.Index < this.Items.Count))
{
SListItem item = this.Items[e.Index];
if ((item.Height > 0) && (item.Visible))
{
bool selected = ((e.State & DrawItemState.Selected) ==
DrawItemState.Selected);
bool rightToLeft = (this.RightToLeft == RightToLeft.Yes);
if (selected)
{
if (item.Indentation > 0)
{
if (rightToLeft)
{
Rectangle indented = new Rectangle(
e.Bounds.X,
e.Bounds.Y,
e.Bounds.Width - item.Indentation,
e.Bounds.Height);
//e.Graphics.FillRectangle(SystemBrushes.Highlight,
indented);
}
else
{
Rectangle indented = new Rectangle(
e.Bounds.X + item.Indentation,
e.Bounds.Y,
e.Bounds.Width - item.Indentation,
e.Bounds.Height);
//e.Graphics.FillRectangle(SystemBrushes.Highlight,
indented);
}
}
else
{
//e.Graphics.FillRectangle(SystemBrushes.Highlight,
e.Bounds);
}
}
else
{
//e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
}
Rectangle itemRect = e.Bounds;
itemRect.Inflate(-1, -1);
item.DrawItem(e.Graphics, imgList, itemRect, this.Font,
selected, true, rightToLeft);
}
}
base.OnDrawItem(e);
}
/// <summary>
/// Measures the item and raises the MeasureItem event
/// </summary>
/// <param name="e">MeasureItemEventArgs for the item</param>
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
if ((e.Index >= 0) && (e.Index < this.Items.Count))
{
SListItem item = this.Items[e.Index];
if (item.Visible)
{
e.ItemHeight = item.RenderHeight;
}
else
{
e.ItemHeight = 0;
}
e.ItemWidth = 1024;
}
base.OnMeasureItem(e);
}
/// <summary>
/// Initializes a new instance of the SListBox object.
/// </summary>
public SListBox()
{
this.DrawMode = DrawMode.OwnerDrawVariable;
this.columns = new SListColumnCollection(this);
}
}
#endregion
#region SListItemBase
/// <summary>
/// Class which SListItems derive from. Provides base
/// services for item properties such as text to display,
/// Font, Colours, indentation etc
/// </summary>
public abstract class SListItemBase : IDisposable, IComparable
{
private object text = null;
private string textFormat = "";
private int iconIndex = -1;
private StringFormat stringFormat = null;
private Font font = null;
private Color foreColor = Color.FromKnownColor(KnownColor.WindowText);
private Color backColor = Color.FromKnownColor(KnownColor.Window);
private int indentation = 0;
private int height = 20;
private object tag = null;
/// <summary>
/// Compares the current instance with another object of the same type.
/// The class attempts to use the CompareTo method of the text object
/// for comparision. If this is not available, the ToString() method
/// of this class is used for comparison.
/// </summary>
/// <param name="obj">An object to compare with this instance.</param>
/// <returns>A 32-bit signed integer that indicates the relative order of
the comparands.
/// The return value has these meanings:
/// Less than zero This instance is less than obj.
/// Zero This instance is equal to obj.
/// Greater than zero This instance is greater than obj.</returns>
public virtual int CompareTo ( object obj )
{
int res = 0;
try
{
bool doDefault = false;
IComparable icThis = null;
try
{
icThis = (IComparable)this.text;
}
catch
{
doDefault = true;
}
if (doDefault)
{
res = this.ToString().CompareTo(obj.ToString());
}
else
{
res = icThis.CompareTo(((SListItemBase)obj).Text);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception {0}", ex);
}
return res;
}
/// <summary>
/// Gets/sets the text to display for this item.
/// Defaults to no text. The ToString method of the
/// object will be used to render it unless a TextFormat
/// string is set, in which case the String.Format method
/// will be invoked.
/// </summary>
public object Text
{
get
{
return this.text;
}
set
{
this.text = value;
OnItemChanged();
}
}
public string TextFormat
{
get
{
return this.textFormat;
}
set
{
this.textFormat = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets the index of the icon within the ImageList
/// to display for this item. Set to -1 for no icon.
/// Defaults to -1.
/// </summary>
public int Icon
{
get
{
return this.iconIndex;
}
set
{
this.iconIndex = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets the indentation of this item.
/// </summary>
public int Indentation
{
get
{
return this.indentation;
}
set
{
this.indentation = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets a specific string format to use
/// when drawing this item. Set to null to
/// use default text rendering. Defaults to 0
/// </summary>
public System.Drawing.StringFormat StringFormat
{
get
{
return this.stringFormat;
}
set
{
this.stringFormat = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets a specific font to use
/// when drawing this item. When null, the
/// owner ListBox font is used for drawing.
/// Default is null.
/// </summary>
public System.Drawing.Font Font
{
get
{
return this.font;
}
set
{
this.font = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets a customised foreground
/// color for this item. Default is
/// WindowText.
/// </summary>
public Color ForeColor
{
get
{
return this.foreColor;
}
set
{
this.foreColor = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets a customised background
/// color for this item. Default is
/// WindowBackground.
/// </summary>
public Color BackColor
{
get
{
return this.backColor;
}
set
{
this.backColor = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets an object associated with the item.
/// </summary>
public object Tag
{
get
{
return this.tag;
}
set
{
this.tag = value;
OnItemChanged();
}
}
/// <summary>
/// Gets/sets the height of this item
/// </summary>
public int Height
{
get
{
return this.height;
}
set
{
this.height = value;
OnHeightChanged();
}
}
/// <summary>
/// Called whenever a sizing property is changed
/// </summary>
protected virtual void OnHeightChanged()
{
}
/// <summary>
/// Called whenever a non-sizing property is changed
/// </summary>
protected virtual void OnItemChanged()
{
}
/// <summary>
/// Draws this item.
/// Used internally to draw the ListBox.
/// </summary>
/// <param name="graphics">Graphics object to draw to</param>
/// <param name="imageList">Source of images</param>
/// <param name="bounds">Bounding rectangle for the item</param>
/// <param name="defaultFont">Font to use if the item does not have a
font specified</param>
/// <param name="selected">Whether the item should be rendered selected
or not</param>
/// <param name="firstItem">Whether the item is the first in the list
box</param>
/// <param name="rightToLeft">Whether item should be rendered
right-to-left</param>
public virtual void DrawItem(
Graphics graphics,
ImageList imageList,
Rectangle bounds,
Font defaultFont,
bool selected,
bool firstItem,
bool rightToLeft
)
{
// fill the background:
Rectangle fillRect = bounds;
if (firstItem)
{
if (rightToLeft)
{
fillRect.X = bounds.X;
fillRect.Y = bounds.Y;
fillRect.Width = bounds.Width - this.indentation;
fillRect.Height = bounds.Height;
}
else
{
fillRect.X = bounds.X + this.indentation;
fillRect.Y = bounds.Y;
fillRect.Width = bounds.Width - this.indentation;
fillRect.Height = bounds.Height;
}
}
if (selected)
{
graphics.FillRectangle(SystemBrushes.Highlight, fillRect);
}
else
{
Brush backBrush = new SolidBrush(this.backColor);
graphics.FillRectangle(backBrush, fillRect);
backBrush.Dispose();
}
// Get the item rectangle:
Rectangle itemRect;
int iconX;
if (rightToLeft)
{
itemRect = new Rectangle(
bounds.X,
bounds.Y,
bounds.Width - this.indentation,
bounds.Height);
iconX = bounds.X + bounds.Width - this.indentation;
}
else
{
itemRect = new Rectangle(
bounds.X + this.indentation,
bounds.Y,
bounds.Width - this.indentation,
bounds.Height);
iconX = bounds.X + this.indentation;
}
// Draw the icon
if (imageList != null)
{
if (iconIndex > -1)
{
if (rightToLeft)
{
iconX -= imageList.ImageSize.Width;
}
int iconY = bounds.Top + (bounds.Height -
imageList.ImageSize.Height) / 2;
if (iconY < bounds.Top)
{
imageList.Draw(
graphics,
iconX,
iconY,
imageList.ImageSize.Width,
bounds.Height,
this.iconIndex);
}
else
{
imageList.Draw(
graphics,
iconX,
iconY,
this.iconIndex);
}
if (!rightToLeft)
{
iconX += imageList.ImageSize.Width;
}
}
}
Font itemFont = defaultFont;
if (this.font != null)
{
itemFont = this.font;
}
Brush itemBrush;
if (selected)
{
itemBrush = new
SolidBrush(Color.FromKnownColor(KnownColor.HighlightText));
}
else
{
itemBrush= new SolidBrush(this.foreColor);
}
StringFormat format;
if (this.stringFormat != null)
{
format = this.stringFormat;
}
else
{
format = new StringFormat();
format.Alignment = StringAlignment.Near;
format.LineAlignment = StringAlignment.Center;
format.FormatFlags = StringFormatFlags.LineLimit;
format.Trimming = StringTrimming.EllipsisCharacter;
}
RectangleF textRect;
if (rightToLeft)
{
textRect = new RectangleF(
(float)bounds.X, (float)bounds.Y, (float)(iconX - bounds.X),
(float)bounds.Height);
}
else
{
textRect = new RectangleF(
(float)iconX, (float)bounds.Y, (float)itemRect.Width,
(float)bounds.Height);
}
// Draw the text:
graphics.DrawString(
this.ToString(),
itemFont,
itemBrush,
textRect,
format);
itemBrush.Dispose();
if (this.stringFormat == null)
{
format.Dispose();
}
}
/// <summary>
/// Returns the text of the item.
/// </summary>
/// <returns>The text for the item</returns>
public override string ToString()
{
if (this.textFormat.Length > 0)
{
return String.Format(textFormat, this.text);
}
else
{
return this.text.ToString();
}
}
/// <summary>
/// Default Constructor
/// </summary>
public SListItemBase()
{
this.text = (object)"";
}
public SListItemBase(
object text
) : this()
{
this.text = text;
}
public SListItemBase(
object text,
int iconIndex
) : this(text)
{
this.iconIndex = iconIndex;
}
public SListItemBase(
object text,
int iconIndex,
int indentation
) : this (text, iconIndex)
{
this.indentation = indentation;
}
public SListItemBase(
object text,
int iconIndex,
int indentation,
Color foreColor,
Color backColor
) : this (text, iconIndex, indentation)
{
this.foreColor = foreColor;
this.backColor = backColor;
}
public SListItemBase(
object text,
int iconIndex,
int indentation,
Color foreColor,
Color backColor,
Font font
) : this(text, iconIndex, indentation, foreColor, backColor)
{
this.font = font;
}
/// <summary>
/// Disposes of any resources associated with the object
/// </summary>
public void Dispose()
{
if (this.stringFormat != null)
{
this.stringFormat.Dispose();
this.stringFormat = null;
}
}
}
#endregion
#region SListColumnCollection
public class SListColumnCollection : ReadOnlyCollectionBase
{
private bool internalOrderSetting = false;
private SListBox owner = null;
public SListBox Owner
{
get
{
return this.owner;
}
}
/// <summary>
/// Gets the column at the specified index
/// </summary>
public SListColumn this[int index]
{
get
{
return (SListColumn)this.InnerList[index];
}
}
/// <summary>
/// Returns the column at the specified order,
/// or null if no column.
/// </summary>
/// <param name="order">Order index</param>
/// <returns>The SListColumn object for the column order
/// specified, or null if nothing at the specified order
/// </returns>
public SListColumn ColumnAtOrder(int order)
{
SListColumn ret = null;
foreach (SListColumn col in this.InnerList)
{
if (col.Order == order)
{
ret = col;
}
}
return ret;
}
/// <summary>
/// Removes the column at the specified index
/// </summary>
/// <param name="index">Index of column in control</param>
public void Remove(int index)
{
SListColumn colToRemove = (SListColumn)this.InnerList[index];
int order = colToRemove.Order;
internalOrderSetting = true;
foreach (SListColumn col in this.InnerList)
{
if (col.Order > order)
{
col.Order = col.Order - 1;
}
}
internalOrderSetting = false;
this.InnerList.RemoveAt(index);
}
/// <summary>
/// Adds a column to the end of the column list
/// with the default properties
/// </summary>
public void Add()
{
SListColumn col = new SListColumn();
col.Owner = this;
Add(col);
}
/// <summary>
/// Adds a column to the end of the column list
/// </summary>
/// <param name="column">Column to add</param>
public void Add(SListColumn column)
{
column.Owner = this;
internalOrderSetting = true;
column.Order = this.InnerList.Count;
internalOrderSetting = false;
this.InnerList.Add(column);
owner.OnColumnChanged(column);
}
/// <summary>
/// Called when a column's width is changed
/// </summary>
/// <param name="item">The column whose width has changed</param>
public virtual void OnColumnResize(
SListColumn item
)
{
if (this.owner != null)
{
this.owner.OnColumnChanged(item);
}
}
/// <summary>
/// Called when the SortOrder property of
/// a column is changed
/// </summary>
/// <param name="item">Column whose sort order has been changed</param>
public void SortByColumn(
SListColumn item
)
{
if (this.owner != null)
{
int colIndex = this.InnerList.IndexOf(item);
this.owner.SortByColumn(colIndex);
}
}
/// <summary>
/// Internal method for returning the X position
/// of a column.
/// </summary>
/// <param name="item">The column to get the X position for</param>
/// <returns>X position of the column</returns>
internal int ColumnX(
SListColumn item
)
{
int x = 0;
int colOrder = item.Order;
bool rightToLeft = false;
if (owner != null)
{
rightToLeft = (owner.RightToLeft == RightToLeft.Yes);
}
if (rightToLeft)
{
x = owner.HorizontalExtent;
if (owner.Width > x)
{
x = owner.Width;
}
}
// All grouped columns are counted first against the edge:
int myGroupOrder = item.GroupOrder;
if (myGroupOrder <= 0)
{
myGroupOrder = 0x7FFFFFFF;
}
foreach(SListColumn col in this.InnerList)
{
if ((col.GroupOrder > 0) && (col.GroupOrder < myGroupOrder))
{
if (rightToLeft)
{
x -= 16;
}
else
{
x += 16;
}
}
}
if (item.GroupOrder <=0)
{
foreach(SListColumn col in this.InnerList)
{
if (col.Order < colOrder)
{
if (rightToLeft)
{
x -= col.Width;
}
else
{
x += col.Width;
}
}
}
}
return x;
}
/// <summary>
/// Internal method for modifying the order
/// of a column. Called when the Order property
/// of an SListColumn is changed
/// </summary>
/// <param name="item">The item to change order</param>
/// <param name="order">New order</param>
internal int SetColumnOrder(
SListColumn item,
int order
)
{
if (internalOrderSetting)
{
return order;
}
else
{
if (item.Order == order)
{
return item.Order;
}
else
{
internalOrderSetting = true;
if (order > item.Order)
{
// anything between item.Order and order should
// be reduced by 1:
foreach (SListColumn otherItem in this.InnerList)
{
if (otherItem.Order != item.Order)
{
if ((otherItem.Order >= item.Order) && (otherItem.Order
<= order ))
{
otherItem.Order = otherItem.Order - 1;
}
}
}
// set this item:
internalOrderSetting = false;
return order;
}
else
{
// anything with an order >= order and <= item.Order should
// be increased by 1:
foreach (SListColumn otherItem in this.InnerList)
{
if (otherItem.Order != item.Order)
{
if ((otherItem.Order >= order) && (otherItem.Order <=
item.Order))
{
otherItem.Order = otherItem.Order + 1;
}
}
}
internalOrderSetting = false;
return order;
}
}
}
}
internal void SetColumnGroupOrder(
SListColumn item,
int order
)
{
owner.SetColumnGroupOrder(item ,order);
}
internal SListColumnCollection(
SListBox owner)
{
this.owner = owner;
}
}
#endregion
#region SListColumn
public class SListColumn
{
private string text = "";
private object tag = null;
private int order = 0;
private int groupOrder = 0;
private int width = 64;
private SListColumnCollection owner;
private SortOrder sortOrder;
/// <summary>
/// Gets/sets the owner collection for this column.
/// Set automatically when the object is added to the
/// SListColumCollection.
/// </summary>
[Description("Gets/sets the owner collection for this column.")]
public SListColumnCollection Owner
{
get
{
return this.owner;
}
set
{
this.owner = value;
}
}
/// <summary>
/// Gets/sets the text for this column
/// </summary>
[Description("Gets/sets the text for this column.")]
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
/// <summary>
/// Gets/sets an object associated with this column.
/// </summary>
[Description("Gets/sets an object associated with this column.")]
public object Tag
{
get
{
return this.tag;
}
set
{
this.tag = value;
}
}
/// <summary>
/// Gets/sets the width of this column
/// </summary>
/// <exception cref="NotSupportedException">If you attempt to set the
width whilst
/// the column is grouped.</exception>
[Description("Gets/sets the width of this column.")]
public int Width
{
get
{
if ((this.groupOrder > 0) && (owner.Owner.ShowGroupingRows))
{
return 16;
}
else
{
return this.width;
}
}
set
{
this.width = value;
if (owner != null)
{
owner.OnColumnResize(this);
}
}
}
/// <summary>
/// Gets/sets the order at which this
/// column is displayed in the ListBox.
/// Only valid if the column is not grouped.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">If the order is out of
bounds</exception>
/// <exception cref="InvalidOperationException">If the column is not part
of a control
/// or the column is grouped.</exception>
[Description("Gets/sets the order at which this column is displayed in
the ListBox.")]
public int Order
{
get
{
return this.order;
}
set
{
if (owner == null)
{
throw new InvalidOperationException("Order cannot be set until
the column is assigned to an SListBox control");
}
if ((this.order < 0) || (this.order > owner.Count))
{
throw new ArgumentOutOfRangeException("order", value, "Order
must be greater than 0 and less than the number of columns.");
}
this.order = owner.SetColumnOrder(this, value);
}
}
/// <summary>
/// Gets/sets the order at which this column
/// is grouped in.
/// If GroupOrder is < 0, then the column is
/// not grouped. If GroupOrder is > 0 then
/// the data within the column is grouped.
/// </summary>
/// <exception cref="InvalidOperationException">If the column is not part
of a control.</exception>
[Description("Gets/sets the order at which this column is grouped in. If
GroupOrder is < 0 then the column is not grouped, otherwise it is.")]
public int GroupOrder
{
get
{
return this.groupOrder;
}
set
{
if (this.owner == null)
{
throw new InvalidOperationException("GroupOrder cannot be set
until the column is assigned to an SListBox control");
}
else
{
owner.SetColumnGroupOrder(this, value);
}
}
}
internal void SetColumnGroupOrder(int order)
{
this.groupOrder = order;
}
/// <summary>
/// Gets the horizontal start position of this column
/// </summary>
/// <exception cref="InvalidOperationException">If the column is not part
of a control</exception>
[Description("Gets the horizontal start position of this column.")]
public int X
{
get
{
if (owner == null)
{
throw new InvalidOperationException("X cannot be retrieved until
the column is assigned to an SListBox control");
}
return owner.ColumnX(this);
}
}
/// <summary>
/// Returns a string representing this column. If the text property is
set,
/// returns the text, otherwise returns "[Unnamed Column]"
/// </summary>
/// <returns>String representing this column.</returns>
public override string ToString()
{
if (this.text.Length > 0)
{
return this.text;
}
else
{
return "[Unnamed Column]";
}
}
/// <summary>
/// Gets/sets the sorting order of this column.
/// </summary>
/// <param name="sortOrder">Sort Order</param>
public System.Windows.Forms.SortOrder SortOrder
{
get
{
return this.sortOrder;
}
set
{
this.sortOrder = value;
owner.SortByColumn(this);
}
}
/// <summary>
/// Initializes a new SListColumn object
/// </summary>
public SListColumn()
{
}
}
#endregion
#region SListColumnItem
/// <summary>
/// A single column item within the SListBox
/// </summary>
public class SListColumnItem : SListItemBase
{
public int CompareTo ( object obj, SortOrder sortOrder )
{
int ret = base.CompareTo(obj);
if (sortOrder == SortOrder.Descending)
{
ret = -1 * ret;
}
return ret;
}
/// <summary>
/// Draws this item.
/// Used internally to draw the ListBox.
/// </summary>
/// <param name="graphics">Graphics object to draw to</param>
/// <param name="imageList">Source of images</param>
/// <param name="bounds">Bounding rectangle for the item</param>
/// <param name="defaultFont">Font to use if the item does not have a
font specified</param>
/// <param name="selected">Whether the item should be rendered selected
or not</param>
/// <param name="rightToLeft">Whether item should be rendered
right-to-left</param>
public override void DrawItem(
Graphics graphics,
ImageList imageList,
Rectangle bounds,
Font defaultFont,
bool selected,
bool firstItem,
bool rightToLeft
)
{
base.DrawItem(
graphics,
imageList,
bounds,
defaultFont,
selected,
firstItem,
rightToLeft);
}
public SListColumnItem() : base()
{
}
public SListColumnItem(
object text
) : base(text)
{
}
public SListColumnItem(
object text,
int iconIndex
) : base(text, iconIndex)
{
}
public SListColumnItem(
object text,
int iconIndex,
int indentation
) : base(text, iconIndex, indentation)
{
}
public SListColumnItem(
object text,
int iconIndex,
int indentation,
Color foreColor,
Color backColor
) : base(text, iconIndex, indentation, foreColor, backColor)
{
}
public SListColumnItem(
object text,
int iconIndex,
int indentation,
Color foreColor,
Color backColor,
Font font
) : base(text, iconIndex, indentation, foreColor, backColor, font)
{
}
}
#endregion
#region SListItem
/// <summary>
/// An item within the SListBox. Rows themselves
/// can have associated text and data if desired,
/// and will be displayed instead of the columns
/// if the ShowColumns property is set to False.
/// </summary>
public class SListItem : SListItemBase, ICollection
{
private ArrayList columns = null;
private bool groupRow = false;
private bool showColumns = false;
private bool showText = true;
private bool visible = true;
private SListBox owner = null;
/// <summary>
/// Compares the current instance with another object of the same type.
///
/// The class attempts to use the CompareTo method of the text object
/// for comparision. If this is not available, the ToString() method
/// of this class is used for comparison.
/// </summary>
/// <param name="obj">An object to compare with this instance.</param>
/// <returns>A 32-bit signed integer that indicates the relative order of
the comparands.
/// The return value has these meanings:
/// Less than zero - This instance is less than obj.
/// Zero - This instance is equal to obj.
/// Greater than zero - This instance is greater than obj.</returns>
public override int CompareTo( object obj )
{
if (owner == null)
{
return base.CompareTo(obj);
}
else
{
int[] sortColumns = owner.SortColumnArray();
SortOrder[] order = owner.SortColumnOrder();
int ret = 0;
int index = 0;
foreach (int sortColumn in sortColumns)
{
ret = this[sortColumn].CompareTo(((SListItem)obj)[sortColumn],
order[index]);
if (ret != 0)
{
break;
}
index++;
}
return ret;
}
}
/// <summary>
/// Draws this item.
/// Used internally to draw the ListBox.
/// </summary>
/// <param name="graphics">Graphics object to draw to</param>
/// <param name="imageList">Source of images</param>
/// <param name="bounds">Bounding rectangle for the item</param>
/// <param name="defaultFont">Font to use if the item does not have a
font specified</param>
/// <param name="selected">Whether the item should be rendered selected
or not</param>
/// <param name="firstItem">Whether this item is the first item in the
row</param>
/// <param name="rightToLeft">Whether item should be rendered
right-to-left</param>
public override void DrawItem(
Graphics graphics,
ImageList imageList,
Rectangle bounds,
Font defaultFont,
bool selected,
bool firstItem,
bool rightToLeft
)
{
Rectangle groupedArea = bounds;
groupedArea.Width = 0;
bool drawGroupArea = false;
Rectangle itemBounds = bounds;
if (this.showColumns)
{
int height = 0;
if (owner != null)
{
int index = 0;
foreach (SListColumn col in owner.Columns)
{
if (index < this.columns.Count)
{
Rectangle columnBounds;
if (rightToLeft)
{
columnBounds = new Rectangle(
bounds.X + col.X - col.Width,
bounds.Y,
col.Width,
bounds.Height);
}
else
{
columnBounds = new Rectangle(
bounds.X + col.X,
bounds.Y,
col.Width,
bounds.Height);
}
SListColumnItem colItem = this[index];
columnBounds.Height = colItem.Height;
if (columnBounds.Height > height)
{
height = columnBounds.Height;
}
if ((col.GroupOrder <=0) || (!owner.ShowGroupingRows))
{
colItem.DrawItem(
graphics,
imageList,
columnBounds,
defaultFont,
selected,
firstItem,
rightToLeft);
}
else
{
columnBounds.Y -= 1;
columnBounds.X -=1;
columnBounds.Height += 1;
columnBounds.Width += 1;
graphics.FillRectangle(SystemBrushes.Control,
columnBounds);
drawGroupArea = true;
if (columnBounds.X < groupedArea.X)
{
groupedArea.X = columnBounds.X;
}
if (columnBounds.Y < groupedArea.Y)
{
groupedArea.Y = columnBounds.Y;
}
groupedArea.Width += columnBounds.Width;
groupedArea.Height += columnBounds.Height;
}
firstItem = false;
}
index++;
}
itemBounds.Y += height;
itemBounds.Height -= height;
}
}
if (drawGroupArea)
{
Rectangle groupBounds = itemBounds;
groupBounds.Height += 1;
if (rightToLeft)
{
groupBounds.X = itemBounds.X - groupedArea.Width;
groupBounds.Width = groupedArea.Width;
itemBounds.X -= groupBounds.Width;
}
else
{
groupBounds.X = groupedArea.X;
groupBounds.Width = groupedArea.Width;
itemBounds.X += groupBounds.Width;
}
graphics.FillRectangle(SystemBrushes.Control, groupBounds);
}
base.DrawItem(
graphics,
imageList,
itemBounds,
defaultFont,
selected,
true,
rightToLeft);
}
/// <summary>
/// Gets/sets the owning SListBox of this item. Set
/// automatically when the item is added to an SListBox.
/// The same SListItem cannot belong to two different
/// SListBoxes.
/// </summary>
public SListBox Owner
{
get
{
return this.owner;
}
set
{
this.owner = value;
}
}
/// <summary>
/// Determines whether this item is expanded or not.
/// </summary>
/// <returns></returns>
public bool Expanded
{
get
{
bool expanded = true;
if (owner != null)
{
expanded = owner.ItemIsExpanded(this);
}
return expanded;
}
set
{
if (owner != null)
{
owner.ExpandCollapse(this, true);
}
}
}
/// <summary>
/// Returns whether this row is a grouping row or not.
/// </summary>
/// <returns></returns>
public bool GroupRow
{
get
{
return this.groupRow;
}
set
{
this.groupRow = value;
}
}
/// <summary>
/// Expands (shows) any child items of this item. Child items are defined
/// as items with a larger indentation.
/// </summary>
public void Expand()
{
if (owner != null)
{
owner.ExpandCollapse(this, true);
}
}
/// <summary>
/// Collapses (hides) any child items of this item. Child items are
defined
/// as items with a larger indentation.
/// </summary>
public void Collapse()
{
if (owner != null)
{
owner.ExpandCollapse(this, false);
}
}
/// <summary>
/// Gets the overall rendering height of this item in the
/// list box
/// </summary>
public int RenderHeight
{
get
{
int renderHeight = 0;
if (this.Visible)
{
if (this.showColumns)
{
int maxColHeight = 0;
foreach (SListColumnItem colItem in this.columns)
{
if (colItem.Height > maxColHeight)
{
maxColHeight = colItem.Height;
}
}
renderHeight += maxColHeight;
}
if (this.showText)
{
renderHeight += this.Height;
}
}
return renderHeight;
}
}
public bool Visible
{
get
{
return this.visible;
}
set
{
this.visible = value;
OnHeightChanged();
}
}
protected override void OnHeightChanged()
{
if (owner != null)
{
owner.OnHeightChanged(this);
}
}
protected override void OnItemChanged()
{
if (owner != null)
{
owner.OnItemChanged(this);
}
}
/// <summary>
/// Gets/sets whether columns are displayed for this
/// row. If columns are not displayed, the Text, Icon
/// etc properties of this class will be used for the
/// display. Defaults to true if the class is constructed
/// with a series of columns, otherwise defaults to false.
/// </summary>
public bool ShowColumns
{
get
{
return this.showColumns;
}
set
{
this.showColumns = value;
OnHeightChanged();
}
}
/// <summary>
/// Gets/sets whether the text for this row is displayed.
/// Defaults to True.
/// </summary>
public bool ShowText
{
get
{
return this.showText;
}
set
{
this.showText = value;
OnHeightChanged();
}
}
/// <summary>
/// Returns the item at the specified column index
/// </summary>
public SListColumnItem this[int index]
{
get
{
return (SListColumnItem)this.columns[index];
}
}
/// <summary>
/// Gets the number of columns associated with this row
/// (if any)
/// </summary>
public int Count
{
get
{
return columns.Count;
}
}
/// <summary>
/// Gets a value indicating whether the columns collection
/// is synchronized (thread-safe) or not.
/// </summary>
public bool IsSynchronized
{
get
{
return this.columns.IsSynchronized;
}
}
/// <summary>
/// Gets an object which can be used to synchronize access
/// to the columns collection.
/// </summary>
public object SyncRoot
{
get
{
return this.columns.SyncRoot;
}
}
/// <summary>
/// Returns an enumerator for the columns in the collection
/// </summary>
/// <returns>An IEnumerator object for the columns in the item</returns>
public System.Collections.IEnumerator GetEnumerator()
{
return this.columns.GetEnumerator();
}
/// <summary>
/// Copies a range of items from the columns collection to
/// a compatible one-dimensional array.
/// </summary>
/// <param name="array">Array to copy to</param>
/// <param name="index">Index to start at</param>
public void CopyTo(System.Array array, int index)
{
this.columns.CopyTo(array, index);
}
private void init()
{
this.columns = new ArrayList();
}
private void init(
SListColumnItem[] columns
)
{
init();
foreach (SListColumnItem column in columns)
{
this.columns.Add(column);
}
}
/// <summary>
/// Constructs a new the SListItem object
/// </summary>
public SListItem() : base()
{
this.columns = new ArrayList();
}
public SListItem(
SListColumnItem[] columnItems
) : base()
{
init(columnItems);
this.showColumns = true;
}
public SListItem(
SListColumnItem[] columnItems,
object text
) : base(text, -1, 0)
{
init(columnItems);
this.showColumns = true;
}
public SListItem(
SListColumnItem[] columnItems,
object text,
int indentation
) : base(text, -1, indentation)
{
init(columnItems);
this.showColumns = true;
}
public SListItem(
SListColumnItem[] columnItems,
object text,
int indentation,
int iconIndex
) : base(text, iconIndex, indentation)
{
init(columnItems);
this.showColumns = true;
}
public SListItem(
object text
) : base(text)
{
init();
}
public SListItem(
object text,
int iconIndex
) : base(text, iconIndex)
{
init();
}
public SListItem(
object text,
int iconIndex,
int indentation
) : base(text, iconIndex, indentation)
{
init();
}
public SListItem(
object text,
int iconIndex,
int indentation,
Color foreColor,
Color backColor
) : base(text, iconIndex, indentation, foreColor, backColor)
{
init();
}
public SListItem(
object text,
int iconIndex,
int indentation,
Color foreColor,
Color backColor,
Font font
) : base(text, iconIndex, indentation, foreColor, backColor, font)
{
init();
}
}
#endregion
}
|
|