| File: System\Windows\Forms\DataBinding\ListBindingConverter.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Globalization; using System.Reflection; namespace System.Windows.Forms; public class ListBindingConverter : TypeConverter { private static Type[]? s_ctorTypes; // the list of type of our ctor parameters. private static string?[]? s_ctorParamProps; // the name of each property to check to see if we need to init with a ctor. /// <summary> /// Creates our array of types on demand. /// </summary> private static Type[] ConstructorParamaterTypes { get { s_ctorTypes ??= [typeof(string), typeof(object), typeof(string), typeof(bool), typeof(DataSourceUpdateMode), typeof(object), typeof(string), typeof(IFormatProvider)]; return s_ctorTypes; } } /// <summary> /// Creates our array of param names on demand. /// </summary> private static string?[] ConstructorParameterProperties { get { s_ctorParamProps ??= [null, null, null, "FormattingEnabled", "DataSourceUpdateMode", "NullValue", "FormatString", "FormatInfo",]; return s_ctorParamProps; } } /// <summary> /// Gets a value indicating whether this converter can /// convert an object to the given destination type using the context. /// </summary> public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) { if (destinationType == typeof(InstanceDescriptor)) { return true; } return base.CanConvertTo(context, destinationType); } /// <summary> /// Converts the given object to another type. The most common types to convert /// are to and from a string object. The default implementation will make a call /// to ToString on the object if the object is valid and if the destination /// type is string. If this cannot convert to the destination type, this will /// throw a NotSupportedException. /// </summary> public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { ArgumentNullException.ThrowIfNull(destinationType); if (destinationType == typeof(InstanceDescriptor) && value is Binding) { Binding b = (Binding)value; return GetInstanceDescriptorFromValues(b); } return base.ConvertTo(context, culture, value, destinationType); } /// <summary> /// Creates an instance of this type given a set of property values /// for the object. This is useful for objects that are immutable, but still /// want to provide changeable properties. /// </summary> public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) { try { return new Binding( (string)propertyValues["PropertyName"]!, propertyValues["DataSource"], (string)propertyValues["DataMember"]!); } catch (InvalidCastException invalidCast) { throw new ArgumentException(SR.PropertyValueInvalidEntry, invalidCast); } catch (NullReferenceException nullRef) { throw new ArgumentException(SR.PropertyValueInvalidEntry, nullRef); } } /// <summary> /// Determines if changing a value on this object should require a call to /// CreateInstance to create a new value. /// </summary> public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) { return true; } /// <summary> /// Gets the best matching ctor for a given binding and fills it out, based on the /// state of the Binding and the optimal ctor. /// </summary> private static InstanceDescriptor GetInstanceDescriptorFromValues(Binding b) { // The BindingFormattingDialog turns on Binding::FormattingEnabled property. // however, when the user data binds a property using the PropertyBrowser, // Binding::FormattingEnabled is set to false. // The Binding class is not a component class, so we don't have the ComponentInitialize // method where we can set FormattingEnabled to true so we set it here. b.FormattingEnabled = true; bool isComplete = true; int lastItem = ConstructorParameterProperties.Length - 1; for (; lastItem >= 0; lastItem--) { // null means no prop is available, we quit here. if (ConstructorParameterProperties[lastItem] is null) { break; } // get the property and see if it needs to be serialized. string? constructorParameterProperty = ConstructorParameterProperties[lastItem]; if (constructorParameterProperty is null) { break; } PropertyDescriptor? prop = TypeDescriptor.GetProperties(b)[constructorParameterProperty]; if (prop is not null && prop.ShouldSerializeValue(b)) { break; } } // now copy the type array up to the point we quit. Type[] ctorParams = new Type[lastItem + 1]; Array.Copy(ConstructorParamaterTypes, 0, ctorParams, 0, ctorParams.Length); // Get the ctor info. ConstructorInfo? ctor = typeof(Binding).GetConstructor(ctorParams); Debug.Assert(ctor is not null, "Failed to find Binding ctor for types!"); if (ctor is null) { isComplete = false; ctor = typeof(Binding).GetConstructor( [ typeof(string), typeof(object), typeof(string) ]); } // now fill in the values. object?[] values = new object[ctorParams.Length]; for (int i = 0; i < values.Length; i++) { object? val = null; switch (i) { case 0: val = b.PropertyName; break; case 1: val = b.DataSource; break; case 2: val = b.BindingMemberInfo.BindingMember; break; default: string? constructorParameterProperty = ConstructorParameterProperties[i]; if (constructorParameterProperty is not null) { val = TypeDescriptor.GetProperties(b)[constructorParameterProperty]?.GetValue(b); } break; } values[i] = val; } return new InstanceDescriptor(ctor, values, isComplete); } }