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 Control control;
45 private BindingManagerBase manager;
46 private PropertyDescriptor control_property;
47 private PropertyDescriptor is_null_desc;
50 private Type data_type;
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;
61 #region Public Constructors
63 public Binding (string propertyName, object dataSource, string dataMember)
64 : this (propertyName, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
68 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled)
69 : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
73 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)
74 : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null)
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)
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)
88 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)
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;
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));
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 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));
116 #endregion // Public Constructors
118 #region Public Instance Properties
120 public IBindableComponent BindableComponent {
126 public BindingManagerBase BindingManagerBase {
132 public BindingMemberInfo BindingMemberInfo {
134 return binding_member_info;
138 [DefaultValue (null)]
139 public Control Control {
146 [DefaultValue (ControlUpdateMode.OnPropertyChanged)]
147 public ControlUpdateMode ControlUpdateMode {
149 return control_update_mode;
152 control_update_mode = value;
157 public object DataSource {
164 [DefaultValue (DataSourceUpdateMode.OnValidation)]
165 public DataSourceUpdateMode DataSourceUpdateMode {
167 return datasource_update_mode;
170 datasource_update_mode = value;
174 [DefaultValue (null)]
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 public bool IsBinding {
205 public string PropertyName {
207 return property_name;
210 #endregion // Public Instance Properties
213 public void ReadValue ()
218 public void WriteValue ()
224 #region Protected Instance Methods
226 protected virtual void OnBindingComplete (BindingCompleteEventArgs args)
228 if (BindingComplete != null)
229 BindingComplete (this, args);
233 protected virtual void OnFormat (ConvertEventArgs cevent)
236 Format (this, cevent);
239 protected virtual void OnParse (ConvertEventArgs cevent)
242 Parse (this, cevent);
244 #endregion // Protected Instance Methods
246 internal string DataMember {
247 get { return data_member; }
250 internal void SetControl (Control control)
252 if (control == this.control)
255 control_property = TypeDescriptor.GetProperties (control).Find (property_name, true);
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."));
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);
265 EventDescriptor prop_changed_event = GetPropertyChangedEvent (control, property_name);
266 if (prop_changed_event != null)
267 prop_changed_event.AddEventHandler (control, new EventHandler (ControlPropertyChangedHandler));
270 this.control = control;
273 internal void Check ()
275 if (control == null || control.BindingContext == null)
278 if (manager == null) {
279 manager = control.BindingContext [data_source];
280 manager.AddBinding (this);
281 manager.PositionChanged += new EventHandler (PositionChangedHandler);
284 if (manager.Position == -1)
287 if (!checked_isnull) {
288 is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false);
289 checked_isnull = true;
295 internal void PullData ()
300 void PullData (bool force)
302 if (IsBinding == false || manager.Current == null)
305 if (!force && datasource_update_mode == DataSourceUpdateMode.Never)
309 data = control_property.GetValue (control);
312 SetPropertyValue (data);
313 } catch (Exception e) {
315 if (formatting_enabled) {
316 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, e, e.Message);
324 if (formatting_enabled)
325 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null);
329 internal void PushData ()
334 void PushData (bool force)
336 if (manager == null || manager.IsSuspended || manager.Current == null)
339 if (!force && control_update_mode == ControlUpdateMode.Never)
343 if (is_null_desc != null) {
344 bool is_null = (bool) is_null_desc.GetValue (manager.Current);
346 data = Convert.DBNull;
351 PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
353 data = manager.Current;
355 data = pd.GetValue (manager.Current);
359 data = FormatData (data);
360 SetControlValue (data);
361 } catch (Exception e) {
363 if (formatting_enabled) {
364 FireBindingComplete (BindingCompleteContext.ControlUpdate, e, e.Message);
372 if (formatting_enabled)
373 FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null);
377 internal void UpdateIsBinding ()
380 if (control == null || !control.Created)
382 if (manager == null || manager.IsSuspended)
389 private void SetControlValue (object data)
391 control_property.SetValue (control, data);
394 private void SetPropertyValue (object data)
396 PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
399 data = ParseData (data, pd.PropertyType);
400 pd.SetValue (manager.Current, data);
403 private void ControlValidatingHandler (object sender, CancelEventArgs e)
406 if (datasource_update_mode != DataSourceUpdateMode.OnValidation)
410 object old_data = data;
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.
418 SetControlValue (data);
422 private void PositionChangedHandler (object sender, EventArgs e)
428 EventDescriptor GetPropertyChangedEvent (object o, string property_name)
430 if (o == null || property_name == null || property_name.Length == 0)
433 string event_name = property_name + "Changed";
434 Type event_handler_type = typeof (EventHandler);
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;
444 return prop_changed_event;
447 void SourcePropertyChangedHandler (object o, EventArgs args)
453 void ControlPropertyChangedHandler (object o, EventArgs args)
455 if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged)
462 private object ParseData (object data, Type data_type)
464 ConvertEventArgs e = new ConvertEventArgs (data, data_type);
467 if (data_type.IsInstanceOfType (e.Value))
469 if (e.Value == Convert.DBNull)
473 return data_type.IsByRef ? null : Convert.DBNull;
476 return ConvertData (e.Value, data_type);
479 private object FormatData (object data)
481 ConvertEventArgs e = new ConvertEventArgs (data, data_type);
484 if (data_type.IsInstanceOfType (e.Value))
487 if (e.Value == null && data_type == typeof (object))
488 return Convert.DBNull;
490 return ConvertData (data, data_type);
493 private object ConvertData (object data, Type data_type)
498 TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ());
499 if (converter != null && converter.CanConvertTo (data_type))
500 return converter.ConvertTo (data, data_type);
502 if (data is IConvertible) {
503 object res = Convert.ChangeType (data, data_type);
504 if (data_type.IsInstanceOfType (res))
511 void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message)
513 BindingCompleteEventArgs args = new BindingCompleteEventArgs (this,
514 exc == null ? BindingCompleteState.Success : BindingCompleteState.Exception,
517 args.SetException (exc);
518 args.SetErrorText (error_message);
521 OnBindingComplete (args);
526 public event ConvertEventHandler Format;
527 public event ConvertEventHandler Parse;
529 public event BindingCompleteEventHandler BindingComplete;