Normalize line endings.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ListControl.cs
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:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
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.
19 //
20 // Copyright (c) 2004-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jordi Mas i Hernandez, jordi@ximian.com
24 //
25 //
26
27 // COMPLETE
28
29 using System;
30 using System.Drawing;
31 using System.Collections;
32 using System.ComponentModel;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
35
36 namespace System.Windows.Forms
37 {
38 #if NET_2_0
39         [ClassInterface (ClassInterfaceType.AutoDispatch)]
40         [ComVisible (true)]
41         [LookupBindingProperties ("DataSource", "DisplayMember", "ValueMember", "SelectedValue")]
42 #endif
43         public abstract class ListControl : Control
44         {
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;
50 #if NET_2_0
51                 private IFormatProvider format_info;
52                 private string format_string = string.Empty;
53                 private bool formatting_enabled;
54 #endif
55
56                 protected ListControl ()
57                 {
58                         value_member = new BindingMemberInfo (string.Empty);
59                         display_member = string.Empty;
60                         SetStyle (ControlStyles.StandardClick | ControlStyles.UserPaint
61 #if NET_2_0
62                                 | ControlStyles.UseTextForAccessibility
63 #endif
64                                 , false);
65                 }
66
67                 #region Events
68                 static object DataSourceChangedEvent = new object ();
69                 static object DisplayMemberChangedEvent = new object ();
70 #if NET_2_0
71                 static object FormatEvent = new object ();
72                 static object FormatInfoChangedEvent = new object ();
73                 static object FormatStringChangedEvent = new object ();
74                 static object FormattingEnabledChangedEvent = new object ();
75 #endif
76                 static object SelectedValueChangedEvent = new object ();
77                 static object ValueMemberChangedEvent = new object ();
78
79                 public event EventHandler DataSourceChanged {
80                         add { Events.AddHandler (DataSourceChangedEvent, value); }
81                         remove { Events.RemoveHandler (DataSourceChangedEvent, value); }
82                 }
83
84                 public event EventHandler DisplayMemberChanged {
85                         add { Events.AddHandler (DisplayMemberChangedEvent, value); }
86                         remove { Events.RemoveHandler (DisplayMemberChangedEvent, value); }
87                 }
88
89 #if NET_2_0
90                 public event ListControlConvertEventHandler Format {
91                         add { Events.AddHandler (FormatEvent, value); }
92                         remove { Events.RemoveHandler (FormatEvent, value); }
93                 }
94
95                 [Browsable (false)]
96                 [EditorBrowsable (EditorBrowsableState.Advanced)]
97                 public event EventHandler FormatInfoChanged {
98                         add { Events.AddHandler (FormatInfoChangedEvent, value); }
99                         remove { Events.RemoveHandler (FormatInfoChangedEvent, value); }
100                 }
101                 
102                 public event EventHandler FormatStringChanged {
103                         add { Events.AddHandler (FormatStringChangedEvent, value); }
104                         remove { Events.RemoveHandler (FormatStringChangedEvent, value); }
105                 }
106                 
107                 public event EventHandler FormattingEnabledChanged {
108                         add { Events.AddHandler (FormattingEnabledChangedEvent, value); }
109                         remove { Events.RemoveHandler (FormattingEnabledChangedEvent, value); }
110                 }
111 #endif
112
113                 public event EventHandler SelectedValueChanged {
114                         add { Events.AddHandler (SelectedValueChangedEvent, value); }
115                         remove { Events.RemoveHandler (SelectedValueChangedEvent, value); }
116                 }
117
118                 public event EventHandler ValueMemberChanged {
119                         add { Events.AddHandler (ValueMemberChangedEvent, value); }
120                         remove { Events.RemoveHandler (ValueMemberChangedEvent, value); }
121                 }
122
123                 #endregion // Events
124
125                 #region .NET 2.0 Public Properties
126 #if NET_2_0
127                 [Browsable (false)]
128                 [DefaultValue (null)]
129                 [EditorBrowsable (EditorBrowsableState.Advanced)]
130                 public IFormatProvider FormatInfo {
131                         get { return format_info; }
132                         set {
133                                 if (format_info != value) {
134                                         format_info = value;
135                                         RefreshItems ();
136                                         OnFormatInfoChanged (EventArgs.Empty);
137                                 }
138                         }
139                 }
140                 
141                 [DefaultValue ("")]
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; }
146                         set {
147                                 if (format_string != value) {
148                                         format_string = value;
149                                         RefreshItems ();
150                                         OnFormatStringChanged (EventArgs.Empty);
151                                 }
152                         }
153                 }
154                 
155                 [DefaultValue (false)]
156                 public bool FormattingEnabled {
157                         get { return formatting_enabled; }
158                         set { 
159                                 if (formatting_enabled != value) {
160                                         formatting_enabled = value;
161                                         RefreshItems ();
162                                         OnFormattingEnabledChanged (EventArgs.Empty);
163                                 }
164                         }
165                 }
166 #endif
167                 #endregion
168
169                 #region Public Properties
170
171                 [DefaultValue(null)]
172                 [RefreshProperties(RefreshProperties.Repaint)]
173 #if NET_2_0
174                 [AttributeProvider (typeof (IListSource))]
175 #else
176                 [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, " + Consts.AssemblySystem_Design)]
177 #endif
178                 [MWFCategory("Data")]
179                 public object DataSource {
180                         get { return data_source; }
181                         set {
182                                 if (data_source == value)
183                                         return;
184
185                                 if (value == null)
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");
190
191                                 data_source = value;
192                                 ConnectToDataSource ();
193                                 OnDataSourceChanged (EventArgs.Empty);
194                         }
195                 }
196
197                 [DefaultValue("")]
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 {
202                         get { 
203                                 return display_member;
204                         }
205                         set {
206                                 if (value == null)
207                                         value = String.Empty;
208
209                                 if (display_member == value) {
210                                         return;
211                                 }
212
213                                 display_member = value;
214                                 ConnectToDataSource ();
215                                 OnDisplayMemberChanged (EventArgs.Empty);
216                         }
217                 }
218
219                 public abstract int SelectedIndex {
220                         get;
221                         set;
222                 }
223
224                 [Bindable(BindableSupport.Yes)]
225                 [Browsable(false)]
226                 [DefaultValue(null)]
227                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
228                 public object SelectedValue {
229                         get {
230                                 if (data_manager == null || SelectedIndex == -1)
231                                         return null;
232                                 
233                                 object item = data_manager [SelectedIndex];
234                                 object fil = FilterItemOnProperty (item, ValueMember);
235                                 return fil;
236                         }
237                         set {
238                                 if (data_manager == null)
239                                         return;
240
241                                 if (value == null)
242                                         throw new ArgumentNullException ("value");
243
244                                 PropertyDescriptorCollection col = data_manager.GetItemProperties ();
245                                 PropertyDescriptor prop = col.Find (ValueMember, true);
246
247                                 for (int i = 0; i < data_manager.Count; i++) {
248                                         if (value.Equals (prop.GetValue (data_manager [i]))) {
249                                                 SelectedIndex = i;
250                                                 return;
251                                         }
252                                 }
253                                 SelectedIndex = -1;
254                         }
255                 }
256
257                 [DefaultValue("")]
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; }
262                         set {
263                                 BindingMemberInfo new_value = new BindingMemberInfo (value);
264                                 
265                                 if (value_member.Equals (new_value))
266                                         return;
267                                 
268                                 value_member = new_value;
269                                 
270                                 if (display_member == string.Empty)
271                                         DisplayMember = value_member.BindingMember;
272
273                                 ConnectToDataSource ();
274                                 OnValueMemberChanged (EventArgs.Empty);
275                         }
276                 }
277
278 #if NET_2_0
279                 protected virtual
280 #endif
281                 bool AllowSelection {
282                         get { return true; }
283                 }
284
285                 #endregion Public Properties
286
287                 #region Private Properties
288
289                 internal override bool ScaleChildrenInternal {
290                         get { return false; }
291                 }
292
293                 #endregion Private Properties
294
295                 #region Public Methods
296
297                 protected object FilterItemOnProperty (object item)
298                 {
299                         return FilterItemOnProperty (item, string.Empty);
300                 }
301
302                 protected object FilterItemOnProperty (object item, string field)
303                 {
304                         if (item == null)
305                                 return null;
306
307                         if (field == null || field == string.Empty)
308                                 return item;
309
310                         PropertyDescriptor prop = null;
311
312                         if (data_manager != null) {
313                                 PropertyDescriptorCollection col = data_manager.GetItemProperties ();
314                                 prop = col.Find (field, true);
315                         } else {
316                                 PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (item);
317                                 prop = properties.Find (field, true);
318                         }
319                         
320                         if (prop == null)
321                                 return item;
322                         
323                         return prop.GetValue (item);
324                 }
325
326                 public string GetItemText (object item)
327                 {
328                         object o = FilterItemOnProperty (item, DisplayMember);
329                         
330                         if (o == null)
331                                 o = item;
332
333                         string retval = o.ToString ();
334                         
335 #if NET_2_0
336                         if (FormattingEnabled) {
337                                 ListControlConvertEventArgs e = new ListControlConvertEventArgs (retval, typeof (string), item);
338                                 OnFormat (e);
339                                 
340                                 // The user provided their own value
341                                 if (e.Value.ToString () != retval)
342                                         return e.Value.ToString ();
343                                         
344                                 if (o is IFormattable)
345                                         return ((IFormattable)o).ToString (string.IsNullOrEmpty (FormatString) ? null : FormatString, FormatInfo);
346                         }
347 #endif
348                                 
349                         return retval;
350                 }
351
352                 protected CurrencyManager DataManager {
353                         get { return data_manager; }
354                 }
355
356                 // Used only by ListBox to avoid to break Listbox's member signature
357                 protected override bool IsInputKey (Keys keyData)
358                 {
359                         switch (keyData) {
360                         case Keys.Up:
361                         case Keys.Down:
362                         case Keys.PageUp:
363                         case Keys.PageDown:
364                         case Keys.Right:
365                         case Keys.Left:
366                         case Keys.End:
367                         case Keys.Home:
368                         case Keys.ControlKey:
369                         case Keys.Space:
370                         case Keys.ShiftKey:
371                                 return true;
372
373                         default:
374                                 return false;
375                         }
376                 }
377
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)
382                 {
383                         base.OnBindingContextChanged (e);
384                         if (last_binding_context == BindingContext)
385                                 return;
386
387                         last_binding_context = BindingContext;
388                         ConnectToDataSource ();
389
390                         if (DataManager != null) {
391                                 SetItemsCore (DataManager.List);
392                                 if (AllowSelection)
393                                         SelectedIndex = DataManager.Position;
394                         }
395                 }
396
397                 protected virtual void OnDataSourceChanged (EventArgs e)
398                 {
399                         EventHandler eh = (EventHandler)(Events [DataSourceChangedEvent]);
400                         if (eh != null)
401                                 eh (this, e);
402                 }
403
404                 protected virtual void OnDisplayMemberChanged (EventArgs e)
405                 {
406                         EventHandler eh = (EventHandler)(Events [DisplayMemberChangedEvent]);
407                         if (eh != null)
408                                 eh (this, e);
409                 }
410
411 #if NET_2_0
412                 protected virtual void OnFormat (ListControlConvertEventArgs e)
413                 {
414                         ListControlConvertEventHandler eh = (ListControlConvertEventHandler)(Events[FormatEvent]);
415                         if (eh != null)
416                                 eh (this, e);
417                 }
418                 
419                 protected virtual void OnFormatInfoChanged (EventArgs e)
420                 {
421                         EventHandler eh = (EventHandler)(Events[FormatInfoChangedEvent]);
422                         if (eh != null)
423                                 eh (this, e);
424                 }
425                 
426                 protected virtual void OnFormatStringChanged (EventArgs e)
427                 {
428                         EventHandler eh = (EventHandler)(Events[FormatStringChangedEvent]);
429                         if (eh != null)
430                                 eh (this, e);
431                 }
432                 
433                 protected virtual void OnFormattingEnabledChanged (EventArgs e)
434                 {
435                         EventHandler eh = (EventHandler)(Events[FormattingEnabledChangedEvent]);
436                         if (eh != null)
437                                 eh (this, e);
438                 }
439 #endif
440
441                 protected virtual void OnSelectedIndexChanged (EventArgs e)
442                 {
443                         if (data_manager == null)
444                                 return;
445                         if (data_manager.Position == SelectedIndex)
446                                 return;
447                         data_manager.Position = SelectedIndex;
448                 }
449
450                 protected virtual void OnSelectedValueChanged (EventArgs e)
451                 {
452                         EventHandler eh = (EventHandler)(Events [SelectedValueChangedEvent]);
453                         if (eh != null)
454                                 eh (this, e);
455                 }
456
457                 protected virtual void OnValueMemberChanged (EventArgs e)
458                 {
459                         EventHandler eh = (EventHandler)(Events [ValueMemberChangedEvent]);
460                         if (eh != null)
461                                 eh (this, e);
462                 }
463
464                 protected abstract void RefreshItem (int index);
465                 
466 #if NET_2_0
467                 protected virtual void RefreshItems ()
468                 {
469                 }
470 #endif
471
472                 protected virtual void SetItemCore (int index,  object value)
473                 {
474                 }
475
476                 protected abstract void SetItemsCore (IList items);
477                 
478                 #endregion Public Methods
479                 
480                 #region Private Methods
481
482                 internal void BindDataItems ()
483                 {
484                         SetItemsCore (data_manager != null ? data_manager.List : new object [0]);
485                 }
486
487                 private void ConnectToDataSource ()
488                 {
489                         if (BindingContext == null)
490                                 return;
491
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);
500                                 }
501                                 if (newDataMgr != null) {
502                                         newDataMgr.PositionChanged += new EventHandler (OnPositionChanged);
503                                         newDataMgr.ItemChanged += new ItemChangedEventHandler (OnItemChanged);
504                                 }
505                                 data_manager = newDataMgr;
506                         }
507                 }
508
509                 private void OnItemChanged (object sender, ItemChangedEventArgs e)
510                 {
511                         /* if the list has changed, tell our subclass to re-bind */
512                         if (e.Index == -1)
513                                 SetItemsCore (data_manager.List);
514                         else
515                                 RefreshItem (e.Index);
516
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;
522                 }
523
524                 private void OnPositionChanged (object sender, EventArgs e)
525                 {
526                         /* For the first added item, PositionChanged is fired
527                          * _before_ ItemChanged (items not yet added), which leave us in a temporary
528                          * invalid state */
529                         if (AllowSelection && data_manager.Count > 1)
530                                 SelectedIndex = data_manager.Position;
531                 }
532
533                 #endregion Private Methods
534         }
535 }