* TabControl.cs: Show the tooltip depending on the value
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Binding.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 //      Peter Bartok    pbartok@novell.com
24 //      Jackson Harper  jackson@ximian.com
25 //
26
27
28 using System.ComponentModel;
29
30 namespace System.Windows.Forms {
31
32         [TypeConverter (typeof (ListBindingConverter))]
33         public class Binding {
34
35                 private string property_name;
36                 private object data_source;
37                 private string data_member;
38
39                 private bool is_binding;
40                 private bool checked_isnull;
41
42                 private BindingMemberInfo binding_member_info;
43 #if NET_2_0
44                 private IBindableComponent control;
45 #else
46                 private Control control;
47 #endif
48
49                 private BindingManagerBase manager;
50                 private PropertyDescriptor control_property;
51                 private PropertyDescriptor is_null_desc;
52
53                 private object data;
54                 private Type data_type;
55
56 #if NET_2_0
57                 private DataSourceUpdateMode datasource_update_mode;
58                 private ControlUpdateMode control_update_mode;
59                 private object datasource_null_value = Convert.DBNull;
60                 private object null_value;
61                 private IFormatProvider format_info;
62                 private string format_string;
63                 private bool formatting_enabled;
64 #endif
65                 #region Public Constructors
66 #if NET_2_0
67                 public Binding (string propertyName, object dataSource, string dataMember) 
68                         : this (propertyName, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
69                 {
70                 }
71                 
72                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled)
73                         : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
74                 {
75                 }
76                 
77                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)
78                         : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null)
79                 {
80                 }
81                 
82                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue)
83                         : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, string.Empty, null)
84                 {
85                 }
86
87                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString)
88                         : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null)
89                 {
90                 }
91
92                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)
93                 {
94                         property_name = propertyName;
95                         data_source = dataSource;
96                         data_member = dataMember;
97                         binding_member_info = new BindingMemberInfo (dataMember);
98                         datasource_update_mode = dataSourceUpdateMode;
99                         null_value = nullValue;
100                         format_string = formatString;
101                         format_info = formatInfo;
102                 }
103 #else
104                 public Binding (string propertyName, object dataSource, string dataMember)
105                 {
106                         property_name = propertyName;
107                         data_source = dataSource;
108                         data_member = dataMember;
109                         binding_member_info = new BindingMemberInfo (dataMember);
110                 }               
111 #endif
112                 #endregion      // Public Constructors
113
114                 #region Public Instance Properties
115 #if NET_2_0
116                 [DefaultValue (null)]
117                 public IBindableComponent BindableComponent {
118                         get {
119                                 return control;
120                         }
121                 }
122 #endif
123                 public BindingManagerBase BindingManagerBase {
124                         get {
125                                 return manager;
126                         }
127                 }
128
129                 public BindingMemberInfo BindingMemberInfo {
130                         get {
131                                 return binding_member_info;
132                         }
133                 }
134
135                 [DefaultValue (null)]
136                 public Control Control {
137                         get {
138 #if NET_2_0
139                                 return control as Control;
140 #else
141                                 return control;
142 #endif
143                         }
144                 }
145
146 #if NET_2_0
147                 [DefaultValue (ControlUpdateMode.OnPropertyChanged)]
148                 public ControlUpdateMode ControlUpdateMode {
149                         get {
150                                 return control_update_mode;
151                         }
152                         set {
153                                 control_update_mode = value;
154                         }
155                 }
156 #endif
157
158                 public object DataSource {
159                         get {
160                                 return data_source;
161                         }
162                 }
163
164 #if NET_2_0
165                 [DefaultValue (DataSourceUpdateMode.OnValidation)]
166                 public DataSourceUpdateMode DataSourceUpdateMode {
167                         get {
168                                 return datasource_update_mode;
169                         }
170                         set {
171                                 datasource_update_mode = value;
172                         }
173                 }
174
175                 public object DataSourceNullValue {
176                         get {
177                                 return datasource_null_value;
178                         }
179                         set {
180                                 datasource_null_value = value;
181                         }
182                 }
183
184                 [DefaultValue (false)]
185                 public bool FormattingEnabled {
186                         get {
187                                 return formatting_enabled;
188                         }
189                         set {
190                                 if (formatting_enabled == value)
191                                         return;
192
193                                 formatting_enabled = value;
194                                 PushData ();
195                         }
196                 }
197
198                 [DefaultValue (null)]
199                 public IFormatProvider FormatInfo {
200                         get {
201                                 return format_info;
202                         }
203                         set {
204                                 if (value == format_info)
205                                         return;
206
207                                 format_info = value;
208                                 if (formatting_enabled)
209                                         PushData ();
210                         }
211                 }
212
213                 public string FormatString {
214                         get {
215                                 return format_string;
216                         }
217                         set {
218                                 if (value == null)
219                                         value = String.Empty;
220                                 if (value == format_string)
221                                         return;
222
223                                 format_string = value;
224                                 if (formatting_enabled)
225                                         PushData ();
226                         }
227                 }
228 #endif
229
230                 public bool IsBinding {
231                         get {
232                                 if (manager == null || manager.IsSuspended)
233                                         return false;
234
235                                 return is_binding;
236                         }
237                 }
238
239 #if NET_2_0
240                 public object NullValue {
241                         get {
242                                 return null_value;
243                         }
244                         set {
245                                 if (value == null_value)
246                                         return;
247
248                                 null_value = value;
249                                 if (formatting_enabled)
250                                         PushData ();
251                         }
252                 }
253 #endif
254
255                 [DefaultValue ("")]
256                 public string PropertyName {
257                         get {
258                                 return property_name;
259                         }
260                 }
261                 #endregion      // Public Instance Properties
262
263 #if NET_2_0
264                 public void ReadValue ()
265                 {
266                         PushData (true);
267                 }
268
269                 public void WriteValue ()
270                 {
271                         PullData (true);
272                 }
273 #endif
274
275                 #region Protected Instance Methods
276 #if NET_2_0
277                 protected virtual void OnBindingComplete (BindingCompleteEventArgs e)
278                 {
279                         if (BindingComplete != null)
280                                 BindingComplete (this, e);
281                 }
282 #endif
283
284                 protected virtual void OnFormat (ConvertEventArgs cevent)
285                 {
286                         if (Format!=null)
287                                 Format (this, cevent);
288                 }
289
290                 protected virtual void OnParse (ConvertEventArgs cevent)
291                 {
292                         if (Parse!=null)
293                                 Parse (this, cevent);
294                 }
295                 #endregion      // Protected Instance Methods
296
297                 internal string DataMember {
298                         get { return data_member; }
299                 }
300                 
301 #if NET_2_0
302                 internal void SetControl (IBindableComponent control)
303 #else
304                 internal void SetControl (Control control)
305 #endif
306                 {
307                         if (control == this.control)
308                                 return;
309
310                         control_property = TypeDescriptor.GetProperties (control).Find (property_name, true);                   
311
312                         if (control_property == null)
313                                 throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' on target control."));
314                         if (control_property.IsReadOnly)
315                                 throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' because it is read only."));
316                                 
317                         data_type = control_property.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached
318
319                         Control ctrl = control as Control;
320                         if (ctrl != null) {
321                                 ctrl.Validating += new CancelEventHandler (ControlValidatingHandler);
322                                 if (!ctrl.IsHandleCreated)
323                                         ctrl.HandleCreated += new EventHandler (ControlCreatedHandler);
324                         }
325
326 #if NET_2_0
327                         EventDescriptor prop_changed_event = GetPropertyChangedEvent (control, property_name);
328                         if (prop_changed_event != null)
329                                 prop_changed_event.AddEventHandler (control, new EventHandler (ControlPropertyChangedHandler));
330 #endif
331                         this.control = control;
332                         UpdateIsBinding ();
333                 }
334
335                 internal void Check ()
336                 {
337                         if (control == null || control.BindingContext == null)
338                                 return;
339
340                         if (manager == null) {
341                                 manager = control.BindingContext [data_source, binding_member_info.BindingPath];
342
343                                 if (manager.Position > -1 && binding_member_info.BindingField != String.Empty &&
344                                         TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true) == null)
345                                         throw new ArgumentException ("Cannot bind to property '" + binding_member_info.BindingField + "' on DataSource.", 
346                                                         "dataMember");
347
348                                 manager.AddBinding (this);
349                                 manager.PositionChanged += new EventHandler (PositionChangedHandler);
350
351                                 if (manager is PropertyManager) { // Match .net, which only watchs simple objects
352                                         EventDescriptor prop_changed_event = GetPropertyChangedEvent (manager.Current, binding_member_info.BindingField);
353                                         if (prop_changed_event != null)
354                                                 prop_changed_event.AddEventHandler (manager.Current, new EventHandler (SourcePropertyChangedHandler));
355                                 }
356                         }
357
358                         if (manager.Position == -1)
359                                 return;
360
361                         if (!checked_isnull) {
362                                 is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false);
363                                 checked_isnull = true;
364                         }
365
366                         PushData ();
367                 }
368
369                 internal bool PullData ()
370                 {
371                         return PullData (false);
372                 }
373
374                 // Return false ONLY in case of error - and return true even in cases
375                 // where no update was possible
376                 bool PullData (bool force)
377                 {
378                         if (IsBinding == false || manager.Current == null)
379                                 return true;
380 #if NET_2_0
381                         if (!force && datasource_update_mode == DataSourceUpdateMode.Never)
382                                 return true;
383 #endif
384
385                         data = control_property.GetValue (control);
386 #if NET_2_0
387                         if (data == null)
388                                 data = datasource_null_value;
389 #endif
390
391                         try {
392                                 SetPropertyValue (data);
393                         } catch (Exception e) {
394 #if NET_2_0
395                                 if (formatting_enabled) {
396                                         FireBindingComplete (BindingCompleteContext.DataSourceUpdate, e, e.Message);
397                                         return false;
398                                 }
399 #endif
400                                 throw e;
401                         }
402
403 #if NET_2_0
404                         if (formatting_enabled)
405                                 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null);
406 #endif
407                         return true;
408                 }
409
410                 internal void PushData ()
411                 {
412                         PushData (false);
413                 }
414
415                 void PushData (bool force)
416                 {
417                         if (manager == null || manager.IsSuspended || manager.Count == 0 || manager.Position == -1)
418                                 return;
419 #if NET_2_0
420                         if (!force && control_update_mode == ControlUpdateMode.Never)
421                                 return;
422 #endif
423
424                         if (is_null_desc != null) {
425                                 bool is_null = (bool) is_null_desc.GetValue (manager.Current);
426                                 if (is_null) {
427                                         data = Convert.DBNull;
428                                         return;
429                                 }
430                         }
431
432                         PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
433                         if (pd == null) {
434                                 data = manager.Current;
435                         } else {
436                                 data = pd.GetValue (manager.Current);
437                         }
438
439 #if NET_2_0
440                         if ((data == null || data == DBNull.Value) && null_value != null)
441                                 data = null_value;
442 #endif
443
444                         try {
445                                 data = FormatData (data);
446                                 SetControlValue (data);
447                         } catch (Exception e) {
448 #if NET_2_0
449                                 if (formatting_enabled) {
450                                         FireBindingComplete (BindingCompleteContext.ControlUpdate, e, e.Message);
451                                         return;
452                                 }
453 #endif
454                                 throw e;
455                         }
456
457 #if NET_2_0
458                         if (formatting_enabled)
459                                 FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null);
460 #endif
461                 }
462
463                 internal void UpdateIsBinding ()
464                 {
465                         is_binding = false;
466 #if NET_2_0
467                         if (control == null || (control is Control && !((Control)control).IsHandleCreated))
468 #else
469                         if (control == null && !control.IsHandleCreated)
470 #endif
471                                 return;
472
473                         is_binding = true;
474                         PushData ();
475                 }
476
477                 private void SetControlValue (object data)
478                 {
479                         control_property.SetValue (control, data);
480                 }
481
482                 private void SetPropertyValue (object data)
483                 {
484                         PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
485                         if (pd.IsReadOnly)
486                                 return;
487                         data = ParseData (data, pd.PropertyType);
488                         pd.SetValue (manager.Current, data);
489                 }
490
491                 private void ControlValidatingHandler (object sender, CancelEventArgs e)
492                 {
493 #if NET_2_0
494                         if (datasource_update_mode != DataSourceUpdateMode.OnValidation)
495                                 return;
496 #endif
497
498                         bool ok = true;
499                         // If the data doesn't seem to be valid (it can't be converted,
500                         // is the wrong type, etc, we reset to the old data value.
501                         // If Formatting is enabled, no exception is fired, but we get a false value
502                         try {
503                                 ok = PullData ();
504                         } catch {
505                                 ok = false;
506                         }
507
508                         e.Cancel = !ok;
509                 }
510
511                 private void ControlCreatedHandler (object o, EventArgs args)
512                 {
513                         UpdateIsBinding ();
514                 }
515
516                 private void PositionChangedHandler (object sender, EventArgs e)
517                 {
518                         Check ();
519                         PushData ();
520                 }
521
522                 EventDescriptor GetPropertyChangedEvent (object o, string property_name)
523                 {
524                         if (o == null || property_name == null || property_name.Length == 0)
525                                 return null;
526
527                         string event_name = property_name + "Changed";
528                         Type event_handler_type = typeof (EventHandler);
529
530                         EventDescriptor prop_changed_event = null;
531                         foreach (EventDescriptor event_desc in TypeDescriptor.GetEvents (o)) {
532                                 if (event_desc.Name == event_name && event_desc.EventType == event_handler_type) {
533                                         prop_changed_event = event_desc;
534                                         break;
535                                 }
536                         }
537
538                         return prop_changed_event;
539                 }
540
541                 void SourcePropertyChangedHandler (object o, EventArgs args)
542                 {
543                         PushData ();
544                 }
545
546 #if NET_2_0
547                 void ControlPropertyChangedHandler (object o, EventArgs args)
548                 {
549                         if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged)
550                                 return;
551
552                         PullData ();
553                 }
554 #endif
555
556                 private object ParseData (object data, Type data_type)
557                 {
558                         ConvertEventArgs e = new ConvertEventArgs (data, data_type);
559
560                         OnParse (e);
561                         if (data_type.IsInstanceOfType (e.Value))
562                                 return e.Value;
563                         if (e.Value == Convert.DBNull)
564                                 return e.Value;
565 #if NET_2_0
566                         if (e.Value == null) {
567                                 bool nullable = data_type.IsGenericType && !data_type.ContainsGenericParameters &&
568                                         data_type.GetGenericTypeDefinition () == typeof (Nullable<>);
569                                 return data_type.IsValueType && !nullable ? Convert.DBNull : null;
570                         }
571 #endif
572
573                         return ConvertData (e.Value, data_type);
574                 }
575
576                 private object FormatData (object data)
577                 {
578                         ConvertEventArgs e = new ConvertEventArgs (data, data_type);
579
580                         OnFormat (e);
581                         if (data_type.IsInstanceOfType (e.Value))
582                                 return e.Value;
583
584 #if NET_2_0
585                         if (formatting_enabled) {
586                                 if ((e.Value == null || e.Value == Convert.DBNull) && null_value != null)
587                                         return null_value;
588                                 
589                                 if (e.Value is IFormattable && data_type == typeof (string)) {
590                                         IFormattable formattable = (IFormattable) e.Value;
591                                         return formattable.ToString (format_string, format_info);
592                                 }
593                         }
594 #endif
595                         if (e.Value == null && data_type == typeof (object))
596                                 return Convert.DBNull;
597
598                         return ConvertData (data, data_type);
599                 }
600
601                 private object ConvertData (object data, Type data_type)
602                 {
603                         if (data == null)
604                                 return null;
605
606                         TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ());
607                         if (converter != null && converter.CanConvertTo (data_type))
608                                 return converter.ConvertTo (data, data_type);
609
610                         converter = TypeDescriptor.GetConverter (data_type);
611                         if (converter != null && converter.CanConvertFrom (data.GetType()))
612                                 return converter.ConvertFrom (data);
613
614                         if (data is IConvertible) {
615                                 object res = Convert.ChangeType (data, data_type);
616                                 if (data_type.IsInstanceOfType (res))
617                                         return res;
618                         }
619
620                         return null;
621                 }
622 #if NET_2_0
623                 void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message)
624                 {
625                         BindingCompleteEventArgs args = new BindingCompleteEventArgs (this, 
626                                         exc == null ? BindingCompleteState.Success : BindingCompleteState.Exception,
627                                         context);
628                         if (exc != null) {
629                                 args.SetException (exc);
630                                 args.SetErrorText (error_message);
631                         }
632
633                         OnBindingComplete (args);
634                 }
635 #endif
636
637                 #region Events
638                 public event ConvertEventHandler Format;
639                 public event ConvertEventHandler Parse;
640 #if NET_2_0
641                 public event BindingCompleteEventHandler BindingComplete;
642 #endif
643                 #endregion      // Events
644         }
645 }