2008-02-25 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Binding.cs
index 36b5510e4abfebbc6fd78d08c14cd560c34ba8d3..b2d818ac0f0cf63da69a7ae1d7eea184c25312af 100644 (file)
@@ -36,40 +36,93 @@ namespace System.Windows.Forms {
                private object data_source;
                private string data_member;
 
-               private string row_name;
-               private string col_name;
+               private bool is_binding;
+               private bool checked_isnull;
 
                private BindingMemberInfo binding_member_info;
                private Control control;
 
                private BindingManagerBase manager;
-               private PropertyDescriptor prop_desc;
+               private PropertyDescriptor control_property;
                private PropertyDescriptor is_null_desc;
 
-               private EventDescriptor changed_event;
-               private EventHandler property_value_changed_handler;
-               private object event_current; // The manager.Current as far as the changed_event knows
-
                private object data;
                private Type data_type;
 
+#if NET_2_0
+               private DataSourceUpdateMode datasource_update_mode;
+               private ControlUpdateMode control_update_mode;
+               private object datasource_null_value;
+               private object null_value;
+               private IFormatProvider format_info;
+               private string format_string;
+               private bool formatting_enabled;
+#endif
                #region Public Constructors
-               public Binding (string propertyName, object dataSource, string dataMember)
+#if NET_2_0
+               public Binding (string propertyName, object dataSource, string dataMember) 
+                       : this (propertyName, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
+               {
+               }
+               
+               public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled)
+                       : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null)
+               {
+               }
+               
+               public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)
+                       : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null)
+               {
+               }
+               
+               public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue)
+                       : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, string.Empty, null)
+               {
+               }
+
+               public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString)
+                       : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null)
+               {
+               }
+
+               public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)
                {
                        property_name = propertyName;
                        data_source = dataSource;
                        data_member = dataMember;
                        binding_member_info = new BindingMemberInfo (dataMember);
-
-                       int sp = data_member.IndexOf ('.');
-                       if (sp != -1) {
-                               row_name = data_member.Substring (0, sp);
-                               col_name = data_member.Substring (sp + 1, data_member.Length - sp - 1);
-                       }
+                       datasource_update_mode = dataSourceUpdateMode;
+                       null_value = nullValue;
+                       format_string = formatString;
+                       format_info = formatInfo;
+
+                       EventDescriptor prop_changed_event = GetPropertyChangedEvent (data_source, binding_member_info.BindingField);
+                       if (prop_changed_event != null)
+                               prop_changed_event.AddEventHandler (data_source, new EventHandler (SourcePropertyChangedHandler));
                }
+#else
+               public Binding (string propertyName, object dataSource, string dataMember)
+               {
+                       property_name = propertyName;
+                       data_source = dataSource;
+                       data_member = dataMember;
+                       binding_member_info = new BindingMemberInfo (dataMember);
+                               
+                       EventDescriptor prop_changed_event = GetPropertyChangedEvent (data_source, binding_member_info.BindingField);
+                       if (prop_changed_event != null)
+                               prop_changed_event.AddEventHandler (data_source, new EventHandler (SourcePropertyChangedHandler));
+               }               
+#endif
                #endregion      // Public Constructors
 
                #region Public Instance Properties
