tag:blogger.com,1999:blog-11087968580827012812024-03-24T16:32:03.315-07:00C# .Net & EPiServer DevelopmentAnonymoushttp://www.blogger.com/profile/04180534287038596566noreply@blogger.comBlogger4125tag:blogger.com,1999:blog-1108796858082701281.post-49012216298503663472013-11-05T00:27:00.000-08:002013-11-05T01:23:40.834-08:00EPiServer 6: Sharing plugins between applicationsIn this example I will demonstrate the possibility to share EPiServer plugins between applications. The idea is to make this as seamless as possible without configuration or having to add front-end files into every project.<br />
<br />
What we will do is to create a class library project, build a release DLL and put this DLL into the bin folder of another project. And all of this will produce a new admin tool as depicted below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrZ_MRtGZAKmwBS2_UB7Zxl5IcLDoPNSoa6Q50ueFubHB-0f9tn_8rMK7sXbRghMfONjDzw44iYcf5bBZZKSyMsixOn7Zb_j8kUJL7GJ8KJmzKLI_XDufx2EtPTKW9QnslvnHWQ-tqml7w/s1600/page-type-usage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="253" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrZ_MRtGZAKmwBS2_UB7Zxl5IcLDoPNSoa6Q50ueFubHB-0f9tn_8rMK7sXbRghMfONjDzw44iYcf5bBZZKSyMsixOn7Zb_j8kUJL7GJ8KJmzKLI_XDufx2EtPTKW9QnslvnHWQ-tqml7w/s640/page-type-usage.png" width="640" /></a></div><br />
<h2>Create the plugin</h2><br />
We create the plugin and what is worth noting here is the Url on line 4 (this is used with the route helper, see below) and the inheritance of the IPageTypeUsage interface on line 5.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[GuiPlugIn(DisplayName = <span class="str">"Page Type Usage"</span>,</pre><pre><span class="lnum"> 2: </span> Description = <span class="str">"See the usage of a selected page type."</span>,</pre><pre class="alt"><span class="lnum"> 3: </span> Area = PlugInArea.AdminMenu,</pre><pre><span class="lnum"> 4: </span> Url = <span class="str">"/PageTypeUsage.aspx"</span>)]</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">public</span> <span class="kwrd">class</span> PageTypeUsage : Page, IPageTypeUsage</pre></div><br />
We will not use an ASPX file at all but instead add all the controls needed to the ControlCollection of the plugin page. And this must be added in the OnInit method in order to be able to use them properly.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnInit(EventArgs e)</pre><pre><span class="lnum"> 2: </span>{</pre><pre class="alt"><span class="lnum"> 3: </span> AddControls();</pre><pre><span class="lnum"> 4: </span> InitPageTypesDropDown();</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">base</span>.OnInit(e);</pre><pre><span class="lnum"> 6: </span>}</pre></div><br />
A little example of the AddControls method. Once added they will render properly without us having to doing anything in particular.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">private</span> <span class="kwrd">void</span> AddControls()</pre><pre><span class="lnum"> 2: </span>{</pre><pre class="alt"><span class="lnum"> 3: </span> HtmlGenericControl html = <span class="kwrd">new</span> HtmlGenericControl(<span class="str">"html"</span>);</pre><pre><span class="lnum"> 4: </span> HtmlHead head = <span class="kwrd">new</span> HtmlHead();</pre><pre class="alt"><span class="lnum"> 5: </span> HtmlTitle title = <span class="kwrd">new</span> HtmlTitle { Text = <span class="str">"Page Type Usage"</span> };</pre><pre><span class="lnum"> 6: </span> HtmlGenericControl javascript = ControlHelper.GetJavaScriptLinkControl(<span class="str">"/util/javascript/system.js"</span>);</pre><pre class="alt"><span class="lnum"> 7: </span> HtmlGenericControl css1 = ControlHelper.GetCssLinkControl(<span class="str">"/epi/Shell/ClientResources/ShellCore.css"</span>);</pre><pre><span class="lnum"> 8: </span> Controls.Add(html);</pre><pre class="alt"><span class="lnum"> 9: </span> html.Controls.Add(head);</pre><pre><span class="lnum"> 10: </span> head.Controls.Add(title);</pre><pre class="alt"><span class="lnum"> 11: </span> head.Controls.Add(javascript);</pre><pre><span class="lnum"> 12: </span> head.Controls.Add(css1);</pre></div><br />
<h2>Route handler</h2><br />
In order for this to work without using an ASPX file and having a virtual path we will use the power of Routes.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> RouteHandler : PlugInAttribute, IRouteHandler</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="preproc">#region</span> IRouteHandler Members</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> IHttpHandler GetHttpHandler(RequestContext requestContext)</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">var</span> interfaceType = <span class="kwrd">typeof</span>(IPageTypeUsage);</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">var</span> classType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).First(p => interfaceType.IsAssignableFrom(p) && p.IsClass);</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">return</span> Activator.CreateInstance(classType) <span class="kwrd">as</span> IHttpHandler;</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> <span class="preproc">#endregion</span></pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> RegisterRoutes()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> RouteTable.Routes.Add(<span class="kwrd">new</span> Route(<span class="str">"PageTypeUsage.aspx"</span>, <span class="kwrd">new</span> RouteHandler()));</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Start()</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> RegisterRoutes();</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre></div><br />
By inheriting PlugInAttribute on line 1 we can access the Start method (on line 17) and register the routes. And we register the route "PageTypeUsage.aspx" - which is the same as we specified on the plugin. Once this route is hit or accessed the GetHttpHandler method on line 4 is used. Basically this utilize a reflection search of the interface IPageTypeUsage and instantiates a PageTypeUsage object (on line 8).<br />
<br />
And voila! We have a plugin that we can share between different projects!Anonymoushttp://www.blogger.com/profile/04180534287038596566noreply@blogger.com32tag:blogger.com,1999:blog-1108796858082701281.post-64468997313099969142013-10-29T04:53:00.003-07:002014-04-18T07:58:21.162-07:00EPiServer 7: A custom property combined with attributes and a custom EditorDescriptor - UPDATEDIn this post I will demonstrate the possibility to create an EPiServer 7 custom property and combine it with property attributes for edit mode configuration (which is utilized by a custom EditorDescriptor).<br />
<br />
<b>This entire post has been revised and updated to work with EPiServer 7.5.</b><br />
<br />
What I have created is a custom property to let the editor create key-value items that can be used to render, for instance, a drop-down list.<br />
<br />
<h3>Overview</h3>In order to achieve this we have to create the files marked in blue in the image below. And I will go trough each and every one of them to try and explain how to create a custom property in EPiServer 7.5.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRS0VA65o2vRQ2SCr_xJFFGzDCC0MggeG6UY694oJuuHsdswMK4yakbOCTWumBVp338THUr872IMRcWnYD8vaENhADGREGlQPeWa46GfGMcd-1ey8EmjizOM_omCLe4-hujIMuQ_Nev-7Q/s1600/keyvalueitems-overview.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRS0VA65o2vRQ2SCr_xJFFGzDCC0MggeG6UY694oJuuHsdswMK4yakbOCTWumBVp338THUr872IMRcWnYD8vaENhADGREGlQPeWa46GfGMcd-1ey8EmjizOM_omCLe4-hujIMuQ_Nev-7Q/s1600/keyvalueitems-overview.png" /></a><br />
<br />
<h3>StartPage.cs</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[ContentType(GUID = <span class="str">"333e2a8b-46d4-4ed9-8f05-ac4a3c09058e"</span>, GroupName = SystemTabNames.Content)]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> StartPage : PageData</pre><pre class="alt"><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> [Display(GroupName = SystemTabNames.Content, Order = 100)]</pre><pre class="alt"><span class="lnum"> 5: </span> [BackingType(<span class="kwrd">typeof</span>(PropertyKeyValueItems))]</pre><pre><span class="lnum"> 6: </span> [KeyValueItems(<span class="str">"Name"</span>, <span class="str">"E-mail"</span>, <span class="str">"Add"</span>, <span class="str">"X"</span>, <span class="str">""</span>, <span class="str">""</span>, <span class="str">@"^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"</span>, <span class="str">"The entered value is not valid. Must be a valid e-mail."</span>)]</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> IEnumerable<KeyValueItem> KeyValueItems { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre><span class="lnum"> 8: </span>}</pre></div><br />
Three things are of importance here.<br />
1. The BackingType attribute specified on line 5 which is specifying what property to use when retrieving and storing data.<br />
2. The KeyValueItems attribute on line 6 which is the attribute that lets us add specific parameters for this particular property<br />
3. The IEnumerable<keyvalueitem> on line 7 which match the type used in the backing type property<br />
<br />
</keyvalueitem><br />
<h3>PropertyKeyValueItems.cs</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[PropertyDefinitionTypePlugIn(Description = <span class="str">"A property for list of key-value-items."</span>, DisplayName = <span class="str">"Key-Value Items"</span>)]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> PropertyKeyValueItems : PropertyLongString</pre><pre class="alt"><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> Type PropertyValueType</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">get</span> { <span class="kwrd">return</span> <span class="kwrd">typeof</span>(IEnumerable<KeyValueItem>); }</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">object</span> Value</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">get</span></pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">var</span> value = <span class="kwrd">base</span>.Value <span class="kwrd">as</span> <span class="kwrd">string</span>;</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">if</span> (value == <span class="kwrd">null</span>) { <span class="kwrd">return</span> <span class="kwrd">null</span>; }</pre><pre class="alt"><span class="lnum"> 15: </span> JavaScriptSerializer serializer = <span class="kwrd">new</span> JavaScriptSerializer();</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">return</span> serializer.Deserialize(value, <span class="kwrd">typeof</span>(IEnumerable<KeyValueItem>));</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">set</span></pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">if</span> (value <span class="kwrd">is</span> IEnumerable<KeyValueItem>)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> JavaScriptSerializer serializer = <span class="kwrd">new</span> JavaScriptSerializer();</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">base</span>.Value = serializer.Serialize(value);</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">else</span> { <span class="kwrd">base</span>.Value = value; }</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre><pre><span class="lnum"> 28: </span>}</pre><pre class="alt"><span class="lnum"> 29: </span> </pre><pre><span class="lnum"> 30: </span><span class="kwrd">public</span> <span class="kwrd">class</span> KeyValueItem</pre><pre class="alt"><span class="lnum"> 31: </span>{</pre><pre><span class="lnum"> 32: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Key { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre class="alt"><span class="lnum"> 33: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Value { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre><span class="lnum"> 34: </span>}</pre></div><br />
I have edited the file and in the above code only display the essential (the source code for all files are found at the botton of this post).<br />
<br />
The are two classes here: 1. PropertyKeyValueItems, 2. KeyValueItem. <br />
<br />
PropertyKeyValueItems is used to retrieve and store the value from editor input. Since the property is of type 'IEnumerable<keyvalueitem>' as specified in property 'PropertyValueType' but it does in fact inherit 'PropertyLongString' we need to serialize and deserialize the data and this is done in property 'Value'. The 'KeyValueItem' class is simply used to help with the serialization and deserialization.<br />
<br />
<h3>KeyValueItemsAttribute.cs</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[AttributeUsage(AttributeTargets.Property)]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> KeyValueItemsAttribute : Attribute</pre><pre class="alt"><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> KeyLabel { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> ValueLabel { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> AddButtonLabel { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> RemoveButtonLabel { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> KeyValidationExpression { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> ValueValidationExpression { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> KeyValidationMessage { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> ValueValidationMessage { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> KeyValueItemsAttribute(<span class="kwrd">string</span> keyLabel, </pre><pre><span class="lnum"> 14: </span> <span class="kwrd">string</span> valueLabel, </pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">string</span> addButtonLabel, </pre><pre><span class="lnum"> 16: </span> <span class="kwrd">string</span> removeButtonLabel, </pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">string</span> keyValidationExpression,</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">string</span> keyValidationMessage,</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">string</span> valueValidationExpression,</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">string</span> valueValidationMessage)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> KeyLabel = keyLabel;</pre><pre class="alt"><span class="lnum"> 23: </span> ValueLabel = valueLabel;</pre><pre><span class="lnum"> 24: </span> AddButtonLabel = addButtonLabel;</pre><pre class="alt"><span class="lnum"> 25: </span> RemoveButtonLabel = removeButtonLabel;</pre><pre><span class="lnum"> 26: </span> KeyValidationExpression = keyValidationExpression;</pre><pre class="alt"><span class="lnum"> 27: </span> KeyValidationMessage = keyValidationMessage;</pre><pre><span class="lnum"> 28: </span> ValueValidationExpression = valueValidationExpression;</pre><pre class="alt"><span class="lnum"> 29: </span> ValueValidationMessage = valueValidationMessage;</pre><pre><span class="lnum"> 30: </span> }</pre><pre class="alt"><span class="lnum"> 31: </span>}</pre></div><br />
This is a class used to dress up a specific property. In this case we say that the value the key is 'Name' and the value is 'E-mail'. We send all of these calues up to the front layer with the help of the EditorDescriptor coming next.<br />
<br />
<h3>KeyValueItemsEditorDescriptor.cs</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[EditorDescriptorRegistration(TargetType = <span class="kwrd">typeof</span>(IEnumerable<KeyValueItem>))]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> KeyValueItemsEditorDescriptor : EditorDescriptor</pre><pre class="alt"><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> KeyValueItemsEditorDescriptor()</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> ClientEditingClass = <span class="str">"episerver75.editors.KeyValueItems"</span>;</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> SetEditorConfiguration(ExtendedMetadata metadata)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">var</span> keyValueItemsAttribute = metadata.Attributes.FirstOrDefault(a => <span class="kwrd">typeof</span>(KeyValueItemsAttribute) == a.GetType()) <span class="kwrd">as</span> KeyValueItemsAttribute;</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">if</span> (keyValueItemsAttribute != <span class="kwrd">null</span>)</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.KeyLabel))</pre><pre class="alt"><span class="lnum"> 15: </span> EditorConfiguration[<span class="str">"keyLabel"</span>] = keyValueItemsAttribute.KeyLabel;</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.ValueLabel))</pre><pre class="alt"><span class="lnum"> 17: </span> EditorConfiguration[<span class="str">"valueLabel"</span>] = keyValueItemsAttribute.ValueLabel;</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.AddButtonLabel))</pre><pre class="alt"><span class="lnum"> 19: </span> EditorConfiguration[<span class="str">"addButtonLabel"</span>] = keyValueItemsAttribute.AddButtonLabel;</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.RemoveButtonLabel))</pre><pre class="alt"><span class="lnum"> 21: </span> EditorConfiguration[<span class="str">"removeButtonLabel"</span>] = keyValueItemsAttribute.RemoveButtonLabel;</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.KeyValidationExpression))</pre><pre class="alt"><span class="lnum"> 23: </span> EditorConfiguration[<span class="str">"keyValidationExpression"</span>] = keyValueItemsAttribute.KeyValidationExpression;</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.KeyValidationMessage))</pre><pre class="alt"><span class="lnum"> 25: </span> EditorConfiguration[<span class="str">"keyValidationMessage"</span>] = keyValueItemsAttribute.KeyValidationMessage;</pre><pre><span class="lnum"> 26: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.ValueValidationExpression))</pre><pre class="alt"><span class="lnum"> 27: </span> EditorConfiguration[<span class="str">"valueValidationExpression"</span>] = keyValueItemsAttribute.ValueValidationExpression;</pre><pre><span class="lnum"> 28: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(keyValueItemsAttribute.ValueValidationMessage))</pre><pre class="alt"><span class="lnum"> 29: </span> EditorConfiguration[<span class="str">"valueValidationMessage"</span>] = keyValueItemsAttribute.ValueValidationMessage;</pre><pre><span class="lnum"> 30: </span> }</pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">base</span>.SetEditorConfiguration(metadata);</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span>}</pre></div><br />
This is the piece of code that binds everything together (back-end with front-end). First we need to specify that whenever editing a property of the type 'IEnumerable<keyvalueitem>' then this is the code to use (line 1). In the constructor we specify which front-end code to use (line 6, more on this path late i 'module.config'). Third we override the 'SetEditorConfiguration' method and simply reads the 'KeyValueItemsAttribute' and its properties and add them to the EditorConfiguration property we get when inheriting the 'EditorDescriptor' class.<br />
<br />
<h3>module.config</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><?xml version=<span class="str">"1.0"</span> encoding=<span class="str">"utf-8"</span>?></pre><pre><span class="lnum"> 2: </span><module></pre><pre class="alt"><span class="lnum"> 3: </span> <clientResources></pre><pre><span class="lnum"> 4: </span> <add name=<span class="str">"epi-cms.widgets.base"</span> path=<span class="str">"Styles/Styles.css"</span> resourceType=<span class="str">"Style"</span>/></pre><pre class="alt"><span class="lnum"> 5: </span> </clientResources></pre><pre><span class="lnum"> 6: </span> <dojo></pre><pre class="alt"><span class="lnum"> 7: </span> <paths></pre><pre><span class="lnum"> 8: </span> <add name=<span class="str">"episerver75"</span> path=<span class="str">"Scripts"</span> /></pre><pre class="alt"><span class="lnum"> 9: </span> </paths></pre><pre><span class="lnum"> 10: </span> </dojo></pre><pre class="alt"><span class="lnum"> 11: </span></module></pre></div><br />
This config file specifies the base path for the client resources used by our custom front-end code (on line 8). This is what makes the 'ClientEditingClass = "episerver75.editors.KeyValueItems";' work in the EditorDescriptor class described above.<br />
<br />
<h3>Style.css</h3>Any styles needed for the front-end custom property.<br />
<br />
<h3>KeyValueItems.js</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>define(</pre><pre><span class="lnum"> 2: </span><span class="str">"episerver75/editors/KeyValueItems"</span>,</pre><pre class="alt"><span class="lnum"> 3: </span>[</pre><pre><span class="lnum"> 4: </span><span class="str">"dojo/_base/array"</span>,</pre><pre class="alt"><span class="lnum"> 5: </span><span class="str">"dojo/_base/declare"</span>,</pre><pre><span class="lnum"> 6: </span><span class="str">"dojo/_base/lang"</span>,</pre><pre class="alt"><span class="lnum"> 7: </span><span class="str">"dojo/_base/json"</span>,</pre><pre><span class="lnum"> 8: </span><span class="str">"dojo/query"</span>,</pre><pre class="alt"><span class="lnum"> 9: </span><span class="str">"dojo/dom-construct"</span>,</pre><pre><span class="lnum"> 10: </span><span class="str">"dojo/on"</span>,</pre><pre class="alt"><span class="lnum"> 11: </span><span class="str">"dijit/focus"</span>,</pre><pre><span class="lnum"> 12: </span><span class="str">"dijit/_TemplatedMixin"</span>,</pre><pre class="alt"><span class="lnum"> 13: </span><span class="str">"dijit/_Widget"</span>,</pre><pre><span class="lnum"> 14: </span><span class="str">"dijit/form/ValidationTextBox"</span>,</pre><pre class="alt"><span class="lnum"> 15: </span><span class="str">"dijit/form/Button"</span>,</pre><pre><span class="lnum"> 16: </span><span class="str">"epi/shell/widget/_ValueRequiredMixin"</span></pre><pre class="alt"><span class="lnum"> 17: </span>],</pre><pre><span class="lnum"> 18: </span>function (array, declare, lang, json, query, domConstruct, on, focus, templatedMixin, widget, textbox, button, valueRequiredMixin) {</pre><pre class="alt"><span class="lnum"> 19: </span><span class="kwrd">return</span> declare([widget, templatedMixin, valueRequiredMixin], {</pre><pre><span class="lnum"> 20: </span> templateString: <span class="str">"<div data-dojo-attach-point=\"stateNode, tooltipNode\" class=\"dijit dijitReset dijitInline\"> \</pre><pre class="alt"><span class="lnum"> 21: </span> <div data-dojo-attach-point=\"keyValueItemsNode\" class=\"dijit dijitReset\"></div> \</pre><pre><span class="lnum"> 22: </span> <div class=\"dijit dijitReset\"> \</pre><pre class="alt"><span class="lnum"> 23: </span> <button data-dojo-attach-event=\"onclick:addKeyValueItem\" type=\"button\" class=\"\">${addButtonLabel}</button> \</pre><pre><span class="lnum"> 24: </span> </div> \</pre><pre class="alt"><span class="lnum"> 25: </span> </div>"</span>,</pre><pre><span class="lnum"> 26: </span> baseClass: <span class="str">"keyValueItems"</span>,</pre><pre class="alt"><span class="lnum"> 27: </span> keyLabel: <span class="str">"Key"</span>,</pre><pre><span class="lnum"> 28: </span> valueLabel: <span class="str">"Value"</span>,</pre><pre class="alt"><span class="lnum"> 29: </span> addButtonLabel: <span class="str">"Add"</span>,</pre><pre><span class="lnum"> 30: </span> removeButtonLabel: <span class="str">"X"</span>,</pre><pre class="alt"><span class="lnum"> 31: </span> keyValidationExpression: <span class="str">""</span>,</pre><pre><span class="lnum"> 32: </span> keyValidationMessage: <span class="str">""</span>,</pre><pre class="alt"><span class="lnum"> 33: </span> valueValidationExpression: <span class="str">""</span>,</pre><pre><span class="lnum"> 34: </span> valueValidationMessage: <span class="str">""</span>,</pre><pre class="alt"><span class="lnum"> 35: </span> valueIsCsv: <span class="kwrd">true</span>,</pre><pre><span class="lnum"> 36: </span> valueIsInclusive: <span class="kwrd">true</span>,</pre><pre class="alt"><span class="lnum"> 37: </span> value: <span class="kwrd">null</span>,</pre><pre><span class="lnum"> 38: </span> widgetsInTemplate: <span class="kwrd">true</span>,</pre><pre class="alt"><span class="lnum"> 39: </span> constructor: function() {</pre><pre><span class="lnum"> 40: </span> <span class="kwrd">this</span>._keyValueItems = [];</pre><pre class="alt"><span class="lnum"> 41: </span> },</pre><pre><span class="lnum"> 42: </span> postMixInProperties: function() {</pre><pre class="alt"><span class="lnum"> 43: </span> <span class="kwrd">this</span>.inherited(arguments);</pre><pre><span class="lnum"> 44: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.keyLabel)</pre><pre class="alt"><span class="lnum"> 45: </span> <span class="kwrd">this</span>.keyLabel = <span class="kwrd">this</span>.<span class="kwrd">params</span>.keyLabel;</pre><pre><span class="lnum"> 46: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.valueLabel)</pre><pre class="alt"><span class="lnum"> 47: </span> <span class="kwrd">this</span>.valueLabel = <span class="kwrd">this</span>.<span class="kwrd">params</span>.valueLabel;</pre><pre><span class="lnum"> 48: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.addButtonLabel)</pre><pre class="alt"><span class="lnum"> 49: </span> <span class="kwrd">this</span>.addButtonLabel = <span class="kwrd">this</span>.<span class="kwrd">params</span>.addButtonLabel;</pre><pre><span class="lnum"> 50: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.removeButtonLabel)</pre><pre class="alt"><span class="lnum"> 51: </span> <span class="kwrd">this</span>.removeButtonLabel = <span class="kwrd">this</span>.<span class="kwrd">params</span>.removeButtonLabel;</pre><pre><span class="lnum"> 52: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.keyValidationExpression)</pre><pre class="alt"><span class="lnum"> 53: </span> <span class="kwrd">this</span>.keyValidationExpression = <span class="kwrd">this</span>.<span class="kwrd">params</span>.keyValidationExpression;</pre><pre><span class="lnum"> 54: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.keyValidationMessage)</pre><pre class="alt"><span class="lnum"> 55: </span> <span class="kwrd">this</span>.keyValidationMessage = <span class="kwrd">this</span>.<span class="kwrd">params</span>.keyValidationMessage;</pre><pre><span class="lnum"> 56: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.valueValidationExpression)</pre><pre class="alt"><span class="lnum"> 57: </span> <span class="kwrd">this</span>.valueValidationExpression = <span class="kwrd">this</span>.<span class="kwrd">params</span>.valueValidationExpression;</pre><pre><span class="lnum"> 58: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>.<span class="kwrd">params</span>.valueValidationMessage)</pre><pre class="alt"><span class="lnum"> 59: </span> <span class="kwrd">this</span>.valueValidationMessage = <span class="kwrd">this</span>.<span class="kwrd">params</span>.valueValidationMessage;</pre><pre><span class="lnum"> 60: </span> },</pre><pre class="alt"><span class="lnum"> 61: </span> destroy: function() {</pre><pre><span class="lnum"> 62: </span> <span class="kwrd">var</span> _a;</pre><pre class="alt"><span class="lnum"> 63: </span> while (_a = <span class="kwrd">this</span>._keyValueItems.pop()) {</pre><pre><span class="lnum"> 64: </span> _a.div.destroyRecursive();</pre><pre class="alt"><span class="lnum"> 65: </span> }</pre><pre><span class="lnum"> 66: </span> <span class="kwrd">this</span>.inherited(arguments);</pre><pre class="alt"><span class="lnum"> 67: </span> },</pre><pre><span class="lnum"> 68: </span> focus: function() {</pre><pre class="alt"><span class="lnum"> 69: </span> <span class="kwrd">try</span> {</pre><pre><span class="lnum"> 70: </span> <span class="kwrd">if</span> (<span class="kwrd">this</span>._keyValueItems.length > 0) {</pre><pre class="alt"><span class="lnum"> 71: </span> focus.focus(<span class="kwrd">this</span>._keyValueItems[0].div.keyValueItemsNode);</pre><pre><span class="lnum"> 72: </span> }</pre><pre class="alt"><span class="lnum"> 73: </span> } <span class="kwrd">catch</span> (e) {</pre><pre><span class="lnum"> 74: </span> }</pre><pre class="alt"><span class="lnum"> 75: </span> },</pre><pre><span class="lnum"> 76: </span> onChange: function() {},</pre><pre class="alt"><span class="lnum"> 77: </span> onBlur: function() {},</pre><pre><span class="lnum"> 78: </span> onFocus: function() {},</pre><pre class="alt"><span class="lnum"> 79: </span> isValid: function() {</pre><pre><span class="lnum"> 80: </span> <span class="kwrd">var</span> isValid = <span class="kwrd">true</span>;</pre><pre class="alt"><span class="lnum"> 81: </span> array.forEach(<span class="kwrd">this</span>._keyValueItems, function(entry) {</pre><pre><span class="lnum"> 82: </span> <span class="kwrd">var</span> keyTextbox = entry.keyTextbox,</pre><pre class="alt"><span class="lnum"> 83: </span> valueTextbox = entry.valueTextbox;</pre><pre><span class="lnum"> 84: </span> </pre><pre class="alt"><span class="lnum"> 85: </span> isValid = isValid && keyTextbox.isValid() && valueTextbox.isValid();</pre><pre><span class="lnum"> 86: </span> });</pre><pre class="alt"><span class="lnum"> 87: </span> <span class="kwrd">return</span> isValid;</pre><pre><span class="lnum"> 88: </span> },</pre><pre class="alt"><span class="lnum"> 89: </span> _calculateValue: function () {</pre><pre><span class="lnum"> 90: </span> <span class="kwrd">var</span> value = [];</pre><pre class="alt"><span class="lnum"> 91: </span> array.forEach(<span class="kwrd">this</span>._keyValueItems, function(entry) {</pre><pre><span class="lnum"> 92: </span> <span class="kwrd">var</span> keyTextbox = entry.keyTextbox,</pre><pre class="alt"><span class="lnum"> 93: </span> valueTextbox = entry.valueTextbox;</pre><pre><span class="lnum"> 94: </span> </pre><pre class="alt"><span class="lnum"> 95: </span> <span class="kwrd">if</span> (keyTextbox.value && valueTextbox.value && keyTextbox.isValid() && valueTextbox.isValid()) {</pre><pre><span class="lnum"> 96: </span> <span class="kwrd">var</span> keyValuePair = <span class="kwrd">new</span> Object();</pre><pre class="alt"><span class="lnum"> 97: </span> keyValuePair.key = keyTextbox.value;</pre><pre><span class="lnum"> 98: </span> keyValuePair.value = valueTextbox.value;</pre><pre class="alt"><span class="lnum"> 99: </span> value.push(keyValuePair);</pre><pre><span class="lnum"> 100: </span> }</pre><pre class="alt"><span class="lnum"> 101: </span> });</pre><pre><span class="lnum"> 102: </span> </pre><pre class="alt"><span class="lnum"> 103: </span> <span class="kwrd">this</span>._set(<span class="str">"value"</span>, value);</pre><pre><span class="lnum"> 104: </span> },</pre><pre class="alt"><span class="lnum"> 105: </span> _setValueAttr: function (value) {</pre><pre><span class="lnum"> 106: </span> <span class="kwrd">this</span>._set(<span class="str">"value"</span>, value);</pre><pre class="alt"><span class="lnum"> 107: </span> </pre><pre><span class="lnum"> 108: </span> array.forEach(value, <span class="kwrd">this</span>._addKeyValueTextboxesForItem, <span class="kwrd">this</span>);</pre><pre class="alt"><span class="lnum"> 109: </span> },</pre><pre><span class="lnum"> 110: </span> _onBlur: function () {</pre><pre class="alt"><span class="lnum"> 111: </span> <span class="kwrd">this</span>.inherited(arguments);</pre><pre><span class="lnum"> 112: </span> <span class="kwrd">this</span>.onBlur();</pre><pre class="alt"><span class="lnum"> 113: </span> },</pre><pre><span class="lnum"> 114: </span> addKeyValueItem: function () {</pre><pre class="alt"><span class="lnum"> 115: </span> <span class="kwrd">this</span>._addKeyValueTextboxesForItem({ <span class="str">"Key"</span>: <span class="str">""</span>, <span class="str">"Value"</span>: <span class="str">""</span> });</pre><pre><span class="lnum"> 116: </span> },</pre><pre class="alt"><span class="lnum"> 117: </span> _addKeyValueTextboxesForItem: function (keyValueItem) {</pre><pre><span class="lnum"> 118: </span> <span class="kwrd">var</span> div = domConstruct.create(<span class="str">"div"</span>, <span class="kwrd">null</span>, <span class="kwrd">this</span>.keyValueItemsNode);</pre><pre class="alt"><span class="lnum"> 119: </span> div.setAttribute(<span class="str">"class"</span>, <span class="str">"keyValueItemContainer"</span>);</pre><pre><span class="lnum"> 120: </span> </pre><pre class="alt"><span class="lnum"> 121: </span> <span class="kwrd">var</span> keyTextbox = <span class="kwrd">this</span>._getTextbox(keyValueItem.key, <span class="str">"keyTextbox"</span>, <span class="kwrd">this</span>.keyValidationMessage, <span class="kwrd">this</span>.keyValidationExpression);</pre><pre><span class="lnum"> 122: </span> <span class="kwrd">var</span> valueTextbox = <span class="kwrd">this</span>._getTextbox(keyValueItem.value, <span class="str">"valueTextbox"</span>, <span class="kwrd">this</span>.valueValidationMessage, <span class="kwrd">this</span>.valueValidationExpression);</pre><pre class="alt"><span class="lnum"> 123: </span> </pre><pre><span class="lnum"> 124: </span> keyTextbox.placeAt(div);</pre><pre class="alt"><span class="lnum"> 125: </span> valueTextbox.placeAt(div);</pre><pre><span class="lnum"> 126: </span> </pre><pre class="alt"><span class="lnum"> 127: </span> <span class="kwrd">var</span> btn = <span class="kwrd">new</span> button({</pre><pre><span class="lnum"> 128: </span> label: <span class="kwrd">this</span>.removeButtonLabel,</pre><pre class="alt"><span class="lnum"> 129: </span> main: <span class="kwrd">this</span>,</pre><pre><span class="lnum"> 130: </span> container: div</pre><pre class="alt"><span class="lnum"> 131: </span> });</pre><pre><span class="lnum"> 132: </span> btn.on(<span class="str">"click"</span>, function () {</pre><pre class="alt"><span class="lnum"> 133: </span> <span class="kwrd">this</span>.main._removeKeyValueItem(<span class="kwrd">this</span>.container);</pre><pre><span class="lnum"> 134: </span> domConstruct.destroy(<span class="kwrd">this</span>.container);</pre><pre class="alt"><span class="lnum"> 135: </span> <span class="kwrd">this</span>.main._calculateValue();</pre><pre><span class="lnum"> 136: </span> <span class="kwrd">this</span>.main.onChange(<span class="kwrd">this</span>.main.value);</pre><pre class="alt"><span class="lnum"> 137: </span> </pre><pre><span class="lnum"> 138: </span> });</pre><pre class="alt"><span class="lnum"> 139: </span> btn.placeAt(div);</pre><pre><span class="lnum"> 140: </span> </pre><pre class="alt"><span class="lnum"> 141: </span> <span class="kwrd">this</span>._pushKeyValueItem(div, keyTextbox, valueTextbox);</pre><pre><span class="lnum"> 142: </span> },</pre><pre class="alt"><span class="lnum"> 143: </span> _removeKeyValueItem: function (div) {</pre><pre><span class="lnum"> 144: </span> <span class="kwrd">var</span> newKeyValueItems = [];</pre><pre class="alt"><span class="lnum"> 145: </span> </pre><pre><span class="lnum"> 146: </span> array.forEach(<span class="kwrd">this</span>._keyValueItems, function (entry) {</pre><pre class="alt"><span class="lnum"> 147: </span> <span class="kwrd">if</span> (entry.div != div) {</pre><pre><span class="lnum"> 148: </span> newKeyValueItems.push(entry);</pre><pre class="alt"><span class="lnum"> 149: </span> }</pre><pre><span class="lnum"> 150: </span> });</pre><pre class="alt"><span class="lnum"> 151: </span> </pre><pre><span class="lnum"> 152: </span> <span class="kwrd">this</span>._keyValueItems = newKeyValueItems;</pre><pre class="alt"><span class="lnum"> 153: </span> },</pre><pre><span class="lnum"> 154: </span> _pushKeyValueItem: function(div, keyTextbox, valueTextbox) {</pre><pre class="alt"><span class="lnum"> 155: </span> <span class="kwrd">var</span> o = <span class="kwrd">new</span> Object();</pre><pre><span class="lnum"> 156: </span> o.div = div;</pre><pre class="alt"><span class="lnum"> 157: </span> o.keyTextbox = keyTextbox;</pre><pre><span class="lnum"> 158: </span> o.valueTextbox = valueTextbox;</pre><pre class="alt"><span class="lnum"> 159: </span> </pre><pre><span class="lnum"> 160: </span> <span class="kwrd">this</span>._keyValueItems.push(o);</pre><pre class="alt"><span class="lnum"> 161: </span> },</pre><pre><span class="lnum"> 162: </span> _getTextbox: function (value, cssClass, message, expression) {</pre><pre class="alt"><span class="lnum"> 163: </span> <span class="kwrd">var</span> tb = <span class="kwrd">new</span> textbox({</pre><pre><span class="lnum"> 164: </span> value: value,</pre><pre class="alt"><span class="lnum"> 165: </span> invalidMessage: message,</pre><pre><span class="lnum"> 166: </span> regExp: expression</pre><pre class="alt"><span class="lnum"> 167: </span> });</pre><pre><span class="lnum"> 168: </span> tb.setAttribute(<span class="str">"class"</span>, cssClass);</pre><pre class="alt"><span class="lnum"> 169: </span> </pre><pre><span class="lnum"> 170: </span> tb.on(<span class="str">"change"</span>, lang.hitch(<span class="kwrd">this</span>, function () {</pre><pre class="alt"><span class="lnum"> 171: </span> <span class="kwrd">this</span>._calculateValue();</pre><pre><span class="lnum"> 172: </span> <span class="kwrd">this</span>.onChange(<span class="kwrd">this</span>.value);</pre><pre class="alt"><span class="lnum"> 173: </span> }));</pre><pre><span class="lnum"> 174: </span> tb.on(<span class="str">"focus"</span>, lang.hitch(<span class="kwrd">this</span>, function () {</pre><pre class="alt"><span class="lnum"> 175: </span> <span class="kwrd">this</span>._set(<span class="str">"focused"</span>, <span class="kwrd">true</span>);</pre><pre><span class="lnum"> 176: </span> <span class="kwrd">this</span>.onFocus();</pre><pre class="alt"><span class="lnum"> 177: </span> }));</pre><pre><span class="lnum"> 178: </span> tb.on(<span class="str">"blur"</span>, lang.hitch(<span class="kwrd">this</span>, function () {</pre><pre class="alt"><span class="lnum"> 179: </span> <span class="kwrd">this</span>._set(<span class="str">"focused"</span>, <span class="kwrd">false</span>);</pre><pre><span class="lnum"> 180: </span> <span class="kwrd">this</span>._onBlur();</pre><pre class="alt"><span class="lnum"> 181: </span> }));</pre><pre><span class="lnum"> 182: </span> </pre><pre class="alt"><span class="lnum"> 183: </span> <span class="kwrd">return</span> tb;</pre><pre><span class="lnum"> 184: </span> }</pre><pre class="alt"><span class="lnum"> 185: </span>});</pre><pre><span class="lnum"> 186: </span>});</pre></div><br />
Ok. This is where everything is happening on the fron-end. The concept is the get the value and then render the controls needed as well as setting a new value based on the rendered controls if this makes sense. And, I see now that I forgot to use the 'Key' and 'Value' labels but it works fine without them. ;-)<br />
<br />
Some of the important methods here are:<br />
<br />
<h4>postMixInProperties</h4>This is where we read the parameters we specified in 'KeyValueItemsEditorDescriptor'.<br />
<br />
<h4>isValid</h4>This is where we makes sure if the user input is valid or not.<br />
<br />
<h4>_calculateValue</h4>This is used to calculate the new value based on user input.<br />
<br />
<h4>_setValueAttr</h4>This is in this cased used to get the value stored in the database and render the controls.<br />
<br />
<h4>_addKeyValueTextboxesForItem</h4>This is the method to render the controls base on a single KeyValueItem value.<br />
<br />
<h4>Output</h4><a href="https://www.blogger.com/blogger.g?blogID=1108796858082701281" imageanchor="1"><img border="0" src="" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglvGPs-kEkkaoK297O6rPCragkibv0tkdbW-w3cUtrPa3AlV1_-j8IfmfPilI2zocwv2stGS_3bL6HbxTfaPSdCPUA3jn7sBo9qVAk08qRPrK9RmJdXNmsLnH_10F0kRUpI5uExbPVMSxo/s1600/keyvalueitems-edit.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglvGPs-kEkkaoK297O6rPCragkibv0tkdbW-w3cUtrPa3AlV1_-j8IfmfPilI2zocwv2stGS_3bL6HbxTfaPSdCPUA3jn7sBo9qVAk08qRPrK9RmJdXNmsLnH_10F0kRUpI5uExbPVMSxo/s1600/keyvalueitems-edit.png" height="164" width="640" /></a><br />
<br />
<br />
<h3>Index.cshtml</h3><!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>@Html.DropDownListFor(m => m.KeyValueItems, <span class="kwrd">new</span> SelectList(Model.KeyValueItems, <span class="str">"Value"</span>, <span class="str">"Key"</span>))</pre></div><br />
This is an example of how to render the property as a drop-down list.<br />
<br />
<h3>That's it</h3>Yes, a lot of code and examples but I hope you get the idea. :-)<br />
<br />
<a href="https://sites.google.com/site/lofmanpeter/zip/KeyValueItems.zip?attredirects=0&d=1">Source code</a>.Anonymoushttp://www.blogger.com/profile/04180534287038596566noreply@blogger.com41tag:blogger.com,1999:blog-1108796858082701281.post-8900073925677465042013-10-10T23:21:00.000-07:002013-10-13T23:28:45.856-07:00EPiServer 7: Set default values on properties with attributeI have been looking into setting default values on a property in EPiServer 7 MVC to be set if the value is null, but so far I have only stumbled upon <a href="http://world.episerver.com/Blogs/Alexander-Haneng/Dates/2012/9/How-to-define-default-values-for-pages-and-blocks-in-EPiServer-CMS-7/" target="_blank">setting a default value on a property when creating a new page</a>.<br />
<br />
I want to be able to specify the default value as an attribute to the property and found what I thought would work and that is <b>System.ComponentModel.DefaultValueAttribute</b>.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span>[DefaultValue(<span class="str">"Y"</span>)]</pre>
<pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">string</span> DisplayTeasers { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre>
</div>
<br />
It looked perfect, but didn't work. :-(<br />
<br />
So, I tweaked it a little to make it work the way I wanted it to. Perhaps you need to be familiar with the <a href="http://world.episerver.com/Articles/Items/ASPNET-MVC-Templates-for-EPiServer-7-CMS/" target="_blank">EPiServer 7 MVC templates</a> in order to achieve this but the idea is to "transform" to CurrentPage object and set the default values if the current property value is null, and we do this when creating the PageViewModel.<br />
<br />
The attribute contains constructors for setting the default values and some static methods to transform an entire CurrentPage object based on any default value attributes specified.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> ViewModelDefaultValueAttribute : DefaultValueAttribute</pre>
<pre><span class="lnum"> 2: </span>{</pre>
<pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> ViewModelDefaultValueAttribute(<span class="kwrd">string</span> value) : <span class="kwrd">base</span>(value)</pre>
<pre><span class="lnum"> 4: </span> { }</pre>
<pre class="alt"><span class="lnum"> 5: </span> </pre>
<pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> ViewModelDefaultValueAttribute(<span class="kwrd">int</span> value) : <span class="kwrd">base</span>(value)</pre>
<pre class="alt"><span class="lnum"> 7: </span> { }</pre>
<pre><span class="lnum"> 8: </span> </pre>
<pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> ViewModelDefaultValueAttribute(<span class="kwrd">bool</span> value) : <span class="kwrd">base</span>(value)</pre>
<pre><span class="lnum"> 10: </span> { }</pre>
<pre class="alt"><span class="lnum"> 11: </span> </pre>
<pre><span class="lnum"> 12: </span> </pre>
<pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> T SetDefaultValues<T>(T currentPage) where T : SitePageData</pre>
<pre><span class="lnum"> 14: </span> {</pre>
<pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">if</span> (currentPage == <span class="kwrd">null</span>)</pre>
<pre><span class="lnum"> 16: </span> <span class="kwrd">return</span> currentPage;</pre>
<pre class="alt"><span class="lnum"> 17: </span> </pre>
<pre><span class="lnum"> 18: </span> Type currentPageType = GetType<T>(currentPage);</pre>
<pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">if</span> (currentPageType == <span class="kwrd">null</span>)</pre>
<pre><span class="lnum"> 20: </span> <span class="kwrd">return</span> currentPage;</pre>
<pre class="alt"><span class="lnum"> 21: </span> </pre>
<pre><span class="lnum"> 22: </span> T clone = currentPage.CreateWritableClone() <span class="kwrd">as</span> T;</pre>
<pre class="alt"><span class="lnum"> 23: </span> </pre>
<pre><span class="lnum"> 24: </span> Type defaultValueType = <span class="kwrd">typeof</span>(ViewModelDefaultValueAttribute);</pre>
<pre class="alt"><span class="lnum"> 25: </span> </pre>
<pre><span class="lnum"> 26: </span> <span class="kwrd">foreach</span> (PropertyInfo property <span class="kwrd">in</span> currentPageType.GetProperties())</pre>
<pre class="alt"><span class="lnum"> 27: </span> {</pre>
<pre><span class="lnum"> 28: </span> <span class="kwrd">if</span> (clone[property.Name] != <span class="kwrd">null</span>)</pre>
<pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">continue</span>;</pre>
<pre><span class="lnum"> 30: </span> </pre>
<pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">object</span>[] attributes = property.GetCustomAttributes(defaultValueType, <span class="kwrd">false</span>);</pre>
<pre><span class="lnum"> 32: </span> <span class="kwrd">if</span> (attributes == <span class="kwrd">null</span> || attributes.Length == 0)</pre>
<pre class="alt"><span class="lnum"> 33: </span> <span class="kwrd">continue</span>;</pre>
<pre><span class="lnum"> 34: </span> </pre>
<pre class="alt"><span class="lnum"> 35: </span> ViewModelDefaultValueAttribute defaultValue = attributes.Cast<ViewModelDefaultValueAttribute>().Single();</pre>
<pre><span class="lnum"> 36: </span> <span class="kwrd">if</span> (defaultValue == <span class="kwrd">null</span>)</pre>
<pre class="alt"><span class="lnum"> 37: </span> <span class="kwrd">continue</span>;</pre>
<pre><span class="lnum"> 38: </span> </pre>
<pre class="alt"><span class="lnum"> 39: </span> clone.Property[property.Name].Value = defaultValue.Value;</pre>
<pre><span class="lnum"> 40: </span> }</pre>
<pre class="alt"><span class="lnum"> 41: </span> </pre>
<pre><span class="lnum"> 42: </span> <span class="kwrd">if</span> (clone.IsModified)</pre>
<pre class="alt"><span class="lnum"> 43: </span> {</pre>
<pre><span class="lnum"> 44: </span> clone.MakeReadOnly();</pre>
<pre class="alt"><span class="lnum"> 45: </span> <span class="kwrd">return</span> clone;</pre>
<pre><span class="lnum"> 46: </span> }</pre>
<pre class="alt"><span class="lnum"> 47: </span> </pre>
<pre><span class="lnum"> 48: </span> <span class="kwrd">return</span> currentPage;</pre>
<pre class="alt"><span class="lnum"> 49: </span> }</pre>
<pre><span class="lnum"> 50: </span> </pre>
<pre class="alt"><span class="lnum"> 51: </span> <span class="kwrd">private</span> <span class="kwrd">static</span> Type GetType<T>(T currentPage) where T : SitePageData</pre>
<pre><span class="lnum"> 52: </span> {</pre>
<pre class="alt"><span class="lnum"> 53: </span> Type type = currentPage.GetType();</pre>
<pre><span class="lnum"> 54: </span> </pre>
<pre class="alt"><span class="lnum"> 55: </span> while (type != <span class="kwrd">null</span> && !type.FullName.Contains(<span class="str">"EPi7.Models.Pages"</span>))</pre>
<pre><span class="lnum"> 56: </span> {</pre>
<pre class="alt"><span class="lnum"> 57: </span> type = type.BaseType;</pre>
<pre><span class="lnum"> 58: </span> }</pre>
<pre class="alt"><span class="lnum"> 59: </span> </pre>
<pre><span class="lnum"> 60: </span> <span class="kwrd">return</span> type;</pre>
<pre class="alt"><span class="lnum"> 61: </span> }</pre>
<pre><span class="lnum"> 62: </span>}</pre>
</div>
<br />
As you can see we inherit the DefaultValueAttribute in order to utilize existing functionality. <br />
<br />
The static method <b>SetDefaultValues </b>is used to "transform" the CurrentPage object and it loops through all the properties on the page type and then finds any ViewModelDefaultValueAttributes specified and if the property value is null sets the default value specified. <br />
<br />
SitePageData is the base class for all pages types.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span><span class="rem">/// <summary></span></pre>
<pre><span class="lnum"> 2: </span><span class="rem">/// Base class for all page types</span></pre>
<pre class="alt"><span class="lnum"> 3: </span><span class="rem">/// </summary></span></pre>
<pre><span class="lnum"> 4: </span><span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> SitePageData : PageData</pre>
</div>
<br />
You specify the default value like this on a property.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span>[ViewModelDefaultValue(<span class="str">"Y"</span>)]</pre>
<pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">string</span> DisplayTeasers { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre>
</div>
<br />
We also need to use the <b>SetDefaultValues </b>method somewhere and this is done in the constructor when creating the PageViewModel.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> PageViewModel<T> : IPageViewModel<T> where T : SitePageData</pre>
<pre><span class="lnum"> 2: </span>{</pre>
<pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> PageViewModel(T currentPage)</pre>
<pre><span class="lnum"> 4: </span> {</pre>
<pre class="alt"><span class="lnum"> 5: </span> CurrentPage = ViewModelDefaultValueAttribute.SetDefaultValues(currentPage);</pre>
<pre><span class="lnum"> 6: </span> }</pre>
<pre class="alt"><span class="lnum"> 7: </span>}</pre>
</div>
<br />
When putting all of this together you can specify a default value as a attribute on a property and before the CurrentPage object is sent to the view (via view model) it is tranformed to set default values if the property is null.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi57xTUgk4-U1hzRL9_7t0wDLzWia3rf6K4i1qx5fNwsarloKVLIPlh0NsHRUN-q_NhMA0kYBlLBawoXdEINwp1hxLaRHzlvbmlMbYOuaSBFIX42v7NEy8-7kUiKDpI_dMQFxuxE6Tciov/s1600/defaultvalue.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi57xTUgk4-U1hzRL9_7t0wDLzWia3rf6K4i1qx5fNwsarloKVLIPlh0NsHRUN-q_NhMA0kYBlLBawoXdEINwp1hxLaRHzlvbmlMbYOuaSBFIX42v7NEy8-7kUiKDpI_dMQFxuxE6Tciov/s320/defaultvalue.png" width="222" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
As you can see in the image above the default value "Y" is displayed (as an example) on the page but when editing the property it is blank (null).Anonymoushttp://www.blogger.com/profile/04180534287038596566noreply@blogger.com0tag:blogger.com,1999:blog-1108796858082701281.post-38307534343321931882013-10-10T07:17:00.000-07:002013-10-10T07:29:55.822-07:00EPiServer 7: Attribute for rendering generic drop-down list or check-boxesEver wanted to help your EPiServer 7 editor with choosing from a set of options in edit mode? In this example we work with the hypothetical scenario of letting an editor chose to display teaser or not (and in the next post I will demonstrate how to set default values with property attributes). We achieve this by utilizing a generic data annotation attribute for a property and <a href="http://world.episerver.com/Blogs/Linus-Ekstrom/Dates/2012/9/EPiServer-7-Configuring-editors-for-your-properties/" target="_blank">configuring editors for EPiServer 7</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7Ho1cn2W_LK5IRim5zSwTTs93sZSmi4qPQkh36WGlpBkUIFd7rqdJuPRR9SkjP-1cWbf-nJsnSPyoThm9U6YJ7TXnOuFysZPE-z6DZjB7I351XvakdG4U9_BR-6zwlbh-GC618sM5OFbg/s1600/display-teasers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7Ho1cn2W_LK5IRim5zSwTTs93sZSmi4qPQkh36WGlpBkUIFd7rqdJuPRR9SkjP-1cWbf-nJsnSPyoThm9U6YJ7TXnOuFysZPE-z6DZjB7I351XvakdG4U9_BR-6zwlbh-GC618sM5OFbg/s1600/display-teasers.png" /></a></div>
The image above depicts what we will achieve, a simple drop-down list with the options "Yes" and "No" (we'll discuss the default in the next post).<br />
<br />
However, this generic attribute and editor configuration we will create will also handle rendering the options as check-boxes, such as the image below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlgEUK0gR0kV1OqZAO12_PrTKhj09OP_9mGhf3jA8tXlfyvDWRwxIn3Hc3TQA5MYDKvjnTjQ_Nki0n40Y2bbMcIGxrr8QhyphenhyphenVoW_LIiCZ9C3r54RCHn4CaWixM8NG93MjAjkdOuH_MofQ47/s1600/teasers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlgEUK0gR0kV1OqZAO12_PrTKhj09OP_9mGhf3jA8tXlfyvDWRwxIn3Hc3TQA5MYDKvjnTjQ_Nki0n40Y2bbMcIGxrr8QhyphenhyphenVoW_LIiCZ9C3r54RCHn4CaWixM8NG93MjAjkdOuH_MofQ47/s1600/teasers.png" /></a></div>
<br />
First we create the attribute.<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span>[AttributeUsage(AttributeTargets.Property)]</pre>
<pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> ListItemsAttribute : Attribute</pre>
<pre class="alt"><span class="lnum"> 3: </span>{</pre>
<pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">enum</span> ControlRenderType { Dropdown, Checkboxes };</pre>
<pre class="alt"><span class="lnum"> 5: </span> </pre>
<pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> IEnumerable<ISelectItem> Items { <span class="kwrd">get</span>; <span class="kwrd">protected</span> <span class="kwrd">set</span>; }</pre>
<pre class="alt"><span class="lnum"> 7: </span> </pre>
<pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> ControlRenderType ControlType { <span class="kwrd">get</span>; <span class="kwrd">protected</span> <span class="kwrd">set</span>; }</pre>
<pre class="alt"><span class="lnum"> 9: </span> </pre>
<pre><span class="lnum"> 10: </span> </pre>
<pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> ListItemsAttribute(<span class="kwrd">string</span> listItems)</pre>
<pre><span class="lnum"> 12: </span> : <span class="kwrd">this</span>(listItems, ControlRenderType.Dropdown, <span class="kwrd">true</span>)</pre>
<pre class="alt"><span class="lnum"> 13: </span> { }</pre>
<pre><span class="lnum"> 14: </span> </pre>
<pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">public</span> ListItemsAttribute(<span class="kwrd">string</span> listItems, ControlRenderType controlType)</pre>
<pre><span class="lnum"> 16: </span> : <span class="kwrd">this</span>(listItems, controlType, <span class="kwrd">true</span>)</pre>
<pre class="alt"><span class="lnum"> 17: </span> { }</pre>
<pre><span class="lnum"> 18: </span> </pre>
<pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">public</span> ListItemsAttribute(<span class="kwrd">string</span> listItems, ControlRenderType controlType, <span class="kwrd">bool</span> addEmpty)</pre>
<pre><span class="lnum"> 20: </span> {</pre>
<pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">if</span> (<span class="kwrd">string</span>.IsNullOrWhiteSpace(listItems))</pre>
<pre><span class="lnum"> 22: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception(<span class="str">"List items string cannot be null or empty"</span>);</pre>
<pre class="alt"><span class="lnum"> 23: </span> </pre>
<pre><span class="lnum"> 24: </span> <span class="kwrd">var</span> selectItems = <span class="kwrd">new</span> List<SelectItem>();</pre>
<pre class="alt"><span class="lnum"> 25: </span> </pre>
<pre><span class="lnum"> 26: </span> <span class="kwrd">string</span>[] items = listItems.Split(<span class="kwrd">new</span> <span class="kwrd">char</span>[] { <span class="str">';'</span> });</pre>
<pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">foreach</span> (<span class="kwrd">string</span> item <span class="kwrd">in</span> items)</pre>
<pre><span class="lnum"> 28: </span> {</pre>
<pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">string</span>[] pair = item.Split(<span class="kwrd">new</span> <span class="kwrd">char</span>[] { <span class="str">':'</span> });</pre>
<pre><span class="lnum"> 30: </span> </pre>
<pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">if</span> (pair.Length > 2)</pre>
<pre><span class="lnum"> 32: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception(<span class="str">"List items string contains faulty item"</span>);</pre>
<pre class="alt"><span class="lnum"> 33: </span> </pre>
<pre><span class="lnum"> 34: </span> <span class="kwrd">string</span> text = pair[0];</pre>
<pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">string</span> value = pair.Length == 2 ? pair[1] : pair[0];</pre>
<pre><span class="lnum"> 36: </span> </pre>
<pre class="alt"><span class="lnum"> 37: </span> selectItems.Add(<span class="kwrd">new</span> SelectItem() { Text = text, Value = value });</pre>
<pre><span class="lnum"> 38: </span> }</pre>
<pre class="alt"><span class="lnum"> 39: </span> </pre>
<pre><span class="lnum"> 40: </span> <span class="kwrd">if</span> (addEmpty && controlType == ControlRenderType.Dropdown)</pre>
<pre class="alt"><span class="lnum"> 41: </span> selectItems.Insert(0, <span class="kwrd">new</span> SelectItem());</pre>
<pre><span class="lnum"> 42: </span> </pre>
<pre class="alt"><span class="lnum"> 43: </span> Items = selectItems;</pre>
<pre><span class="lnum"> 44: </span> </pre>
<pre class="alt"><span class="lnum"> 45: </span> ControlType = controlType;</pre>
<pre><span class="lnum"> 46: </span> }</pre>
<pre class="alt"><span class="lnum"> 47: </span>}</pre>
</div>
<br />
The idea here is to add a string with the options as well as the type to be rendered (either drop-down list or check-boxes) and if we should add an empty selection to the drop-down list or not.<br />
<br />
Basically a string with this pattern "Text1:Value1;Text2:Value2;TextN;ValueN" is passed into a the constructor and parsed into the property Items of IEnumerable<ISelectItem>, as displayed below.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span>[ListItems(<span class="str">"Yes (default):Y;No:N"</span>, ListItemsAttribute.ControlRenderType.Dropdown)]</pre>
<pre><span class="lnum"> 2: </span>[UIHint("ListItems")]</pre>
<pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">string</span> DisplayTeasers { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }</pre>
</div>
<br />
Ok, so now we have the attribute but now we need to <a href="http://world.episerver.com/Blogs/Linus-Ekstrom/Dates/2012/9/EPiServer-7-Configuring-editors-for-your-properties/" target="_blank">configure the editors</a> in order to render the property properly in edit mode. Basically you inherit the EditorDescriptor class and override the ModifyMetadata method. The hook-up that connects the property (in this example "DisplayTeasers") and the EditorDescriptor is made by both the property's attribute and the EditorDescriptor class EditorDescriptorRegistration UIHint having the same value of, in this example, "ListItems".<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span>[EditorDescriptorRegistration(TargetType = <span class="kwrd">typeof</span>(<span class="kwrd">string</span>), UIHint = "ListItems")]</pre>
<pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> ListItemsEditorDescriptor : EditorDescriptor</pre>
<pre class="alt"><span class="lnum"> 3: </span>{</pre>
<pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)</pre>
<pre class="alt"><span class="lnum"> 5: </span> {</pre>
<pre><span class="lnum"> 6: </span> SelectionFactoryType = <span class="kwrd">typeof</span>(ListItemsSelectionFactory);</pre>
<pre class="alt"><span class="lnum"> 7: </span> </pre>
<pre><span class="lnum"> 8: </span> ListItemsAttribute.ControlRenderType controlType = ListItemsSelectionFactory.GetListItemsControlType(metadata);</pre>
<pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">switch</span> (controlType)</pre>
<pre><span class="lnum"> 10: </span> {</pre>
<pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">case</span> ListItemsAttribute.ControlRenderType.Dropdown:</pre>
<pre><span class="lnum"> 12: </span> ClientEditingClass = <span class="str">"epi/cms.contentediting.editors.SelectionEditor"</span>;</pre>
<pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">break</span>;</pre>
<pre><span class="lnum"> 14: </span> <span class="kwrd">case</span> ListItemsAttribute.ControlRenderType.Checkboxes:</pre>
<pre class="alt"><span class="lnum"> 15: </span> ClientEditingClass = <span class="str">"epi/cms.contentediting.editors.CheckBoxListEditor"</span>;</pre>
<pre><span class="lnum"> 16: </span> <span class="kwrd">break</span>;</pre>
<pre class="alt"><span class="lnum"> 17: </span> }</pre>
<pre><span class="lnum"> 18: </span> </pre>
<pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">base</span>.ModifyMetadata(metadata, attributes);</pre>
<pre><span class="lnum"> 20: </span> }</pre>
<pre class="alt"><span class="lnum"> 21: </span>}</pre>
</div>
<br />
Based on the ListItemsAttribute and its ControlRenderType we either render a drop-down list or check-boxes. We also specify a custom SelectionFactoryType, as described below, and it is this piece of code that supplies the editor control with the specified options.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ --><br />
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> ListItemsSelectionFactory : ISelectionFactory</pre>
<pre><span class="lnum"> 2: </span>{</pre>
<pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)</pre>
<pre><span class="lnum"> 4: </span> {</pre>
<pre class="alt"><span class="lnum"> 5: </span> ListItemsAttribute attribute = GetListItemsAttribute(metadata);</pre>
<pre><span class="lnum"> 6: </span> </pre>
<pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">return</span> attribute == <span class="kwrd">null</span></pre>
<pre><span class="lnum"> 8: </span> ? <span class="kwrd">new</span> List<SelectItem>()</pre>
<pre class="alt"><span class="lnum"> 9: </span> : attribute.Items;</pre>
<pre><span class="lnum"> 10: </span> }</pre>
<pre class="alt"><span class="lnum"> 11: </span> </pre>
<pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> ListItemsAttribute GetListItemsAttribute(ExtendedMetadata metadata)</pre>
<pre class="alt"><span class="lnum"> 13: </span> {</pre>
<pre><span class="lnum"> 14: </span> <span class="kwrd">return</span> metadata.Attributes.FirstOrDefault(a => <span class="kwrd">typeof</span>(ListItemsAttribute) == a.GetType()) <span class="kwrd">as</span> ListItemsAttribute;</pre>
<pre class="alt"><span class="lnum"> 15: </span> }</pre>
<pre><span class="lnum"> 16: </span> </pre>
<pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> ListItemsAttribute.ControlRenderType GetListItemsControlType(ExtendedMetadata metadata)</pre>
<pre><span class="lnum"> 18: </span> {</pre>
<pre class="alt"><span class="lnum"> 19: </span> ListItemsAttribute attribute = GetListItemsAttribute(metadata);</pre>
<pre><span class="lnum"> 20: </span> </pre>
<pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">return</span> attribute == <span class="kwrd">null</span></pre>
<pre><span class="lnum"> 22: </span> ? ListItemsAttribute.ControlRenderType.Dropdown</pre>
<pre class="alt"><span class="lnum"> 23: </span> : attribute.ControlType;</pre>
<pre><span class="lnum"> 24: </span> }</pre>
<pre class="alt"><span class="lnum"> 25: </span>}</pre>
</div>
<br />
We read the the ExtendedMetadata variable supplied in the method and get the ListItemsAttribute which contains all the items to be rendered.<br />
<br />
And all of this together let us define an input field in edit mode with a data annotation attribute.Anonymoushttp://www.blogger.com/profile/04180534287038596566noreply@blogger.com0