2009-02-21 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Managed.Windows.Forms / System.Windows.Forms / Binding.cs
blob86db66e6a1ae71b213e4a166de8f0fbc1f3e0036
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.
20 // Copyright (c) 2004-2005 Novell, Inc.
22 // Authors:
23 // Peter Bartok pbartok@novell.com
24 // Jackson Harper jackson@ximian.com
28 using System.ComponentModel;
30 namespace System.Windows.Forms {
32 [TypeConverter (typeof (ListBindingConverter))]
33 public class Binding {
35 private string property_name;
36 private object data_source;
37 private string data_member;
39 private bool is_binding;
40 private bool checked_isnull;
42 private BindingMemberInfo binding_member_info;
43 #if NET_2_0
44 private IBindableComponent control;
45 #else
46 private Control control;
47 #endif
49 private BindingManagerBase manager;
50 private PropertyDescriptor control_property;
51 private PropertyDescriptor is_null_desc;
53 private object data;
54 private Type data_type;
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)
72 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled)
73 : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
77 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)
78 : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null)
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)
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)
92 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)
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;
103 #else
104 public Binding (string propertyName, object dataSource, string dataMember)
106 property_name = propertyName;
107 data_source = dataSource;
108 data_member = dataMember;
109 binding_member_info = new BindingMemberInfo (dataMember);
111 #endif
112 #endregion // Public Constructors
114 #region Public Instance Properties
115 #if NET_2_0
116 [DefaultValue (null)]
117 public IBindableComponent BindableComponent {
118 get {
119 return control;
122 #endif
123 public BindingManagerBase BindingManagerBase {
124 get {
125 return manager;
129 public BindingMemberInfo BindingMemberInfo {
130 get {
131 return binding_member_info;
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
146 #if NET_2_0
147 [DefaultValue (ControlUpdateMode.OnPropertyChanged)]
148 public ControlUpdateMode ControlUpdateMode {
149 get {
150 return control_update_mode;
152 set {
153 control_update_mode = value;
156 #endif
158 public object DataSource {
159 get {
160 return data_source;
164 #if NET_2_0
165 [DefaultValue (DataSourceUpdateMode.OnValidation)]
166 public DataSourceUpdateMode DataSourceUpdateMode {
167 get {
168 return datasource_update_mode;
170 set {
171 datasource_update_mode = value;
175 public object DataSourceNullValue {
176 get {
177 return datasource_null_value;
179 set {
180 datasource_null_value = value;
184 [DefaultValue (false)]
185 public bool FormattingEnabled {
186 get {
187 return formatting_enabled;
189 set {
190 if (formatting_enabled == value)
191 return;
193 formatting_enabled = value;
194 PushData ();
198 [DefaultValue (null)]
199 public IFormatProvider FormatInfo {
200 get {
201 return format_info;
203 set {
204 if (value == format_info)
205 return;
207 format_info = value;
208 if (formatting_enabled)
209 PushData ();
213 public string FormatString {
214 get {
215 return format_string;
217 set {
218 if (value == null)
219 value = String.Empty;
220 if (value == format_string)
221 return;
223 format_string = value;
224 if (formatting_enabled)
225 PushData ();
228 #endif
230 public bool IsBinding {
231 get {
232 if (manager == null || manager.IsSuspended)
233 return false;
235 return is_binding;
239 #if NET_2_0
240 public object NullValue {
241 get {
242 return null_value;
244 set {
245 if (value == null_value)
246 return;
248 null_value = value;
249 if (formatting_enabled)
250 PushData ();
253 #endif
255 [DefaultValue ("")]
256 public string PropertyName {
257 get {
258 return property_name;
261 #endregion // Public Instance Properties
263 #if NET_2_0
264 public void ReadValue ()
266 PushData (true);
269 public void WriteValue ()
271 PullData (true);
273 #endif
275 #region Protected Instance Methods
276 #if NET_2_0
277 protected virtual void OnBindingComplete (BindingCompleteEventArgs e)
279 if (BindingComplete != null)
280 BindingComplete (this, e);
282 #endif
284 protected virtual void OnFormat (ConvertEventArgs cevent)
286 if (Format!=null)
287 Format (this, cevent);
290 protected virtual void OnParse (ConvertEventArgs cevent)
292 if (Parse!=null)
293 Parse (this, cevent);
295 #endregion // Protected Instance Methods
297 internal string DataMember {
298 get { return data_member; }
301 #if NET_2_0
302 internal void SetControl (IBindableComponent control)
303 #else
304 internal void SetControl (Control control)
305 #endif
307 if (control == this.control)
308 return;
310 control_property = TypeDescriptor.GetProperties (control).Find (property_name, true);
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."));
317 data_type = control_property.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached
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);
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 ();
335 internal void Check ()
337 if (control == null || control.BindingContext == null)
338 return;
340 if (manager == null) {
341 manager = control.BindingContext [data_source, binding_member_info.BindingPath];
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");
348 manager.AddBinding (this);
349 manager.PositionChanged += new EventHandler (PositionChangedHandler);
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));
358 if (manager.Position == -1)
359 return;
361 if (!checked_isnull) {
362 is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false);
363 checked_isnull = true;
366 PushData ();
369 internal bool PullData ()
371 return PullData (false);
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)
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
385 data = control_property.GetValue (control);
386 #if NET_2_0
387 if (data == null)
388 data = datasource_null_value;
389 #endif
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;
399 #endif
400 throw e;
403 #if NET_2_0
404 if (formatting_enabled)
405 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null);
406 #endif
407 return true;
410 internal void PushData ()
412 PushData (false);
415 void PushData (bool force)
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
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;
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);
439 #if NET_2_0
440 if ((data == null || data == DBNull.Value) && null_value != null)
441 data = null_value;
442 #endif
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;
453 #endif
454 throw e;
457 #if NET_2_0
458 if (formatting_enabled)
459 FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null);
460 #endif
463 internal void UpdateIsBinding ()
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;
473 is_binding = true;
474 PushData ();
477 private void SetControlValue (object data)
479 control_property.SetValue (control, data);
482 private void SetPropertyValue (object data)
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);
491 private void ControlValidatingHandler (object sender, CancelEventArgs e)
493 #if NET_2_0
494 if (datasource_update_mode != DataSourceUpdateMode.OnValidation)
495 return;
496 #endif
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;
508 e.Cancel = !ok;
511 private void ControlCreatedHandler (object o, EventArgs args)
513 UpdateIsBinding ();
516 private void PositionChangedHandler (object sender, EventArgs e)
518 Check ();
519 PushData ();
522 EventDescriptor GetPropertyChangedEvent (object o, string property_name)
524 if (o == null || property_name == null || property_name.Length == 0)
525 return null;
527 string event_name = property_name + "Changed";
528 Type event_handler_type = typeof (EventHandler);
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;
538 return prop_changed_event;
541 void SourcePropertyChangedHandler (object o, EventArgs args)
543 PushData ();
546 #if NET_2_0
547 void ControlPropertyChangedHandler (object o, EventArgs args)
549 if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged)
550 return;
552 PullData ();
554 #endif
556 private object ParseData (object data, Type data_type)
558 ConvertEventArgs e = new ConvertEventArgs (data, data_type);
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;
571 #endif
573 return ConvertData (e.Value, data_type);
576 private object FormatData (object data)
578 ConvertEventArgs e = new ConvertEventArgs (data, data_type);
580 OnFormat (e);
581 if (data_type.IsInstanceOfType (e.Value))
582 return e.Value;
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;
589 if (e.Value is IFormattable && data_type == typeof (string)) {
590 IFormattable formattable = (IFormattable) e.Value;
591 return formattable.ToString (format_string, format_info);
594 #endif
595 if (e.Value == null && data_type == typeof (object))
596 return Convert.DBNull;
598 return ConvertData (data, data_type);
601 private object ConvertData (object data, Type data_type)
603 if (data == null)
604 return null;
606 TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ());
607 if (converter != null && converter.CanConvertTo (data_type))
608 return converter.ConvertTo (data, data_type);
610 converter = TypeDescriptor.GetConverter (data_type);
611 if (converter != null && converter.CanConvertFrom (data.GetType()))
612 return converter.ConvertFrom (data);
614 if (data is IConvertible) {
615 object res = Convert.ChangeType (data, data_type);
616 if (data_type.IsInstanceOfType (res))
617 return res;
620 return null;
622 #if NET_2_0
623 void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message)
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);
633 OnBindingComplete (args);
635 #endif
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