Over the years, several articles have been written about the ColorPicker property editor. I also decided to look into this topic. I wanted to create something modern, simple and configurable. That’s why I used a simple HTML input color wrapped in a dijit widget.
Since there was very little code, I didn’t create a nuget package, just a code snippet that can be used in project. For simple scenario we need only EditorDescriptor and dijit widget.
EditorDescriptor
In the EditorDescriptor we are just registering dijit widget. Please note, that alloy prefix for “alloy/Editors/ColorEditor” will be different in your project.
Additionally I allow to pass list of predefined colors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Collections.Generic; using System.Linq; using EPiServer.ServiceLocation; using EPiServer.Shell.ObjectEditing.EditorDescriptors; namespace AlloyTemplates.Business.EditorDescriptors; [EditorDescriptorRegistration(TargetType = typeof(string), UIHint = UIHint)] public class ColorEditorDescriptor : EditorDescriptor { public const string UIHint = "AlloyColor"; private IPredefinedColors _predefinedColors; public ColorEditorDescriptor(IPredefinedColors predefinedColors) { _predefinedColors = predefinedColors; } public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes) { ClientEditingClass = "alloy/Editors/ColorEditor"; this.EditorConfiguration["predefinedColors"] = _predefinedColors.GetPredefinedColors(metadata.PropertyName, metadata.ContainerType.Name); base.ModifyMetadata(metadata, attributes); } } public interface IPredefinedColors { IEnumerable<string> GetPredefinedColors(string propertyName, string contentTypeName); } [ServiceConfiguration(typeof(IPredefinedColors))] internal class DefaultPredefinedColors : IPredefinedColors { public IEnumerable<string> GetPredefinedColors(string propertyName, string contentTypeName) => Enumerable.Empty<string>(); } |
Client side ColorPicker property editor
Client editor is a dijit widget wrapper for input color HTML element. It supports readonly state and validation when property is required.
When predefined list of colors is defined, the datalist element will be renderred.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
define([ "dojo/_base/declare", "dijit/_CssStateMixin", "dijit/_Widget", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "epi/shell/widget/_ValueRequiredMixin" ], function ( declare, _CssStateMixin, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _ValueRequiredMixin ) { return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin], { templateString: `<div class="dijitInline" tabindex="-1" role="presentation"> <div data-dojo-attach-point="stateNode, tooltipNode"> <input type="color" data-dojo-attach-point="colorPicker" /> </div> </div>`, value: null, onChange: function (value) { }, postCreate: function () { this.inherited(arguments); this.connect(this.colorPicker, "onChange", this._onColorChanged); if (this.params.predefinedColors.length > 0) { var list = document.createElement("datalist"); list.id = this.id + "-list"; this.params.predefinedColors.forEach(function (color) { var option = document.createElement("option"); option.value = color; list.appendChild(option); }); this.stateNode.appendChild(list); this.colorPicker.setAttribute("list", list.id); } }, isValid: function () { if (!this.required) { return true; } return this.value !== ""; }, _setValueAttr: function (value) { this._set("value", value); this.colorPicker.value = value; }, _getValueAttr: function () { var val = this.colorPicker && this.colorPicker.value; return val; }, _setReadOnlyAttr: function (value) { this._set("readOnly", value); if (value) { this.colorPicker.disabled = true; } else { this.colorPicker.removeAttribute("disabled"); } }, _onColorChanged: function (value) { this._set("value", value); if (this._started && this.validate()) { this.onChange(value); } } }); }); |
Using ColorPicker property
To use ColorPicker property we have to simply annotate property with ColorEditorDescriptor.UIHint UIHint.
1 2 3 4 5 6 7 |
public class StandardPage : SitePageData { // ... other property definitions [UIHint(ColorEditorDescriptor.UIHint)] public virtual string BackgroundColor { get; set; } } |
Registering list of predefined colors
List of colors can be defined using IPredefinedColors service. The interface has only one method that has to be implemented: GetPredefinedColors.
Method returns list of colors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
[ModuleDependency(typeof(InitializationModule))] public class ColorPickerInitializer : IConfigurableModule { public void ConfigureContainer(ServiceConfigurationContext context) { context.ConfigurationComplete += (o, e) => { context.Services.AddTransient<IPredefinedColors, CustomPredefinedColors>(); }; } public void Initialize(InitializationEngine context) {}; public void Uninitialize(InitializationEngine context) {}; public void Preload(string[] parameters){} } public class CustomPredefinedColors : IPredefinedColors { public IEnumerable<string> GetPredefinedColors(string propertyName, string contentTypeName) { return new[] { "#ff0000", "#0000ff", "#00ff00", "#ffff00", "#ddffff", "#0000ff", "#00ff00", "#ff00ff", "#ffffff", "#000000", }; } } |
When predefined list of colors is provided, then Editor can select one of the colors from the list.
Code can be copied from Gist.