-
-
Save robertjf/78a97240e4304b8e5bcc to your computer and use it in GitHub Desktop.
using nuPickers; | |
using Our.Umbraco.Ditto; | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Globalization; | |
using System.Linq; | |
using System.Reflection; | |
using System.Web; | |
using Umbraco.Core.Models; | |
using Umbraco.Web; | |
namespace Extensions.TypeConverters | |
{ | |
/// <summary> | |
/// Converts a NuPicker value into a Ditto object or string. | |
/// </summary> | |
/// <remarks> | |
/// This can be used with a single type (e.g. string) or IEnumerable<>. | |
/// | |
/// If the target type is string, then this will return the Name(s) of each picked content node. | |
/// Otherwise it will use Ditto to try and convert the IPublishedContent item(s) to the target type. | |
/// | |
/// Handles single objects as well as IEnumerable<>. | |
/// | |
/// E.G.: | |
/// | |
/// [TypeConverter(typeof(DittoNuPickerTypeConverter))] | |
/// public IEnumerable<MyPoco> MyProperty { get; set; } | |
/// | |
/// // Returns a single MyPoco | |
/// [TypeConverter(typeof(DittoNuPickerTypeConverter))] | |
/// public MyPoco MyProperty { get; set; } | |
/// | |
/// // Returns a list of strings based on the Name of each item. | |
/// [TypeConverter(typeof(DittoNuPickerTypeConverter))] | |
/// public IEnumerable<string> MyProperty { get; set; } | |
/// | |
/// // Returns the Name of the first item. | |
/// [TypeConverter(typeof(DittoNuPickerTypeConverter))] | |
/// public string MyProperty { get; set; } | |
/// </remarks> | |
public class DittoNuPickerTypeConverter : TypeConverter | |
{ | |
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) | |
{ | |
return typeof(Picker).IsAssignableFrom(sourceType); | |
} | |
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) | |
{ | |
MethodInfo dittoAs = typeof(Our.Umbraco.Ditto.PublishedContentExtensions) | |
.GetMethod( | |
"As", | |
BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly, | |
null, | |
new Type[] { | |
typeof(IPublishedContent), | |
typeof(Action<ConvertingTypeEventArgs>), | |
typeof(Action<ConvertedTypeEventArgs>), | |
typeof(CultureInfo) }, | |
null); | |
// get type of objects to be returned in the collection | |
Type outputItemType = context.PropertyDescriptor.PropertyType; | |
bool isList = (outputItemType.IsGenericType && outputItemType.GetGenericTypeDefinition() == typeof(IEnumerable<>)); | |
bool isString = isList ? | |
typeof(string).IsAssignableFrom(outputItemType.GetGenericArguments()[0]) : | |
outputItemType.IsAssignableFrom(typeof(string)); | |
// create a list for the output collection | |
// Get the list as IPublishedContent. | |
var picker = value as Picker; | |
var items = picker.AsPublishedContent(); | |
if (isList) | |
{ | |
object outputList = Activator.CreateInstance(typeof(List<>).MakeGenericType(outputItemType.GetGenericArguments()[0])); | |
foreach (IPublishedContent inputItem in items) | |
{ | |
if (isString) | |
{ | |
((IList)outputList).Add(inputItem.Name); | |
} | |
else { | |
((IList)outputList).Add( | |
dittoAs | |
.MakeGenericMethod(new[] { outputItemType }) | |
.Invoke(null, new[] { inputItem, null, null, null }) | |
); | |
} | |
} | |
return outputList; | |
} | |
else | |
{ | |
if (isString) | |
{ | |
return items.Any() ? items.FirstOrDefault().Name : null; | |
} | |
else | |
{ | |
return dittoAs | |
.MakeGenericMethod(new[] { outputItemType }) | |
.Invoke(null, new[] { items.FirstOrDefault(), null, null, null }); | |
} | |
} | |
} | |
} | |
} |
yep, that works really nicely - I'd seen that in the source earlier but hadn't considered using it like this...
however, will it return a single object (string or POCO) if the target type isn't IEnumerable<>?
@JimBobSquarePants - Just tested it; fails with the following:
NuPickerMultiNodePickerConverter cannot convert from System.String[].
looks like it needs some tweaking.
What Ditto version are you running and what configuration is the picker set to. json, xml, or csv?
It's just that I've been running that particular bit of code now for weeks without issue.
Just tipping in, running version 0.7.0 it will indeed fail with the error 'cannot convert from System.String[]' if either the property is not an IEnumerable<> or if the PickedKeys is an empty collection.
What I ended up doing for now is:
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var picker = value as Picker;
if (picker == null || !picker.PickedKeys.Any())
{
return Enumerable.Empty<string>(); //base.ConvertFrom(context, culture, Enumerable.Empty<string>());
}
return base.ConvertFrom(context, culture, picker.PickedKeys);
}
And then I have a NuPickersSingleConverter
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var picker = value as Picker;
if (picker == null || !picker.PickedKeys.Any())
{
return null;
}
return base.ConvertFrom(context, culture, picker.PickedKeys.First());
}
Not too pretty, but it works 😃
Hey, @JimBobSquarePants, does your technique still work? Or is there a better way now?
You can make this a lot simpler by inheriting one of the built in converters. I'm using latest MyGet build here...