TARGET_J2EE:
[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                 private Control control;
44
45                 private BindingManagerBase manager;
46                 private PropertyDescriptor control_property;
47                 private PropertyDescriptor is_null_desc;
48
49                 private object data;
50                 private Type data_type;
51
52 #if NET_2_0
53                 private DataSourceUpdateMode datasource_update_mode;
54                 private ControlUpdateMode control_update_mode;
55                 private object datasource_null_value;
56                 //private object null_value;
57                 //private string format_string;
58                 //private IFormatProvider format_info;
59                 private bool formatting_enabled;
60 #endif
61                 #region Public Constructors
62 #if NET_2_0
63                 public Binding (string propertyName, object dataSource, string dataMember) 
64                         : this (propertyName, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
65                 {
66                 }
67                 
68                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled)
69                         : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
70                 {
71                 }
72                 
73                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)
74                         : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null)
75                 {
76                 }
77                 
78                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue)
79                         : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, string.Empty, null)
80                 {
81                 }
82
83                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString)
84                         : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null)
85                 {
86                 }
87
88                 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)
89                 {
90                         property_name = propertyName;
91                         data_source = dataSource;
92                         data_member = dataMember;
93                         binding_member_info = new BindingMemberInfo (dataMember);
94                         datasource_update_mode = dataSourceUpdateMode;
95                         //null_value = nullValue;
96                         //format_string = formatString;
97                         //format_info = formatInfo;
98
99                         EventDescriptor prop_changed_event = GetPropertyChangedEvent (data_source, binding_member_info.BindingField);
100                         if (prop_changed_event != null)
101                                 prop_changed_event.AddEventHandler (data_source, new EventHandler (SourcePropertyChangedHandler));
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                         EventDescriptor prop_changed_event = GetPropertyChangedEvent (data_source, binding_member_info.BindingField);
112                         if (prop_changed_event != null)
113                                 prop_changed_event.AddEventHandler (data_source, new EventHandler (SourcePropertyChangedHandler));
114                 }               
115 #endif
116                 #endregion      // Public Constructors
117
118                 #region Public Instance Properties
119 #if NET_2_0
120                 public IBindableComponent BindableComponent {
121                         get {
122                                 return control;
123                         }
124                 }
125 #endif
126                 public BindingManagerBase BindingManagerBase {
127                         get {
128                                 return manager;
129                         }
130                 }
131
132                 public BindingMemberInfo BindingMemberInfo {
133                         get {
134                                 return binding_member_info;
135                         }
136                 }
137
138                 [DefaultValue (null)]
139                 public Control Control {
140                         get {
141                                 return control;
142                         }
143                 }
144
145 #if NET_2_0
146                 [DefaultValue (ControlUpdateMode.OnPropertyChanged)]
147                 public ControlUpdateMode ControlUpdateMode {
148                         get {
149                                 return control_update_mode;
150                         }
151                         set {
152                                 control_update_mode = value;
153                         }
154                 }
155 #endif
156
157                 public object DataSource {
158                         get {
159                                 return data_source;
160                         }
161                 }
162
163 #if NET_2_0
164                 [DefaultValue (DataSourceUpdateMode.OnValidation)]
165                 public DataSourceUpdateMode DataSourceUpdateMode {
166                         get {
167                                 return datasource_update_mode;
168                         }
169                         set {
170                                 datasource_update_mode = value;
171                         }
172                 }
173
174                 [DefaultValue (null)]
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                         }
195                 }
196 #endif
197
198                 public bool IsBinding {
199                         get {
200                                 return is_binding;
201                         }
202                 }
203
204                 [DefaultValue ("")]
205                 public string PropertyName {
206                         get {
207                                 return property_name;
208                         }
209                 }
210                 #endregion      // Public Instance Properties
211
212 #if NET_2_0
213                 public void ReadValue ()
214                 {
215                         PushData (true);
216                 }
217
218                 public void WriteValue ()
219                 {
220                         PullData (true);
221                 }
222 #endif
223
224                 #region Protected Instance Methods
225 #if NET_2_0
226                 protected virtual void OnBindingComplete (BindingCompleteEventArgs args)
227                 {
228                         if (BindingComplete != null)
229                                 BindingComplete (this, args);
230                 }
231 #endif
232
233                 protected virtual void OnFormat (ConvertEventArgs cevent)
234                 {
235                         if (Format!=null)
236                                 Format (this, cevent);
237                 }
238
239                 protected virtual void OnParse (ConvertEventArgs cevent)
240                 {
241                         if (Parse!=null)
242                                 Parse (this, cevent);
243                 }
244                 #endregion      // Protected Instance Methods
245
246                 internal string DataMember {
247                         get { return data_member; }
248                 }
249                 
250                 internal void SetControl (Control control)
251                 {
252                         if (control == this.control)
253                                 return;
254
255                         control_property = TypeDescriptor.GetProperties (control).Find (property_name, true);                   
256
257                         if (control_property == null)
258                                 throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' on target control."));
259                         if (control_property.IsReadOnly)
260                                 throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' because it is read only."));
261                                 
262                         data_type = control_property.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached
263                         control.Validating += new CancelEventHandler (ControlValidatingHandler);
264 #if NET_2_0
265                         EventDescriptor prop_changed_event = GetPropertyChangedEvent (control, property_name);
266                         if (prop_changed_event != null)
267                                 prop_changed_event.AddEventHandler (control, new EventHandler (ControlPropertyChangedHandler));
268 #endif
269
270                         this.control = control;
271                 }
272
273                 internal void Check ()
274                 {
275                         if (control == null || control.BindingContext == null)
276                                 return;
277
278                         if (manager == null) {
279                                 manager = control.BindingContext [data_source];
280                                 manager.AddBinding (this);
281                                 manager.PositionChanged += new EventHandler (PositionChangedHandler);
282                         }
283
284                         if (manager.Position == -1)
285                                 return;
286
287                         if (!checked_isnull) {
288                                 is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false);
289                                 checked_isnull = true;
290                         }
291
292                         PushData ();
293                 }
294
295                 internal void PullData ()
296                 {
297                         PullData (false);
298                 }
299
300                 void PullData (bool force)
301                 {
302                         if (IsBinding == false || manager.Current == null)
303                                 return;
304 #if NET_2_0
305                         if (!force && datasource_update_mode == DataSourceUpdateMode.Never)
306                                 return;
307 #endif
308
309                         data = control_property.GetValue (control);
310
311                         try {
312                                 SetPropertyValue (data);
313                         } catch (Exception e) {
314 #if NET_2_0
315                                 if (formatting_enabled) {
316                                         FireBindingComplete (BindingCompleteContext.DataSourceUpdate, e, e.Message);
317                                         return;
318                                 }
319 #endif
320                                 throw e;
321                         }
322
323 #if NET_2_0
324                         if (formatting_enabled)
325                                 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null);
326 #endif
327                 }
328
329                 internal void PushData ()
330                 {
331                         PushData (false);
332                 }
333
334                 void PushData (bool force)
335                 {
336                         if (manager == null || manager.IsSuspended || manager.Current == null)
337                                 return;
338 #if NET_2_0
339                         if (!force && control_update_mode == ControlUpdateMode.Never)
340                                 return;
341 #endif
342
343                         if (is_null_desc != null) {
344                                 bool is_null = (bool) is_null_desc.GetValue (manager.Current);
345                                 if (is_null) {
346                                         data = Convert.DBNull;
347                                         return;
348                                 }
349                         }
350
351                         PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
352                         if (pd == null) {
353                                 data = manager.Current;
354                         } else {
355                                 data = pd.GetValue (manager.Current);
356                         }
357
358                         try {
359                                 data = FormatData (data);
360                                 SetControlValue (data);
361                         } catch (Exception e) {
362 #if NET_2_0
363                                 if (formatting_enabled) {
364                                         FireBindingComplete (BindingCompleteContext.ControlUpdate, e, e.Message);
365                                         return;
366                                 }
367 #endif
368                                 throw e;
369                         }
370
371 #if NET_2_0
372                         if (formatting_enabled)
373                                 FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null);
374 #endif
375                 }
376
377                 internal void UpdateIsBinding ()
378                 {
379                         is_binding = false;
380                         if (control == null || !control.Created)
381                                 return;
382                         if (manager == null || manager.IsSuspended)
383                                 return;
384
385                         is_binding = true;
386                         PushData ();
387                 }
388
389                 private void SetControlValue (object data)
390                 {
391                         control_property.SetValue (control, data);
392                 }
393
394                 private void SetPropertyValue (object data)
395                 {
396                         PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
397                         if (pd.IsReadOnly)
398                                 return;
399                         data = ParseData (data, pd.PropertyType);
400                         pd.SetValue (manager.Current, data);
401                 }
402
403                 private void ControlValidatingHandler (object sender, CancelEventArgs e)
404                 {
405 #if NET_2_0
406                         if (datasource_update_mode != DataSourceUpdateMode.OnValidation)
407                                 return;
408 #endif
409
410                         object old_data = data;
411
412                         // If the data doesn't seem to be valid (it can't be converted,
413                         // is the wrong type, etc, we reset to the old data value.
414                         try {
415                                 PullData ();
416                         } catch {
417                                 data = old_data;
418                                 SetControlValue (data);
419                         }
420                 }
421
422                 private void PositionChangedHandler (object sender, EventArgs e)
423                 {
424                         Check ();
425                         PushData ();
426                 }
427
428                 EventDescriptor GetPropertyChangedEvent (object o, string property_name)
429                 {
430                         if (o == null || property_name == null || property_name.Length == 0)
431                                 return null;
432
433                         string event_name = property_name + "Changed";
434                         Type event_handler_type = typeof (EventHandler);
435
436                         EventDescriptor prop_changed_event = null;
437                         foreach (EventDescriptor event_desc in TypeDescriptor.GetEvents (o)) {
438                                 if (event_desc.Name == event_name && event_desc.EventType == event_handler_type) {
439                                         prop_changed_event = event_desc;
440                                         break;
441                                 }
442                         }
443
444                         return prop_changed_event;
445                 }
446
447                 void SourcePropertyChangedHandler (object o, EventArgs args)
448                 {
449                         PushData ();
450                 }
451
452 #if NET_2_0
453                 void ControlPropertyChangedHandler (object o, EventArgs args)
454                 {
455                         if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged)
456                                 return;
457
458                         PullData ();
459                 }
460 #endif
461
462                 private object ParseData (object data, Type data_type)
463                 {
464                         ConvertEventArgs e = new ConvertEventArgs (data, data_type);
465
466                         OnParse (e);
467                         if (data_type.IsInstanceOfType (e.Value))
468                                 return e.Value;
469                         if (e.Value == Convert.DBNull)
470                                 return e.Value;
471 #if NET_2_0
472                         if (e.Value == null)
473                                 return data_type.IsByRef ? null : Convert.DBNull;
474 #endif
475
476                         return ConvertData (e.Value, data_type);
477                 }
478
479                 private object FormatData (object data)
480                 {
481                         ConvertEventArgs e = new ConvertEventArgs (data, data_type);
482
483                         OnFormat (e);
484                         if (data_type.IsInstanceOfType (e.Value))
485                                 return e.Value;
486
487                         if (e.Value == null && data_type == typeof (object))
488                                 return Convert.DBNull;
489
490                         return ConvertData (data, data_type);
491                 }
492
493                 private object ConvertData (object data, Type data_type)
494                 {
495                         if (data == null)
496                                 return null;
497
498                         TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ());
499                         if (converter != null && converter.CanConvertTo (data_type))
500                                 return converter.ConvertTo (data, data_type);
501
502                         if (data is IConvertible) {
503                                 object res = Convert.ChangeType (data, data_type);
504                                 if (data_type.IsInstanceOfType (res))
505                                         return res;
506                         }
507
508                         return null;
509                 }
510 #if NET_2_0
511                 void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message)
512                 {
513                         BindingCompleteEventArgs args = new BindingCompleteEventArgs (this, 
514                                         exc == null ? BindingCompleteState.Success : BindingCompleteState.Exception,
515                                         context);
516                         if (exc != null) {
517                                 args.SetException (exc);
518                                 args.SetErrorText (error_message);
519                         }
520
521                         OnBindingComplete (args);
522                 }
523 #endif
524
525                 #region Events
526                 public event ConvertEventHandler Format;
527                 public event ConvertEventHandler Parse;
528 #if NET_2_0
529                 public event BindingCompleteEventHandler BindingComplete;
530 #endif
531                 #endregion      // Events
532         }
533 }