// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2004-2006 Novell, Inc.
//
// Authors:
// Jordi Mas i Hernandez, jordi@ximian.com
// Mike Kestner <mkestner@novell.com>
//
// COMPLETE
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
[DefaultProperty("Items")]
[DefaultEvent("SelectedIndexChanged")]
[Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
public class ListBox : ListControl
{
public const int DefaultItemHeight = 13;
public const int NoMatches = -1;
internal enum ItemNavigation
{
First,
Last,
Next,
Previous,
NextPage,
PreviousPage,
PreviousColumn,
NextColumn
}
Hashtable item_heights;
private int item_height = -1;
private int column_width = 0;
private int requested_height = -1;
private DrawMode draw_mode = DrawMode.Normal;
private int horizontal_extent = 0;
private bool horizontal_scrollbar = false;
private bool integral_height = true;
private bool multicolumn = false;
private bool scroll_always_visible = false;
private int selected_index = -1;
private SelectedIndexCollection selected_indices;
private SelectedObjectCollection selected_items;
private ArrayList selection = new ArrayList ();
private ArrayList display_selection = new ArrayList ();
private SelectionMode selection_mode = SelectionMode.One;
private bool sorted = false;
private bool use_tabstops = true;
private int column_width_internal = 120;
private ImplicitVScrollBar vscrollbar;
private ImplicitHScrollBar hscrollbar;
private int hbar_offset;
private bool suspend_layout;
private bool ctrl_pressed = false;
private bool shift_pressed = false;
private bool has_focus = false;
private bool explicit_item_height = false;
private int top_index = 0;
private int last_visible_index = 0;
private Rectangle items_area;
private int focused_item = -1;
private ObjectCollection items;
public ListBox ()
{
border_style = BorderStyle.Fixed3D;
BackColor = ThemeEngine.Current.ColorWindow;
items = CreateItemCollection ();
selected_indices = new SelectedIndexCollection (this);
selected_items = new SelectedObjectCollection (this);
/* Vertical scrollbar */
vscrollbar = new ImplicitVScrollBar ();
vscrollbar.Minimum = 0;
vscrollbar.SmallChange = 1;
vscrollbar.LargeChange = 1;
vscrollbar.Maximum = 0;
vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
vscrollbar.Visible = false;
/* Horizontal scrollbar */
hscrollbar = new ImplicitHScrollBar ();
hscrollbar.Minimum = 0;
hscrollbar.SmallChange = 1;
hscrollbar.LargeChange = 1;
hscrollbar.Maximum = 0;
hscrollbar.Visible = false;
hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
/* Events */
MouseDown += new MouseEventHandler (OnMouseDownLB);
MouseMove += new MouseEventHandler (OnMouseMoveLB);
MouseUp += new MouseEventHandler (OnMouseUpLB);
MouseWheel += new MouseEventHandler (OnMouseWheelLB);
KeyDown += new KeyEventHandler (OnKeyDownLB);
KeyUp += new KeyEventHandler (OnKeyUpLB);
GotFocus += new EventHandler (OnGotFocus);
LostFocus += new EventHandler (OnLostFocus);
SetStyle (ControlStyles.UserPaint, false);
}
#region Events
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler BackgroundImageChanged {
add { base.BackgroundImageChanged += value; }
remove { base.BackgroundImageChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public new event EventHandler Click {
add { base.Click += value; }
remove { base.Click -= value; }
}
public event DrawItemEventHandler DrawItem;
public event MeasureItemEventHandler MeasureItem;
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event PaintEventHandler Paint {
add { base.Paint += value; }
remove { base.Paint -= value; }
}
public event EventHandler SelectedIndexChanged;
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public new event EventHandler TextChanged {
add { base.TextChanged += value; }
remove { base.TextChanged -= value; }
}
#endregion // Events
#region Public Properties
public override Color BackColor {
get { return base.BackColor; }
set {
if (base.BackColor == value)
return;
base.BackColor = value;
base.Refresh (); // Careful. Calling the base method is not the same that calling
} // the overriden one that refresh also all the items
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public override Image BackgroundImage {
get { return base.BackgroundImage; }
set {
base.BackgroundImage = value;
base.Refresh ();
}
}
[DefaultValue (BorderStyle.Fixed3D)]
[DispId(-504)]
public BorderStyle BorderStyle {
get { return InternalBorderStyle; }
set {
InternalBorderStyle = value;
UpdateBounds ();
}
}
[DefaultValue (0)]
[Localizable (true)]
public int ColumnWidth {
get { return column_width; }
set {
if (value < 0)
throw new ArgumentException ("A value less than zero is assigned to the property.");
column_width = value;
if (value == 0)
ColumnWidthInternal = 120;
else
ColumnWidthInternal = value;
base.Refresh ();
}
}
protected override CreateParams CreateParams {
get { return base.CreateParams;}
}
protected override Size DefaultSize {
get { return new Size (120, 96); }
}
[RefreshProperties(RefreshProperties.Repaint)]
[DefaultValue (DrawMode.Normal)]
public virtual DrawMode DrawMode {
get { return draw_mode; }
set {
if (!Enum.IsDefined (typeof (DrawMode), value))
throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
throw new ArgumentException ("Cannot have variable height and multicolumn");
if (draw_mode == value)
return;
draw_mode = value;
if (draw_mode == DrawMode.OwnerDrawVariable)
item_heights = new Hashtable ();
else
item_heights = null;
base.Refresh ();
}
}
public override Color ForeColor {
get { return base.ForeColor; }
set {
if (base.ForeColor == value)
return;
base.ForeColor = value;
base.Refresh ();
}
}
[DefaultValue (0)]
[Localizable (true)]
public int HorizontalExtent {
get { return horizontal_extent; }
set {
if (horizontal_extent == value)
return;
horizontal_extent = value;
base.Refresh ();
}
}
[DefaultValue (false)]
[Localizable (true)]
public bool HorizontalScrollbar {
get { return horizontal_scrollbar; }
set {
if (horizontal_scrollbar == value)
return;
horizontal_scrollbar = value;
UpdateScrollBars ();
base.Refresh ();
}
}
[DefaultValue (true)]
[Localizable (true)]
[RefreshProperties(RefreshProperties.Repaint)]
public bool IntegralHeight {
get { return integral_height; }
set {
if (integral_height == value)
return;
integral_height = value;
UpdateBounds ();
}
}
[DefaultValue (13)]
[Localizable (true)]
[RefreshProperties(RefreshProperties.Repaint)]
public virtual int ItemHeight {
get {
if (item_height == -1) {
SizeF sz = DeviceContext.MeasureString ("The quick brown Fox", Font);
item_height = (int) sz.Height;
}
return item_height;
}
set {
if (value > 255)
throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
explicit_item_height = true;
if (item_height == value)
return;
item_height = value;
if (IntegralHeight)
UpdateBounds ();
Layout ();
}
}
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
[Localizable (true)]
[Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
public ObjectCollection Items {
get { return items; }
}
[DefaultValue (false)]
public bool MultiColumn {
get { return multicolumn; }
set {
if (multicolumn == value)
return;
if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
multicolumn = value;
Layout ();
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public int PreferredHeight {
get {
int itemsHeight = 0;
if (draw_mode == DrawMode.Normal)
itemsHeight = FontHeight * items.Count;
else if (draw_mode == DrawMode.OwnerDrawFixed)
itemsHeight = ItemHeight * items.Count;
else if (draw_mode == DrawMode.OwnerDrawVariable) {
for (int i = 0; i < items.Count; i++)
itemsHeight += (int) item_heights [Items [i]];
}
return itemsHeight;
}
}
public override RightToLeft RightToLeft {
get { return base.RightToLeft; }
set {
base.RightToLeft = value;
if (base.RightToLeft == RightToLeft.Yes)
StringFormat.Alignment = StringAlignment.Far;
else
StringFormat.Alignment = StringAlignment.Near;
base.Refresh ();
}
}
// Only affects the Vertical ScrollBar
[DefaultValue (false)]
[Localizable (true)]
public bool ScrollAlwaysVisible {
get { return scroll_always_visible; }
set {
if (scroll_always_visible == value)
return;
scroll_always_visible = value;
UpdateScrollBars ();
}
}
[Bindable(true)]
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public override int SelectedIndex {
get { return selected_index;}
set {
if (value < -1 || value >= Items.Count)
throw new ArgumentOutOfRangeException ("Index of out range");
if (SelectionMode == SelectionMode.None)
throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
if (selected_index == value)
return;
if (value == -1)
ClearSelected ();
else if (SelectionMode == SelectionMode.One)
UnSelectItem (selected_index, true);
SelectItem (value);
selected_index = value;
FocusedItem = value;
OnSelectedIndexChanged (new EventArgs ());
OnSelectedValueChanged (new EventArgs ());
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public SelectedIndexCollection SelectedIndices {
get { return selected_indices; }
}
[Bindable(true)]
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public object SelectedItem {
get {
if (SelectedItems.Count > 0)
return SelectedItems[0];
else
return null;
}
set {
if (value != null && !Items.Contains (value))
return; // FIXME: this is probably an exception
SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public SelectedObjectCollection SelectedItems {
get {return selected_items;}
}
[DefaultValue (SelectionMode.One)]
public virtual SelectionMode SelectionMode {
get { return selection_mode; }
set {
if (!Enum.IsDefined (typeof (SelectionMode), value))
throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
if (selection_mode == value)
return;
selection_mode = value;
switch (selection_mode) {
case SelectionMode.None:
ClearSelected ();
break;
case SelectionMode.One:
while (SelectedIndices.Count > 1)
UnSelectItem (SelectedIndices [SelectedIndices.Count - 1], true);
break;
default:
break;
}
}
}
[DefaultValue (false)]
public bool Sorted {
get { return sorted; }
set {
if (sorted == value)
return;
sorted = value;
if (sorted)
Sort ();
}
}
[Bindable (false)]
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public override string Text {
get {
if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
return GetItemText (SelectedItem);
return base.Text;
}
set {
base.Text = value;
if (SelectionMode == SelectionMode.None)
return;
int index;
index = FindStringExact (value);
if (index == -1)
return;
SelectedIndex = index;
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public int TopIndex {
get { return top_index; }
set {
if (value == top_index)
return;
if (value < 0 || value >= Items.Count)
return;
top_index = value;
UpdateTopItem ();
base.Refresh ();
}
}
[DefaultValue (true)]
public bool UseTabStops {
get { return use_tabstops; }
set {
if (use_tabstops == value)
return;
use_tabstops = value;
if (use_tabstops)
StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
else
StringFormat.SetTabStops (0, new float [0]);
base.Refresh ();
}
}
#endregion Public Properties
#region Private Properties
private int ColumnWidthInternal {
get { return column_width_internal; }
set { column_width_internal = value; }
}
private int row_count = 1;
private int RowCount {
get {
return MultiColumn ? row_count : Items.Count;
}
}
#endregion Private Properties
#region Public Methods
protected virtual void AddItemsCore (object[] value)
{
Items.AddRange (value);
}
public void BeginUpdate ()
{
suspend_layout = true;
}
public void ClearSelected ()
{
foreach (int i in selected_indices) {
UnSelectItem (i, false);
}
selection.Clear ();
}
protected virtual ObjectCollection CreateItemCollection ()
{
return new ObjectCollection (this);
}
public void EndUpdate ()
{
suspend_layout = false;
Layout ();
base.Refresh ();
}
public int FindString (String s)
{
return FindString (s, -1);
}
public int FindString (string s, int startIndex)
{
if (Items.Count == 0)
return -1; // No exception throwing if empty
if (startIndex < -1 || startIndex >= Items.Count - 1)
throw new ArgumentOutOfRangeException ("Index of out range");
startIndex++;
for (int i = startIndex; i < Items.Count; i++) {
if ((GetItemText (Items[i])).StartsWith (s))
return i;
}
return NoMatches;
}
public int FindStringExact (string s)
{
return FindStringExact (s, -1);
}
public int FindStringExact (string s, int startIndex)
{
if (Items.Count == 0)
return -1; // No exception throwing if empty
if (startIndex < -1 || startIndex >= Items.Count - 1)
throw new ArgumentOutOfRangeException ("Index of out range");
startIndex++;
for (int i = startIndex; i < Items.Count; i++) {
if ((GetItemText (Items[i])).Equals (s))
return i;
}
return NoMatches;
}
public int GetItemHeight (int index)
{
if (index < 0 || index >= Items.Count)
throw new ArgumentOutOfRangeException ("Index of out range");
if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
object o = Items [index];
if (item_heights.Contains (o))
return (int) item_heights [o];
MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
OnMeasureItem (args);
item_heights [o] = args.ItemHeight;
return args.ItemHeight;
}
return ItemHeight;
}
public Rectangle GetItemRectangle (int index)
{
if (index < 0 || index >= Items.Count)
throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
Rectangle rect = new Rectangle ();
if (MultiColumn) {
int col = index / RowCount;
rect.Y = ((index - top_index) % RowCount) * ItemHeight;
rect.X = col * ColumnWidthInternal;
rect.Height = ItemHeight;
rect.Width = ColumnWidthInternal;
} else {
rect.X = 0;
rect.Height = GetItemHeight (index);
rect.Width = items_area.Width;
if (DrawMode == DrawMode.OwnerDrawVariable) {
rect.Y = 0;
if (index >= top_index) {
for (int i = top_index; i < index; i++) {
rect.Y += GetItemHeight (i);
}
} else {
for (int i = index; i < top_index; i++) {
rect.Y -= GetItemHeight (i);
}
}
} else {
rect.Y = ItemHeight * (index - top_index);
}
}
return rect;
}
public bool GetSelected (int index)
{
if (index < 0 || index >= Items.Count)
throw new ArgumentOutOfRangeException ("Index of out range");
return SelectedIndices.Contains (index);
}
public int IndexFromPoint (Point p)
{
return IndexFromPoint (p.X, p.Y);
}
// Only returns visible points
public int IndexFromPoint (int x, int y)
{
if (Items.Count == 0) {
return -1;
}
for (int i = top_index; i <= last_visible_index; i++) {
if (GetItemRectangle (i).Contains (x,y) == true)
return i;
}
return -1;
}
protected override void OnChangeUICues (UICuesEventArgs e)
{
base.OnChangeUICues (e);
}
protected override void OnDataSourceChanged (EventArgs e)
{
base.OnDataSourceChanged (e);
BindDataItems ();
if (DataSource == null || DataManager == null) {
SelectedIndex = -1;
}
else {
SelectedIndex = DataManager.Position;
}
}
protected override void OnDisplayMemberChanged (EventArgs e)
{
base.OnDisplayMemberChanged (e);
if (DataManager == null || !IsHandleCreated)
return;
BindDataItems ();
base.Refresh ();
}
protected virtual void OnDrawItem (DrawItemEventArgs e)
{
switch (DrawMode) {
case DrawMode.OwnerDrawFixed:
case DrawMode.OwnerDrawVariable:
if (DrawItem != null)
DrawItem (this, e);
break;
default:
ThemeEngine.Current.DrawListBoxItem (this, e);
break;
}
}
protected override void OnFontChanged (EventArgs e)
{
base.OnFontChanged (e);
if (use_tabstops)
StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
if (explicit_item_height) {
base.Refresh ();
} else {
SizeF sz = DeviceContext.MeasureString ("The quick brown Fox", Font);
item_height = (int) sz.Height;
if (IntegralHeight)
UpdateBounds ();
Layout ();
}
}
protected override void OnHandleCreated (EventArgs e)
{
base.OnHandleCreated (e);
SuspendLayout ();
Controls.AddImplicit (vscrollbar);
Controls.AddImplicit (hscrollbar);
ResumeLayout ();
Layout ();
}
protected override void OnHandleDestroyed (EventArgs e)
{
base.OnHandleDestroyed (e);
}
protected virtual void OnMeasureItem (MeasureItemEventArgs e)
{
if (draw_mode != DrawMode.OwnerDrawVariable)
return;
if (MeasureItem != null)
MeasureItem (this, e);
}
protected override void OnParentChanged (EventArgs e)
{
base.OnParentChanged (e);
}
protected override void OnResize (EventArgs e)
{
base.OnResize (e);
if (canvas_size.IsEmpty || MultiColumn)
Layout ();
}
protected override void OnSelectedIndexChanged (EventArgs e)
{
base.OnSelectedIndexChanged (e);
if (SelectedIndexChanged != null)
SelectedIndexChanged (this, e);
}
protected override void OnSelectedValueChanged (EventArgs e)
{
base.OnSelectedValueChanged (e);
}
public override void Refresh ()
{
if (draw_mode == DrawMode.OwnerDrawVariable)
item_heights.Clear ();
base.Refresh ();
}
protected override void RefreshItem (int index)
{
if (index < 0 || index >= Items.Count)
throw new ArgumentOutOfRangeException ("Index of out range");
if (draw_mode == DrawMode.OwnerDrawVariable)
item_heights.Remove (Items [index]);
}
protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
{
if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
requested_height = height;
if (IntegralHeight) {
int border;
switch (border_style) {
case BorderStyle.Fixed3D:
border = ThemeEngine.Current.Border3DSize.Height;
break;
case BorderStyle.FixedSingle:
border = ThemeEngine.Current.BorderSize.Height;
break;
case BorderStyle.None:
default:
border = 0;
break;
}
height -= (2 * border);
height -= height % ItemHeight;
height += (2 * border);
}
base.SetBoundsCore (x, y, width, height, specified);
UpdateScrollBars ();
}
protected override void SetItemCore (int index, object value)
{
if (index < 0 || index >= Items.Count)
return;
Items[index] = value;
}
protected override void SetItemsCore (IList value)
{
BeginUpdate ();
try {
Items.Clear ();
Items.AddRange (value);
} finally {
EndUpdate ();
}
}
public void SetSelected (int index, bool value)
{
if (index < 0 || index >= Items.Count)
throw new ArgumentOutOfRangeException ("Index of out range");
if (SelectionMode == SelectionMode.None)
throw new InvalidOperationException ();
if (value)
SelectItem (index);
else
UnSelectItem (index, true);
}
protected virtual void Sort ()
{
if (Items.Count == 0)
return;
Items.Sort ();
base.Refresh ();
}
public override string ToString ()
{
return base.ToString ();
}
protected virtual void WmReflectCommand (ref Message m)
{
}
protected override void WndProc (ref Message m)
{
base.WndProc (ref m);
}
#endregion Public Methods
#region Private Methods
private Size canvas_size;
private void Layout ()
{
if (!IsHandleCreated || suspend_layout)
return;
if (MultiColumn)
LayoutMultiColumn ();
else
LayoutSingleColumn ();
UpdateScrollBars ();
last_visible_index = LastVisibleItem ();
}
private void LayoutSingleColumn ()
{
int height, width;
switch (DrawMode) {
case DrawMode.OwnerDrawVariable:
height = 0;
width = HorizontalExtent;
for (int i = 0; i < Items.Count; i++) {
height += GetItemHeight (i);
}
break;
case DrawMode.OwnerDrawFixed:
height = Items.Count * ItemHeight;
width = HorizontalExtent;
break;
case DrawMode.Normal:
default:
height = Items.Count * ItemHeight;
width = 0;
for (int i = 0; i < Items.Count; i++) {
SizeF sz = DeviceContext.MeasureString (GetItemText (Items[i]), Font);
if ((int) sz.Width > width)
width = (int) sz.Width;
}
break;
}
canvas_size = new Size (width, height);
}
private void LayoutMultiColumn ()
{
int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
row_count = usable_height / ItemHeight;
if (row_count == 0)
row_count = 1;
int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
usable_height = ClientRectangle.Height - hscrollbar.Height;
row_count = usable_height / ItemHeight;
cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
}
canvas_size = sz;
}
internal void Draw (Rectangle clip, Graphics dc)
{
Theme theme = ThemeEngine.Current;
if (hscrollbar.Visible && vscrollbar.Visible) {
// Paint the dead space in the bottom right corner
Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
if (rect.IntersectsWith (clip))
dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
}
dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
if (Items.Count == 0)
return;
for (int i = top_index; i <= last_visible_index; i++) {
Rectangle rect = GetItemDisplayRectangle (i, top_index);
if (!clip.IntersectsWith (rect))
continue;
DrawItemState state = DrawItemState.None;
if (SelectedIndices.Contains (i))
state |= DrawItemState.Selected;
if (has_focus && FocusedItem == i)
state |= DrawItemState.Focus;
if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
rect.X -= hscrollbar.Value;
rect.Width += hscrollbar.Value;
}
OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, ForeColor, BackColor));
}
}
// Converts a GetItemRectangle to a one that we can display
internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
{
Rectangle item_rect;
Rectangle first_item_rect = GetItemRectangle (first_displayble);
item_rect = GetItemRectangle (index);
item_rect.X -= first_item_rect.X;
item_rect.Y -= first_item_rect.Y;
return item_rect;
}
// Value Changed
private void HorizontalScrollEvent (object sender, EventArgs e)
{
if (multicolumn) {
int top_item = top_index;
int last_item = last_visible_index;
top_index = RowCount * hscrollbar.Value;
last_visible_index = LastVisibleItem ();
if (top_item != top_index || last_item != last_visible_index)
Invalidate (items_area);
}
else {
int old_offset = hbar_offset;
hbar_offset = hscrollbar.Value;
if (hbar_offset < 0)
hbar_offset = 0;
XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
}
}
// Only returns visible points. The diference of with IndexFromPoint is that the rectangle
// has screen coordinates
private int IndexAtClientPoint (int x, int y)
{
if (Items.Count == 0)
return -1;
if (x < 0)
x = 0;
else if (x > ClientRectangle.Right)
x = ClientRectangle.Right;
if (y < 0)
y = 0;
else if (y > ClientRectangle.Bottom)
y = ClientRectangle.Bottom;
for (int i = top_index; i <= last_visible_index; i++)
if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
return i;
return -1;
}
private int LastVisibleItem ()
{
Rectangle item_rect;
int top_y = items_area.Y + items_area.Height;
int i = 0;
if (top_index >= Items.Count)
return top_index;
for (i = top_index; i < Items.Count; i++) {
item_rect = GetItemDisplayRectangle (i, top_index);
if (MultiColumn) {
if (item_rect.X > items_area.Width)
return i - 1;
}
else {
if (item_rect.Y + item_rect.Height > top_y) {
return i;
}
}
}
return i - 1;
}
private void UpdateTopItem ()
{
if (MultiColumn) {
int col = top_index / RowCount;
if (col > hscrollbar.Maximum)
hscrollbar.Value = hscrollbar.Maximum;
else
hscrollbar.Value = col;
} else {
int val = vscrollbar.Value;
if (top_index > vscrollbar.Maximum)
vscrollbar.Value = vscrollbar.Maximum;
else
vscrollbar.Value = top_index;
Scroll (vscrollbar, vscrollbar.Value - top_index);
XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * (val - vscrollbar.Value), false);
}
}
// Navigates to the indicated item and returns the new item
private int NavigateItemVisually (ItemNavigation navigation)
{
int page_size, columns, selected_index = -1;
if (multicolumn) {
columns = items_area.Width / ColumnWidthInternal;
page_size = columns * RowCount;
if (page_size == 0) {
page_size = RowCount;
}
} else {
page_size = items_area.Height / ItemHeight;
}
switch (navigation) {
case ItemNavigation.PreviousColumn: {
if (FocusedItem - RowCount < 0) {
return -1;
}
if (FocusedItem - RowCount < top_index) {
top_index = FocusedItem - RowCount;
UpdateTopItem ();
}
selected_index = FocusedItem - RowCount;
break;
}
case ItemNavigation.NextColumn: {
if (FocusedItem + RowCount >= Items.Count) {
break;
}
if (FocusedItem + RowCount > last_visible_index) {
top_index = FocusedItem;
UpdateTopItem ();
}
selected_index = FocusedItem + RowCount;
break;
}
case ItemNavigation.First: {
top_index = 0;
selected_index = 0;
UpdateTopItem ();
break;
}
case ItemNavigation.Last: {
int rows = items_area.Height / ItemHeight;
if (Items.Count < rows) {
top_index = 0;
selected_index = Items.Count - 1;
UpdateTopItem ();
} else {
top_index = Items.Count - rows;
selected_index = Items.Count - 1;
UpdateTopItem ();
}
break;
}
case ItemNavigation.Next: {
if (FocusedItem == Items.Count - 1)
return -1;
int bottom = 0;
ArrayList heights = new ArrayList ();
if (draw_mode == DrawMode.OwnerDrawVariable) {
for (int i = top_index; i <= FocusedItem + 1; i++) {
int h = GetItemHeight (i);
bottom += h;
heights.Add (h);
}
} else {
bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
}
if (bottom >= items_area.Height) {
int overhang = bottom - items_area.Height;
int offset = 0;
if (draw_mode == DrawMode.OwnerDrawVariable)
while (overhang > 0)
overhang -= (int) heights [offset];
else
offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
top_index += offset;
UpdateTopItem ();
}
selected_index = FocusedItem + 1;
break;
}
case ItemNavigation.Previous: {
if (FocusedItem > 0) {
if (FocusedItem - 1 < top_index) {
top_index--;
UpdateTopItem ();
}
selected_index = FocusedItem - 1;
}
break;
}
case ItemNavigation.NextPage: {
if (Items.Count < page_size) {
NavigateItemVisually (ItemNavigation.Last);
break;
}
if (FocusedItem + page_size - 1 >= Items.Count) {
top_index = Items.Count - page_size;
UpdateTopItem ();
selected_index = Items.Count - 1;
}
else {
if (FocusedItem + page_size - 1 > last_visible_index) {
top_index = FocusedItem;
UpdateTopItem ();
}
selected_index = FocusedItem + page_size - 1;
}
break;
}
case ItemNavigation.PreviousPage: {
int rows = items_area.Height / ItemHeight;
if (FocusedItem - (rows - 1) <= 0) {
top_index = 0;
UpdateTopItem ();
SelectedIndex = 0;
}
else {
if (FocusedItem - (rows - 1) < top_index) {
top_index = FocusedItem - (rows - 1);
UpdateTopItem ();
}
selected_index = FocusedItem - (rows - 1);
}
break;
}
default:
break;
}
return selected_index;
}
private void OnGotFocus (object sender, EventArgs e)
{
has_focus = true;
if (FocusedItem != -1)
InvalidateItem (FocusedItem);
}
private void OnLostFocus (object sender, EventArgs e)
{
has_focus = false;
if (FocusedItem != -1)
InvalidateItem (FocusedItem);
}
private void OnKeyDownLB (object sender, KeyEventArgs e)
{
int new_item = -1;
if (Items.Count == 0)
return;
switch (e.KeyCode) {
case Keys.ControlKey:
ctrl_pressed = true;
break;
case Keys.ShiftKey:
shift_pressed = true;
break;
case Keys.Home:
new_item = NavigateItemVisually (ItemNavigation.First);
break;
case Keys.End:
new_item = NavigateItemVisually (ItemNavigation.Last);
break;
case Keys.Up:
new_item = NavigateItemVisually (ItemNavigation.Previous);
break;
case Keys.Down:
new_item = NavigateItemVisually (ItemNavigation.Next);
break;
case Keys.PageUp:
new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
break;
case Keys.PageDown:
new_item = NavigateItemVisually (ItemNavigation.NextPage);
break;
case Keys.Right:
if (multicolumn == true) {
new_item = NavigateItemVisually (ItemNavigation.NextColumn);
}
break;
case Keys.Left:
if (multicolumn == true) {
new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
}
break;
case Keys.Space:
if (selection_mode == SelectionMode.MultiSimple) {
SelectedItemFromNavigation (FocusedItem);
}
break;
default:
break;
}
if (new_item != -1) {
FocusedItem = new_item;
if (selection_mode != SelectionMode.MultiSimple && selection_mode != SelectionMode.None) {
SelectedItemFromNavigation (new_item);
}
}
}
private void OnKeyUpLB (object sender, KeyEventArgs e)
{
switch (e.KeyCode) {
case Keys.ControlKey:
ctrl_pressed = false;
break;
case Keys.ShiftKey:
shift_pressed = false;
break;
default:
break;
}
}
internal void InvalidateItem (int index)
{
Rectangle bounds = GetItemDisplayRectangle (index, top_index);
if (ClientRectangle.IntersectsWith (bounds))
Invalidate (bounds);
}
internal virtual void OnItemClick (int index)
{
OnSelectedIndexChanged (EventArgs.Empty);
OnSelectedValueChanged (EventArgs.Empty);
}
int anchor = -1;
int[] prev_selection;
bool button_pressed = false;
private void SelectExtended (int index)
{
SuspendLayout ();
ArrayList new_selection = new ArrayList ();
int start = anchor < index ? anchor : index;
int end = anchor > index ? anchor : index;
for (int i = start; i <= end; i++)
new_selection.Add (i);
if (ctrl_pressed)
foreach (int i in prev_selection)
if (!selection.Contains (i))
new_selection.Add (i);
foreach (int i in SelectedIndices)
if (!new_selection.Contains (i))
UnSelectItem (i, true);
foreach (int i in new_selection)
if (!SelectedIndices.Contains (i))
SelectItem (i);
ResumeLayout ();
}
private void OnMouseDownLB (object sender, MouseEventArgs e)
{
int index = IndexAtClientPoint (e.X, e.Y);
if (index == -1)
return;
switch (SelectionMode) {
case SelectionMode.One:
if (SelectedIndex != index) {
UnSelectItem (SelectedIndex, true);
SelectItem (index);
}
selected_index = index;
break;
case SelectionMode.MultiSimple:
if (SelectedIndices.Contains (index))
UnSelectItem (index, true);
else
SelectItem (index);
break;
case SelectionMode.MultiExtended:
shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
if (ctrl_pressed) {
prev_selection = new int [selection.Count];
SelectedIndices.CopyTo (prev_selection, 0);
} else
ClearSelected ();
if (!shift_pressed)
anchor = index;
SelectExtended (index);
break;
case SelectionMode.None:
default:
return;
}
button_pressed = true;
FocusedItem = index;
}
private void OnMouseMoveLB (object sender, MouseEventArgs e)
{
if (!button_pressed)
return;
int index = IndexAtClientPoint (e.X, e.Y);
switch (SelectionMode) {
case SelectionMode.One:
if (index == selected_index)
return;
UnSelectItem (SelectedIndex, true);
SelectItem (index);
selected_index = index;
break;
case SelectionMode.MultiSimple:
break;
case SelectionMode.MultiExtended:
SelectExtended (index);
break;
case SelectionMode.None:
default:
return;
}
FocusedItem = index;
}
private void OnMouseUpLB (object sender, MouseEventArgs e)
{
if (!button_pressed)
return;
int index = IndexAtClientPoint (e.X, e.Y);
OnItemClick (index);
button_pressed = ctrl_pressed = shift_pressed = false;
}
private void Scroll (ScrollBar scrollbar, int delta)
{
if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
return;
int max;
if (scrollbar == hscrollbar)
max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
else
max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
int val = scrollbar.Value + delta;
if (val > max)
val = max;
else if (val < scrollbar.Minimum)
val = scrollbar.Minimum;
scrollbar.Value = val;
}
private void OnMouseWheelLB (object sender, MouseEventArgs me)
{
if (Items.Count == 0)
return;
int lines = me.Delta / 120;
if (MultiColumn)
Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
else
Scroll (vscrollbar, -lines);
}
internal override void OnPaintInternal (PaintEventArgs pevent)
{
if (suspend_layout)
return;
Draw (pevent.ClipRectangle, pevent.Graphics);
}
internal void RepositionScrollBars ()
{
if (vscrollbar.is_visible) {
vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
vscrollbar.Location = new Point (items_area.Width, 0);
}
if (hscrollbar.is_visible) {
hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
hscrollbar.Location = new Point (0, items_area.Height);
}
}
// Add an item in the Selection array and marks it visually as selected
private void SelectItem (int index)
{
if (index == -1 || SelectedIndices.Contains (index))
return;
selection.Add (Items[index]);
InvalidateItem (index);
}
// An item navigation operation (mouse or keyboard) has caused to select a new item
internal void SelectedItemFromNavigation (int index)
{
switch (SelectionMode) {
case SelectionMode.None: // Do nothing
break;
case SelectionMode.One: {
SelectedIndex = index;
break;
}
case SelectionMode.MultiSimple: {
if (SelectedIndex == -1) {
SelectedIndex = index;
} else {
if (SelectedIndices.Contains (index))
UnSelectItem (index, true);
else {
SelectItem (index);
OnSelectedIndexChanged (new EventArgs ());
OnSelectedValueChanged (new EventArgs ());
}
}
break;
}
case SelectionMode.MultiExtended: {
if (SelectedIndex == -1) {
SelectedIndex = index;
} else {
if (ctrl_pressed == false && shift_pressed == false) {
ClearSelected ();
}
if (shift_pressed == true) {
ShiftSelection (index);
} else { // ctrl_pressed or single item
SelectItem (index);
}
OnSelectedIndexChanged (new EventArgs ());
OnSelectedValueChanged (new EventArgs ());
}
break;
}
default:
break;
}
}
private void ShiftSelection (int index)
{
int shorter_item = -1, dist = Items.Count + 1, cur_dist;
foreach (int idx in selected_indices) {
if (idx > index) {
cur_dist = idx - index;
}
else {
cur_dist = index - idx;
}
if (cur_dist < dist) {
dist = cur_dist;
shorter_item = idx;
}
}
if (shorter_item != -1) {
int start, end;
if (shorter_item > index) {
start = index;
end = shorter_item;
} else {
start = shorter_item;
end = index;
}
ClearSelected ();
for (int idx = start; idx <= end; idx++) {
SelectItem (idx);
}
}
}
internal int FocusedItem {
get { return focused_item; }
set {
if (focused_item == value)
return;
int prev = focused_item;
focused_item = value;
if (has_focus == false)
return;
if (prev != -1)
InvalidateItem (prev);
if (value != -1)
InvalidateItem (value);
}
}
// Removes an item in the Selection array and marks it visually as unselected
private void UnSelectItem (int index, bool remove)
{
if (index == -1)
return;
if (remove)
selection.Remove (Items[index]);
InvalidateItem (index);
}
StringFormat string_format;
internal StringFormat StringFormat {
get {
if (string_format == null) {
string_format = new StringFormat ();
if (RightToLeft == RightToLeft.Yes)
string_format.Alignment = StringAlignment.Far;
else
string_format.Alignment = StringAlignment.Near;
if (use_tabstops)
string_format.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
}
return string_format;
}
}
internal virtual void CollectionChanged ()
{
if (sorted)
Sort ();
if (!IsHandleCreated || suspend_layout)
return;
Layout ();
if (Items.Count == 0) {
selected_index = -1;
focused_item = -1;
}
base.Refresh ();
}
private void UpdateBounds ()
{
if (requested_height == -1)
return;
SetBounds(0, 0, 0, requested_height, BoundsSpecified.Height);
}
private void UpdateScrollBars ()
{
items_area = ClientRectangle;
if (UpdateHorizontalScrollBar ()) {
items_area.Height -= hscrollbar.Height;
if (UpdateVerticalScrollBar ()) {
items_area.Width -= vscrollbar.Width;
UpdateHorizontalScrollBar ();
}
} else if (UpdateVerticalScrollBar ()) {
items_area.Width -= vscrollbar.Width;
if (UpdateHorizontalScrollBar ()) {
items_area.Height -= hscrollbar.Height;
UpdateVerticalScrollBar ();
}
}
RepositionScrollBars ();
}
/* Determines if the horizontal scrollbar has to be displyed */
private bool UpdateHorizontalScrollBar ()
{
bool show = false;
bool enabled = true;
if (MultiColumn) {
if (canvas_size.Width > items_area.Width) {
show = true;
hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
} else if (ScrollAlwaysVisible == true) {
enabled = false;
show = true;
hscrollbar.Maximum = 0;
}
} else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
show = true;
hscrollbar.Maximum = canvas_size.Width;
hscrollbar.LargeChange = items_area.Width;
}
hbar_offset = hscrollbar.Value;
hscrollbar.Enabled = enabled;
hscrollbar.Visible = show;
return show;
}
/* Determines if the vertical scrollbar has to be displyed */
private bool UpdateVerticalScrollBar ()
{
if (MultiColumn) {
vscrollbar.Visible = false;
return false;
}
bool show = false;
bool enabled = true;
if (canvas_size.Height > items_area.Height) {
show = true;
vscrollbar.Maximum = Items.Count - 1;
vscrollbar.LargeChange = items_area.Height / ItemHeight;
} else if (ScrollAlwaysVisible) {
show = true;
enabled = false;
vscrollbar.Maximum = 0;
}
vscrollbar.Enabled = enabled;
vscrollbar.Visible = show;
return show;
}
// Value Changed
private void VerticalScrollEvent (object sender, EventArgs e)
{
int top_item = top_index;
top_index = /*row_count + */ vscrollbar.Value;
last_visible_index = LastVisibleItem ();
int diff = top_item - top_index;
XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * diff, false);
}
#endregion Private Methods
[ListBindable (false)]
public class ObjectCollection : IList, ICollection, IEnumerable
{
internal class ListObjectComparer : IComparer
{
private ListBox owner;
public ListObjectComparer (ListBox owner)
{
this.owner = owner;
}
public int Compare (object a, object b)
{
string str1 = a.ToString ();
string str2 = b.ToString ();
return str1.CompareTo (str2);
}
}
private ListBox owner;
internal ArrayList object_items = new ArrayList ();
public ObjectCollection (ListBox owner)
{
this.owner = owner;
}
public ObjectCollection (ListBox owner, object[] obj)
{
this.owner = owner;
AddRange (obj);
}
public ObjectCollection (ListBox owner, ObjectCollection obj)
{
this.owner = owner;
AddRange (obj);
}
#region Public Properties
public int Count {
get { return object_items.Count; }
}
public bool IsReadOnly {
get { return false; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual object this [int index] {
get {
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException ("Index of out range");
return object_items[index];
}
set {
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException ("Index of out range");
if (value == null)
throw new ArgumentNullException ("value");
object_items[index] = value;
owner.CollectionChanged ();
}
}
bool ICollection.IsSynchronized {
get { return false; }
}
object ICollection.SyncRoot {
get { return this; }
}
bool IList.IsFixedSize {
get { return false; }
}
#endregion Public Properties
#region Public Methods
public int Add (object item)
{
int idx;
idx = AddItem (item);
owner.CollectionChanged ();
return idx;
}
public void AddRange (object[] items)
{
foreach (object mi in items)
AddItem (mi);
owner.CollectionChanged ();
}
public void AddRange (ObjectCollection col)
{
foreach (object mi in col)
AddItem (mi);
owner.CollectionChanged ();
}
internal void AddRange (IList list)
{
foreach (object mi in list)
AddItem (mi);
owner.CollectionChanged ();
}
public virtual void Clear ()
{
owner.selection.Clear ();
object_items.Clear ();
owner.CollectionChanged ();
}
public bool Contains (object obj)
{
return object_items.Contains (obj);
}
public void CopyTo (object[] dest, int arrayIndex)
{
object_items.CopyTo (dest, arrayIndex);
}
void ICollection.CopyTo (Array dest, int index)
{
object_items.CopyTo (dest, index);
}
public IEnumerator GetEnumerator ()
{
return object_items.GetEnumerator ();
}
int IList.Add (object item)
{
return Add (item);
}
public int IndexOf (object value)
{
return object_items.IndexOf (value);
}
public void Insert (int index, object item)
{
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException ("Index of out range");
owner.BeginUpdate ();
object_items.Insert (index, item);
owner.CollectionChanged ();
owner.EndUpdate ();
}
public void Remove (object value)
{
RemoveAt (IndexOf (value));
}
public void RemoveAt (int index)
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException ("Index of out range");
owner.selection.Remove (object_items [index]);
object_items.RemoveAt (index);
owner.CollectionChanged ();
}
#endregion Public Methods
#region Private Methods
internal int AddItem (object item)
{
if (item == null)
throw new ArgumentNullException ("item");
int cnt = object_items.Count;
object_items.Add (item);
return cnt;
}
internal void Sort ()
{
object_items.Sort (new ListObjectComparer (owner));
}
#endregion Private Methods
}
public class SelectedIndexCollection : IList, ICollection, IEnumerable
{
private ListBox owner;
public SelectedIndexCollection (ListBox owner)
{
this.owner = owner;
}
#region Public Properties
[Browsable (false)]
public int Count {
get { return owner.selection.Count; }
}
public bool IsReadOnly {
get { return true; }
}
public int this [int index] {
get {
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException ("Index of out range");
return owner.Items.IndexOf (owner.selection [index]);
}
}
bool ICollection.IsSynchronized {
get { return true; }
}
bool IList.IsFixedSize{
get { return true; }
}
object ICollection.SyncRoot {
get { return this; }
}
#endregion Public Properties
#region Public Methods
public bool Contains (int selectedIndex)
{
foreach (object o in owner.selection)
if (owner.Items.IndexOf (o) == selectedIndex)
return true;
return false;
}
public void CopyTo (Array dest, int index)
{
foreach (object o in owner.selection)
dest.SetValue(owner.Items.IndexOf (o), index++);
}
public IEnumerator GetEnumerator ()
{
//FIXME: write an enumerator that uses owner.selection.GetEnumerator
// so that invalidation is write on selection changes
ArrayList indices = new ArrayList ();
foreach (object o in owner.selection)
indices.Add (owner.Items.IndexOf (o));
return indices.GetEnumerator ();
}
int IList.Add (object obj)
{
throw new NotSupportedException ();
}
void IList.Clear ()
{
throw new NotSupportedException ();
}
bool IList.Contains (object selectedIndex)
{
return Contains ((int)selectedIndex);
}
int IList.IndexOf (object selectedIndex)
{
return IndexOf ((int) selectedIndex);
}
void IList.Insert (int index, object value)
{
throw new NotSupportedException ();
}
void IList.Remove (object value)
{
throw new NotSupportedException ();
}
void IList.RemoveAt (int index)
{
throw new NotSupportedException ();
}
object IList.this[int index]{
get {return owner.Items.IndexOf (owner.selection [index]); }
set {throw new NotImplementedException (); }
}
public int IndexOf (int selectedIndex)
{
for (int i = 0; i < owner.selection.Count; i++)
if (owner.Items.IndexOf (owner.selection [i]) == selectedIndex)
return i;
return -1;
}
#endregion Public Methods
}
public class SelectedObjectCollection : IList, ICollection, IEnumerable
{
private ListBox owner;
public SelectedObjectCollection (ListBox owner)
{
this.owner = owner;
}
#region Public Properties
public int Count {
get { return owner.selection.Count; }
}
public bool IsReadOnly {
get { return true; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object this [int index] {
get {
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException ("Index of out range");
return owner.selection [index];
}
set {throw new NotSupportedException ();}
}
bool ICollection.IsSynchronized {
get { return true; }
}
object ICollection.SyncRoot {
get { return this; }
}
bool IList.IsFixedSize {
get { return true; }
}
#endregion Public Properties
#region Public Methods
public bool Contains (object selectedObject)
{
return owner.selection.Contains (selectedObject);
}
public void CopyTo (Array dest, int index)
{
owner.selection.CopyTo (dest, index);
}
int IList.Add (object value)
{
throw new NotSupportedException ();
}
void IList.Clear ()
{
throw new NotSupportedException ();
}
void IList.Insert (int index, object value)
{
throw new NotSupportedException ();
}
void IList.Remove (object value)
{
throw new NotSupportedException ();
}
void IList.RemoveAt (int index)
{
throw new NotSupportedException ();
}
public int IndexOf (object item)
{
return owner.selection.IndexOf (item);
}
public IEnumerator GetEnumerator ()
{
return owner.selection.GetEnumerator ();
}
#endregion Public Methods
}
}
}