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