One of the most frequently and eagerly used programming constructs of the Microsoft.Net Framework is Enum. There are several interesting features that make it very compelling to use to for all kinds of dropdowns and checklists:

  • The bounds factor ? proper use of Enum type guarantee that the selected value will fall within the constraints of the allowed value set.
  • The ability to treat Enums as flags (and compound them into flag sets) as well as a one-of selector.
  • The ease of use and potentially complete separation of the ?Enum value? from the underlying machine type representation that ensures the most efficient memory usage.

Surprisingly enough EPiServer as it stands right now does not have an easy facility to turn Enums into properties. To give credit where credit is due, the EPiServer framework provides a nice surrogate that mimic that behaviour to a degree. The relevant property types are:

  • PropertyAppSettingsMultiple  – which ?creates check boxes with options that are defined in the AppSettings section in web.config. The name of the property should match the key for the app setting.?
  • PropertyAppSettings  – which ?creates a drop down list with options that are defined in the AppSettings section in web.config. The name of the property should match the key for the app setting.?

You quickly realize though that the properties have some limitations that makes their use a bit less compelling:

  • The properties are not strongly typed
  • The property entry in AppSettings section has to have the name that matches the property name on the page.
  • It?s rather poorly documented, Other than relating to this blog entry or Erik?s post documenting it I could not find any other examples on how to use them. (but then again, who needs docs really when we have Reflector)
  • You cannot have the very same property duplicated on the page since you can only have a single property of a given name per page. So you need to have multiple entries in AppSettings that match the name of each of those properties on your pages. I know? semantics but still?
  • You are working on strings rather than enums (Did i mention it?s not type safe?)
  • The values in the AppSettings are stored in a somewhat DLS-y manner (consecutive options are separated from each other with the ?|? character, the name and the value are separated with a ?;?, for example: <add key = "RegionId" value="First Option;Option1|Default Option;Option2|Disabled Option;Option3" /> ) and I have had on an occasion entered a string there that caused the server to crash.
  • The values are not translatable, or at least I could not find how to do it and any Reflector digging rendered no results either.

The solution to the problem:

The solution that the blog post describes is:

  • Type safe
  • Supports UI localization
  • Easy to use in your custom implementation
  • Flexible in allowing you to switch between the Enum being multi-choice or a single choice solution
  • fully defined in the code ? no need to modify web,config.

All you need to do to create your own custom Enum property is declare it.

using Cognifide.EPiserverControls.EnumDropDown; [PageDefinitionTypePlugIn(DisplayName = "Sample Enum Property")] public class SampleProperty : PropertyEnumSelector<MySampleEnum> { }

That?s it, really!

Now if you want to customize it; obviously you need to provide the information for the property to work with. But let?s stop for a while and think, where does that information belong?

  • The property should worry about the mechanics of editing and the tediousness of persistence of the edited value, it clearly is not the place to specify the link between the Enum type and its human friendly drop down text.
  • Web.config is for settings, not for describing content types and definitely not to enforce the property names.
  • So where should the metadata around your Enum type be? How about we put it around the Enum type itself?
[Flags] public enum MySampleEnum { [ValueVisualisation(Name="First Option", LanguageKey="/enum/first")] Option1,
[ValueVisualisation(Name="Default Option", Selected=true, LanguageKey="/enum/second")] Option2,
[ValueVisualisation(Name="Disabled Option", Enabled=false, LanguageKey="/enum/disabled")] Option3,
[ValueVisualisation(Name="Hidden Option", Hide=true, LanguageKey="/enum/hidden")] Option4,
[ValueVisualisation(Name="Fifth Option", LanguageKey="/enum/fifth")] Option5,
}

What does all that do?

The property using an Enum described this way will look the following way in EPiServer:

SampleEnumPropertyFlags

Let?s get into the what?s happened here as there?s more than catches the eye.

As you can see the enum values are named like they are in the attributes describing them, Default option is selected just like you could have expected by looking at the Selected=true in its attribute. The Enabled=false in the Disabled option results in an option that cannot be changed. The unchangeable option can be both unselected or selected by default. You may want to disable some deprecated options but still show them in the UI as a legacy but for some reason you might not want users to be able to change their value. Option4 is clearly missing, which is intended and specified by the Hide=true, as you might want to hide options that have been removed form the UI and on the same time you don?t want to loose compatibility with a legacy API that uses the option.

