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