+#if NET_2_0
+               public IBindableComponent BindableComponent {
+                       get {
+                               return control;
+                       }
+               }
+#endif
                public BindingManagerBase BindingManagerBase {
                        get {
                                return manager;
@@ -89,21 +142,114 @@ namespace System.Windows.Forms {
                        }
                }
 
+#if NET_2_0
+               [DefaultValue (ControlUpdateMode.OnPropertyChanged)]
+               public ControlUpdateMode ControlUpdateMode {
+                       get {
+                               return control_update_mode;
+                       }
+                       set {
+                               control_update_mode = value;
+                       }
+               }
+#endif
+
                public object DataSource {
                        get {
                                return data_source;
                        }
                }
 
+#if NET_2_0
+               [DefaultValue (DataSourceUpdateMode.OnValidation)]
+               public DataSourceUpdateMode DataSourceUpdateMode {
+                       get {
+                               return datasource_update_mode;
+                       }
+                       set {
+                               datasource_update_mode = value;
+                       }
+               }
+
+               [DefaultValue (null)]
+               public object DataSourceNullValue {
+                       get {
+                               return datasource_null_value;
+                       }
+                       set {
+                               datasource_null_value = value;
+                       }
+               }
+
+               [DefaultValue (false)]
+               public bool FormattingEnabled {
+                       get {
+                               return formatting_enabled;
+                       }
+                       set {
+                               if (formatting_enabled == value)
+                                       return;
+
+                               formatting_enabled = value;
+                               PushData ();
+                       }
+               }
+
+               [DefaultValue (null)]
+               public IFormatProvider FormatInfo {
+                       get {
+                               return format_info;
+                       }
+                       set {
+                               if (value == format_info)
+                                       return;
+
+                               format_info = value;
+                               if (formatting_enabled)
+                                       PushData ();
+                       }
+               }
+
+               [DefaultValue ("")]
+               public string FormatString {
+                       get {
+                               return format_string;
+                       }
+                       set {
+                               if (value == null)
+                                       value = String.Empty;
+                               if (value == format_string)
+                                       return;
+
+                               format_string = value;
+                               if (formatting_enabled)
+                                       PushData ();
+                       }
+               }
+#endif
+
                public bool IsBinding {
                        get {
-                               if (control == null || !control.Created)
-                                       return false;
-                               if (manager == null || manager.IsSuspended)
-                                       return false;
-                               return true;
+                               return is_binding;
+                       }
+               }
+
+#if NET_2_0
+               [DefaultValue (null)]
+               public object NullValue {
+                       get {
+                               return null_value;
+                       }
+                       set {
+                               if (value == null_value)
+                                       return;
+
+                               null_value = value;
+                               if (formatting_enabled)
+                                       PushData ();
                        }
                }
+#endif
 
                [DefaultValue ("")]
                public string PropertyName {
@@ -113,7 +259,27 @@ namespace System.Windows.Forms {
                }
                #endregion      // Public Instance Properties
 
+#if NET_2_0
+               public void ReadValue ()
+               {
+                       PushData (true);
+               }
+
+               public void WriteValue ()
+               {
+                       PullData (true);
+               }
+#endif
+
                #region Protected Instance Methods
+#if NET_2_0
+               protected virtual void OnBindingComplete (BindingCompleteEventArgs args)
+               {
+                       if (BindingComplete != null)
+                               BindingComplete (this, args);
+               }
+#endif
+
                protected virtual void OnFormat (ConvertEventArgs cevent)
                {
                        if (Format!=null)
@@ -127,59 +293,105 @@ namespace System.Windows.Forms {
                }
                #endregion      // Protected Instance Methods
 
+               internal string DataMember {
+                       get { return data_member; }
+               }
                
                internal void SetControl (Control control)
                {
                        if (control == this.control)
                                return;
 
-                       prop_desc = TypeDescriptor.GetProperties (control).Find (property_name, true);                  
-                       
-                       if (prop_desc == null)
+                       control_property = TypeDescriptor.GetProperties (control).Find (property_name, true);                   
+
+                       if (control_property == null)
                                throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' on target control."));
-                       if (prop_desc.IsReadOnly)
+                       if (control_property.IsReadOnly)
                                throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' because it is read only."));
                                
-                       data_type = prop_desc.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached
+                       data_type = control_property.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached
                        control.Validating += new CancelEventHandler (ControlValidatingHandler);
+#if NET_2_0
+                       EventDescriptor prop_changed_event = GetPropertyChangedEvent (control, property_name);
+                       if (prop_changed_event != null)
+                               prop_changed_event.AddEventHandler (control, new EventHandler (ControlPropertyChangedHandler));
+#endif
 
                        this.control = control;
-                       control.DataBindings.Add (this);
                }
 
-               internal void Check (BindingContext binding_context)
+               internal void Check ()
                {
-                       if (control == null)
+                       if (control == null || control.BindingContext == null)
                                return;
 
-                       string member_name = data_member;
-                       if (row_name != null)
-                               member_name = row_name;
-                       manager = control.BindingContext [data_source, member_name];
-                       manager.AddBinding (this);
-                       manager.PositionChanged += new EventHandler (PositionChangedHandler);
+                       if (manager == null) {
+                               manager = control.BindingContext [data_source];
+                               manager.AddBinding (this);
+                               manager.PositionChanged += new EventHandler (PositionChangedHandler);
+                       }
 
-                       WirePropertyValueChangedEvent ();
+                       if (manager.Position == -1)
+                               return;
 
-                       is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false);
+                       if (!checked_isnull) {
+                               is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false);
+                               checked_isnull = true;
+                       }
 
-                       PullData ();
+                       PushData ();
                }
 
-               internal void PushData ()
+               internal bool PullData ()
                {
-                       if (IsBinding == false)
-                               return;
+                       return PullData (false);
+               }
+
+               // Return false ONLY in case of error - and return true even in cases
+               // where no update was possible
+               bool PullData (bool force)
+               {
+                       if (IsBinding == false || manager.Current == null)
+                               return true;
+#if NET_2_0
+                       if (!force && datasource_update_mode == DataSourceUpdateMode.Never)
+                               return true;
+#endif
 
-                       data = prop_desc.GetValue (control);
-                       data = FormatData (data);
-                       SetPropertyValue (data);
+                       data = control_property.GetValue (control);
+
+                       try {
+                               SetPropertyValue (data);
+                       } catch (Exception e) {
+#if NET_2_0
+                               if (formatting_enabled) {
+                                       FireBindingComplete (BindingCompleteContext.DataSourceUpdate, e, e.Message);
+                                       return false;
+                               }
+#endif
+                               throw e;
+                       }
+
+#if NET_2_0
+                       if (formatting_enabled)
+                               FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null);
+#endif
+                       return true;
+               }
+
+               internal void PushData ()
+               {
+                       PushData (false);
                }
 
