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. 19 // 20 // Copyright (c) 2004-2005 Novell, Inc. 21 // 22 // Authors: 23 // Peter Bartok pbartok@novell.com 24 // Jackson Harper jackson@ximian.com 25 // 26 27 28 using System.ComponentModel; 29 30 namespace System.Windows.Forms { 31 32 [TypeConverter (typeof (ListBindingConverter))] 33 public class Binding { 34 35 private string property_name; 36 private object data_source; 37 private string data_member; 38 39 private bool is_binding; 40 private bool checked_isnull; 41 42 private BindingMemberInfo binding_member_info; 43 private IBindableComponent control; 44 45 private BindingManagerBase manager; 46 private PropertyDescriptor control_property; 47 private PropertyDescriptor is_null_desc; 48 49 private object data; 50 private Type data_type; 51 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 Binding(string propertyName, object dataSource, string dataMember)60 public Binding (string propertyName, object dataSource, string dataMember) 61 : this (propertyName, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null) 62 { 63 } 64 Binding(string propertyName, object dataSource, string dataMember, bool formattingEnabled)65 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled) 66 : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null) 67 { 68 } 69 Binding(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode)70 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode) 71 : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null) 72 { 73 } 74 Binding(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue)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) 77 { 78 } 79 Binding(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString)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) 82 { 83 } 84 Binding(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo)85 public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo) 86 { 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; 95 } 96 #endregion // Public Constructors 97 98 #region Public Instance Properties 99 [DefaultValue (null)] 100 public IBindableComponent BindableComponent { 101 get { 102 return control; 103 } 104 } 105 106 public BindingManagerBase BindingManagerBase { 107 get { 108 return manager; 109 } 110 } 111 112 public BindingMemberInfo BindingMemberInfo { 113 get { 114 return binding_member_info; 115 } 116 } 117 118 [DefaultValue (null)] 119 public Control Control { 120 get { 121 return control as Control; 122 } 123 } 124 125 [DefaultValue (ControlUpdateMode.OnPropertyChanged)] 126 public ControlUpdateMode ControlUpdateMode { 127 get { 128 return control_update_mode; 129 } 130 set { 131 control_update_mode = value; 132 } 133 } 134 135 public object DataSource { 136 get { 137 return data_source; 138 } 139 } 140 141 [DefaultValue (DataSourceUpdateMode.OnValidation)] 142 public DataSourceUpdateMode DataSourceUpdateMode { 143 get { 144 return datasource_update_mode; 145 } 146 set { 147 datasource_update_mode = value; 148 } 149 } 150 151 public object DataSourceNullValue { 152 get { 153 return datasource_null_value; 154 } 155 set { 156 datasource_null_value = value; 157 } 158 } 159 160 [DefaultValue (false)] 161 public bool FormattingEnabled { 162 get { 163 return formatting_enabled; 164 } 165 set { 166 if (formatting_enabled == value) 167 return; 168 169 formatting_enabled = value; 170 PushData (); 171 } 172 } 173 174 [DefaultValue (null)] 175 public IFormatProvider FormatInfo { 176 get { 177 return format_info; 178 } 179 set { 180 if (value == format_info) 181 return; 182 183 format_info = value; 184 if (formatting_enabled) 185 PushData (); 186 } 187 } 188 189 public string FormatString { 190 get { 191 return format_string; 192 } 193 set { 194 if (value == null) 195 value = String.Empty; 196 if (value == format_string) 197 return; 198 199 format_string = value; 200 if (formatting_enabled) 201 PushData (); 202 } 203 } 204 205 public bool IsBinding { 206 get { 207 if (manager == null || manager.IsSuspended) 208 return false; 209 210 return is_binding; 211 } 212 } 213 214 public object NullValue { 215 get { 216 return null_value; 217 } 218 set { 219 if (value == null_value) 220 return; 221 222 null_value = value; 223 if (formatting_enabled) 224 PushData (); 225 } 226 } 227 228 [DefaultValue ("")] 229 public string PropertyName { 230 get { 231 return property_name; 232 } 233 } 234 #endregion // Public Instance Properties 235 ReadValue()236 public void ReadValue () 237 { 238 PushData (true); 239 } 240 WriteValue()241 public void WriteValue () 242 { 243 PullData (true); 244 } 245 246 #region Protected Instance Methods OnBindingComplete(BindingCompleteEventArgs e)247 protected virtual void OnBindingComplete (BindingCompleteEventArgs e) 248 { 249 if (BindingComplete != null) 250 BindingComplete (this, e); 251 } 252 OnFormat(ConvertEventArgs cevent)253 protected virtual void OnFormat (ConvertEventArgs cevent) 254 { 255 if (Format!=null) 256 Format (this, cevent); 257 } 258 OnParse(ConvertEventArgs cevent)259 protected virtual void OnParse (ConvertEventArgs cevent) 260 { 261 if (Parse!=null) 262 Parse (this, cevent); 263 } 264 #endregion // Protected Instance Methods 265 266 internal string DataMember { 267 get { return data_member; } 268 } 269 SetControl(IBindableComponent control)270 internal void SetControl (IBindableComponent control) 271 { 272 if (control == this.control) 273 return; 274 275 control_property = TypeDescriptor.GetProperties (control).Find (property_name, true); 276 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.")); 281 282 data_type = control_property.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached 283 284 Control ctrl = control as Control; 285 if (ctrl != null) { 286 ctrl.Validating += new CancelEventHandler (ControlValidatingHandler); 287 if (!ctrl.IsHandleCreated) 288 ctrl.HandleCreated += new EventHandler (ControlCreatedHandler); 289 } 290 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; 295 UpdateIsBinding (); 296 } 297 Check()298 internal void Check () 299 { 300 if (control == null || control.BindingContext == null) 301 return; 302 303 if (manager == null) { 304 manager = control.BindingContext [data_source, binding_member_info.BindingPath]; 305 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.", 309 "dataMember"); 310 311 manager.AddBinding (this); 312 manager.PositionChanged += new EventHandler (PositionChangedHandler); 313 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)); 318 } 319 } 320 321 if (manager.Position == -1) 322 return; 323 324 if (!checked_isnull) { 325 is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false); 326 checked_isnull = true; 327 } 328 329 PushData (); 330 } 331 PullData()332 internal bool PullData () 333 { 334 return PullData (false); 335 } 336 337 // Return false ONLY in case of error - and return true even in cases 338 // where no update was possible PullData(bool force)339 bool PullData (bool force) 340 { 341 if (IsBinding == false || manager.Current == null) 342 return true; 343 if (!force && datasource_update_mode == DataSourceUpdateMode.Never) 344 return true; 345 346 data = control_property.GetValue (control); 347 if (data == null) 348 data = datasource_null_value; 349 350 try { 351 SetPropertyValue (data); 352 } catch (Exception e) { 353 if (formatting_enabled) { 354 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, e, e.Message); 355 return false; 356 } 357 throw e; 358 } 359 360 if (formatting_enabled) 361 FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null); 362 return true; 363 } 364 PushData()365 internal void PushData () 366 { 367 PushData (false); 368 } 369 PushData(bool force)370 void PushData (bool force) 371 { 372 if (manager == null || manager.IsSuspended || manager.Count == 0 || manager.Position == -1) 373 return; 374 if (!force && control_update_mode == ControlUpdateMode.Never) 375 return; 376 377 if (is_null_desc != null) { 378 bool is_null = (bool) is_null_desc.GetValue (manager.Current); 379 if (is_null) { 380 data = Convert.DBNull; 381 return; 382 } 383 } 384 385 PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true); 386 if (pd == null) { 387 data = manager.Current; 388 } else { 389 data = pd.GetValue (manager.Current); 390 } 391 392 if ((data == null || data == DBNull.Value) && null_value != null) 393 data = null_value; 394 395 try { 396 data = FormatData (data); 397 SetControlValue (data); 398 } catch (Exception e) { 399 if (formatting_enabled) { 400 FireBindingComplete (BindingCompleteContext.ControlUpdate, e, e.Message); 401 return; 402 } 403 throw e; 404 } 405 406 if (formatting_enabled) 407 FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null); 408 } 409 UpdateIsBinding()410 internal void UpdateIsBinding () 411 { 412 is_binding = false; 413 if (control == null || (control is Control && !((Control)control).IsHandleCreated)) 414 return; 415 416 is_binding = true; 417 PushData (); 418 } 419 SetControlValue(object data)420 private void SetControlValue (object data) 421 { 422 control_property.SetValue (control, data); 423 } 424 SetPropertyValue(object data)425 private void SetPropertyValue (object data) 426 { 427 PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true); 428 if (pd.IsReadOnly) 429 return; 430 data = ParseData (data, pd.PropertyType); 431 pd.SetValue (manager.Current, data); 432 } 433 ControlValidatingHandler(object sender, CancelEventArgs e)434 private void ControlValidatingHandler (object sender, CancelEventArgs e) 435 { 436 if (datasource_update_mode != DataSourceUpdateMode.OnValidation) 437 return; 438 439 bool ok = true; 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 443 try { 444 ok = PullData (); 445 } catch { 446 ok = false; 447 } 448 449 e.Cancel = !ok; 450 } 451 ControlCreatedHandler(object o, EventArgs args)452 private void ControlCreatedHandler (object o, EventArgs args) 453 { 454 UpdateIsBinding (); 455 } 456 PositionChangedHandler(object sender, EventArgs e)457 private void PositionChangedHandler (object sender, EventArgs e) 458 { 459 Check (); 460 PushData (); 461 } 462 GetPropertyChangedEvent(object o, string property_name)463 EventDescriptor GetPropertyChangedEvent (object o, string property_name) 464 { 465 if (o == null || property_name == null || property_name.Length == 0) 466 return null; 467 468 string event_name = property_name + "Changed"; 469 Type event_handler_type = typeof (EventHandler); 470 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; 475 break; 476 } 477 } 478 479 return prop_changed_event; 480 } 481 SourcePropertyChangedHandler(object o, EventArgs args)482 void SourcePropertyChangedHandler (object o, EventArgs args) 483 { 484 PushData (); 485 } 486 ControlPropertyChangedHandler(object o, EventArgs args)487 void ControlPropertyChangedHandler (object o, EventArgs args) 488 { 489 if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged) 490 return; 491 492 PullData (); 493 } 494 ParseData(object data, Type data_type)495 private object ParseData (object data, Type data_type) 496 { 497 ConvertEventArgs e = new ConvertEventArgs (data, data_type); 498 499 OnParse (e); 500 if (data_type.IsInstanceOfType (e.Value)) 501 return e.Value; 502 if (e.Value == Convert.DBNull) 503 return e.Value; 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; 508 } 509 510 return ConvertData (e.Value, data_type); 511 } 512 FormatData(object data)513 private object FormatData (object data) 514 { 515 ConvertEventArgs e = new ConvertEventArgs (data, data_type); 516 517 OnFormat (e); 518 if (data_type.IsInstanceOfType (e.Value)) 519 return e.Value; 520 521 if (formatting_enabled) { 522 if ((e.Value == null || e.Value == Convert.DBNull) && null_value != null) 523 return null_value; 524 525 if (e.Value is IFormattable && data_type == typeof (string)) { 526 IFormattable formattable = (IFormattable) e.Value; 527 return formattable.ToString (format_string, format_info); 528 } 529 } 530 531 if (e.Value == null && data_type == typeof (object)) 532 return Convert.DBNull; 533 534 return ConvertData (data, data_type); 535 } 536 ConvertData(object data, Type data_type)537 private object ConvertData (object data, Type data_type) 538 { 539 if (data == null) 540 return null; 541 542 TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ()); 543 if (converter != null && converter.CanConvertTo (data_type)) 544 return converter.ConvertTo (data, data_type); 545 546 converter = TypeDescriptor.GetConverter (data_type); 547 if (converter != null && converter.CanConvertFrom (data.GetType())) 548 return converter.ConvertFrom (data); 549 550 if (data is IConvertible) { 551 object res = Convert.ChangeType (data, data_type); 552 if (data_type.IsInstanceOfType (res)) 553 return res; 554 } 555 556 return null; 557 } FireBindingComplete(BindingCompleteContext context, Exception exc, string error_message)558 void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message) 559 { 560 BindingCompleteEventArgs args = new BindingCompleteEventArgs (this, 561 exc == null ? BindingCompleteState.Success : BindingCompleteState.Exception, 562 context); 563 if (exc != null) { 564 args.SetException (exc); 565 args.SetErrorText (error_message); 566 } 567 568 OnBindingComplete (args); 569 } 570 571 #region Events 572 public event ConvertEventHandler Format; 573 public event ConvertEventHandler Parse; 574 public event BindingCompleteEventHandler BindingComplete; 575 #endregion // Events 576 } 577 } 578