Multiple property value converters for a property editor
Hi!
I have a scenario with a MultiNodeTreePicker2 where I don't what to have the "whole" IPublishedContent but just the Udi's from the property.
I figured that I could implement a Property Value Converter that handles "IEnumerable
[PropertyValueType(typeof(IEnumerable<Udi>))]
public class MultiNodeTreePicker2UidPropertyConverter : PropertyValueConverterBase {
....
}
Now all the calls, even the ones with "cointent.GetPropertyValue< IEnumerable< IPublishedContent > >("yada") is routed to my new property value converter and the core converter is never used.
What I'm I doing wrong? Or can there only be one property value converter per property editor?
My problem is that I want my convert to take care of the Udi's and then the core converter to return ipublished content. So I don't want to de-register it I want them to run side by side but respecting the attribute:
Ah ok, then yes - currently only one property value converter can be registered to a property-editor.
The .GetPropertyValue<T> method doesn't add any influence to the value-converter at all. The <T> bit is ultimately used by the TryConvertTo<T> extension method, of which a .NET TypeConverter could be registered and used to convert from one object-type to another.
It requires a bit more code, but it's possible.
Let me know if that doesn't make sense, I'll try to provide code snippets.
So if I understand you correct, since there is only one property value converter per property editor - I need to go with my custom converter, that returns a udi, and then hook into the TryConvertTo-extention and handle when there is a IPublishContent-type parameter to this method?
If you have any hints on how to do the last part that would be very much appreciated.
The TryConvertTo extension method (in Umbraco.Core) does various things to attempt to return the correct type, including calling any registered TypeConverter classes.
Here's an example that I've just coded... keep in mind that I have not tested this at all, you'll need to tinker with it
public class MyUdiConverterType : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(IPublishedContent) || destinationType == typeof(IEnumerable<IPublishedContent>))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var helper = new UmbracoHelper(UmbracoContext.Current);
// deal with single items
if (destinationType == typeof(IPublishedContent))
{
if (value is GuidUdi guidUdi)
return helper.TypedContent(guidUdi.Guid);
}
// deal with multiple items
if (destinationType == typeof(IEnumerable<IPublishedContent>))
{
if (value is IEnumerable<GuidUdi> guidUdis)
return helper.TypedContent(guidUdis.Select(x => x.Guid));
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Then in your app-startup code, you'll need to dynamically register the custom TypeConverter...
TypeDescriptor.AddAttributes(typeof(Udi), new TypeConverterAttribute(typeof(MyUdiConverterType)));
So once the custom TypeConverter is in place, whenever the content.GetPropertyValue<IEnumerable<IPublishedContent>>("alias") is called... your custom property-value-converter will return a collection of Udi objects, then Umbraco will use TryConvertTo to cast/convert the IEnumerable<Udi> to IEnumerable<IPublishedContent>, by running it through the MyUdiConverterType.ConvertTo method.
You'll probably need to do some tinkering around with the ConvertTo method to make sure that returns exactly what you want.
I hope that makes sense? It feels like there are a few moving parts that need aligning with all that?
public class MyUdiConverterType : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(IPublishedContent) ||
destinationType == typeof(IEnumerable<IPublishedContent>) ||
destinationType == typeof(List<IPublishedContent>) ||
destinationType == typeof(IList<IPublishedContent>))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var helper = new UmbracoHelper(UmbracoContext.Current);
// deal with single items
if (destinationType == typeof(IPublishedContent))
{
if (value is GuidUdi guidUdi)
return helper.TypedContent(guidUdi.Guid);
}
// deal with multiple items
if (destinationType == typeof(IEnumerable<IPublishedContent>))
{
if (value is IEnumerable<GuidUdi> guidUdis)
{
var list = new List<IPublishedContent>();
foreach (var item in guidUdis)
{
list.Add(helper.TypedContent(item.Guid));
}
return list;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
My custom property value converter
[PropertyValueType(typeof(IEnumerable<GuidUdi>))]
[PropertyValueCache(PropertyCacheValue.Object, PropertyCacheLevel.ContentCache)]
[PropertyValueCache(PropertyCacheValue.Source, PropertyCacheLevel.Content)]
[PropertyValueCache(PropertyCacheValue.XPath, PropertyCacheLevel.Content)]
public class MultiNodeTreePickerPropertyConverter : PropertyValueConverterBase
{
public override bool IsConverter(PublishedPropertyType propertyType)
{
if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePicker2Alias))
return true;
return false;
}
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePicker2Alias))
{
var nodeIds = source.ToString()
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
.Select(GuidUdi.Parse).ToList();
return nodeIds;
}
return null;
}
public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
{
if (source == null)
{
return null;
}
return (IEnumerable<GuidUdi>) source;
}
}
And then the registration during startup:
public class Startup : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
PropertyValueConvertersResolver.Current.RemoveType<Umbraco.Web.PropertyEditors.ValueConverters.MultiNodeTreePickerPropertyConverter>();
TypeDescriptor.AddAttributes(typeof(GuidUdi), new TypeConverterAttribute(typeof(MyUdiConverterType)));
TypeDescriptor.AddAttributes(typeof(IEnumerable<GuidUdi>), new TypeConverterAttribute(typeof(MyUdiConverterType)));
}
}
Then my view will return a List of GuidUdi's when not providing the type parameter, but when I provide the type parameter I get IPublishedContent back.
<p>
@Umbraco.Field("relatedContent")
</p>
<p>
Val:
@{
var test = Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("relatedContent");
}
@if (test != null)
{
<text>Count:</text> @test.Count()
}
Thanks a lot for taking the time to help me on this one! #h5yr!
Multiple property value converters for a property editor
Hi!
I have a scenario with a MultiNodeTreePicker2 where I don't what to have the "whole" IPublishedContent but just the Udi's from the property.
I figured that I could implement a Property Value Converter that handles "IEnumerable
Now all the calls, even the ones with "cointent.GetPropertyValue< IEnumerable< IPublishedContent > >("yada") is routed to my new property value converter and the core converter is never used.
What I'm I doing wrong? Or can there only be one property value converter per property editor?
// m
Hi Markus,
You'll need to de-register the existing (core) property-value converter, like this...
Then your custom converter will be picked up by Umbraco core's startup code.
Cheers,
- Lee
Thanks Lee!
My problem is that I want my convert to take care of the Udi's and then the core converter to return ipublished content. So I don't want to de-register it I want them to run side by side but respecting the attribute:
/ m
Hi Markus,
Ah ok, then yes - currently only one property value converter can be registered to a property-editor.
The
.GetPropertyValue<T>
method doesn't add any influence to the value-converter at all. The<T>
bit is ultimately used by theTryConvertTo<T>
extension method, of which a .NET TypeConverter could be registered and used to convert from one object-type to another.It requires a bit more code, but it's possible.
Let me know if that doesn't make sense, I'll try to provide code snippets.
Cheers,
- Lee
Hi!
So if I understand you correct, since there is only one property value converter per property editor - I need to go with my custom converter, that returns a udi, and then hook into the TryConvertTo-extention and handle when there is a IPublishContent-type parameter to this method?
If you have any hints on how to do the last part that would be very much appreciated.
Thanks again Lee!
Hi Markus,
The
TryConvertTo
extension method (in Umbraco.Core) does various things to attempt to return the correct type, including calling any registeredTypeConverter
classes.Here's an example that I've just coded... keep in mind that I have not tested this at all, you'll need to tinker with it
Then in your app-startup code, you'll need to dynamically register the custom
TypeConverter
......
So once the custom TypeConverter is in place, whenever the
content.GetPropertyValue<IEnumerable<IPublishedContent>>("alias")
is called... your custom property-value-converter will return a collection ofUdi
objects, then Umbraco will useTryConvertTo
to cast/convert theIEnumerable<Udi>
toIEnumerable<IPublishedContent>
, by running it through theMyUdiConverterType.ConvertTo
method.You'll probably need to do some tinkering around with the
ConvertTo
method to make sure that returns exactly what you want.I hope that makes sense? It feels like there are a few moving parts that need aligning with all that?
Cheers,
- Lee
Hi Lee!
What a hero you are, thanks for explaining this in a very clear way!
I'll try to get this working and get back when I have something running.
Thanks man!
// m
Thanks Lee!
I got it to work!
My custom property value converter
And then the registration during startup:
Then my view will return a List of GuidUdi's when not providing the type parameter, but when I provide the type parameter I get IPublishedContent back.
Thanks a lot for taking the time to help me on this one! #h5yr!
Cool! Happy to hear that it worked for you.
is working on a reply...