Last active
May 20, 2019 15:45
-
-
Save jnm2/aa79720170f91ec3cd515c657da4347c to your computer and use it in GitHub Desktop.
Adds an easy IsNullable property to the End User Designer property grid to toggle the type between non-nullable and nullable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Required assembly references: | |
| - DevExpress.Data | |
| - DevExpress.Printing.Core | |
| - DevExpress.Utils.UI */ | |
| using System; | |
| using System.ComponentModel; | |
| using System.Globalization; | |
| using DevExpress.XtraReports.Design; | |
| using DevExpress.XtraReports.Native; | |
| using DevExpress.XtraReports.Parameters; | |
| public static class NullableParametersFeature | |
| { | |
| public static void Apply() | |
| { | |
| CustomTypeDescriptionProvider.Install(typeof(Parameter), (w, i) => new ParameterTypeDescriptor(w)); | |
| } | |
| private sealed class ParameterTypeDescriptor : CustomTypeDescriptor | |
| { | |
| public ParameterTypeDescriptor(ICustomTypeDescriptor wrapped) | |
| : base(wrapped) | |
| { | |
| } | |
| public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) | |
| { | |
| var properties = base.GetProperties(attributes); | |
| var r = new PropertyDescriptor[properties.Count + 1]; | |
| properties.CopyTo(r, 0); | |
| r[r.Length - 1] = IsNullablePropertyDescriptor.Instance; | |
| var typeProperty = properties[nameof(Parameter.Type)]; | |
| r[Array.IndexOf(r, typeProperty)] = new TypePropertyDescriptor(typeProperty); | |
| return new PropertyDescriptorCollection(r, true); | |
| } | |
| private sealed class IsNullablePropertyDescriptor : PropertyDescriptor | |
| { | |
| public static readonly IsNullablePropertyDescriptor Instance = new IsNullablePropertyDescriptor(); | |
| private IsNullablePropertyDescriptor() | |
| : base("IsNullable", new Attribute[] { BrowsableAttribute.Yes, new DisplayNameAttribute("Is Nullable"), new CategoryAttribute("Data") }) | |
| { | |
| } | |
| public override bool CanResetValue(object component) | |
| { | |
| var type = (component as Parameter)?.Type; | |
| return type != null && (!type.IsValueType || Nullable.GetUnderlyingType(type) != null); | |
| } | |
| public override object GetValue(object component) | |
| { | |
| var type = (component as Parameter)?.Type; | |
| if (type == null) return null; | |
| return !type.IsValueType || Nullable.GetUnderlyingType(type) != null; | |
| } | |
| public override void ResetValue(object component) => SetValue(component, false); | |
| public override void SetValue(object component, object value) | |
| { | |
| if (!(value is bool)) return; | |
| var parameter = component as Parameter; | |
| var type = parameter?.Type; | |
| if (type == null || !type.IsValueType) return; | |
| if ((bool)value) | |
| { | |
| if (Nullable.GetUnderlyingType(type) == null) | |
| parameter.Type = typeof(Nullable<>).MakeGenericType(type); | |
| } | |
| else | |
| { | |
| if (Nullable.GetUnderlyingType(type) != null) | |
| parameter.Type = Nullable.GetUnderlyingType(type); | |
| } | |
| } | |
| public override bool ShouldSerializeValue(object component) => false; | |
| public override Type ComponentType => typeof(Parameter); | |
| public override bool IsReadOnly => false; | |
| public override Type PropertyType => typeof(bool); | |
| } | |
| private sealed class TypePropertyDescriptor : PropertyDescriptorWrapper | |
| { | |
| public TypePropertyDescriptor(PropertyDescriptor wrapped) : base(wrapped, new Attribute[] | |
| { | |
| new TypeConverterAttribute(typeof(ParameterTypeConverterEx)) | |
| }) | |
| { | |
| } | |
| private sealed class ParameterTypeConverterEx : ParameterTypeConverter | |
| { | |
| public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) | |
| { | |
| var makeNullable = false; | |
| var str = value as string; | |
| if (str != null) | |
| { | |
| if (str.EndsWith(" (nullable)")) | |
| { | |
| value = str.Substring(0, str.Length - 11); | |
| makeNullable = true; | |
| } | |
| else if (str.EndsWith(", nullable)")) | |
| { | |
| value = str.Substring(0, str.Length - 11) + ')'; | |
| makeNullable = true; | |
| } | |
| } | |
| var r = (Type)base.ConvertFrom(context, culture, value); | |
| return makeNullable && r?.IsValueType == true ? typeof(Nullable<>).MakeGenericType(r) : r; | |
| } | |
| public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) | |
| { | |
| var type = value as Type; | |
| if (type != null) | |
| { | |
| type = Nullable.GetUnderlyingType(type); | |
| if (type != null) | |
| { | |
| var rString = base.ConvertTo(context, culture, type, destinationType) as string; | |
| if (rString != null) | |
| { | |
| rString = rString.Trim(); | |
| return rString.EndsWith(")") | |
| ? rString.Substring(0, rString.Length - 1) + ", nullable)" | |
| : rString + " (nullable)"; | |
| } | |
| } | |
| } | |
| return base.ConvertTo(context, culture, value, destinationType); | |
| } | |
| public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) | |
| { | |
| var r = base.GetStandardValues(context); | |
| if (r != null && context.Instance != null && IsNullablePropertyDescriptor.Instance.GetValue(context.Instance) as bool? == true) | |
| { | |
| var newValues = new object[r.Count]; | |
| r.CopyTo(newValues, 0); | |
| for (var i = 0; i < newValues.Length; i++) | |
| { | |
| var type = newValues[i] as Type; | |
| if (type?.IsValueType == true) newValues[i] = typeof(Nullable<>).MakeGenericType(type); | |
| } | |
| return new StandardValuesCollection(newValues); | |
| } | |
| return r; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| public class CustomTypeDescriptionProvider : TypeDescriptionProvider | |
| { | |
| public delegate ICustomTypeDescriptor GetTypeDescriptorDelegate(ICustomTypeDescriptor wrapped, object instance); | |
| private readonly GetTypeDescriptorDelegate getTypeDescriptor; | |
| public CustomTypeDescriptionProvider(TypeDescriptionProvider wrapped, GetTypeDescriptorDelegate getTypeDescriptor) | |
| : base(wrapped) | |
| { | |
| this.getTypeDescriptor = getTypeDescriptor; | |
| } | |
| public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) | |
| { | |
| return getTypeDescriptor(base.GetTypeDescriptor(objectType, instance), instance); | |
| } | |
| public static void Install(Type componentType, GetTypeDescriptorDelegate getTypeDescriptor) | |
| { | |
| TypeDescriptor.AddProvider(new CustomTypeDescriptionProvider(TypeDescriptor.GetProvider(componentType), getTypeDescriptor), componentType); | |
| } | |
| } |
Author
@HaidarZ Sorry, gist authors are not notified when people comment. You use it by calling NullableParametersFeature.Apply(); once in your application (technically, once per AppDomain). I updated the gist to include the missing using directive and list the assembly references needed in your csproj.
Author
@HaidarZ GitHub just enabled notifications for gist comments!
Thanks @jnm2 👍
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How do we use this solution ? Moreover there's missing references.