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
;
43 private IBindableComponent control
;
45 private BindingManagerBase manager
;
46 private PropertyDescriptor control_property
;
47 private PropertyDescriptor is_null_desc
;
50 private Type data_type
;
52 private DataSourceUpdateMode datasource_update_mode
;
53 private ControlUpdateMode control_update_mode
;
54 private object datasource_null_value
= Convert
.DBNull
;
55 private object null_value
;
56 private IFormatProvider format_info
;
57 private string format_string
;
58 private bool formatting_enabled
;
59 #region Public Constructors
60 public Binding (string propertyName
, object dataSource
, string dataMember
)
61 : this (propertyName
, dataSource
, dataMember
, false, DataSourceUpdateMode
.OnValidation
, null, string.Empty
, null)
65 public Binding (string propertyName
, object dataSource
, string dataMember
, bool formattingEnabled
)
66 : this (propertyName
, dataSource
, dataMember
, formattingEnabled
, DataSourceUpdateMode
.OnValidation
, null, string.Empty
, null)
70 public Binding (string propertyName
, object dataSource
, string dataMember
, bool formattingEnabled
, DataSourceUpdateMode dataSourceUpdateMode
)
71 : this (propertyName
, dataSource
, dataMember
, formattingEnabled
, dataSourceUpdateMode
, null, string.Empty
, null)
75 public Binding (string propertyName
, object dataSource
, string dataMember
, bool formattingEnabled
, DataSourceUpdateMode dataSourceUpdateMode
, object nullValue
)
76 : this (propertyName
, dataSource
, dataMember
, formattingEnabled
, dataSourceUpdateMode
, nullValue
, string.Empty
, null)
80 public Binding (string propertyName
, object dataSource
, string dataMember
, bool formattingEnabled
, DataSourceUpdateMode dataSourceUpdateMode
, object nullValue
, string formatString
)
81 : this (propertyName
, dataSource
, dataMember
, formattingEnabled
, dataSourceUpdateMode
, nullValue
, formatString
, null)
85 public Binding (string propertyName
, object dataSource
, string dataMember
, bool formattingEnabled
, DataSourceUpdateMode dataSourceUpdateMode
, object nullValue
, string formatString
, IFormatProvider formatInfo
)
87 property_name
= propertyName
;
88 data_source
= dataSource
;
89 data_member
= dataMember
;
90 binding_member_info
= new BindingMemberInfo (dataMember
);
91 datasource_update_mode
= dataSourceUpdateMode
;
92 null_value
= nullValue
;
93 format_string
= formatString
;
94 format_info
= formatInfo
;
96 #endregion // Public Constructors
98 #region Public Instance Properties
100 public IBindableComponent BindableComponent
{
106 public BindingManagerBase BindingManagerBase
{
112 public BindingMemberInfo BindingMemberInfo
{
114 return binding_member_info
;
118 [DefaultValue (null)]
119 public Control Control
{
121 return control
as Control
;
125 [DefaultValue (ControlUpdateMode
.OnPropertyChanged
)]
126 public ControlUpdateMode ControlUpdateMode
{
128 return control_update_mode
;
131 control_update_mode
= value;
135 public object DataSource
{
141 [DefaultValue (DataSourceUpdateMode
.OnValidation
)]
142 public DataSourceUpdateMode DataSourceUpdateMode
{
144 return datasource_update_mode
;
147 datasource_update_mode
= value;
151 public object DataSourceNullValue
{
153 return datasource_null_value
;
156 datasource_null_value
= value;
160 [DefaultValue (false)]
161 public bool FormattingEnabled
{
163 return formatting_enabled
;
166 if (formatting_enabled
== value)
169 formatting_enabled
= value;
174 [DefaultValue (null)]
175 public IFormatProvider FormatInfo
{
180 if (value == format_info
)
184 if (formatting_enabled
)
189 public string FormatString
{
191 return format_string
;
195 value = String
.Empty
;
196 if (value == format_string
)
199 format_string
= value;
200 if (formatting_enabled
)
205 public bool IsBinding
{
207 if (manager
== null || manager
.IsSuspended
)
214 public object NullValue
{
219 if (value == null_value
)
223 if (formatting_enabled
)
229 public string PropertyName
{
231 return property_name
;
234 #endregion // Public Instance Properties
236 public void ReadValue ()
241 public void WriteValue ()
246 #region Protected Instance Methods
247 protected virtual void OnBindingComplete (BindingCompleteEventArgs e
)
249 if (BindingComplete
!= null)
250 BindingComplete (this, e
);
253 protected virtual void OnFormat (ConvertEventArgs cevent
)
256 Format (this, cevent
);
259 protected virtual void OnParse (ConvertEventArgs cevent
)
262 Parse (this, cevent
);
264 #endregion // Protected Instance Methods
266 internal string DataMember
{
267 get { return data_member; }
270 internal void SetControl (IBindableComponent control
)
272 if (control
== this.control
)
275 control_property
= TypeDescriptor
.GetProperties (control
).Find (property_name
, true);
277 if (control_property
== null)
278 throw new ArgumentException (String
.Concat ("Cannot bind to property '", property_name
, "' on target control."));
279 if (control_property
.IsReadOnly
)
280 throw new ArgumentException (String
.Concat ("Cannot bind to property '", property_name
, "' because it is read only."));
282 data_type
= control_property
.PropertyType
; // Getting the PropertyType is kinda slow and it should never change, so it is cached
284 Control ctrl
= control
as Control
;
286 ctrl
.Validating
+= new CancelEventHandler (ControlValidatingHandler
);
287 if (!ctrl
.IsHandleCreated
)
288 ctrl
.HandleCreated
+= new EventHandler (ControlCreatedHandler
);
291 EventDescriptor prop_changed_event
= GetPropertyChangedEvent (control
, property_name
);
292 if (prop_changed_event
!= null)
293 prop_changed_event
.AddEventHandler (control
, new EventHandler (ControlPropertyChangedHandler
));
294 this.control
= control
;
298 internal void Check ()
300 if (control
== null || control
.BindingContext
== null)
303 if (manager
== null) {
304 manager
= control
.BindingContext
[data_source
, binding_member_info
.BindingPath
];
306 if (manager
.Position
> -1 && binding_member_info
.BindingField
!= String
.Empty
&&
307 TypeDescriptor
.GetProperties (manager
.Current
).Find (binding_member_info
.BindingField
, true) == null)
308 throw new ArgumentException ("Cannot bind to property '" + binding_member_info
.BindingField
+ "' on DataSource.",
311 manager
.AddBinding (this);
312 manager
.PositionChanged
+= new EventHandler (PositionChangedHandler
);
314 if (manager
is PropertyManager
) { // Match .net, which only watchs simple objects
315 EventDescriptor prop_changed_event
= GetPropertyChangedEvent (manager
.Current
, binding_member_info
.BindingField
);
316 if (prop_changed_event
!= null)
317 prop_changed_event
.AddEventHandler (manager
.Current
, new EventHandler (SourcePropertyChangedHandler
));
321 if (manager
.Position
== -1)
324 if (!checked_isnull
) {
325 is_null_desc
= TypeDescriptor
.GetProperties (manager
.Current
).Find (property_name
+ "IsNull", false);
326 checked_isnull
= true;
332 internal bool PullData ()
334 return PullData (false);
337 // Return false ONLY in case of error - and return true even in cases
338 // where no update was possible
339 bool PullData (bool force
)
341 if (IsBinding
== false || manager
.Current
== null)
343 if (!force
&& datasource_update_mode
== DataSourceUpdateMode
.Never
)
346 data
= control_property
.GetValue (control
);
348 data
= datasource_null_value
;
351 SetPropertyValue (data
);
352 } catch (Exception e
) {
353 if (formatting_enabled
) {
354 FireBindingComplete (BindingCompleteContext
.DataSourceUpdate
, e
, e
.Message
);
360 if (formatting_enabled
)
361 FireBindingComplete (BindingCompleteContext
.DataSourceUpdate
, null, null);
365 internal void PushData ()
370 void PushData (bool force
)
372 if (manager
== null || manager
.IsSuspended
|| manager
.Count
== 0 || manager
.Position
== -1)
374 if (!force
&& control_update_mode
== ControlUpdateMode
.Never
)
377 if (is_null_desc
!= null) {
378 bool is_null
= (bool) is_null_desc
.GetValue (manager
.Current
);
380 data
= Convert
.DBNull
;
385 PropertyDescriptor pd
= TypeDescriptor
.GetProperties (manager
.Current
).Find (binding_member_info
.BindingField
, true);
387 data
= manager
.Current
;
389 data
= pd
.GetValue (manager
.Current
);
392 if ((data
== null || data
== DBNull
.Value
) && null_value
!= null)
396 data
= FormatData (data
);
397 SetControlValue (data
);
398 } catch (Exception e
) {
399 if (formatting_enabled
) {
400 FireBindingComplete (BindingCompleteContext
.ControlUpdate
, e
, e
.Message
);
406 if (formatting_enabled
)
407 FireBindingComplete (BindingCompleteContext
.ControlUpdate
, null, null);
410 internal void UpdateIsBinding ()
413 if (control
== null || (control
is Control
&& !((Control
)control
).IsHandleCreated
))
420 private void SetControlValue (object data
)
422 control_property
.SetValue (control
, data
);
425 private void SetPropertyValue (object data
)
427 PropertyDescriptor pd
= TypeDescriptor
.GetProperties (manager
.Current
).Find (binding_member_info
.BindingField
, true);
430 data
= ParseData (data
, pd
.PropertyType
);
431 pd
.SetValue (manager
.Current
, data
);
434 private void ControlValidatingHandler (object sender
, CancelEventArgs e
)
436 if (datasource_update_mode
!= DataSourceUpdateMode
.OnValidation
)
440 // If the data doesn't seem to be valid (it can't be converted,
441 // is the wrong type, etc, we reset to the old data value.
442 // If Formatting is enabled, no exception is fired, but we get a false value
452 private void ControlCreatedHandler (object o
, EventArgs args
)
457 private void PositionChangedHandler (object sender
, EventArgs e
)
463 EventDescriptor
GetPropertyChangedEvent (object o
, string property_name
)
465 if (o
== null || property_name
== null || property_name
.Length
== 0)
468 string event_name
= property_name
+ "Changed";
469 Type event_handler_type
= typeof (EventHandler
);
471 EventDescriptor prop_changed_event
= null;
472 foreach (EventDescriptor event_desc
in TypeDescriptor
.GetEvents (o
)) {
473 if (event_desc
.Name
== event_name
&& event_desc
.EventType
== event_handler_type
) {
474 prop_changed_event
= event_desc
;
479 return prop_changed_event
;
482 void SourcePropertyChangedHandler (object o
, EventArgs args
)
487 void ControlPropertyChangedHandler (object o
, EventArgs args
)
489 if (datasource_update_mode
!= DataSourceUpdateMode
.OnPropertyChanged
)
495 private object ParseData (object data
, Type data_type
)
497 ConvertEventArgs e
= new ConvertEventArgs (data
, data_type
);
500 if (data_type
.IsInstanceOfType (e
.Value
))
502 if (e
.Value
== Convert
.DBNull
)
504 if (e
.Value
== null) {
505 bool nullable
= data_type
.IsGenericType
&& !data_type
.ContainsGenericParameters
&&
506 data_type
.GetGenericTypeDefinition () == typeof (Nullable
<>);
507 return data_type
.IsValueType
&& !nullable
? Convert
.DBNull
: null;
510 return ConvertData (e
.Value
, data_type
);
513 private object FormatData (object data
)
515 ConvertEventArgs e
= new ConvertEventArgs (data
, data_type
);
518 if (data_type
.IsInstanceOfType (e
.Value
))
521 if (formatting_enabled
) {
522 if ((e
.Value
== null || e
.Value
== Convert
.DBNull
) && null_value
!= null)
525 if (e
.Value
is IFormattable
&& data_type
== typeof (string)) {
526 IFormattable formattable
= (IFormattable
) e
.Value
;
527 return formattable
.ToString (format_string
, format_info
);
531 if (e
.Value
== null && data_type
== typeof (object))
532 return Convert
.DBNull
;
534 return ConvertData (data
, data_type
);
537 private object ConvertData (object data
, Type data_type
)
542 TypeConverter converter
= TypeDescriptor
.GetConverter (data
.GetType ());
543 if (converter
!= null && converter
.CanConvertTo (data_type
))
544 return converter
.ConvertTo (data
, data_type
);
546 converter
= TypeDescriptor
.GetConverter (data_type
);
547 if (converter
!= null && converter
.CanConvertFrom (data
.GetType()))
548 return converter
.ConvertFrom (data
);
550 if (data
is IConvertible
) {
551 object res
= Convert
.ChangeType (data
, data_type
);
552 if (data_type
.IsInstanceOfType (res
))
558 void FireBindingComplete (BindingCompleteContext context
, Exception exc
, string error_message
)
560 BindingCompleteEventArgs args
= new BindingCompleteEventArgs (this,
561 exc
== null ? BindingCompleteState
.Success
: BindingCompleteState
.Exception
,
564 args
.SetException (exc
);
565 args
.SetErrorText (error_message
);
568 OnBindingComplete (args
);
572 public event ConvertEventHandler Format
;
573 public event ConvertEventHandler Parse
;
574 public event BindingCompleteEventHandler BindingComplete
;