Also There is one thing about the first option on the list ? it?s translated! The mechanism to specify the translation is based on the standard EPiServer localization files that you can find in the ?lang? folder in your typical EPiServer installation. The Translation for the property looks like the following

<?xml version="1.0" encoding="utf-8" standalone="yes"?> <languages> <language name="English" id="en"> <enum> <first>First Option Translated EN</first> </enum> </language> </languages>

so you see the value will take its translation key from the LanguageKey="/enum/first".

Ok, so we have the PropertyAppSettingsMultiple replacement, but what about PropertyAppSettings?

So here I reach the place where I am not 100% certain whether I took the proper path but I am sure I will hear about it if I didn?t :) The place where the property determines if it?s supposed to allow for multi selection or not is whether the Enum is marked with the [Flags] attribute. Obviously Flags mean that the property is a form of a bit flag and should be able to allow the user to flip the bits independently rather than being the ?one-of? type. So once you remove the [Flags] form your enum you?re going to see it rendered the following way:

SampleEnumPropertyNoFlags

But hey, it?s not a one way trip, if you decide that you want the enum rendered in another type of property and force the opposite behaviour you can always override it in the derived property like follows:

[PageDefinitionTypePlugIn(DisplayName = "Sample Enum Property Checklist")] public class SamplePropertyChecklist : PropertyEnumSelector<MySampleEnum> { static SamplePropertyChecklist() { MultiSelectEnum = true; } }

so even if you think my usage of [Flags] is faulty, the damage can be undone :)

Ok, but what about the enums that we have no control over ? like enums form libraries. Obviously cannot retrofit attributes into them. Well, true but we still can have all of the control over the property.

Let?s assume for a moment that this is our legacy enum:

public enum MySampleLegacyEnum { Option1, Option2, Option3, Option4, Option5, }

You can just do nothing and simply use it in the property but the UI will display the not so nice Option1? names. That can be easily fixed ? Again, specifying translation is the easiest way of achieving that goal. An xml for that enum would look like follows:

<?xml version="1.0" encoding="utf-8" standalone="yes"?> <languages> <language name="English" id="en"> <MySampleLegacyEnum> <Option1>First Option</Option1> <Option2>Default Option</Option2> <Option3>Disabled Option</Option3> <Option4>Hidden Option</Option4> <Option5>Fifth Option</Option5> </MySampleLegacyEnum> </language> </languages>

The default values can be added by retrofitting the ?DefaultValues? list with the elements that you want selected in the scenario where the property is first displayed for editing. You can also modify the inherited AvailableItems list to set the rest of the properties otherwise adjustable by the attributes.

Following is a sample of such retrofitted rules to match our original attribute-defined-enum.

[PageDefinitionTypePlugIn(DisplayName = "Sample Legacy Enum Property")] public class SampleProperty : PropertyEnumSelector<MySampleLegacyEnum> { static SampleProperty() { MultiSelectEnum = true; // Make Option2 a default selected value DefaultValues.Add(MySampleLegacyEnum.Option2.ToString()); AvailableItems.Where(p => p.Value == MySampleLegacyEnum.Option2.ToString()).First().Selected = true; //Disable selection of Option3 AvailableItems.Where(p => p.Value == MySampleLegacyEnum.Option3.ToString()).First().Enabled = false; //Remove hidden Option4 AvailableItems.RemoveAll(p => p.Value == MySampleLegacyEnum.Option4.ToString()); } }

The [code for the EnumProperty] as well as [the compiled binaries] are available as Open Source and you can incorporate it in your projects as long as the Cognifide namespace is used for the code.

What do you think? Is that going to be useful for you?

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 4.33 out of 5)
Loading...



This entry (Permalink) was posted on Saturday, December 26th, 2009 at 8:29 pm and is filed under .Net Framework, ASP.NET, Best Practices, C#, Code Samples, Downloadable, EPiServer, Open Source, Software Development, Solution, Web applications. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response , or trackback from your own site.