Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

509 lines
14 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-2005 Novell, Inc.
//
// Authors:
// Jordi Mas i Hernandez, jordi@ximian.com
//
//
// COMPLETE
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
[ClassInterface (ClassInterfaceType.AutoDispatch)]
[ComVisible (true)]
[LookupBindingProperties ("DataSource", "DisplayMember", "ValueMember", "SelectedValue")]
public abstract class ListControl : Control
{
private object data_source;
private BindingMemberInfo value_member;
private string display_member;
private CurrencyManager data_manager;
private BindingContext last_binding_context;
private IFormatProvider format_info;
private string format_string = string.Empty;
private bool formatting_enabled;
protected ListControl ()
{
value_member = new BindingMemberInfo (string.Empty);
display_member = string.Empty;
SetStyle (ControlStyles.StandardClick | ControlStyles.UserPaint | ControlStyles.UseTextForAccessibility, false);
}
#region Events
static object DataSourceChangedEvent = new object ();
static object DisplayMemberChangedEvent = new object ();
static object FormatEvent = new object ();
static object FormatInfoChangedEvent = new object ();
static object FormatStringChangedEvent = new object ();
static object FormattingEnabledChangedEvent = new object ();
static object SelectedValueChangedEvent = new object ();
static object ValueMemberChangedEvent = new object ();
public event EventHandler DataSourceChanged {
add { Events.AddHandler (DataSourceChangedEvent, value); }
remove { Events.RemoveHandler (DataSourceChangedEvent, value); }
}
public event EventHandler DisplayMemberChanged {
add { Events.AddHandler (DisplayMemberChangedEvent, value); }
remove { Events.RemoveHandler (DisplayMemberChangedEvent, value); }
}
public event ListControlConvertEventHandler Format {
add { Events.AddHandler (FormatEvent, value); }
remove { Events.RemoveHandler (FormatEvent, value); }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public event EventHandler FormatInfoChanged {
add { Events.AddHandler (FormatInfoChangedEvent, value); }
remove { Events.RemoveHandler (FormatInfoChangedEvent, value); }
}
public event EventHandler FormatStringChanged {
add { Events.AddHandler (FormatStringChangedEvent, value); }
remove { Events.RemoveHandler (FormatStringChangedEvent, value); }
}
public event EventHandler FormattingEnabledChanged {
add { Events.AddHandler (FormattingEnabledChangedEvent, value); }
remove { Events.RemoveHandler (FormattingEnabledChangedEvent, value); }
}
public event EventHandler SelectedValueChanged {
add { Events.AddHandler (SelectedValueChangedEvent, value); }
remove { Events.RemoveHandler (SelectedValueChangedEvent, value); }
}
public event EventHandler ValueMemberChanged {
add { Events.AddHandler (ValueMemberChangedEvent, value); }
remove { Events.RemoveHandler (ValueMemberChangedEvent, value); }
}
#endregion // Events
#region .NET 2.0 Public Properties
[Browsable (false)]
[DefaultValue (null)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public IFormatProvider FormatInfo {
get { return format_info; }
set {
if (format_info != value) {
format_info = value;
RefreshItems ();
OnFormatInfoChanged (EventArgs.Empty);
}
}
}
[DefaultValue ("")]
[MergableProperty (false)]
[Editor ("System.Windows.Forms.Design.FormatStringEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
public string FormatString {
get { return format_string; }
set {
if (format_string != value) {
format_string = value;
RefreshItems ();
OnFormatStringChanged (EventArgs.Empty);
}
}
}
[DefaultValue (false)]
public bool FormattingEnabled {
get { return formatting_enabled; }
set {
if (formatting_enabled != value) {
formatting_enabled = value;
RefreshItems ();
OnFormattingEnabledChanged (EventArgs.Empty);
}
}
}
#endregion
#region Public Properties
[DefaultValue(null)]
[RefreshProperties(RefreshProperties.Repaint)]
[AttributeProvider (typeof (IListSource))]
[MWFCategory("Data")]
public object DataSource {
get { return data_source; }
set {
if (data_source == value)
return;
if (value == null)
display_member = String.Empty;
else if (!(value is IList || value is IListSource))
throw new Exception ("Complex DataBinding accepts as a data source " +
"either an IList or an IListSource");
data_source = value;
ConnectToDataSource ();
OnDataSourceChanged (EventArgs.Empty);
}
}
[DefaultValue("")]
[Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
[TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, " + Consts.AssemblySystem_Design)]
[MWFCategory("Data")]
public string DisplayMember {
get {
return display_member;
}
set {
if (value == null)
value = String.Empty;
if (display_member == value) {
return;
}
display_member = value;
ConnectToDataSource ();
OnDisplayMemberChanged (EventArgs.Empty);
}
}
public abstract int SelectedIndex {
get;
set;
}
[Bindable(BindableSupport.Yes)]
[Browsable(false)]
[DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object SelectedValue {
get {
if (data_manager == null || SelectedIndex == -1)
return null;
object item = data_manager [SelectedIndex];
object fil = FilterItemOnProperty (item, ValueMember);
return fil;
}
set {
if (data_manager == null)
return;
if (value == null)
throw new ArgumentNullException ("value");
PropertyDescriptorCollection col = data_manager.GetItemProperties ();
PropertyDescriptor prop = col.Find (ValueMember, true);
for (int i = 0; i < data_manager.Count; i++) {
if (value.Equals (prop.GetValue (data_manager [i]))) {
SelectedIndex = i;
return;
}
}
SelectedIndex = -1;
}
}
[DefaultValue("")]
[Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
[MWFCategory("Data")]
public string ValueMember {
get { return value_member.BindingMember; }
set {
BindingMemberInfo new_value = new BindingMemberInfo (value);
if (value_member.Equals (new_value))
return;
value_member = new_value;
if (display_member == string.Empty)
DisplayMember = value_member.BindingMember;
ConnectToDataSource ();
OnValueMemberChanged (EventArgs.Empty);
}
}
protected virtual bool AllowSelection {
get { return true; }
}
#endregion Public Properties
#region Private Properties
internal override bool ScaleChildrenInternal {
get { return false; }
}
#endregion Private Properties
#region Public Methods
protected object FilterItemOnProperty (object item)
{
return FilterItemOnProperty (item, string.Empty);
}
protected object FilterItemOnProperty (object item, string field)
{
if (item == null)
return null;
if (field == null || field == string.Empty)
return item;
PropertyDescriptor prop = null;
if (data_manager != null) {
PropertyDescriptorCollection col = data_manager.GetItemProperties ();
prop = col.Find (field, true);
} else {
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (item);
prop = properties.Find (field, true);
}
if (prop == null)
return item;
return prop.GetValue (item);
}
public string GetItemText (object item)
{
object o = FilterItemOnProperty (item, DisplayMember);
if (o == null)
o = item;
string retval = o.ToString ();
if (FormattingEnabled) {
ListControlConvertEventArgs e = new ListControlConvertEventArgs (o, typeof (string), item);
OnFormat (e);
// The user provided their own value
if (e.Value.ToString () != retval)
return e.Value.ToString ();
if (o is IFormattable)
return ((IFormattable)o).ToString (string.IsNullOrEmpty (FormatString) ? null : FormatString, FormatInfo);
}
return retval;
}
protected CurrencyManager DataManager {
get { return data_manager; }
}
// Used only by ListBox to avoid to break Listbox's member signature
protected override bool IsInputKey (Keys keyData)
{
switch (keyData) {
case Keys.Up:
case Keys.Down:
case Keys.PageUp:
case Keys.PageDown:
case Keys.Right:
case Keys.Left:
case Keys.End:
case Keys.Home:
case Keys.ControlKey:
case Keys.Space:
case Keys.ShiftKey:
return true;
default:
return false;
}
}
// Since this event is fired twice for the same binding context instance
// (when the control is added to the form and when the form is shown),
// we only take into account the first time it happens
protected override void OnBindingContextChanged (EventArgs e)
{
base.OnBindingContextChanged (e);
if (last_binding_context == BindingContext)
return;
last_binding_context = BindingContext;
ConnectToDataSource ();
if (DataManager != null) {
SetItemsCore (DataManager.List);
if (AllowSelection)
SelectedIndex = DataManager.Position;
}
}
protected virtual void OnDataSourceChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [DataSourceChangedEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnDisplayMemberChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [DisplayMemberChangedEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnFormat (ListControlConvertEventArgs e)
{
ListControlConvertEventHandler eh = (ListControlConvertEventHandler)(Events[FormatEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnFormatInfoChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events[FormatInfoChangedEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnFormatStringChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events[FormatStringChangedEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnFormattingEnabledChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events[FormattingEnabledChangedEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnSelectedIndexChanged (EventArgs e)
{
if (data_manager == null)
return;
if (data_manager.Position == SelectedIndex)
return;
data_manager.Position = SelectedIndex;
}
protected virtual void OnSelectedValueChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [SelectedValueChangedEvent]);
if (eh != null)
eh (this, e);
}
protected virtual void OnValueMemberChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [ValueMemberChangedEvent]);
if (eh != null)
eh (this, e);
}
protected abstract void RefreshItem (int index);
protected virtual void RefreshItems ()
{
}
protected virtual void SetItemCore (int index, object value)
{
}
protected abstract void SetItemsCore (IList items);
#endregion Public Methods
#region Private Methods
internal void BindDataItems ()
{
SetItemsCore (data_manager != null ? data_manager.List : new object [0]);
}
private void ConnectToDataSource ()
{
if (BindingContext == null)
return;
CurrencyManager newDataMgr = null;
if (data_source != null)
newDataMgr = (CurrencyManager) BindingContext [data_source];
if (newDataMgr != data_manager) {
if (data_manager != null) {
// Disconnect handlers from previous manager
data_manager.PositionChanged -= new EventHandler (OnPositionChanged);
data_manager.ItemChanged -= new ItemChangedEventHandler (OnItemChanged);
}
if (newDataMgr != null) {
newDataMgr.PositionChanged += new EventHandler (OnPositionChanged);
newDataMgr.ItemChanged += new ItemChangedEventHandler (OnItemChanged);
}
data_manager = newDataMgr;
}
}
private void OnItemChanged (object sender, ItemChangedEventArgs e)
{
/* if the list has changed, tell our subclass to re-bind */
if (e.Index == -1)
SetItemsCore (data_manager.List);
else
RefreshItem (e.Index);
/* For the first added item, ItemChanged is fired _after_ PositionChanged,
* so we need to set Index _only_ for that case - normally we would do that
* in PositionChanged handler */
if (AllowSelection && SelectedIndex == -1 && data_manager.Count == 1)
SelectedIndex = data_manager.Position;
}
private void OnPositionChanged (object sender, EventArgs e)
{
/* For the first added item, PositionChanged is fired
* _before_ ItemChanged (items not yet added), which leave us in a temporary
* invalid state */
if (AllowSelection && data_manager.Count > 1)
SelectedIndex = data_manager.Position;
}
#endregion Private Methods
}
}