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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
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
;
44 private IBindableComponent control
;
46 private Control control
;
49 private BindingManagerBase manager
;
50 private PropertyDescriptor control_property
;
51 private PropertyDescriptor is_null_desc
;
54 private Type data_type
;
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
;
65 #region Public Constructors
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
;
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
);
112 #endregion // Public Constructors
114 #region Public Instance Properties
116 [DefaultValue (null)]
117 public IBindableComponent BindableComponent
{
123 public BindingManagerBase BindingManagerBase
{
129 public BindingMemberInfo BindingMemberInfo
{
131 return binding_member_info
;
135 [DefaultValue (null)]
136 public Control Control
{
139 return control
as Control
;
147 [DefaultValue (ControlUpdateMode
.OnPropertyChanged
)]
148 public ControlUpdateMode ControlUpdateMode
{
150 return control_update_mode
;
153 control_update_mode
= value;
158 public object DataSource
{
165 [DefaultValue (DataSourceUpdateMode
.OnValidation
)]
166 public DataSourceUpdateMode DataSourceUpdateMode
{
168 return datasource_update_mode
;
171 datasource_update_mode
= value;
175 public object DataSourceNullValue
{
177 return datasource_null_value
;
180 datasource_null_value
= value;
184 [DefaultValue (false)]
185 public bool FormattingEnabled
{
187 return formatting_enabled
;
190 if (formatting_enabled
== value)
193 formatting_enabled
= value;
198 [DefaultValue (null)]
199 public IFormatProvider FormatInfo
{
204 if (value == format_info
)
208 if (formatting_enabled
)
213 public string FormatString
{
215 return format_string
;
219 value = String
.Empty
;
220 if (value == format_string
)
223 format_string
= value;
224 if (formatting_enabled
)
230 public bool IsBinding
{
232 if (manager
== null || manager
.IsSuspended
)
240 public object NullValue
{
245 if (value == null_value
)
249 if (formatting_enabled
)
256 public string PropertyName
{
258 return property_name
;
261 #endregion // Public Instance Properties
264 public void ReadValue ()
269 public void WriteValue ()
275 #region Protected Instance Methods
277 protected virtual void OnBindingComplete (BindingCompleteEventArgs e
)
279 if (BindingComplete
!= null)
280 BindingComplete (this, e
);
284 protected virtual void OnFormat (ConvertEventArgs cevent
)
287 Format (this, cevent
);
290 protected virtual void OnParse (ConvertEventArgs cevent
)
293 Parse (this, cevent
);
295 #endregion // Protected Instance Methods
297 internal string DataMember
{
298 get { return data_member; }
302 internal void SetControl (IBindableComponent control
)
304 internal void SetControl (Control control
)
307 if (control
== this.control
)
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
;
321 ctrl
.Validating
+= new CancelEventHandler (ControlValidatingHandler
);
322 if (!ctrl
.IsHandleCreated
)
323 ctrl
.HandleCreated
+= new EventHandler (ControlCreatedHandler
);
327 EventDescriptor prop_changed_event
= GetPropertyChangedEvent (control
, property_name
);
328 if (prop_changed_event
!= null)
329 prop_changed_event
.AddEventHandler (control
, new EventHandler (ControlPropertyChangedHandler
));
331 this.control
= control
;
335 internal void Check ()
337 if (control
== null || control
.BindingContext
== null)
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.",
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)
361 if (!checked_isnull
) {
362 is_null_desc
= TypeDescriptor
.GetProperties (manager
.Current
).Find (property_name
+ "IsNull", false);
363 checked_isnull
= true;
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)
381 if (!force
&& datasource_update_mode
== DataSourceUpdateMode
.Never
)
385 data
= control_property
.GetValue (control
);
388 data
= datasource_null_value
;
392 SetPropertyValue (data
);
393 } catch (Exception e
) {
395 if (formatting_enabled
) {
396 FireBindingComplete (BindingCompleteContext
.DataSourceUpdate
, e
, e
.Message
);
404 if (formatting_enabled
)
405 FireBindingComplete (BindingCompleteContext
.DataSourceUpdate
, null, null);
410 internal void PushData ()
415 void PushData (bool force
)
417 if (manager
== null || manager
.IsSuspended
|| manager
.Count
== 0 || manager
.Position
== -1)
420 if (!force
&& control_update_mode
== ControlUpdateMode
.Never
)
424 if (is_null_desc
!= null) {
425 bool is_null
= (bool) is_null_desc
.GetValue (manager
.Current
);
427 data
= Convert
.DBNull
;
432 PropertyDescriptor pd
= TypeDescriptor
.GetProperties (manager
.Current
).Find (binding_member_info
.BindingField
, true);
434 data
= manager
.Current
;
436 data
= pd
.GetValue (manager
.Current
);
440 if ((data
== null || data
== DBNull
.Value
) && null_value
!= null)
445 data
= FormatData (data
);
446 SetControlValue (data
);
447 } catch (Exception e
) {
449 if (formatting_enabled
) {
450 FireBindingComplete (BindingCompleteContext
.ControlUpdate
, e
, e
.Message
);
458 if (formatting_enabled
)
459 FireBindingComplete (BindingCompleteContext
.ControlUpdate
, null, null);
463 internal void UpdateIsBinding ()
467 if (control
== null || (control
is Control
&& !((Control
)control
).IsHandleCreated
))
469 if (control
== null && !control
.IsHandleCreated
)
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);
487 data
= ParseData (data
, pd
.PropertyType
);
488 pd
.SetValue (manager
.Current
, data
);
491 private void ControlValidatingHandler (object sender
, CancelEventArgs e
)
494 if (datasource_update_mode
!= DataSourceUpdateMode
.OnValidation
)
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
511 private void ControlCreatedHandler (object o
, EventArgs args
)
516 private void PositionChangedHandler (object sender
, EventArgs e
)
522 EventDescriptor
GetPropertyChangedEvent (object o
, string property_name
)
524 if (o
== null || property_name
== null || property_name
.Length
== 0)
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
;
538 return prop_changed_event
;
541 void SourcePropertyChangedHandler (object o
, EventArgs args
)
547 void ControlPropertyChangedHandler (object o
, EventArgs args
)
549 if (datasource_update_mode
!= DataSourceUpdateMode
.OnPropertyChanged
)
556 private object ParseData (object data
, Type data_type
)
558 ConvertEventArgs e
= new ConvertEventArgs (data
, data_type
);
561 if (data_type
.IsInstanceOfType (e
.Value
))
563 if (e
.Value
== Convert
.DBNull
)
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;
573 return ConvertData (e
.Value
, data_type
);
576 private object FormatData (object data
)
578 ConvertEventArgs e
= new ConvertEventArgs (data
, data_type
);
581 if (data_type
.IsInstanceOfType (e
.Value
))
585 if (formatting_enabled
) {
586 if ((e
.Value
== null || e
.Value
== Convert
.DBNull
) && null_value
!= null)
589 if (e
.Value
is IFormattable
&& data_type
== typeof (string)) {
590 IFormattable formattable
= (IFormattable
) e
.Value
;
591 return formattable
.ToString (format_string
, format_info
);
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
)
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
))
623 void FireBindingComplete (BindingCompleteContext context
, Exception exc
, string error_message
)
625 BindingCompleteEventArgs args
= new BindingCompleteEventArgs (this,
626 exc
== null ? BindingCompleteState
.Success
: BindingCompleteState
.Exception
,
629 args
.SetException (exc
);
630 args
.SetErrorText (error_message
);
633 OnBindingComplete (args
);
638 public event ConvertEventHandler Format
;
639 public event ConvertEventHandler Parse
;
641 public event BindingCompleteEventHandler BindingComplete
;