-               internal void PullData ()
+               void PushData (bool force)
                {
-                       if (IsBinding == false)
+                       if (manager == null || manager.IsSuspended || manager.Current == null)
                                return;
+#if NET_2_0
+                       if (!force && control_update_mode == ControlUpdateMode.Never)
+                               return;
+#endif
 
                        if (is_null_desc != null) {
                                bool is_null = (bool) is_null_desc.GetValue (manager.Current);
@@ -189,77 +401,117 @@ namespace System.Windows.Forms {
                                }
                        }
 
-                       if (row_name != null && col_name != null) {
-                               PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (col_name, true);
-                               object pulled = pd.GetValue (manager.Current);
-                               data = ParseData (pulled, pd.PropertyType);
-                       } else if (data_member != null) {
-                               PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (data_member, true);
-                               object pulled = pd.GetValue (manager.Current);
-                               data = ParseData (pulled, pd.PropertyType);
+                       PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
+                       if (pd == null) {
+                               data = manager.Current;
                        } else {
-                               object pulled = manager.Current;
-                               data = ParseData (pulled, pulled.GetType ());
+                               data = pd.GetValue (manager.Current);
+                       }
+
+                       try {
+                               data = FormatData (data);
+                               SetControlValue (data);
+                       } catch (Exception e) {
+#if NET_2_0
+                               if (formatting_enabled) {
+                                       FireBindingComplete (BindingCompleteContext.ControlUpdate, e, e.Message);
+                                       return;
+                               }
+#endif
+                               throw e;
                        }
 
-                       data = FormatData (data);
-                       SetControlValue (data);
+#if NET_2_0
+                       if (formatting_enabled)
+                               FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null);
+#endif
                }
 
                internal void UpdateIsBinding ()
                {
-                       PullData ();
+                       is_binding = false;
+                       if (control == null || !control.Created)
+                               return;
+                       if (manager == null || manager.IsSuspended)
+                               return;
+
+                       is_binding = true;
+                       PushData ();
                }
 
                private void SetControlValue (object data)
                {
-                       prop_desc.SetValue (control, data);
+                       control_property.SetValue (control, data);
                }
 
                private void SetPropertyValue (object data)
                {
-                       string member_name = data_member;
-                       if (col_name != null)
-                               member_name = col_name;
-                       PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (member_name, true);
+                       PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true);
                        if (pd.IsReadOnly)
                                return;
+                       data = ParseData (data, pd.PropertyType);
                        pd.SetValue (manager.Current, data);
                }
 
