a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
3093 lines
72 KiB
C#
3093 lines
72 KiB
C#
/// 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.Globalization;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Collections.Generic;
|
|
|
|
namespace System.Windows.Forms
|
|
{
|
|
[DefaultProperty("Items")]
|
|
[DefaultEvent("SelectedIndexChanged")]
|
|
[Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
|
|
[DefaultBindingProperty ("SelectedValue")]
|
|
[ClassInterface (ClassInterfaceType.AutoDispatch)]
|
|
[ComVisible (true)]
|
|
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;
|
|
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 SelectedIndexCollection selected_indices;
|
|
private SelectedObjectCollection selected_items;
|
|
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 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;
|
|
private IntegerCollection custom_tab_offsets;
|
|
private Padding padding;
|
|
private bool use_custom_tab_offsets;
|
|
|
|
public ListBox ()
|
|
{
|
|
items = CreateItemCollection ();
|
|
selected_indices = new SelectedIndexCollection (this);
|
|
selected_items = new SelectedObjectCollection (this);
|
|
|
|
requested_height = bounds.Height;
|
|
InternalBorderStyle = BorderStyle.Fixed3D;
|
|
BackColor = ThemeEngine.Current.ColorWindow;
|
|
|
|
/* 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);
|
|
|
|
Controls.AddImplicit (vscrollbar);
|
|
Controls.AddImplicit (hscrollbar);
|
|
|
|
/* Events */
|
|
MouseDown += new MouseEventHandler (OnMouseDownLB);
|
|
MouseMove += new MouseEventHandler (OnMouseMoveLB);
|
|
MouseUp += new MouseEventHandler (OnMouseUpLB);
|
|
MouseWheel += new MouseEventHandler (OnMouseWheelLB);
|
|
KeyUp += new KeyEventHandler (OnKeyUpLB);
|
|
GotFocus += new EventHandler (OnGotFocus);
|
|
LostFocus += new EventHandler (OnLostFocus);
|
|
|
|
SetStyle (ControlStyles.UserPaint, false);
|
|
|
|
custom_tab_offsets = new IntegerCollection (this);
|
|
}
|
|
|
|
#region Events
|
|
static object DrawItemEvent = new object ();
|
|
static object MeasureItemEvent = new object ();
|
|
static object SelectedIndexChangedEvent = new object ();
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public new event EventHandler BackgroundImageChanged {
|
|
add { base.BackgroundImageChanged += value; }
|
|
remove { base.BackgroundImageChanged -= value; }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public new event EventHandler BackgroundImageLayoutChanged {
|
|
add { base.BackgroundImageLayoutChanged += value; }
|
|
remove { base.BackgroundImageLayoutChanged -= value; }
|
|
}
|
|
|
|
[Browsable (true)]
|
|
[EditorBrowsable (EditorBrowsableState.Always)]
|
|
public new event EventHandler Click {
|
|
add { base.Click += value; }
|
|
remove { base.Click -= value; }
|
|
}
|
|
|
|
public event DrawItemEventHandler DrawItem {
|
|
add { Events.AddHandler (DrawItemEvent, value); }
|
|
remove { Events.RemoveHandler (DrawItemEvent, value); }
|
|
}
|
|
|
|
public event MeasureItemEventHandler MeasureItem {
|
|
add { Events.AddHandler (MeasureItemEvent, value); }
|
|
remove { Events.RemoveHandler (MeasureItemEvent, value); }
|
|
}
|
|
|
|
[Browsable (true)]
|
|
[EditorBrowsable (EditorBrowsableState.Always)]
|
|
public new event MouseEventHandler MouseClick {
|
|
add { base.MouseClick += value; }
|
|
remove { base.MouseClick -= value; }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public new event EventHandler PaddingChanged {
|
|
add { base.PaddingChanged += value; }
|
|
remove { base.PaddingChanged -= value; }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public new event PaintEventHandler Paint {
|
|
add { base.Paint += value; }
|
|
remove { base.Paint -= value; }
|
|
}
|
|
|
|
public event EventHandler SelectedIndexChanged {
|
|
add { Events.AddHandler (SelectedIndexChangedEvent, value); }
|
|
remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
|
public new event EventHandler TextChanged {
|
|
add { base.TextChanged += value; }
|
|
remove { base.TextChanged -= value; }
|
|
}
|
|
#endregion // Events
|
|
|
|
#region UIA Framework Events
|
|
//NOTE:
|
|
// We are using Reflection to add/remove internal events.
|
|
// Class ListProvider uses the events.
|
|
//
|
|
//Event used to generate UIA Selection Pattern
|
|
static object UIASelectionModeChangedEvent = new object ();
|
|
|
|
internal event EventHandler UIASelectionModeChanged {
|
|
add { Events.AddHandler (UIASelectionModeChangedEvent, value); }
|
|
remove { Events.RemoveHandler (UIASelectionModeChangedEvent, value); }
|
|
}
|
|
|
|
internal void OnUIASelectionModeChangedEvent ()
|
|
{
|
|
EventHandler eh = (EventHandler) Events [UIASelectionModeChangedEvent];
|
|
if (eh != null)
|
|
eh (this, EventArgs.Empty);
|
|
}
|
|
|
|
static object UIAFocusedItemChangedEvent = new object ();
|
|
|
|
internal event EventHandler UIAFocusedItemChanged {
|
|
add { Events.AddHandler (UIAFocusedItemChangedEvent, value); }
|
|
remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); }
|
|
}
|
|
|
|
internal void OnUIAFocusedItemChangedEvent ()
|
|
{
|
|
EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent];
|
|
if (eh != null)
|
|
eh (this, EventArgs.Empty);
|
|
}
|
|
#endregion UIA Framework 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 ();
|
|
}
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public override ImageLayout BackgroundImageLayout {
|
|
get { return base.BackgroundImageLayout; }
|
|
set { base.BackgroundImageLayout = value; }
|
|
}
|
|
|
|
[DefaultValue (BorderStyle.Fixed3D)]
|
|
[DispId(-504)]
|
|
public BorderStyle BorderStyle {
|
|
get { return InternalBorderStyle; }
|
|
set {
|
|
InternalBorderStyle = value;
|
|
UpdateListBoxBounds ();
|
|
}
|
|
}
|
|
|
|
[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;}
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
|
|
public IntegerCollection CustomTabOffsets {
|
|
get { return custom_tab_offsets; }
|
|
}
|
|
|
|
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;
|
|
|
|
if (Parent != null)
|
|
Parent.PerformLayout (this, "DrawMode");
|
|
base.Refresh ();
|
|
}
|
|
}
|
|
|
|
public override Font Font {
|
|
get { return base.Font; }
|
|
set { base.Font = value; }
|
|
}
|
|
|
|
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;
|
|
UpdateListBoxBounds ();
|
|
}
|
|
}
|
|
|
|
[DefaultValue (13)]
|
|
[Localizable (true)]
|
|
[RefreshProperties(RefreshProperties.Repaint)]
|
|
public virtual int ItemHeight {
|
|
get {
|
|
if (item_height == -1) {
|
|
SizeF sz = TextRenderer.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)
|
|
UpdateListBoxBounds ();
|
|
LayoutListBox ();
|
|
}
|
|
}
|
|
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
|
|
[Localizable (true)]
|
|
[Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
|
|
[MergableProperty (false)]
|
|
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;
|
|
LayoutListBox ();
|
|
Invalidate ();
|
|
}
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
public new Padding Padding {
|
|
get { return padding; }
|
|
set { padding = value; }
|
|
}
|
|
|
|
[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 {
|
|
if (selected_indices == null)
|
|
return -1;
|
|
|
|
return selected_indices.Count > 0 ? selected_indices [0] : -1;
|
|
}
|
|
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 (value == -1)
|
|
selected_indices.Clear ();
|
|
else
|
|
selected_indices.Add (value);
|
|
}
|
|
}
|
|
|
|
[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:
|
|
SelectedIndices.Clear ();
|
|
break;
|
|
|
|
case SelectionMode.One:
|
|
// FIXME: Probably this can be improved
|
|
ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
|
|
for (int i = 1; i < old_selection.Count; i++)
|
|
SelectedIndices.Remove ((int)old_selection [i]);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// UIA Framework: Generates SelectionModeChanged event.
|
|
OnUIASelectionModeChangedEvent ();
|
|
}
|
|
}
|
|
|
|
[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;
|
|
|
|
int page_size = (items_area.Height / ItemHeight);
|
|
|
|
if (Items.Count < page_size)
|
|
value = 0;
|
|
else if (!multicolumn)
|
|
top_index = Math.Min (value, Items.Count - page_size);
|
|
else
|
|
top_index = value;
|
|
|
|
UpdateTopItem ();
|
|
base.Refresh ();
|
|
}
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[DefaultValue (false)]
|
|
public bool UseCustomTabOffsets {
|
|
get { return use_custom_tab_offsets; }
|
|
set {
|
|
if (use_custom_tab_offsets != value) {
|
|
use_custom_tab_offsets = value;
|
|
CalculateTabStops ();
|
|
}
|
|
}
|
|
}
|
|
|
|
[DefaultValue (true)]
|
|
public bool UseTabStops {
|
|
get { return use_tabstops; }
|
|
set {
|
|
if (use_tabstops == value)
|
|
return;
|
|
|
|
use_tabstops = value;
|
|
CalculateTabStops ();
|
|
}
|
|
}
|
|
|
|
protected override bool AllowSelection {
|
|
get {
|
|
return SelectionMode != SelectionMode.None;
|
|
}
|
|
}
|
|
#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 UIA Framework Properties
|
|
|
|
internal ScrollBar UIAHScrollBar {
|
|
get { return hscrollbar; }
|
|
}
|
|
|
|
internal ScrollBar UIAVScrollBar {
|
|
get { return vscrollbar; }
|
|
}
|
|
|
|
#endregion UIA Framework Properties
|
|
|
|
#region Public Methods
|
|
[Obsolete ("this method has been deprecated")]
|
|
protected virtual void AddItemsCore (object[] value)
|
|
{
|
|
Items.AddRange (value);
|
|
}
|
|
|
|
public void BeginUpdate ()
|
|
{
|
|
suspend_layout = true;
|
|
}
|
|
|
|
public void ClearSelected ()
|
|
{
|
|
selected_indices.Clear ();
|
|
}
|
|
|
|
protected virtual ObjectCollection CreateItemCollection ()
|
|
{
|
|
return new ObjectCollection (this);
|
|
}
|
|
|
|
public void EndUpdate ()
|
|
{
|
|
suspend_layout = false;
|
|
LayoutListBox ();
|
|
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)
|
|
throw new ArgumentOutOfRangeException ("Index of out range");
|
|
|
|
startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
|
|
|
|
int i = startIndex;
|
|
while (true) {
|
|
string text = GetItemText (Items [i]);
|
|
if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
|
|
CompareOptions.IgnoreCase))
|
|
return i;
|
|
|
|
i = (i == Items.Count - 1) ? 0 : i + 1;
|
|
if (i == startIndex)
|
|
break;
|
|
}
|
|
|
|
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)
|
|
throw new ArgumentOutOfRangeException ("Index of out range");
|
|
|
|
startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
|
|
|
|
int i = startIndex;
|
|
while (true) {
|
|
if (String.Compare (GetItemText (Items[i]), s, true) == 0)
|
|
return i;
|
|
|
|
i = (i + 1 == Items.Count) ? 0 : i + 1;
|
|
if (i == startIndex)
|
|
break;
|
|
}
|
|
|
|
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;
|
|
int y = index;
|
|
if (y < 0) // We convert it to valid positive value
|
|
y += RowCount * (top_index / RowCount);
|
|
rect.Y = (y % RowCount) * ItemHeight;
|
|
rect.X = (col - (top_index / RowCount)) * 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);
|
|
}
|
|
}
|
|
|
|
if (this is CheckedListBox)
|
|
rect.Width += 15;
|
|
|
|
return rect;
|
|
}
|
|
|
|
[EditorBrowsable (EditorBrowsableState.Advanced)]
|
|
protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
|
|
{
|
|
bounds.Height = requested_height;
|
|
|
|
return base.GetScaledBounds (bounds, factor, specified);
|
|
}
|
|
|
|
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:
|
|
DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
|
|
if (eh != null)
|
|
eh (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 = TextRenderer.MeasureString ("The quick brown Fox", Font);
|
|
item_height = (int) sz.Height;
|
|
if (IntegralHeight)
|
|
UpdateListBoxBounds ();
|
|
LayoutListBox ();
|
|
}
|
|
}
|
|
|
|
protected override void OnHandleCreated (EventArgs e)
|
|
{
|
|
base.OnHandleCreated (e);
|
|
|
|
if (IntegralHeight)
|
|
UpdateListBoxBounds ();
|
|
|
|
LayoutListBox ();
|
|
EnsureVisible (focused_item);
|
|
}
|
|
|
|
protected override void OnHandleDestroyed (EventArgs e)
|
|
{
|
|
base.OnHandleDestroyed (e);
|
|
}
|
|
|
|
protected virtual void OnMeasureItem (MeasureItemEventArgs e)
|
|
{
|
|
if (draw_mode != DrawMode.OwnerDrawVariable)
|
|
return;
|
|
|
|
MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
|
|
if (eh != null)
|
|
eh (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)
|
|
LayoutListBox ();
|
|
|
|
Invalidate ();
|
|
}
|
|
|
|
protected override void OnSelectedIndexChanged (EventArgs e)
|
|
{
|
|
base.OnSelectedIndexChanged (e);
|
|
|
|
EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
|
|
if (eh != null)
|
|
eh (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 RefreshItems ()
|
|
{
|
|
for (int i = 0; i < Items.Count; i++) {
|
|
RefreshItem (i);
|
|
}
|
|
|
|
LayoutListBox ();
|
|
Refresh ();
|
|
}
|
|
|
|
public override void ResetBackColor ()
|
|
{
|
|
base.ResetBackColor ();
|
|
}
|
|
|
|
public override void ResetForeColor ()
|
|
{
|
|
base.ResetForeColor ();
|
|
}
|
|
|
|
protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
|
|
{
|
|
base.ScaleControl (factor, specified);
|
|
}
|
|
|
|
private int SnapHeightToIntegral (int height)
|
|
{
|
|
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);
|
|
|
|
return height;
|
|
}
|
|
|
|
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 && IsHandleCreated)
|
|
height = SnapHeightToIntegral (height);
|
|
|
|
base.SetBoundsCore (x, y, width, height, specified);
|
|
UpdateScrollBars ();
|
|
last_visible_index = LastVisibleItem ();
|
|
}
|
|
|
|
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.AddItems (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)
|
|
SelectedIndices.Add (index);
|
|
else
|
|
SelectedIndices.Remove (index);
|
|
}
|
|
|
|
protected virtual void Sort ()
|
|
{
|
|
Sort (true);
|
|
}
|
|
|
|
//
|
|
// Sometimes we could need to Sort, and request a Refresh
|
|
// in a different place, to not have the painting done twice
|
|
//
|
|
void Sort (bool paint)
|
|
{
|
|
if (Items.Count == 0)
|
|
return;
|
|
|
|
Items.Sort ();
|
|
|
|
if (paint)
|
|
base.Refresh ();
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return base.ToString ();
|
|
}
|
|
|
|
protected virtual void WmReflectCommand (ref Message m)
|
|
{
|
|
}
|
|
|
|
protected override void WndProc (ref Message m)
|
|
{
|
|
if ((Msg)m.Msg == Msg.WM_KEYDOWN) {
|
|
if (ProcessKeyMessage (ref m))
|
|
m.Result = IntPtr.Zero;
|
|
else {
|
|
HandleKeyDown ((Keys)m.WParam.ToInt32 ());
|
|
DefWndProc (ref m);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
base.WndProc (ref m);
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Private Methods
|
|
|
|
private void CalculateTabStops ()
|
|
{
|
|
if (use_tabstops) {
|
|
if (use_custom_tab_offsets) {
|
|
float[] f = new float[custom_tab_offsets.Count];
|
|
custom_tab_offsets.CopyTo (f, 0);
|
|
StringFormat.SetTabStops (0, f);
|
|
}
|
|
else
|
|
StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
|
|
} else
|
|
StringFormat.SetTabStops (0, new float[0]);
|
|
|
|
this.Invalidate ();
|
|
}
|
|
|
|
private Size canvas_size;
|
|
|
|
private void LayoutListBox ()
|
|
{
|
|
if (!IsHandleCreated || suspend_layout)
|
|
return;
|
|
|
|
if (MultiColumn)
|
|
LayoutMultiColumn ();
|
|
else
|
|
LayoutSingleColumn ();
|
|
|
|
last_visible_index = LastVisibleItem ();
|
|
UpdateScrollBars ();
|
|
}
|
|
|
|
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 = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
|
|
int t = (int)sz.Width;
|
|
|
|
if (this is CheckedListBox)
|
|
t += 15;
|
|
|
|
if (t > width)
|
|
width = t;
|
|
}
|
|
break;
|
|
}
|
|
|
|
canvas_size = new Size (width, height);
|
|
}
|
|
|
|
private void LayoutMultiColumn ()
|
|
{
|
|
int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
|
|
row_count = Math.Max (1, usable_height / ItemHeight);
|
|
|
|
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 = Math.Max (1, 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;
|
|
}
|
|
|
|
Color fore_color = !Enabled ? ThemeEngine.Current.ColorGrayText :
|
|
(state & DrawItemState.Selected) != 0 ? ThemeEngine.Current.ColorHighlightText : ForeColor;
|
|
OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, fore_color, 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;
|
|
|
|
// Subtract the checkboxes from the width
|
|
if (this is CheckedListBox)
|
|
item_rect.Width -= 14;
|
|
|
|
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;
|
|
|
|
if (IsHandleCreated) {
|
|
XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
|
|
|
|
// Invalidate the previous selection border, to keep it properly updated.
|
|
Rectangle selection_border_area = new Rectangle (items_area.Width - (hbar_offset - old_offset) - 3, 0,
|
|
3, items_area.Height);
|
|
Invalidate (selection_border_area);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
internal override bool IsInputCharInternal (char charCode)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
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 {
|
|
if (top_index > vscrollbar.Maximum)
|
|
vscrollbar.Value = vscrollbar.Maximum;
|
|
else
|
|
vscrollbar.Value = top_index;
|
|
Scroll (vscrollbar, vscrollbar.Value - top_index);
|
|
}
|
|
}
|
|
|
|
// 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 (SelectedIndex - RowCount < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (SelectedIndex - RowCount < top_index) {
|
|
top_index = SelectedIndex - RowCount;
|
|
UpdateTopItem ();
|
|
}
|
|
|
|
selected_index = SelectedIndex - RowCount;
|
|
break;
|
|
}
|
|
|
|
case ItemNavigation.NextColumn: {
|
|
if (SelectedIndex + RowCount >= Items.Count) {
|
|
break;
|
|
}
|
|
|
|
if (SelectedIndex + RowCount > last_visible_index) {
|
|
top_index = SelectedIndex;
|
|
UpdateTopItem ();
|
|
}
|
|
|
|
selected_index = SelectedIndex + RowCount;
|
|
break;
|
|
}
|
|
|
|
case ItemNavigation.First: {
|
|
top_index = 0;
|
|
selected_index = 0;
|
|
UpdateTopItem ();
|
|
break;
|
|
}
|
|
|
|
case ItemNavigation.Last: {
|
|
|
|
int rows = items_area.Height / ItemHeight;
|
|
|
|
if (multicolumn) {
|
|
selected_index = Items.Count - 1;
|
|
break;
|
|
}
|
|
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;
|
|
|
|
if (multicolumn) {
|
|
selected_index = FocusedItem + 1;
|
|
break;
|
|
}
|
|
|
|
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 ();
|
|
selected_index = 0;
|
|
}
|
|
else {
|
|
if (SelectedIndex - (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)
|
|
{
|
|
if (Items.Count == 0)
|
|
return;
|
|
|
|
if (FocusedItem == -1)
|
|
FocusedItem = 0;
|
|
|
|
InvalidateItem (FocusedItem);
|
|
}
|
|
|
|
private void OnLostFocus (object sender, EventArgs e)
|
|
{
|
|
if (FocusedItem != -1)
|
|
InvalidateItem (FocusedItem);
|
|
}
|
|
|
|
private bool KeySearch (Keys key)
|
|
{
|
|
char c = (char) key;
|
|
if (!Char.IsLetterOrDigit (c))
|
|
return false;
|
|
|
|
int idx = FindString (c.ToString (), SelectedIndex);
|
|
if (idx != ListBox.NoMatches)
|
|
SelectedIndex = idx;
|
|
|
|
return true;
|
|
}
|
|
|
|
internal void HandleKeyDown (Keys key)
|
|
{
|
|
int new_item = -1;
|
|
|
|
if (Items.Count == 0)
|
|
return;
|
|
|
|
if (KeySearch (key))
|
|
return;
|
|
|
|
switch (key) {
|
|
|
|
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)
|
|
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)
|
|
{
|
|
if (!IsHandleCreated)
|
|
return;
|
|
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;
|
|
Point button_pressed_loc = new Point (-1, -1);
|
|
|
|
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 (!new_selection.Contains (i))
|
|
new_selection.Add (i);
|
|
|
|
// Need to make a copy since we can't enumerate and modify the collection
|
|
// at the same time
|
|
ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
|
|
foreach (int i in sel_indices)
|
|
if (!new_selection.Contains (i))
|
|
selected_indices.Remove (i);
|
|
|
|
foreach (int i in new_selection)
|
|
if (!sel_indices.Contains (i))
|
|
selected_indices.AddCore (i);
|
|
ResumeLayout ();
|
|
}
|
|
|
|
private void OnMouseDownLB (object sender, MouseEventArgs e)
|
|
{
|
|
// Only do stuff with the left mouse button
|
|
if ((e.Button & MouseButtons.Left) == 0)
|
|
return;
|
|
|
|
int index = IndexAtClientPoint (e.X, e.Y);
|
|
if (index == -1)
|
|
return;
|
|
|
|
switch (SelectionMode) {
|
|
case SelectionMode.One:
|
|
SelectedIndices.AddCore (index); // Unselects previous one
|
|
break;
|
|
|
|
case SelectionMode.MultiSimple:
|
|
if (SelectedIndices.Contains (index))
|
|
SelectedIndices.RemoveCore (index);
|
|
else
|
|
SelectedIndices.AddCore (index);
|
|
break;
|
|
|
|
case SelectionMode.MultiExtended:
|
|
shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
|
|
ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
|
|
|
|
if (shift_pressed) {
|
|
SelectedIndices.ClearCore ();
|
|
SelectExtended (index);
|
|
break;
|
|
}
|
|
|
|
anchor = index;
|
|
|
|
if (ctrl_pressed) {
|
|
prev_selection = new int [SelectedIndices.Count];
|
|
SelectedIndices.CopyTo (prev_selection, 0);
|
|
|
|
if (SelectedIndices.Contains (index))
|
|
SelectedIndices.RemoveCore (index);
|
|
else
|
|
SelectedIndices.AddCore (index);
|
|
|
|
break;
|
|
}
|
|
|
|
SelectedIndices.ClearCore ();
|
|
SelectedIndices.AddCore (index);
|
|
break;
|
|
|
|
case SelectionMode.None:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
button_pressed = true;
|
|
button_pressed_loc = new Point (e.X, e.Y);
|
|
FocusedItem = index;
|
|
}
|
|
|
|
private void OnMouseMoveLB (object sender, MouseEventArgs e)
|
|
{
|
|
// Don't take into account MouseMove events generated with MouseDown
|
|
if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y))
|
|
return;
|
|
|
|
int index = IndexAtClientPoint (e.X, e.Y);
|
|
if (index == -1)
|
|
return;
|
|
|
|
switch (SelectionMode) {
|
|
case SelectionMode.One:
|
|
SelectedIndices.AddCore (index); // Unselects previous one
|
|
break;
|
|
|
|
case SelectionMode.MultiSimple:
|
|
break;
|
|
|
|
case SelectionMode.MultiExtended:
|
|
SelectExtended (index);
|
|
break;
|
|
|
|
case SelectionMode.None:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
FocusedItem = index;
|
|
}
|
|
|
|
internal override void OnDragDropEnd (DragDropEffects effects)
|
|
{
|
|
// In the case of a DnD operation (started on MouseDown)
|
|
// there will be no MouseUp event, so we need to reset
|
|
// the state here
|
|
button_pressed = false;
|
|
}
|
|
|
|
private void OnMouseUpLB (object sender, MouseEventArgs e)
|
|
{
|
|
// Only do stuff with the left mouse button
|
|
if ((e.Button & MouseButtons.Left) == 0)
|
|
return;
|
|
|
|
if (e.Clicks > 1) {
|
|
OnDoubleClick (EventArgs.Empty);
|
|
OnMouseDoubleClick (e);
|
|
}
|
|
else if (e.Clicks == 1) {
|
|
OnClick (EventArgs.Empty);
|
|
OnMouseClick (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);
|
|
}
|
|
}
|
|
|
|
// An item navigation operation (mouse or keyboard) has caused to select a new item
|
|
internal void SelectedItemFromNavigation (int index)
|
|
{
|
|
switch (SelectionMode) {
|
|
case SelectionMode.None:
|
|
// .Net doesn't select the item, only ensures that it's visible
|
|
// and fires the selection related events
|
|
EnsureVisible (index);
|
|
OnSelectedIndexChanged (EventArgs.Empty);
|
|
OnSelectedValueChanged (EventArgs.Empty);
|
|
break;
|
|
case SelectionMode.One: {
|
|
SelectedIndex = index;
|
|
break;
|
|
}
|
|
case SelectionMode.MultiSimple: {
|
|
if (SelectedIndex == -1) {
|
|
SelectedIndex = index;
|
|
} else {
|
|
|
|
if (SelectedIndices.Contains (index))
|
|
SelectedIndices.Remove (index);
|
|
else {
|
|
SelectedIndices.AddCore (index);
|
|
|
|
OnSelectedIndexChanged (EventArgs.Empty);
|
|
OnSelectedValueChanged (EventArgs.Empty);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SelectionMode.MultiExtended: {
|
|
if (SelectedIndex == -1) {
|
|
SelectedIndex = index;
|
|
} else {
|
|
|
|
if (ctrl_pressed == false && shift_pressed == false) {
|
|
SelectedIndices.Clear ();
|
|
}
|
|
|
|
if (shift_pressed == true) {
|
|
ShiftSelection (index);
|
|
} else { // ctrl_pressed or single item
|
|
SelectedIndices.AddCore (index);
|
|
|
|
}
|
|
|
|
OnSelectedIndexChanged (EventArgs.Empty);
|
|
OnSelectedValueChanged (EventArgs.Empty);
|
|
}
|
|
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;
|
|
}
|
|
|
|
selected_indices.Clear ();
|
|
for (int idx = start; idx <= end; idx++) {
|
|
selected_indices.AddCore (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);
|
|
|
|
// UIA Framework: Generates FocusedItemChanged event.
|
|
OnUIAFocusedItemChangedEvent ();
|
|
}
|
|
}
|
|
|
|
StringFormat string_format;
|
|
internal StringFormat StringFormat {
|
|
get {
|
|
if (string_format == null) {
|
|
string_format = new StringFormat ();
|
|
string_format.FormatFlags = StringFormatFlags.NoWrap;
|
|
|
|
if (RightToLeft == RightToLeft.Yes)
|
|
string_format.Alignment = StringAlignment.Far;
|
|
else
|
|
string_format.Alignment = StringAlignment.Near;
|
|
CalculateTabStops ();
|
|
}
|
|
return string_format;
|
|
}
|
|
}
|
|
|
|
internal virtual void CollectionChanged ()
|
|
{
|
|
if (sorted)
|
|
Sort (false);
|
|
|
|
if (Items.Count == 0) {
|
|
selected_indices.List.Clear ();
|
|
focused_item = -1;
|
|
top_index = 0;
|
|
}
|
|
if (Items.Count <= focused_item)
|
|
focused_item = Items.Count - 1;
|
|
|
|
if (!IsHandleCreated || suspend_layout)
|
|
return;
|
|
|
|
LayoutListBox ();
|
|
|
|
base.Refresh ();
|
|
}
|
|
|
|
void EnsureVisible (int index)
|
|
{
|
|
if (!IsHandleCreated || index == -1)
|
|
return;
|
|
|
|
if (index < top_index) {
|
|
top_index = index;
|
|
UpdateTopItem ();
|
|
Invalidate ();
|
|
} else if (!multicolumn) {
|
|
int rows = items_area.Height / ItemHeight;
|
|
if (index >= (top_index + rows))
|
|
top_index = index - rows + 1;
|
|
|
|
UpdateTopItem ();
|
|
} else {
|
|
int rows = Math.Max (1, items_area.Height / ItemHeight);
|
|
int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
|
|
|
|
if (index >= (top_index + (rows * cols))) {
|
|
int incolumn = index / rows;
|
|
top_index = (incolumn - (cols - 1)) * rows;
|
|
|
|
UpdateTopItem ();
|
|
Invalidate ();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateListBoxBounds ()
|
|
{
|
|
if (IsHandleCreated)
|
|
SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
|
|
}
|
|
|
|
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 = Math.Max (0, items_area.Width);
|
|
} else if (scroll_always_visible && horizontal_scrollbar) {
|
|
show = true;
|
|
enabled = false;
|
|
hscrollbar.Maximum = 0;
|
|
}
|
|
|
|
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 || (Items.Count == 0 && !scroll_always_visible)) {
|
|
vscrollbar.Visible = false;
|
|
return false;
|
|
} else if (Items.Count == 0) {
|
|
vscrollbar.Visible = true;
|
|
vscrollbar.Enabled = false;
|
|
vscrollbar.Maximum = 0;
|
|
return true;
|
|
}
|
|
|
|
bool show = false;
|
|
bool enabled = true;
|
|
if (canvas_size.Height > items_area.Height) {
|
|
show = true;
|
|
vscrollbar.Maximum = Items.Count - 1;
|
|
vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
|
|
} 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 delta = (top_item - top_index) * ItemHeight;
|
|
if (DrawMode == DrawMode.OwnerDrawVariable) {
|
|
delta = 0;
|
|
|
|
if (top_index < top_item)
|
|
for (int i = top_index; i < top_item; i++)
|
|
delta += GetItemHeight (i);
|
|
else
|
|
for (int i = top_item; i < top_index; i++)
|
|
delta -= GetItemHeight (i);
|
|
}
|
|
|
|
if (IsHandleCreated)
|
|
XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
|
|
}
|
|
|
|
#endregion Private Methods
|
|
|
|
public class IntegerCollection : IList, ICollection, IEnumerable
|
|
{
|
|
private ListBox owner;
|
|
private List<int> list;
|
|
|
|
#region Public Constructor
|
|
public IntegerCollection (ListBox owner)
|
|
{
|
|
this.owner = owner;
|
|
list = new List<int> ();
|
|
}
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
[Browsable (false)]
|
|
public int Count {
|
|
get { return list.Count; }
|
|
}
|
|
|
|
public int this [int index] {
|
|
get { return list[index]; }
|
|
set { list[index] = value; owner.CalculateTabStops (); }
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
public int Add (int item)
|
|
{
|
|
// This collection does not allow duplicates
|
|
if (!list.Contains (item)) {
|
|
list.Add (item);
|
|
list.Sort ();
|
|
owner.CalculateTabStops ();
|
|
}
|
|
|
|
return list.IndexOf (item);
|
|
}
|
|
|
|
public void AddRange (int[] items)
|
|
{
|
|
AddItems (items);
|
|
}
|
|
|
|
public void AddRange (IntegerCollection value)
|
|
{
|
|
AddItems (value);
|
|
}
|
|
|
|
void AddItems (IList items)
|
|
{
|
|
if (items == null)
|
|
throw new ArgumentNullException ("items");
|
|
|
|
foreach (int i in items)
|
|
if (!list.Contains (i))
|
|
list.Add (i);
|
|
|
|
list.Sort ();
|
|
}
|
|
|
|
public void Clear ()
|
|
{
|
|
list.Clear ();
|
|
owner.CalculateTabStops ();
|
|
}
|
|
|
|
public bool Contains (int item)
|
|
{
|
|
return list.Contains (item);
|
|
}
|
|
|
|
public void CopyTo (Array destination, int index)
|
|
{
|
|
for (int i = 0; i < list.Count; i++)
|
|
destination.SetValue (list[i], index++);
|
|
}
|
|
|
|
public int IndexOf (int item)
|
|
{
|
|
return list.IndexOf (item);
|
|
}
|
|
|
|
public void Remove (int item)
|
|
{
|
|
list.Remove (item);
|
|
list.Sort ();
|
|
owner.CalculateTabStops ();
|
|
}
|
|
|
|
public void RemoveAt (int index)
|
|
{
|
|
if (index < 0)
|
|
throw new IndexOutOfRangeException ();
|
|
|
|
list.RemoveAt (index);
|
|
list.Sort ();
|
|
owner.CalculateTabStops ();
|
|
}
|
|
#endregion
|
|
|
|
#region IEnumerable Members
|
|
IEnumerator IEnumerable.GetEnumerator ()
|
|
{
|
|
return list.GetEnumerator ();
|
|
}
|
|
#endregion
|
|
|
|
#region IList Members
|
|
int IList.Add (object item)
|
|
{
|
|
int? intValue = item as int?;
|
|
if (!intValue.HasValue)
|
|
throw new ArgumentException ("item");
|
|
return Add (intValue.Value);
|
|
}
|
|
|
|
void IList.Clear ()
|
|
{
|
|
Clear ();
|
|
}
|
|
|
|
bool IList.Contains (object item)
|
|
{
|
|
int? intValue = item as int?;
|
|
if (!intValue.HasValue)
|
|
return false;
|
|
return Contains (intValue.Value);
|
|
}
|
|
|
|
int IList.IndexOf (object item)
|
|
{
|
|
int? intValue = item as int?;
|
|
if (!intValue.HasValue)
|
|
return -1;
|
|
return IndexOf (intValue.Value);
|
|
}
|
|
|
|
void IList.Insert (int index, object value)
|
|
{
|
|
throw new NotSupportedException (string.Format (
|
|
CultureInfo.InvariantCulture, "No items "
|
|
+ "can be inserted into {0}, since it is"
|
|
+ " a sorted collection.", this.GetType ()));
|
|
}
|
|
|
|
bool IList.IsFixedSize
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
bool IList.IsReadOnly
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
void IList.Remove (object value)
|
|
{
|
|
int? intValue = value as int?;
|
|
if (!intValue.HasValue)
|
|
throw new ArgumentException ("value");
|
|
|
|
Remove (intValue.Value);
|
|
}
|
|
|
|
void IList.RemoveAt (int index)
|
|
{
|
|
RemoveAt (index);
|
|
}
|
|
|
|
object IList.this[int index] {
|
|
get { return this[index]; }
|
|
set { this[index] = (int)value; }
|
|
}
|
|
#endregion
|
|
|
|
#region ICollection Members
|
|
bool ICollection.IsSynchronized {
|
|
get { return true; }
|
|
}
|
|
|
|
object ICollection.SyncRoot {
|
|
get { return this; }
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
[ListBindable (false)]
|
|
public class ObjectCollection : IList, ICollection, IEnumerable
|
|
{
|
|
internal class ListObjectComparer : IComparer
|
|
{
|
|
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 ();
|
|
|
|
#region UIA Framework Events
|
|
//NOTE:
|
|
// We are using Reflection to add/remove internal events.
|
|
// Class ListProvider uses the events.
|
|
//
|
|
//Event used to generate UIA StructureChangedEvent
|
|
static object UIACollectionChangedEvent = new object ();
|
|
|
|
internal event CollectionChangeEventHandler UIACollectionChanged {
|
|
add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
|
|
remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
|
|
}
|
|
|
|
internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
|
|
{
|
|
CollectionChangeEventHandler eh
|
|
= (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
|
|
if (eh != null)
|
|
eh (owner, args);
|
|
}
|
|
#endregion UIA Framework Events
|
|
|
|
public ObjectCollection (ListBox owner)
|
|
{
|
|
this.owner = owner;
|
|
}
|
|
|
|
public ObjectCollection (ListBox owner, object[] value)
|
|
{
|
|
this.owner = owner;
|
|
AddRange (value);
|
|
}
|
|
|
|
public ObjectCollection (ListBox owner, ObjectCollection value)
|
|
{
|
|
this.owner = owner;
|
|
AddRange (value);
|
|
}
|
|
|
|
#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");
|
|
|
|
//UIA Framework event: Item Removed
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index]));
|
|
|
|
object_items[index] = value;
|
|
|
|
//UIA Framework event: Item Added
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, 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;
|
|
object[] selectedItems = null;
|
|
|
|
// we need to remember the original selected items so that we can update the indices
|
|
if (owner.sorted) {
|
|
selectedItems = new object[owner.SelectedItems.Count];
|
|
owner.SelectedItems.CopyTo (selectedItems, 0);
|
|
}
|
|
|
|
idx = AddItem (item);
|
|
owner.CollectionChanged ();
|
|
|
|
// If we are sorted, the item probably moved indexes, get the real one
|
|
if (owner.sorted) {
|
|
// update indices of selected items
|
|
owner.SelectedIndices.Clear ();
|
|
for (int i = 0; i < selectedItems.Length; i++) {
|
|
owner.SelectedIndex = this.IndexOf (selectedItems [i]);
|
|
}
|
|
return this.IndexOf (item);
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
public void AddRange (object[] items)
|
|
{
|
|
AddItems (items);
|
|
}
|
|
|
|
public void AddRange (ObjectCollection value)
|
|
{
|
|
AddItems (value);
|
|
}
|
|
|
|
internal void AddItems (IList items)
|
|
{
|
|
if (items == null)
|
|
throw new ArgumentNullException ("items");
|
|
|
|
foreach (object mi in items)
|
|
AddItem (mi);
|
|
|
|
owner.CollectionChanged ();
|
|
}
|
|
|
|
public virtual void Clear ()
|
|
{
|
|
owner.selected_indices.ClearCore ();
|
|
object_items.Clear ();
|
|
owner.CollectionChanged ();
|
|
|
|
//UIA Framework event: Items list cleared
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
|
|
}
|
|
|
|
public bool Contains (object value)
|
|
{
|
|
if (value == null)
|
|
throw new ArgumentNullException ("value");
|
|
|
|
return object_items.Contains (value);
|
|
}
|
|
|
|
public void CopyTo (object[] destination, int arrayIndex)
|
|
{
|
|
object_items.CopyTo (destination, arrayIndex);
|
|
}
|
|
|
|
void ICollection.CopyTo (Array destination, int index)
|
|
{
|
|
object_items.CopyTo (destination, index);
|
|
}
|
|
|
|
public IEnumerator GetEnumerator ()
|
|
{
|
|
return object_items.GetEnumerator ();
|
|
}
|
|
|
|
int IList.Add (object item)
|
|
{
|
|
return Add (item);
|
|
}
|
|
|
|
public int IndexOf (object value)
|
|
{
|
|
if (value == null)
|
|
throw new ArgumentNullException ("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");
|
|
if (item == null)
|
|
throw new ArgumentNullException ("item");
|
|
|
|
owner.BeginUpdate ();
|
|
object_items.Insert (index, item);
|
|
owner.CollectionChanged ();
|
|
owner.EndUpdate ();
|
|
|
|
//UIA Framework event: Item Added
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
|
|
}
|
|
|
|
public void Remove (object value)
|
|
{
|
|
if (value == null)
|
|
return;
|
|
|
|
int index = IndexOf (value);
|
|
if (index != -1)
|
|
RemoveAt (index);
|
|
}
|
|
|
|
public void RemoveAt (int index)
|
|
{
|
|
if (index < 0 || index >= Count)
|
|
throw new ArgumentOutOfRangeException ("Index of out range");
|
|
|
|
//UIA Framework element removed
|
|
object removed = object_items [index];
|
|
UpdateSelection (index);
|
|
object_items.RemoveAt (index);
|
|
owner.CollectionChanged ();
|
|
|
|
//UIA Framework event: Item Removed
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed));
|
|
}
|
|
#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);
|
|
|
|
//UIA Framework event: Item Added
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
|
|
|
|
return cnt;
|
|
}
|
|
|
|
// we receive the index to be removed
|
|
void UpdateSelection (int removed_index)
|
|
{
|
|
owner.selected_indices.Remove (removed_index);
|
|
|
|
if (owner.selection_mode != SelectionMode.None) {
|
|
int last_idx = object_items.Count - 1;
|
|
|
|
// if the last item was selected, remove it from selection,
|
|
// since it will become invalid after the removal
|
|
if (owner.selected_indices.Contains (last_idx)) {
|
|
owner.selected_indices.Remove (last_idx);
|
|
|
|
// in SelectionMode.One try to put the selection on the new last item
|
|
int new_idx = last_idx - 1;
|
|
if (owner.selection_mode == SelectionMode.One && new_idx > -1)
|
|
owner.selected_indices.Add (new_idx);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
internal void Sort ()
|
|
{
|
|
object_items.Sort (new ListObjectComparer ());
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
|
|
public class SelectedIndexCollection : IList, ICollection, IEnumerable
|
|
{
|
|
private ListBox owner;
|
|
ArrayList selection;
|
|
bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
|
|
|
|
#region UIA Framework Events
|
|
|
|
//NOTE:
|
|
// We are using Reflection to add/remove internal events.
|
|
// Class ListProvider uses the events.
|
|
//
|
|
//Event used to generate UIA StructureChangedEvent
|
|
static object UIACollectionChangedEvent = new object ();
|
|
|
|
internal event CollectionChangeEventHandler UIACollectionChanged {
|
|
add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
|
|
remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
|
|
}
|
|
|
|
internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
|
|
{
|
|
CollectionChangeEventHandler eh
|
|
= (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
|
|
if (eh != null)
|
|
eh (owner, args);
|
|
}
|
|
|
|
#endregion UIA Framework Events
|
|
|
|
|
|
public SelectedIndexCollection (ListBox owner)
|
|
{
|
|
this.owner = owner;
|
|
selection = new ArrayList ();
|
|
}
|
|
|
|
#region Public Properties
|
|
[Browsable (false)]
|
|
public int Count {
|
|
get { return 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");
|
|
|
|
CheckSorted ();
|
|
return (int)selection [index];
|
|
}
|
|
}
|
|
|
|
bool ICollection.IsSynchronized {
|
|
get { return true; }
|
|
}
|
|
|
|
bool IList.IsFixedSize{
|
|
get { return true; }
|
|
}
|
|
|
|
object ICollection.SyncRoot {
|
|
get { return selection; }
|
|
}
|
|
|
|
#endregion Public Properties
|
|
|
|
#region Public Methods
|
|
public void Add (int index)
|
|
{
|
|
if (AddCore (index)) {
|
|
owner.OnSelectedIndexChanged (EventArgs.Empty);
|
|
owner.OnSelectedValueChanged (EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
// Need to separate selection logic from events,
|
|
// since selection changes using keys/mouse handle them their own way
|
|
internal bool AddCore (int index)
|
|
{
|
|
if (selection.Contains (index))
|
|
return false;
|
|
|
|
if (index == -1) // Weird MS behaviour
|
|
return false;
|
|
if (index < -1 || index >= owner.Items.Count)
|
|
throw new ArgumentOutOfRangeException ("index");
|
|
if (owner.selection_mode == SelectionMode.None)
|
|
throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
|
|
|
|
if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
|
|
RemoveCore ((int)selection [0]);
|
|
|
|
selection.Add (index);
|
|
sorting_needed = true;
|
|
owner.EnsureVisible (index);
|
|
owner.FocusedItem = index;
|
|
owner.InvalidateItem (index);
|
|
|
|
// UIA Framework event: Selected item added
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Clear ()
|
|
{
|
|
if (ClearCore ()) {
|
|
owner.OnSelectedIndexChanged (EventArgs.Empty);
|
|
owner.OnSelectedValueChanged (EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
internal bool ClearCore ()
|
|
{
|
|
if (selection.Count == 0)
|
|
return false;
|
|
|
|
foreach (int index in selection)
|
|
owner.InvalidateItem (index);
|
|
|
|
selection.Clear ();
|
|
|
|
// UIA Framework event: Selected items list updated
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool Contains (int selectedIndex)
|
|
{
|
|
foreach (int index in selection)
|
|
if (index == selectedIndex)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
public void CopyTo (Array destination, int index)
|
|
{
|
|
CheckSorted ();
|
|
selection.CopyTo (destination, index);
|
|
}
|
|
|
|
public IEnumerator GetEnumerator ()
|
|
{
|
|
CheckSorted ();
|
|
return selection.GetEnumerator ();
|
|
}
|
|
|
|
// FIXME: Probably we can avoid sorting when calling
|
|
// IndexOf (imagine a scenario where multiple removal of items
|
|
// happens)
|
|
public void Remove (int index)
|
|
{
|
|
// Separate logic from events here too
|
|
if (RemoveCore (index)) {
|
|
owner.OnSelectedIndexChanged (EventArgs.Empty);
|
|
owner.OnSelectedValueChanged (EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
internal bool RemoveCore (int index)
|
|
{
|
|
int idx = IndexOf (index);
|
|
if (idx == -1)
|
|
return false;
|
|
|
|
selection.RemoveAt (idx);
|
|
owner.InvalidateItem (index);
|
|
|
|
// UIA Framework event: Selected item removed from selection
|
|
OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
|
|
|
|
return true;
|
|
}
|
|
|
|
int IList.Add (object value)
|
|
{
|
|
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 this [index]; }
|
|
set {throw new NotImplementedException (); }
|
|
}
|
|
|
|
public int IndexOf (int selectedIndex)
|
|
{
|
|
CheckSorted ();
|
|
|
|
for (int i = 0; i < selection.Count; i++)
|
|
if ((int)selection [i] == selectedIndex)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
#endregion Public Methods
|
|
internal ArrayList List {
|
|
get {
|
|
CheckSorted ();
|
|
return selection;
|
|
}
|
|
}
|
|
|
|
void CheckSorted ()
|
|
{
|
|
if (sorting_needed) {
|
|
sorting_needed = false;
|
|
selection.Sort ();
|
|
}
|
|
}
|
|
}
|
|
|
|
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.selected_indices.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.items [owner.selected_indices [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 void Add (object value)
|
|
{
|
|
if (owner.selection_mode == SelectionMode.None)
|
|
throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
|
|
|
|
int idx = owner.items.IndexOf (value);
|
|
if (idx == -1)
|
|
return;
|
|
|
|
owner.selected_indices.Add (idx);
|
|
}
|
|
|
|
public void Clear ()
|
|
{
|
|
owner.selected_indices.Clear ();
|
|
}
|
|
|
|
public bool Contains (object selectedObject)
|
|
{
|
|
int idx = owner.items.IndexOf (selectedObject);
|
|
return idx == -1 ? false : owner.selected_indices.Contains (idx);
|
|
}
|
|
|
|
public void CopyTo (Array destination, int index)
|
|
{
|
|
for (int i = 0; i < Count; i++)
|
|
destination.SetValue (this [i], index++);
|
|
}
|
|
|
|
public void Remove (object value)
|
|
{
|
|
if (value == null)
|
|
return;
|
|
|
|
int idx = owner.items.IndexOf (value);
|
|
if (idx == -1)
|
|
return;
|
|
|
|
owner.selected_indices.Remove (idx);
|
|
}
|
|
|
|
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 selectedObject)
|
|
{
|
|
int idx = owner.items.IndexOf (selectedObject);
|
|
return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
|
|
}
|
|
|
|
public IEnumerator GetEnumerator ()
|
|
{
|
|
//FIXME: write an enumerator that uses selection.GetEnumerator
|
|
// so that invalidation is write on selection changes
|
|
object [] items = new object [Count];
|
|
for (int i = 0; i < Count; i++) {
|
|
items [i] = owner.items [owner.selected_indices [i]];
|
|
}
|
|
|
|
return items.GetEnumerator ();
|
|
}
|
|
|
|
#endregion Public Methods
|
|
}
|
|
}
|
|
}
|