1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc.
23 // Jordi Mas i Hernandez, jordi@ximian.com
31 using System.Collections;
32 using System.ComponentModel;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
36 namespace System.Windows.Forms
39 [ClassInterface (ClassInterfaceType.AutoDispatch)]
41 [LookupBindingProperties ("DataSource", "DisplayMember", "ValueMember", "SelectedValue")]
43 public abstract class ListControl : Control
45 private object data_source;
46 private BindingMemberInfo value_member;
47 private string display_member;
48 private CurrencyManager data_manager;
49 private BindingContext last_binding_context;
51 private IFormatProvider format_info;
52 private string format_string = string.Empty;
53 private bool formatting_enabled;
56 protected ListControl ()
58 value_member = new BindingMemberInfo (string.Empty);
59 display_member = string.Empty;
60 SetStyle (ControlStyles.StandardClick | ControlStyles.UserPaint
62 | ControlStyles.UseTextForAccessibility
68 static object DataSourceChangedEvent = new object ();
69 static object DisplayMemberChangedEvent = new object ();
71 static object FormatEvent = new object ();
72 static object FormatInfoChangedEvent = new object ();
73 static object FormatStringChangedEvent = new object ();
74 static object FormattingEnabledChangedEvent = new object ();
76 static object SelectedValueChangedEvent = new object ();
77 static object ValueMemberChangedEvent = new object ();
79 public event EventHandler DataSourceChanged {
80 add { Events.AddHandler (DataSourceChangedEvent, value); }
81 remove { Events.RemoveHandler (DataSourceChangedEvent, value); }
84 public event EventHandler DisplayMemberChanged {
85 add { Events.AddHandler (DisplayMemberChangedEvent, value); }
86 remove { Events.RemoveHandler (DisplayMemberChangedEvent, value); }
90 public event ListControlConvertEventHandler Format {
91 add { Events.AddHandler (FormatEvent, value); }
92 remove { Events.RemoveHandler (FormatEvent, value); }
96 [EditorBrowsable (EditorBrowsableState.Advanced)]
97 public event EventHandler FormatInfoChanged {
98 add { Events.AddHandler (FormatInfoChangedEvent, value); }
99 remove { Events.RemoveHandler (FormatInfoChangedEvent, value); }
102 public event EventHandler FormatStringChanged {
103 add { Events.AddHandler (FormatStringChangedEvent, value); }
104 remove { Events.RemoveHandler (FormatStringChangedEvent, value); }
107 public event EventHandler FormattingEnabledChanged {
108 add { Events.AddHandler (FormattingEnabledChangedEvent, value); }
109 remove { Events.RemoveHandler (FormattingEnabledChangedEvent, value); }
113 public event EventHandler SelectedValueChanged {
114 add { Events.AddHandler (SelectedValueChangedEvent, value); }
115 remove { Events.RemoveHandler (SelectedValueChangedEvent, value); }
118 public event EventHandler ValueMemberChanged {
119 add { Events.AddHandler (ValueMemberChangedEvent, value); }
120 remove { Events.RemoveHandler (ValueMemberChangedEvent, value); }
125 #region .NET 2.0 Public Properties
128 [DefaultValue (null)]
129 [EditorBrowsable (EditorBrowsableState.Advanced)]
130 public IFormatProvider FormatInfo {
131 get { return format_info; }
133 if (format_info != value) {
136 OnFormatInfoChanged (EventArgs.Empty);
142 [MergableProperty (false)]
143 [Editor ("System.Windows.Forms.Design.FormatStringEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
144 public string FormatString {
145 get { return format_string; }
147 if (format_string != value) {
148 format_string = value;
150 OnFormatStringChanged (EventArgs.Empty);
155 [DefaultValue (false)]
156 public bool FormattingEnabled {
157 get { return formatting_enabled; }
159 if (formatting_enabled != value) {
160 formatting_enabled = value;
162 OnFormattingEnabledChanged (EventArgs.Empty);
169 #region Public Properties
172 [RefreshProperties(RefreshProperties.Repaint)]
174 [AttributeProvider (typeof (IListSource))]
176 [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, " + Consts.AssemblySystem_Design)]
178 [MWFCategory("Data")]
179 public object DataSource {
180 get { return data_source; }
182 if (data_source == value)
186 display_member = String.Empty;
187 else if (!(value is IList || value is IListSource))
188 throw new Exception ("Complex DataBinding accepts as a data source " +
189 "either an IList or an IListSource");
192 ConnectToDataSource ();
193 OnDataSourceChanged (EventArgs.Empty);
198 [Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
199 [TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, " + Consts.AssemblySystem_Design)]
200 [MWFCategory("Data")]
201 public string DisplayMember {
203 return display_member;
207 value = String.Empty;
209 if (display_member == value) {
213 display_member = value;
214 ConnectToDataSource ();
215 OnDisplayMemberChanged (EventArgs.Empty);
219 public abstract int SelectedIndex {
224 [Bindable(BindableSupport.Yes)]
227 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
228 public object SelectedValue {
230 if (data_manager == null || SelectedIndex == -1)
233 object item = data_manager [SelectedIndex];
234 object fil = FilterItemOnProperty (item, ValueMember);
238 if (data_manager == null)
242 throw new ArgumentNullException ("value");
244 PropertyDescriptorCollection col = data_manager.GetItemProperties ();
245 PropertyDescriptor prop = col.Find (ValueMember, true);
247 for (int i = 0; i < data_manager.Count; i++) {
248 if (value.Equals (prop.GetValue (data_manager [i]))) {
258 [Editor("System.Windows.Forms.Design.DataMemberFieldEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
259 [MWFCategory("Data")]
260 public string ValueMember {
261 get { return value_member.BindingMember; }
263 BindingMemberInfo new_value = new BindingMemberInfo (value);
265 if (value_member.Equals (new_value))
268 value_member = new_value;
270 if (display_member == string.Empty)
271 DisplayMember = value_member.BindingMember;
273 ConnectToDataSource ();
274 OnValueMemberChanged (EventArgs.Empty);
281 bool AllowSelection {
285 #endregion Public Properties
287 #region Private Properties
289 internal override bool ScaleChildrenInternal {
290 get { return false; }
293 #endregion Private Properties
295 #region Public Methods
297 protected object FilterItemOnProperty (object item)
299 return FilterItemOnProperty (item, string.Empty);
302 protected object FilterItemOnProperty (object item, string field)
307 if (field == null || field == string.Empty)
310 PropertyDescriptor prop = null;
312 if (data_manager != null) {
313 PropertyDescriptorCollection col = data_manager.GetItemProperties ();
314 prop = col.Find (field, true);
316 PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (item);
317 prop = properties.Find (field, true);
323 return prop.GetValue (item);
326 public string GetItemText (object item)
328 object o = FilterItemOnProperty (item, DisplayMember);
333 string retval = o.ToString ();
336 if (FormattingEnabled) {
337 ListControlConvertEventArgs e = new ListControlConvertEventArgs (retval, typeof (string), item);
340 // The user provided their own value
341 if (e.Value.ToString () != retval)
342 return e.Value.ToString ();
344 if (o is IFormattable)
345 return ((IFormattable)o).ToString (string.IsNullOrEmpty (FormatString) ? null : FormatString, FormatInfo);
352 protected CurrencyManager DataManager {
353 get { return data_manager; }
356 // Used only by ListBox to avoid to break Listbox's member signature
357 protected override bool IsInputKey (Keys keyData)
368 case Keys.ControlKey:
378 // Since this event is fired twice for the same binding context instance
379 // (when the control is added to the form and when the form is shown),
380 // we only take into account the first time it happens
381 protected override void OnBindingContextChanged (EventArgs e)
383 base.OnBindingContextChanged (e);
384 if (last_binding_context == BindingContext)
387 last_binding_context = BindingContext;
388 ConnectToDataSource ();
390 if (DataManager != null) {
391 SetItemsCore (DataManager.List);
393 SelectedIndex = DataManager.Position;
397 protected virtual void OnDataSourceChanged (EventArgs e)
399 EventHandler eh = (EventHandler)(Events [DataSourceChangedEvent]);
404 protected virtual void OnDisplayMemberChanged (EventArgs e)
406 EventHandler eh = (EventHandler)(Events [DisplayMemberChangedEvent]);
412 protected virtual void OnFormat (ListControlConvertEventArgs e)
414 ListControlConvertEventHandler eh = (ListControlConvertEventHandler)(Events[FormatEvent]);
419 protected virtual void OnFormatInfoChanged (EventArgs e)
421 EventHandler eh = (EventHandler)(Events[FormatInfoChangedEvent]);
426 protected virtual void OnFormatStringChanged (EventArgs e)
428 EventHandler eh = (EventHandler)(Events[FormatStringChangedEvent]);
433 protected virtual void OnFormattingEnabledChanged (EventArgs e)
435 EventHandler eh = (EventHandler)(Events[FormattingEnabledChangedEvent]);
441 protected virtual void OnSelectedIndexChanged (EventArgs e)
443 if (data_manager == null)
445 if (data_manager.Position == SelectedIndex)
447 data_manager.Position = SelectedIndex;
450 protected virtual void OnSelectedValueChanged (EventArgs e)
452 EventHandler eh = (EventHandler)(Events [SelectedValueChangedEvent]);
457 protected virtual void OnValueMemberChanged (EventArgs e)
459 EventHandler eh = (EventHandler)(Events [ValueMemberChangedEvent]);
464 protected abstract void RefreshItem (int index);
467 protected virtual void RefreshItems ()
472 protected virtual void SetItemCore (int index, object value)
476 protected abstract void SetItemsCore (IList items);
478 #endregion Public Methods
480 #region Private Methods
482 internal void BindDataItems ()
484 SetItemsCore (data_manager != null ? data_manager.List : new object [0]);
487 private void ConnectToDataSource ()
489 if (BindingContext == null)
492 CurrencyManager newDataMgr = null;
493 if (data_source != null)
494 newDataMgr = (CurrencyManager) BindingContext [data_source];
495 if (newDataMgr != data_manager) {
496 if (data_manager != null) {
497 // Disconnect handlers from previous manager
498 data_manager.PositionChanged -= new EventHandler (OnPositionChanged);
499 data_manager.ItemChanged -= new ItemChangedEventHandler (OnItemChanged);
501 if (newDataMgr != null) {
502 newDataMgr.PositionChanged += new EventHandler (OnPositionChanged);
503 newDataMgr.ItemChanged += new ItemChangedEventHandler (OnItemChanged);
505 data_manager = newDataMgr;
509 private void OnItemChanged (object sender, ItemChangedEventArgs e)
511 /* if the list has changed, tell our subclass to re-bind */
513 SetItemsCore (data_manager.List);
515 RefreshItem (e.Index);
517 /* For the first added item, ItemChanged is fired _after_ PositionChanged,
518 * so we need to set Index _only_ for that case - normally we would do that
519 * in PositionChanged handler */
520 if (AllowSelection && SelectedIndex == -1 && data_manager.Count == 1)
521 SelectedIndex = data_manager.Position;
524 private void OnPositionChanged (object sender, EventArgs e)
526 /* For the first added item, PositionChanged is fired
527 * _before_ ItemChanged (items not yet added), which leave us in a temporary
529 if (AllowSelection && data_manager.Count > 1)
530 SelectedIndex = data_manager.Position;
533 #endregion Private Methods