-               private void CurrentChangedHandler ()
+               private void ControlValidatingHandler (object sender, CancelEventArgs e)
                {
-                       if (changed_event != null) {
-                               changed_event.RemoveEventHandler (event_current, property_value_changed_handler);
-                               WirePropertyValueChangedEvent ();
+#if NET_2_0
+                       if (datasource_update_mode != DataSourceUpdateMode.OnValidation)
+                               return;
+#endif
+
+                       bool ok = true;
+                       // If the data doesn't seem to be valid (it can't be converted,
+                       // is the wrong type, etc, we reset to the old data value.
+                       // If Formatting is enabled, no exception is fired, but we get a false value
+                       try {
+                               ok = PullData ();
+                       } catch {
+                               ok = false;
                        }
+
+                       e.Cancel = !ok;
                }
 
-               private void WirePropertyValueChangedEvent ()
+               private void PositionChangedHandler (object sender, EventArgs e)
                {
-                       EventDescriptor changed_event = TypeDescriptor.GetEvents (manager.Current).Find (property_name + "Changed", false);
-                       if (changed_event == null)
-                               return;
-                       property_value_changed_handler = new EventHandler (PropertyValueChanged);
-                       changed_event.AddEventHandler (manager.Current, property_value_changed_handler);
-
-                       event_current = manager.Current;
+                       Check ();
+                       PushData ();
                }
 
-               private void PropertyValueChanged (object sender, EventArgs e)
+               EventDescriptor GetPropertyChangedEvent (object o, string property_name)
                {
-                       PullData ();
+                       if (o == null || property_name == null || property_name.Length == 0)
+                               return null;
+
+                       string event_name = property_name + "Changed";
+                       Type event_handler_type = typeof (EventHandler);
+
+                       EventDescriptor prop_changed_event = null;
+                       foreach (EventDescriptor event_desc in TypeDescriptor.GetEvents (o)) {
+                               if (event_desc.Name == event_name && event_desc.EventType == event_handler_type) {
+                                       prop_changed_event = event_desc;
+                                       break;
+                               }
+                       }
+
+                       return prop_changed_event;
                }
 
-               private void ControlValidatingHandler (object sender, CancelEventArgs e)
+               void SourcePropertyChangedHandler (object o, EventArgs args)
                {
-                       PullData ();
+                       PushData ();
                }
 
-               private void PositionChangedHandler (object sender, EventArgs e)
+#if NET_2_0
+               void ControlPropertyChangedHandler (object o, EventArgs args)
                {
+                       if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged)
+                               return;
+
                        PullData ();
                }
+#endif
 
                private object ParseData (object data, Type data_type)
                {
@@ -270,26 +522,45 @@ namespace System.Windows.Forms {
                                return e.Value;
                        if (e.Value == Convert.DBNull)
                                return e.Value;
+#if NET_2_0
+                       if (e.Value == null)
+                               return data_type.IsByRef ? null : Convert.DBNull;
+#endif
 
                        return ConvertData (e.Value, data_type);
                }
 
                private object FormatData (object data)
                {
-                       if (data_type == typeof (object)) 
-                               return data;
-
                        ConvertEventArgs e = new ConvertEventArgs (data, data_type);
 
                        OnFormat (e);
                        if (data_type.IsInstanceOfType (e.Value))
                                return e.Value;
 
+#if NET_2_0
+                       if (formatting_enabled) {
+                               if (e.Value == null || e.Value == Convert.DBNull) {
+                                       return null_value;
+                               }
+                               
+                               if (e.Value is IFormattable && data_type == typeof (string)) {
+                                       IFormattable formattable = (IFormattable) e.Value;
+                                       return formattable.ToString (format_string, format_info);
+                               }
+                       }
+#endif
+                       if (e.Value == null && data_type == typeof (object))
+                               return Convert.DBNull;
+
                        return ConvertData (data, data_type);
                }
 
                private object ConvertData (object data, Type data_type)
                {
+                       if (data == null)
+                               return null;
+
                        TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ());
                        if (converter != null && converter.CanConvertTo (data_type))
                                return converter.ConvertTo (data, data_type);
@@ -302,10 +573,27 @@ namespace System.Windows.Forms {
 
                        return null;
                }
+#if NET_2_0
+               void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message)
+               {
+                       BindingCompleteEventArgs args = new BindingCompleteEventArgs (this, 
+                                       exc == null ? BindingCompleteState.Success : BindingCompleteState.Exception,
+                                       context);
+                       if (exc != null) {
+                               args.SetException (exc);
+                               args.SetErrorText (error_message);
+                       }
+
+                       OnBindingComplete (args);
+               }
+#endif
 
                #region Events
                public event ConvertEventHandler Format;
                public event ConvertEventHandler Parse;
+#if NET_2_0
+               public event BindingCompleteEventHandler BindingComplete;
+#endif
                #endregion      // Events
        }
 }