Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / MonoRail / Castle.MonoRail.Framework / Helpers / FormHelper.cs
blobd7ad14892528f5bda12e87eafb7b07534f34f95b
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework.Helpers
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Collections.Specialized;
21 using System.Data;
22 using System.IO;
23 using System.Reflection;
24 using System.Text;
25 using Configuration;
26 using Services;
27 using HtmlTextWriter = System.Web.UI.HtmlTextWriter;
29 using Castle.Core;
30 using Castle.Core.Logging;
31 using Castle.MonoRail.Framework;
32 using Castle.MonoRail.Framework.Helpers.ValidationStrategy;
33 using Castle.MonoRail.Framework.Internal;
34 using Castle.Components.Binder;
35 using Castle.Components.Validator;
37 /// <summary>
38 /// Represents all scopes that the <see cref="FormHelper"/>
39 /// uses to search for root values
40 /// </summary>
41 public enum RequestContext
43 /// <summary>
44 /// All scopes should be searched
45 /// </summary>
46 All,
47 /// <summary>
48 /// Only PropertyBag should be searched
49 /// </summary>
50 PropertyBag,
51 /// <summary>
52 /// Only Flash should be searched
53 /// </summary>
54 Flash,
55 /// <summary>
56 /// Only Session should be searched
57 /// </summary>
58 Session,
59 /// <summary>
60 /// Only Request should be searched
61 /// </summary>
62 Request,
63 /// <summary>
64 /// Only Params should be searched
65 /// </summary>
66 Params
69 /// <summary>
70 /// The FormHelper allows you to output html input elements using the
71 /// conventions necessary to use the DataBinder on the server side. Ultimately it
72 /// allows you do to a bi-directional binding -- if used properly.
73 /// </summary>
74 ///
75 /// <seealso xref="DataBindAttribute"/>
76 ///
77 /// <example>
78 /// Using simple values:
79 /// <para>On the controller:</para>
80 ///
81 /// <code>
82 /// public void MyAction()
83 /// {
84 /// PropertyBag["name"] = "John Doe";
85 /// }
86 /// </code>
87 ///
88 /// <para>On the view (using NVelocity syntax)</para>
89 ///
90 /// <code lang="none">
91 /// $Form.TextField('name') // Renders an input text with value "John Doe"
92 /// </code>
93 ///
94 /// <para>
95 /// Using complex objects:
96 /// </para>
97 ///
98 /// <code>
99 /// public void MyAction()
100 /// {
101 /// PropertyBag["user"] = new User("John Doe");
102 /// }
103 /// </code>
104 ///
105 /// <para>On the view (using NVelocity syntax)</para>
106 ///
107 /// <code lang="none">
108 /// $Form.TextField('user.name') // Renders an input text with value "John Doe"
109 /// </code>
110 /// </example>
111 ///
112 /// <remarks>
113 /// <b>Elements generation</b> <br/>
114 /// <para>
115 /// <list type="table">
116 /// <item>
117 /// <term>Buttons</term>
118 /// <description>
119 /// <see cref="Submit(string)"/> <br/>
120 /// <see cref="Button(string)" /> <br/>
121 /// <see cref="ButtonElement(string)" />
122 /// </description>
123 /// </item>
124 ///
125 /// <item>
126 /// <term>Select</term>
127 /// <description>
128 /// <see cref="Select(string,IEnumerable)" />
129 /// </description>
130 /// </item>
131 ///
132 /// <item>
133 /// <term>Text area</term>
134 /// <description>
135 /// <see cref="TextArea(string)" />
136 /// </description>
137 /// </item>
138 ///
139 /// <item>
140 /// <term>Hidden field</term>
141 /// <description>
142 /// <see cref="HiddenField(string)" />
143 /// </description>
144 /// </item>
145 ///
146 /// <item>
147 /// <term>Checkbox field</term>
148 /// <description>
149 /// <see cref="CheckboxField(string)" /> <br/>
150 /// <see cref="CreateCheckboxList(string,IEnumerable)" />
151 /// </description>
152 /// </item>
153 ///
154 /// <item>
155 /// <term>Radio field</term>
156 /// <description>
157 /// <see cref="RadioField(string,object)" />
158 /// </description>
159 /// </item>
160 ///
161 /// <item>
162 /// <term>File upload</term>
163 /// <description>
164 /// <see cref="FileField(string)" />
165 /// </description>
166 /// </item>
167 ///
168 /// <item>
169 /// <term>Text field</term>
170 /// <description>
171 /// <see cref="TextField(string)" /> <br/>
172 /// <see cref="TextFieldValue(string, object)"/> <br/>
173 /// <see cref="NumberField(string)" /> <br/>
174 /// <see cref="NumberFieldValue(string, object)"/>
175 /// </description>
176 /// </item>
177 ///
178 /// <item>
179 /// <term>Password field</term>
180 /// <description>
181 /// <see cref="PasswordField(string)" /> <br/>
182 /// <see cref="PasswordNumberField(string)" />
183 /// </description>
184 /// </item>
185 ///
186 /// <item>
187 /// <term>Labels</term>
188 /// <description>
189 /// <see cref="LabelFor(string,string)" /> <br/>
190 /// <see cref="LabelFor(string,string,IDictionary)"/>
191 /// </description>
192 /// </item>
193 ///
194 /// </list>
195 /// </para>
196 ///
197 /// <para>
198 /// <b>FormValidation</b> <br/>
199 /// The following operations are related to the Form Validation support:
200 /// </para>
201 ///
202 /// <list type="table">
203 /// <item>
204 /// <term><see cref="FormTag(IDictionary)"/> and <see cref="EndFormTag"/> </term>
205 /// <description>Opens/close the form tag. They are required to use the automatic form validation</description>
206 /// </item>
207 /// <item>
208 /// <term><see cref="DisableValidation"/> </term>
209 /// <description>Disables validation altogether</description>
210 /// </item>
211 /// <item>
212 /// <term><see cref="UseWebValidatorProvider"/> </term>
213 /// <description>Sets a custom Browser validator provider</description>
214 /// </item>
215 /// <item>
216 /// <term><see cref="UsePrototypeValidation"/> </term>
217 /// <description>Configures the helper to use the prototype easy field validation. Must be invoked before FormTag</description>
218 /// </item>
219 /// <item>
220 /// <term><see cref="UsefValidate"/> </term>
221 /// <description>Configures the helper to use the fValidate. Deprecated.</description>
222 /// </item>
223 /// <item>
224 /// <term><see cref="UseZebdaValidation"/> </term>
225 /// <description>Configures the helper to use the Zebda. Must be invoked before FormTag</description>
226 /// </item>
227 /// </list>
228 ///
229 /// <para>
230 /// <b>Mask support</b>. <br/>
231 /// For most elements, you can use
232 /// the entries <c>mask</c> and optionally <c>mask_separator</c> to define a
233 /// mask for your inputs. Kudos to mordechai Sandhaus - 52action.com
234 /// </para>
235 ///
236 /// <para>
237 /// For example: mask='2,5',mask_separator='/' will mask the content to '12/34/1234'
238 /// </para>
239 /// </remarks>
240 public class FormHelper : AbstractHelper, IServiceEnabledComponent
242 /// <summary>
243 /// Common property flags for reflection
244 /// </summary>
245 protected static readonly BindingFlags PropertyFlags = BindingFlags.GetProperty|BindingFlags.Public|BindingFlags.Instance|BindingFlags.IgnoreCase;
246 /// <summary>
247 /// Common property flags for reflection (with declared only)
248 /// </summary>
249 protected static readonly BindingFlags PropertyFlags2 = BindingFlags.GetProperty|BindingFlags.Public|BindingFlags.Instance|BindingFlags.IgnoreCase|BindingFlags.DeclaredOnly;
250 /// <summary>
251 /// Common field flags for reflection
252 /// </summary>
253 protected static readonly BindingFlags FieldFlags = BindingFlags.GetField|BindingFlags.Public|BindingFlags.Instance|BindingFlags.IgnoreCase;
255 private int formCount;
256 private string currentFormId;
257 private bool isValidationDisabled;
258 private readonly Stack objectStack = new Stack();
259 private BrowserValidationConfiguration validationConfig;
260 private IBrowserValidatorProvider validatorProvider = new PrototypeWebValidator();
261 private IValidatorRegistry validatorRegistry;
262 private ValidatorRunner validatorRunner;
264 #region Constructors
265 /// <summary>
266 /// Initializes a new instance of the <see cref="FormHelper"/> class.
267 /// </summary>
268 public FormHelper()
270 validatorRegistry = new CachedValidationRegistry();
271 validatorRunner = new ValidatorRunner(false, validatorRegistry);
274 /// <summary>
275 /// Initializes a new instance of the <see cref="FormHelper"/> class.
276 /// setting the Controller, Context and ControllerContext.
277 /// </summary>
278 /// <param name="engineContext">The engine context.</param>
279 public FormHelper(IEngineContext engineContext) : base(engineContext)
281 validatorRegistry = new CachedValidationRegistry();
282 validatorRunner = new ValidatorRunner(false, validatorRegistry);
285 #endregion
287 /// <summary>
288 /// Logger instance
289 /// </summary>
290 protected static ILogger logger = NullLogger.Instance;
292 #region IServiceEnabledComponent implementation
294 /// <summary>
295 /// Invoked by the framework in order to give a chance to
296 /// obtain other services
297 /// </summary>
298 /// <param name="provider">The service proviver</param>
299 public virtual void Service(IServiceProvider provider)
301 ILoggerFactory loggerFactory = (ILoggerFactory) provider.GetService(typeof(ILoggerFactory));
303 if (loggerFactory != null)
305 logger = loggerFactory.Create(typeof(FormHelper));
308 IMonoRailConfiguration config = (IMonoRailConfiguration) provider.GetService(typeof(IMonoRailConfiguration));
310 if (config != null)
312 LibraryConfiguration jsLibConfig = config.JSGeneratorConfiguration.DefaultLibrary;
314 if (jsLibConfig != null)
316 if (jsLibConfig.BrowserValidatorProvider != null)
318 validatorProvider = (IBrowserValidatorProvider)
319 Activator.CreateInstance(jsLibConfig.BrowserValidatorProvider);
324 validatorRegistry = (IValidatorRegistry) provider.GetService(typeof(IValidatorRegistry));
326 if (validatorRegistry != null)
328 validatorRunner = new ValidatorRunner(false, validatorRegistry);
330 else
332 validatorRunner = new ValidatorRunner(false, new CachedValidationRegistry());
336 #endregion
338 /// <summary>
339 /// Gets or sets the validator provider.
340 /// </summary>
341 /// <value>The validator provider.</value>
342 public IBrowserValidatorProvider ValidatorProvider
344 get { return validatorProvider; }
345 set { validatorProvider = value; }
348 /// <summary>
349 /// Gets or sets the validator runner.
350 /// </summary>
351 /// <value>The validator runner.</value>
352 public ValidatorRunner ValidatorRunner
354 get { return validatorRunner; }
355 set { validatorRunner = value; }
358 /// <summary>
359 /// Renders a Javascript library inside a single script tag.
360 /// </summary>
361 /// <returns></returns>
362 public virtual string InstallScripts()
364 return RenderScriptBlockToSource("/MonoRail/Files/FormHelperScript");
367 #region FormTag related
369 /// <summary>
370 /// Creates a form tag based on the parameters.
371 /// <para>
372 /// Javascript validation can also be bound to
373 /// the form and|or elements nested as long as the helper is
374 /// able to reach the <see cref="Type"/> of the object used on your view code
375 /// </para>
376 /// <para>
377 /// The action attribute generation will use <see cref="UrlHelper"/>
378 /// </para>
379 /// </summary>
380 ///
381 /// <seealso cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>
382 ///
383 /// <example>
384 ///
385 /// <code lang="none">
386 /// $Form.FormTag("%{action='Save',id='productform'}")
387 /// </code>
388 ///
389 /// Outputs:
390 ///
391 /// <code lang="xml">
392 /// &lt;form method='post' action='/[appdir]/[controller]/Save.[extension]' id='productform'&gt;
393 /// </code>
395 /// </example>
396 ///
397 /// <remarks>
398 /// The parameters are used to build a url and also to form the tag. For notes on the url
399 /// see <see cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>. The other parameters supported
400 /// follows
401 ///
402 /// <list type="table">
403 /// <term>
404 /// <term>noaction</term>
405 /// <description>boolean. Disables the generation of an action</description>
406 /// </term>
407 /// <term>
408 /// <term>method</term>
409 /// <description>string. The http method to use. Defaults to <c>post</c></description>
410 /// </term>
411 /// <term>
412 /// <term>id</term>
413 /// <description>string. The form id.</description>
414 /// </term>
415 /// </list>
416 ///
417 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
418 ///
419 /// </remarks>
420 ///
421 /// <param name="parameters">The parameters for the tag or for action and form validation generation.</param>
422 /// <returns></returns>
423 public virtual string FormTag(IDictionary parameters)
425 string url = null;
427 // Creates action attribute
428 if (CommonUtils.ObtainEntryAndRemove(parameters, "noaction", "false") == "false")
430 url = UrlHelper.For(parameters);
433 return FormTag(url, parameters);
436 /// <summary>
437 /// Creates a form tag based on the parameters.
438 /// <para>
439 /// Javascript validation can also be bound to
440 /// the form and|or elements nested as long as the helper is
441 /// able to reach the <see cref="Type"/> of the object used on your view code
442 /// </para>
443 /// <para>
444 /// The action attribute generation will use <see cref="UrlHelper"/>
445 /// </para>
446 /// </summary>
447 ///
448 /// <example>
449 ///
450 /// <code lang="none">
451 /// $Form.FormTag('mytarget.castle', "%{id='productform'}")
452 /// </code>
453 ///
454 /// Outputs:
455 ///
456 /// <code lang="xml">
457 /// &lt;form method='post' action='mytarget.castle' id='productform'&gt;
458 /// </code>
460 /// </example>
461 ///
462 /// <remarks>
463 /// The following parameters are accepted.
464 ///
465 /// <list type="table">
466 /// <term>
467 /// <term>method</term>
468 /// <description>string. The http method to use. Defaults to <c>post</c></description>
469 /// </term>
470 /// <term>
471 /// <term>id</term>
472 /// <description>string. The form id.</description>
473 /// </term>
474 /// </list>
475 ///
476 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
477 ///
478 /// </remarks>
479 ///
480 /// <param name="url">The hardcoded url.</param>
481 /// <param name="parameters">The parameters for the tag or for action and form validation generation.</param>
482 /// <returns></returns>
483 public virtual string FormTag(string url, IDictionary parameters)
485 string method = CommonUtils.ObtainEntryAndRemove(parameters, "method", "post");
486 currentFormId = CommonUtils.ObtainEntryAndRemove(parameters, "id", "form" + ++formCount);
488 validationConfig = validatorProvider.CreateConfiguration(parameters);
490 string afterFormTag = IsValidationEnabled ?
491 validationConfig.CreateAfterFormOpened(currentFormId) :
492 String.Empty;
494 string formContent;
496 if (url != null)
498 formContent = "<form action='" + url + "' method='" + method + "' " +
499 "id='" + currentFormId + "' " + GetAttributes(parameters) + ">";
501 else
503 formContent = "<form method='" + method + "' id='" + currentFormId + "' " + GetAttributes(parameters) + ">";
506 return formContent + afterFormTag;
509 /// <summary>
510 /// Generate Ajax form tag for ajax based form submission. Experimental.
511 /// </summary>
512 /// <param name="parameters"></param>
513 /// <returns></returns>
514 public virtual string AjaxFormTag(IDictionary parameters)
516 currentFormId = CommonUtils.ObtainEntryAndRemove(parameters, "id", "form" + ++formCount);
518 validationConfig = validatorProvider.CreateConfiguration(parameters);
520 string afterFormTag = IsValidationEnabled ?
521 validationConfig.CreateAfterFormOpened(currentFormId) :
522 String.Empty;
524 string url = UrlHelper.For(parameters);
526 parameters["form"] = true;
528 if (parameters.Contains("onsubmit"))
530 string onSubmitFunc = CommonUtils.ObtainEntryAndRemove(parameters, "onsubmit");
531 //remove return to make it compatible for ajax condition
532 if (onSubmitFunc.StartsWith("return ", StringComparison.InvariantCultureIgnoreCase))
534 onSubmitFunc = onSubmitFunc.Substring(7);
536 if (onSubmitFunc.EndsWith(";", StringComparison.InvariantCultureIgnoreCase))
538 onSubmitFunc = onSubmitFunc.Remove(onSubmitFunc.Length - 1);
540 string conditionFunc = CommonUtils.ObtainEntryAndRemove(parameters, "condition", string.Empty);
541 if (!string.IsNullOrEmpty(conditionFunc))
543 conditionFunc += " && ";
545 conditionFunc += onSubmitFunc;
547 parameters["condition"] = conditionFunc;
549 bool isMethodAssigned = parameters.Contains("method");
551 string method = CommonUtils.ObtainEntryAndRemove(parameters, "method", "post");
553 parameters["url"] = url;
555 // reassign method so in case if there is no value the default is assigned.
557 if (isMethodAssigned)
559 parameters["method"] = method;
562 String remoteFunc = new AjaxHelper().RemoteFunction(parameters);
564 string formContent = String.Format("<form id='{1}' method='{2}' {3} onsubmit=\"{0}; return false;\" enctype=\"multipart/form-data\">", remoteFunc, currentFormId, method,GetAttributes(parameters));
566 return formContent + afterFormTag;
569 /// <summary>
570 /// Renders an end form element.
571 /// </summary>
572 /// <remarks>
573 /// Should be used if you are using form validation. Some validation approaches
574 /// uses the end form before or after appending a javascript snippet.
575 /// </remarks>
576 /// <returns></returns>
577 public virtual string EndFormTag()
579 string beforeEndTag = string.Empty;
581 if (validationConfig != null)
583 beforeEndTag = IsValidationEnabled ?
584 validationConfig.CreateBeforeFormClosed(currentFormId) :
585 String.Empty;
588 return beforeEndTag + "</form>";
591 #endregion
593 #region Object scope related
595 /// <summary>
596 /// Pushes the specified target. Experimental.
597 /// </summary>
598 /// <param name="target">The target.</param>
599 public virtual void Push(string target)
601 Push(target, null);
604 /// <summary>
605 /// Pushes the specified target. Experimental.
606 /// </summary>
607 /// <param name="target">The target.</param>
608 /// <param name="parameters">The parameters.</param>
609 public virtual void Push(string target, IDictionary parameters)
611 string disableValidation = CommonUtils.ObtainEntryAndRemove(parameters, "disablevalidation", "false");
612 object value = ObtainValue(target);
614 if (value != null)
616 objectStack.Push(new FormScopeInfo(target, disableValidation != "true"));
618 else
620 value = ObtainValue(target + "type");
622 if (value != null)
624 objectStack.Push(new FormScopeInfo(target, disableValidation != "true"));
626 else
628 throw new ArgumentException("target could not be evaluated during Push operation. Target: " + target);
633 /// <summary>
634 /// Pops this instance. Experimental.
635 /// </summary>
636 public virtual void Pop()
638 objectStack.Pop();
641 #endregion
643 #region Submit and Button related
645 /// <summary>
646 /// Generates an input submit element.
647 /// </summary>
648 /// <param name="value">The value/caption for the button.</param>
649 /// <returns>The element tag</returns>
650 public virtual string Submit(string value)
652 return Submit(value, null);
655 /// <summary>
656 /// Generates an input submit element.
657 /// </summary>
658 /// <param name="value">The value/caption for the button.</param>
659 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
660 /// <returns>The element tag</returns>
661 public virtual string Submit(string value, IDictionary attributes)
663 return CreateInputElement("submit", value, attributes);
666 /// <summary>
667 /// Generates an graphical submit element.
668 /// </summary>
669 /// <param name="imgsrc">The path the image file.</param>
670 /// <param name="alttext">The alt text displayed by screenreaders, or when images are not enabled.</param>
671 /// <returns>The element tag</returns>
672 public virtual string ImageSubmit(string imgsrc, string alttext)
674 return ImageSubmit(imgsrc, alttext, null);
677 /// <summary>
678 /// Generates an input submit element.
679 /// </summary>
680 /// <param name="imgsrc">The path the image file.</param>
681 /// <param name="alttext">The alt text displayed by screenreaders, or when images are not enabled.</param>
682 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
683 /// <returns>The element tag</returns>
684 public virtual string ImageSubmit(string imgsrc, string alttext, IDictionary attributes)
686 attributes = attributes != null ? attributes : new Hashtable();
687 attributes["src"] = imgsrc;
688 attributes["alt"] = alttext;
689 return CreateInputElement("image", alttext, attributes);
692 /// <summary>
693 /// Generates an input button element.
694 /// </summary>
695 /// <param name="value">The value.</param>
696 /// <returns>The element tag</returns>
697 public virtual string Button(string value)
699 return Button(value, null);
702 /// <summary>
703 /// Generates an input button element.
704 /// </summary>
705 /// <param name="value">The value.</param>
706 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
707 /// <returns>The element tag</returns>
708 public virtual string Button(string value, IDictionary attributes)
710 return CreateInputElement("button", value, attributes);
713 /// <summary>
714 /// Creates a basic button element of type submit.
715 /// </summary>
716 /// <param name="innerText">The inner text of the button element.</param>
717 /// <returns>The generated button element.</returns>
718 public virtual string ButtonElement(string innerText)
720 return ButtonElement(innerText, "submit", null);
723 /// <summary>
724 /// Creates a basic button element of the specified type.
725 /// </summary>
726 /// <param name="innerText">The inner text of the button element.</param>
727 /// <param name="type">The type of the button.</param>
728 /// <returns>The generated button element.</returns>
729 public virtual string ButtonElement(string innerText, string type)
731 return ButtonElement(innerText, type, null);
734 /// <summary>
735 /// Creates a basic button element of the specified type and with specified attributes.
736 /// </summary>
737 /// <param name="innerText">The inner text of the button element.</param>
738 /// <param name="type">The type of the button.</param>
739 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
740 /// <returns>The generated button element.</returns>
741 public virtual string ButtonElement(string innerText, string type, IDictionary attributes)
743 return String.Format("<button type=\"{0}\" {1}>{2}</button>", type, GetAttributes(attributes), innerText);
746 #endregion
748 #region TextFieldValue
750 /// <summary>
751 /// Generates an input text form element
752 /// with the supplied value
753 /// </summary>
754 /// <param name="target">The string to be used to create the element name.</param>
755 /// <param name="value">Value to supply to the element (instead of querying the target)</param>
756 /// <returns>The generated form element</returns>
757 public string TextFieldValue(string target, object value)
759 return TextFieldValue(target, value, null);
762 /// <summary>
763 /// Generates an input text form element
764 /// with the supplied value
765 /// </summary>
766 /// <param name="target">The string to be used to create the element name.</param>
767 /// <param name="value">Value to supply to the element (instead of querying the target)</param>
768 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
769 /// <returns>The generated form element</returns>
770 public string TextFieldValue(string target, object value, IDictionary attributes)
772 return CreateInputElement("text", target, value, attributes);
775 #endregion
777 #region TextField
779 /// <summary>
780 /// Generates an input text element.
781 /// <para>
782 /// The value is extracted from the target (if available)
783 /// </para>
784 /// </summary>
785 ///
786 /// <example>
787 /// The following example assumes that an entry <c>username</c> exists on the
788 /// <see cref="Controller.PropertyBag"/> or <see cref="Controller.Flash"/> or <see cref="Controller.Session"/>
789 ///
790 /// <code lang="none">
791 /// $Form.TextField('username')
792 /// </code>
793 /// Outputs:
794 /// <code lang="xml">
795 /// &lt;input type='text' name='username' id='username' value='John Doe' /&gt;
796 /// </code>
797 ///
798 /// <para>
799 /// The following example assumes that an entry <c>user</c> exists on the
800 /// <see cref="Controller.PropertyBag"/> or <see cref="Controller.Flash"/> or <see cref="Controller.Session"/>
801 /// </para>
802 ///
803 /// <code lang="none">
804 /// $Form.TextField('user.name')
805 /// </code>
806 /// Outputs:
807 /// <code lang="xml">
808 /// &lt;input type='text' name='user.name' id='user_name' value='John Doe' /&gt;
809 /// </code>
810 /// </example>
811 ///
812 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
813 /// <returns>The generated form element</returns>
814 public virtual string TextField(string target)
816 return TextField(target, null);
819 /// <summary>
820 /// Generates an input text element.
821 /// <para>
822 /// The value is extracted from the target (if available)
823 /// </para>
824 /// </summary>
825 ///
826 /// <seealso cref="TextField(string)"/>
827 ///
828 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
829 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
830 /// <returns>The generated form element</returns>
831 public virtual string TextField(string target, IDictionary attributes)
833 target = RewriteTargetIfWithinObjectScope(target);
835 object value = ObtainValue(target);
837 ApplyValidation(InputElementType.Text, target, ref attributes);
839 return CreateInputElement("text", target, value, attributes);
842 /// <summary>
843 /// Combines <see cref="AjaxHelper.InputTextWithAutoCompletion(string,string,IDictionary,IDictionary)"/>
844 /// with <see cref="TextField(string,IDictionary)"/> to achieve an TextField that offers
845 /// both input completion and databinding support.
846 /// </summary>
847 /// <param name="target">The target of the text field (analogous to <see cref="TextField(string,IDictionary)"/>)</param>
848 /// <param name="url">The url to call for completion options (analogous to <see cref="AjaxHelper.InputTextWithAutoCompletion(string,string,IDictionary,IDictionary)"/>)</param>
849 /// <param name="tagAttributes">The attributes to apply to the text input field. If this dictionary does not contain a value for browser-based autocompletion (autocomplete="off|on"), it will default to "off" to prevent interference of AJAX-based and browser-based autocompletion.</param>
850 /// <param name="completionOptions">The options for the AJAX-call analogous to <see cref="AjaxHelper.InputTextWithAutoCompletion(string,string,IDictionary,IDictionary)"/>.</param>
851 /// <returns>The generated form elements.</returns>
852 /// <remarks>Please note that this function requires Ajax scripts (Prototype/Scriptaculous)</remarks>
853 public virtual string TextFieldAutoComplete(string target, string url, IDictionary tagAttributes, IDictionary completionOptions)
855 if (!tagAttributes.Contains("autocomplete")) tagAttributes.Add("autocomplete", "off");
857 StringBuilder sb = new StringBuilder();
858 sb.Append(TextField(target, tagAttributes));
860 string textFieldId = CreateHtmlId(tagAttributes, target);
861 sb.AppendFormat("<div id=\"{0}\" class=\"auto_complete\"></div>", textFieldId + "autocomplete");
862 sb.Append(new AjaxHelper().AutoCompleteInputText(textFieldId, url, completionOptions));
864 return sb.ToString();
868 #endregion
870 #region FilteredTextField
872 /// <summary>
873 /// Generates an input text element with a javascript that prevents the
874 /// chars listed in the forbid attribute from being entered.
875 /// </summary>
876 /// <para>
877 /// You must pass an <c>forbid</c> value through the dictionary.
878 /// It must be a comma separated list of chars that cannot be accepted on the field.
879 /// For example:
880 /// </para>
881 /// <code>
882 /// FormHelper.FilteredTextField("product.price", {forbid='46'})
883 /// </code>
884 /// In this case the key code 46 (period) will not be accepted on the field.
885 /// <para>
886 /// The value is extracted from the target (if available).
887 /// </para>
888 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
889 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
890 /// <returns>The generated form element.</returns>
891 /// <remarks>
892 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it.
893 /// </remarks>
894 public virtual string FilteredTextField(string target, IDictionary attributes)
896 target = RewriteTargetIfWithinObjectScope(target);
898 object value = ObtainValue(target);
900 attributes = attributes != null ? attributes : new Hashtable();
902 ApplyFilterOptions(attributes);
903 ApplyValidation(InputElementType.Text, target, ref attributes);
905 return CreateInputElement("text", target, value, attributes);
908 #endregion
910 #region NumberField
912 /// <summary>
913 /// Generates an input text element with a javascript that prevents
914 /// chars other than numbers from being entered.
915 /// <para>
916 /// The value is extracted from the target (if available)
917 /// </para>
918 /// </summary>
919 ///
920 /// <seealso cref="InstallScripts"/>
921 /// <seealso cref="NumberField(string,IDictionary)"/>
922 ///
923 /// <remarks>
924 /// You must include the formhelper javascript functions to use the NumberField.
925 /// See <see cref="InstallScripts"/>
926 /// </remarks>
927 ///
928 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
929 /// <returns>The generated form element</returns>
930 public virtual string NumberField(string target)
932 return NumberField(target, null);
935 /// <summary>
936 /// Generates an input text element with a javascript that prevents
937 /// chars other than numbers from being entered.
938 /// <para>
939 /// The value is extracted from the target (if available)
940 /// </para>
941 /// </summary>
942 ///
943 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
944 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
945 /// <returns>The generated form element</returns>
946 ///
947 /// <remarks>
948 /// You must include the formhelper javascript functions to use the NumberField.
949 /// See <see cref="InstallScripts"/>
950 /// <para>
951 /// You can optionally pass an <c>exceptions</c> value through the dictionary.
952 /// It must be a comma separated list of chars that can be accepted on the field.
953 /// For example:
954 /// </para>
955 /// <code>
956 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
957 /// </code>
958 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
959 /// <para>
960 /// You can aslo optionally pass an <c>forbid</c> value through the dictionary.
961 /// It must be a comma separated list of chars that cannot be accepted on the field.
962 /// For example:
963 /// </para>
964 /// <code>
965 /// FormHelper.NumberField("product.price", {forbid='46'})
966 /// </code>
967 /// In this case the key code 46 (period) will not be accepted on the field.
968 /// </remarks>
969 public virtual string NumberField(string target, IDictionary attributes)
971 target = RewriteTargetIfWithinObjectScope(target);
973 object value = ObtainValue(target);
975 attributes = attributes != null ? attributes : new Hashtable();
977 ApplyNumberOnlyOptions(attributes);
978 ApplyValidation(InputElementType.Text, target, ref attributes);
980 return CreateInputElement("text", target, value, attributes);
983 #endregion
985 #region NumberFieldValue
987 /// <summary>
988 /// Generates an input text element with a javascript that prevents
989 /// chars other than numbers from being entered. The value is not gathered
990 /// from the context, instead you specify it on the second argument
991 /// </summary>
992 ///
993 /// <seealso cref="InstallScripts"/>
994 /// <seealso cref="NumberField(string,IDictionary)"/>
995 ///
996 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
997 /// <param name="value">The current value to output.</param>
998 /// <returns>The generated form element</returns>
999 ///
1000 /// <remarks>
1001 /// You must include the formhelper javascript functions to use the NumberField.
1002 /// See <see cref="InstallScripts"/>
1003 /// </remarks>
1004 public virtual string NumberFieldValue(string target, object value)
1006 return NumberFieldValue(target, value, null);
1009 /// <summary>
1010 /// Generates an input text element with a javascript that prevents
1011 /// chars other than numbers from being entered. The value is not gathered
1012 /// from the context, instead you specify it on the second argument
1013 /// </summary>
1014 ///
1015 /// <seealso cref="InstallScripts"/>
1016 /// <seealso cref="NumberField(string,IDictionary)"/>
1017 ///
1018 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1019 /// <param name="value">The current value to output.</param>
1020 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1021 /// <returns>The generated form element</returns>
1022 ///
1023 /// <remarks>
1024 /// You must include the formhelper javascript functions to use the NumberField.
1025 /// See <see cref="InstallScripts"/>
1026 /// </remarks>
1027 public virtual string NumberFieldValue(string target, object value, IDictionary attributes)
1029 target = RewriteTargetIfWithinObjectScope(target);
1031 attributes = attributes ?? new Hashtable();
1033 ApplyNumberOnlyOptions(attributes);
1034 ApplyValidation(InputElementType.Text, target, ref attributes);
1036 return CreateInputElement("text", target, value, attributes);
1039 #endregion
1041 #region TextArea
1043 /// <summary>
1044 /// Generates a textarea element.
1045 /// <para>
1046 /// The value is extracted from the target (if available)
1047 /// </para>
1048 /// </summary>
1049 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1050 /// <returns>The generated form element</returns>
1051 public virtual string TextArea(string target)
1053 return TextArea(target, null);
1056 /// <summary>
1057 /// Generates a textarea element.
1058 /// <para>
1059 /// The value is extracted from the target (if available)
1060 /// </para>
1061 /// </summary>
1062 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1063 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1064 /// <returns>The generated form element</returns>
1065 public virtual string TextArea(string target, IDictionary attributes)
1067 string targetForValue = RewriteTargetIfWithinObjectScope(target);
1068 object value = ObtainValue(targetForValue);
1069 return TextAreaValue(target, value, attributes);
1072 /// <summary>
1073 /// Generates a textarea element with a specified value.
1074 /// </summary>
1075 /// <param name="target">The target to base the element name on.</param>
1076 /// <param name="value">The value to apply to the field.</param>
1077 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1078 /// <returns>The generated form element</returns>
1079 public virtual string TextAreaValue(string target, object value, IDictionary attributes)
1081 target = RewriteTargetIfWithinObjectScope(target);
1083 value = value == null ? "" : HtmlEncode(value.ToString());
1085 string id = CreateHtmlId(target);
1087 ApplyValidation(InputElementType.Text, target, ref attributes);
1089 return String.Format("<textarea id=\"{0}\" name=\"{1}\" {2}>{3}</textarea>",
1090 id, target, GetAttributes(attributes), FormatIfNecessary(value, attributes));
1093 #endregion
1095 #region PasswordField
1097 /// <summary>
1098 /// Generates a password input field.
1099 /// </summary>
1100 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1101 /// <returns>The generated form element</returns>
1102 public virtual string PasswordField(string target)
1104 return PasswordField(target, null);
1107 /// <summary>
1108 /// Generates a password input field.
1109 /// </summary>
1110 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1111 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1112 /// <returns>The generated form element</returns>
1113 public virtual string PasswordField(string target, IDictionary attributes)
1115 target = RewriteTargetIfWithinObjectScope(target);
1117 object value = ObtainValue(target);
1119 ApplyValidation(InputElementType.Text, target, ref attributes);
1121 return CreateInputElement("password", target, value, attributes);
1124 #endregion
1126 #region PasswordNumberField
1128 /// <summary>
1129 /// Generates an input password element with a javascript that prevents
1130 /// chars other than numbers from being entered.
1131 /// <para>
1132 /// The value is extracted from the target (if available)
1133 /// </para>
1134 /// </summary>
1135 /// <remarks>
1136 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
1137 /// </remarks>
1138 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1139 /// <returns>The generated form element</returns>
1140 public virtual string PasswordNumberField(string target)
1142 return PasswordNumberField(target, null);
1145 /// <summary>
1146 /// Generates an input password element with a javascript that prevents
1147 /// chars other than numbers from being entered.
1148 /// <para>
1149 /// The value is extracted from the target (if available)
1150 /// </para>
1151 /// <para>
1152 /// You can optionally pass an <c>exceptions</c> value through the dictionary.
1153 /// It must be a comma separated list of chars that can be accepted on the field.
1154 /// For example:
1155 /// </para>
1156 /// <code>
1157 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
1158 /// </code>
1159 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
1160 /// <para>
1161 /// You can aslo optionally pass an <c>forbid</c> value through the dictionary.
1162 /// It must be a comma separated list of chars that cannot be accepted on the field.
1163 /// For example:
1164 /// </para>
1165 /// <code>
1166 /// FormHelper.NumberField("product.price", {forbid='46'})
1167 /// </code>
1168 /// In this case the key code 46 (period) will not be accepted on the field.
1169 /// </summary>
1170 /// <remarks>
1171 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
1172 /// </remarks>
1173 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1174 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1175 /// <returns>The generated form element</returns>
1176 public virtual string PasswordNumberField(string target, IDictionary attributes)
1178 target = RewriteTargetIfWithinObjectScope(target);
1180 object value = ObtainValue(target);
1182 attributes = attributes != null ? attributes : new Hashtable();
1184 ApplyNumberOnlyOptions(attributes);
1185 ApplyValidation(InputElementType.Text, target, ref attributes);
1187 return CreateInputElement("password", target, value, attributes);
1190 #endregion
1192 #region LiteralFor
1194 ///<summary>Returns the value for specified target with no additional markup. If no value is obtained
1195 /// an empty string is returned.
1196 ///</summary>
1197 ///<param name="target">The object to get the value from.</param>
1198 ///<returns>The value or an empty string if none is found.</returns>
1199 public virtual string LiteralFor(string target)
1201 target = RewriteTargetIfWithinObjectScope(target);
1202 object value = ObtainValue(target);
1203 if (value == null)
1205 return string.Empty;
1207 return value.ToString();
1210 #endregion
1212 #region LabelFor
1214 /// <summary>
1215 /// Generates a label element.
1216 /// </summary>
1217 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1218 /// <param name="label">Legend</param>
1219 /// <returns>The generated form element</returns>
1220 public virtual string LabelFor(string target, string label)
1222 return LabelFor(target, label, null);
1225 /// <summary>
1226 /// Generates a label element.
1227 /// </summary>
1228 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1229 /// <param name="label">Legend</param>
1230 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1231 /// <returns>The generated form element</returns>
1232 public virtual string LabelFor(string target, string label, IDictionary attributes)
1234 target = RewriteTargetIfWithinObjectScope(target);
1236 string id = CreateHtmlId(attributes, target);
1238 StringBuilder sb = new StringBuilder();
1239 StringWriter sbWriter = new StringWriter(sb);
1240 HtmlTextWriter writer = new HtmlTextWriter(sbWriter);
1242 writer.WriteBeginTag("label");
1243 writer.WriteAttribute("for", id);
1244 string strAttributes = GetAttributes(attributes);
1245 if (strAttributes != String.Empty)
1247 writer.Write(HtmlTextWriter.SpaceChar);
1250 writer.Write(strAttributes);
1251 writer.Write(HtmlTextWriter.TagRightChar);
1252 writer.Write(label);
1253 writer.WriteEndTag("label");
1255 return sbWriter.ToString();
1258 #endregion
1260 #region HiddenField
1262 /// <summary>
1263 /// Generates a hidden form element.
1264 /// <para>
1265 /// The value is extracted from the target (if available)
1266 /// </para>
1267 /// </summary>
1268 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1269 /// <returns>The generated form element</returns>
1270 public virtual string HiddenField(string target)
1272 target = RewriteTargetIfWithinObjectScope(target);
1274 object value = ObtainValue(target);
1276 return CreateInputElement("hidden", target, value, null);
1279 /// <summary>
1280 /// Generates a hidden form element with the specified value
1281 /// </summary>
1282 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1283 /// <param name="value">The value for the hidden field</param>
1284 /// <returns>The generated form element</returns>
1285 public virtual string HiddenField(string target, object value)
1287 return CreateInputElement("hidden", target, value, null);
1290 /// <summary>
1291 /// Generates a hidden form element.
1292 /// <para>
1293 /// The value is extracted from the target (if available)
1294 /// </para>
1295 /// </summary>
1296 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1297 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1298 /// <returns>The generated form element</returns>
1299 public virtual string HiddenField(string target, IDictionary attributes)
1301 target = RewriteTargetIfWithinObjectScope(target);
1303 object value = ObtainValue(target);
1305 string id = CreateHtmlId(attributes, target);
1307 value = value != null ? value : String.Empty;
1309 return CreateInputElement("hidden", id, target, value.ToString(), attributes);
1312 /// <summary>
1313 /// Generates a hidden form element with the specified value
1314 /// </summary>
1315 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1316 /// <param name="value">The value for the hidden field</param>
1317 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1318 /// <returns>The generated form element</returns>
1319 public virtual string HiddenField(string target, object value, IDictionary attributes)
1321 return CreateInputElement("hidden", target, value, attributes);
1324 #endregion
1326 #region CheckboxList
1328 /// <summary>
1329 /// Creates a <see cref="CheckboxList"/> instance
1330 /// which is enumerable. For each interaction you can invoke
1331 /// <see cref="CheckboxList.Item()"/> which will correctly render
1332 /// a checkbox input element for the current element on the supplied set (<c>dataSource</c>).
1333 /// <para>
1334 /// The enumerable item will be an element of the <c>dataSource</c>.
1335 /// </para>
1336 /// If the <c>dataSource</c>
1337 /// elements are complex objects (ie not string or primitives),
1338 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1339 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1340 /// <para>
1341 /// Usually both the <c>target</c> and obviously the <c>dataSource</c> are sets
1342 /// with multiple items. The element types tend to be the same. If
1343 /// they are not, you might have to specify the <c>suffix</c> parameters on
1344 /// the <c>attributes</c> as it would not be inferred.
1345 /// </para>
1346 /// </summary>
1347 ///
1348 /// <example>
1349 /// Consider the following action code:
1350 /// <code>
1351 /// public void Index()
1352 /// {
1353 /// // data source
1354 /// PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
1355 ///
1356 /// // initial selection
1357 /// PropertyBag["selectedPrimes"] = new int[] { 11, 19 };
1358 /// }
1359 /// </code>
1360 ///
1361 /// And the respective view code
1362 ///
1363 /// <code lang="none">
1364 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1365 ///
1366 /// #foreach($elem in $items)
1367 /// $items.Item() $elem
1368 /// #end
1369 /// </code>
1370 ///
1371 /// That will generates the following html:
1372 ///
1373 /// <code lang="none">
1374 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_0_&quot; name=&quot;selectedPrimes[0]&quot; value=&quot;2&quot; /&gt; 2
1375 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_1_&quot; name=&quot;selectedPrimes[1]&quot; value=&quot;3&quot; /&gt; 3
1376 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_2_&quot; name=&quot;selectedPrimes[2]&quot; value=&quot;5&quot; /&gt; 5
1377 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_3_&quot; name=&quot;selectedPrimes[3]&quot; value=&quot;7&quot; /&gt; 7
1378 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_4_&quot; name=&quot;selectedPrimes[4]&quot; value=&quot;11&quot; checked=&quot;checked&quot; /&gt; 11
1379 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_5_&quot; name=&quot;selectedPrimes[5]&quot; value=&quot;13&quot; /&gt; 13
1380 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_6_&quot; name=&quot;selectedPrimes[6]&quot; value=&quot;17&quot; /&gt; 17
1381 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_7_&quot; name=&quot;selectedPrimes[7]&quot; value=&quot;19&quot; checked=&quot;checked&quot; /&gt; 19
1382 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_8_&quot; name=&quot;selectedPrimes[8]&quot; value=&quot;23&quot; /&gt; 23
1383 /// </code>
1384 ///
1385 /// <para>
1386 /// To customize the id, you can call the <see cref="CheckboxList.Item(string)"/> overload:
1387 /// </para>
1388 ///
1389 /// <code lang="none">
1390 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1391 ///
1392 /// #foreach($elem in $items)
1393 /// $items.Item("myId${velocityCount}") $Form.LabelFor("myId${velocityCount}", $elem.ToString()) <br/>
1394 /// #end
1395 /// </code>
1396 /// </example>
1397 ///
1398 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1399 /// <param name="dataSource">The set of available elements</param>
1400 /// <returns>The generated form element</returns>
1401 public virtual CheckboxList CreateCheckboxList(string target, IEnumerable dataSource)
1403 return CreateCheckboxList(target, dataSource, null);
1406 /// <summary>
1407 /// Creates a <see cref="CheckboxList"/> instance
1408 /// which is enumerable. For each interaction you can invoke
1409 /// <see cref="CheckboxList.Item()"/> which will correctly render
1410 /// a checkbox input element for the current element on the supplied set (<c>dataSource</c>).
1411 /// <para>
1412 /// The enumerable item will be an element of the <c>dataSource</c>.
1413 /// </para>
1414 /// If the <c>dataSource</c>
1415 /// elements are complex objects (ie not string or primitives),
1416 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1417 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1418 /// <para>
1419 /// Usually both the <c>target</c> and obviously the <c>dataSource</c> are sets
1420 /// with multiple items. The element types tend to be the same. If
1421 /// they are not, you might have to specify the <c>suffix</c> parameters on
1422 /// the <c>attributes</c> as it would not be inferred.
1423 /// </para>
1424 /// </summary>
1425 ///
1426 /// <seealso cref="CreateCheckboxList(string,IEnumerable)"/>
1428 /// <example>
1429 /// Consider the following action code:
1430 /// <code>
1431 /// public void Index()
1432 /// {
1433 /// Category[] categories = new Category[] { new Category(1, "Music"), new Category(2, "Humor"), new Category(3, "Politics") };
1434 /// PropertyBag["categories"] = categories; // datasource
1435 ///
1436 /// Blog blog = new Blog();
1437 /// blog.Categories = new Category[] { new Category(2, "Humor") }; // initial selection
1438 /// PropertyBag["blog"] = blog;
1439 /// }
1440 /// </code>
1441 ///
1442 /// And the respective view code
1443 ///
1444 /// <code lang="none">
1445 /// #set($items = $Form.CreateCheckboxList("blog.categories", $categories, "%{value='Id'}"))
1446 ///
1447 /// #foreach($elem in $items)
1448 /// $items.Item() $elem
1449 /// #end
1450 /// </code>
1451 ///
1452 /// That will generates the following html:
1453 ///
1454 /// <code lang="none">
1455 /// &lt;input type=&quot;checkbox&quot; id=&quot;blog_categories_0_&quot; name=&quot;blog.categories[0].Id&quot; value=&quot;1&quot; /&gt; Music
1456 /// &lt;input type=&quot;checkbox&quot; id=&quot;blog_categories_1_&quot; name=&quot;blog.categories[1].Id&quot; value=&quot;2&quot; checked=&quot;checked&quot; /&gt; Humor
1457 /// &lt;input type=&quot;checkbox&quot; id=&quot;blog_categories_2_&quot; name=&quot;blog.categories[2].Id&quot; value=&quot;3&quot; /&gt; Politics
1458 /// </code>
1459 ///
1460 /// </example>
1461 ///
1462 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1463 /// <param name="dataSource">The set of available elements</param>
1464 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1465 /// <returns>The generated form element</returns>
1466 public virtual CheckboxList CreateCheckboxList(string target, IEnumerable dataSource, IDictionary attributes)
1468 target = RewriteTargetIfWithinObjectScope(target);
1470 object value = ObtainValue(target);
1472 return new CheckboxList(this, target, value, dataSource, attributes);
1475 /// <summary>
1476 /// Outputs a checkbox element (for internal use)
1477 /// </summary>
1478 /// <param name="index"></param>
1479 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1480 /// <param name="suffix"></param>
1481 /// <param name="item"></param>
1482 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1483 /// <returns>The generated form element</returns>
1484 internal string CheckboxItem(int index, string target, string suffix, SetItem item, IDictionary attributes)
1486 if (item.IsSelected)
1488 AddChecked(attributes);
1490 else
1492 RemoveChecked(attributes);
1495 target = String.Format("{0}[{1}]", target, index);
1497 string elementId = CreateHtmlId(attributes, target, true);
1499 string computedTarget = target;
1501 if (suffix != null && suffix != String.Empty)
1503 computedTarget += "." + suffix;
1506 return CreateInputElement("checkbox", elementId, computedTarget, item.Value, attributes);
1509 /// <summary>
1510 /// Creates a label for an item of the checkbox list (internal use). The method mirrors
1511 /// <see cref="CheckboxItem"/> to ensure that the HTML ID is created consistently.
1512 /// </summary>
1513 /// <param name="index">Index for creating HTML ID</param>
1514 /// <param name="target">Target object for which the HTML ID is needed</param>
1515 /// <param name="suffix"></param>
1516 /// <param name="label">The label to display</param>
1517 /// <param name="attributes">additional attributes that influence HTML ID creation</param>
1518 /// <returns></returns>
1519 internal string CheckboxLabel(int index, string target, string suffix, string label, IDictionary attributes)
1521 target = String.Format("{0}[{1}]", target, index);
1523 string elementId = CreateHtmlId(attributes, target, true);
1525 return "<label for=\"" + elementId + "\" " + GetAttributes(attributes) + ">" + label + "</label>";
1528 /// <summary>
1529 /// This class is an enumerable list of checkboxes.
1530 /// It uses the <see cref="OperationState"/> to manage the sets
1531 /// and to control the check/uncheck state.
1532 /// </summary>
1533 public sealed class CheckboxList : IEnumerable, IEnumerator
1535 private readonly FormHelper helper;
1536 private readonly string target;
1537 private readonly IDictionary attributes;
1538 private readonly OperationState operationState;
1539 private readonly IEnumerator enumerator;
1540 private bool hasMovedNext, hasItem;
1541 private int index = -1;
1543 /// <summary>
1544 /// Initializes a new instance of the <see cref="CheckboxList"/> class.
1545 /// </summary>
1546 /// <param name="helper">The helper.</param>
1547 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1548 /// <param name="initialSelectionSet">The initial selection set.</param>
1549 /// <param name="dataSource">The set of available elements</param>
1550 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1551 public CheckboxList(FormHelper helper, string target,
1552 object initialSelectionSet, IEnumerable dataSource, IDictionary attributes)
1554 if (dataSource == null) throw new ArgumentNullException("dataSource");
1556 this.helper = helper;
1557 this.target = target;
1558 this.attributes = attributes ?? new HybridDictionary(true);
1560 operationState = SetOperation.IterateOnDataSource(initialSelectionSet, dataSource, attributes);
1561 enumerator = operationState.GetEnumerator();
1564 /// <summary>
1565 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1566 /// on the Set.
1567 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1568 /// </summary>
1569 /// <returns>The generated input element</returns>
1570 public string Item()
1572 return Item(null);
1575 /// <summary>
1576 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1577 /// on the Set.
1578 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1579 /// </summary>
1580 /// <param name="id">The element id</param>
1581 /// <returns>The generated input element</returns>
1582 public string Item(string id)
1584 if (!hasMovedNext)
1586 throw new InvalidOperationException("Before rendering a checkbox item, you must use MoveNext");
1589 if (!hasItem)
1591 // Nothing to render
1592 return String.Empty;
1595 if (id != null)
1597 attributes["id"] = id;
1600 return helper.CheckboxItem(index, target, operationState.TargetSuffix, CurrentSetItem, attributes);
1603 /// <summary>
1604 /// Outputs a label for the current checkbox element based on the generated id.
1605 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1606 /// </summary>
1607 /// <param name="label">The text to display</param>
1608 /// <returns>The generated label element</returns>
1609 public string LabelFor(string label)
1611 return LabelFor(null, label, null);
1614 /// <summary>
1615 /// Outputs a label for the current checkbox element based on the generated id.
1616 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1617 /// </summary>
1618 /// <param name="label">The text to display</param>
1619 /// <param name="attributes">The attributes.</param>
1620 /// <returns>The generated label element</returns>
1621 public string LabelFor(string label, IDictionary attributes)
1623 return LabelFor(null, label, attributes);
1626 /// <summary>
1627 /// Outputs a label for the current checkbox element based on the given id.
1628 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1629 /// </summary>
1630 /// <param name="id">The id to use within the label</param>
1631 /// <param name="label">The text to display</param>
1632 /// <param name="attributes">The attributes.</param>
1633 /// <returns>The generated label element</returns>
1634 public string LabelFor(string id, string label, IDictionary attributes)
1636 if (!hasMovedNext)
1638 throw new InvalidOperationException("Before rendering a checkbox item, you must use MoveNext");
1641 if (attributes == null)
1643 attributes = new HybridDictionary(true);
1646 if (!hasItem)
1648 // Nothing to render
1649 return String.Empty;
1652 if (id != null)
1654 attributes["id"] = id;
1657 return helper.CheckboxLabel(index, target, operationState.TargetSuffix, label, attributes);
1660 /// <summary>
1661 /// Returns an enumerator that iterates through a collection.
1662 /// </summary>
1663 /// <returns>
1664 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
1665 /// </returns>
1666 public IEnumerator GetEnumerator()
1668 return this;
1671 /// <summary>
1672 /// Advances the enumerator to the next element of the collection.
1673 /// </summary>
1674 /// <returns>
1675 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
1676 /// </returns>
1677 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1678 public bool MoveNext()
1680 hasMovedNext = true;
1681 hasItem = enumerator.MoveNext();
1683 if (hasItem) index++;
1685 return hasItem;
1688 /// <summary>
1689 /// Sets the enumerator to its initial position, which is before the first element in the collection.
1690 /// </summary>
1691 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1692 public void Reset()
1694 index = -1;
1695 enumerator.Reset();
1698 /// <summary>
1699 /// Gets the current element in the collection.
1700 /// </summary>
1701 /// <value></value>
1702 /// <returns>The current element in the collection.</returns>
1703 /// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element. </exception>
1704 public object Current
1706 get { return CurrentSetItem.Item; }
1709 /// <summary>
1710 /// Gets the current set item.
1711 /// </summary>
1712 /// <value>The current set item.</value>
1713 public SetItem CurrentSetItem
1715 get { return enumerator.Current as SetItem; }
1719 #endregion
1721 #region CheckboxField
1723 /// <summary>
1724 /// Generates a checkbox field. In fact it generates two as a
1725 /// way to send a value if the primary checkbox is not checked.
1726 /// This allow the process the be aware of the unchecked value
1727 /// and act accordingly.
1728 /// </summary>
1729 ///
1730 /// <example>
1731 /// Consider the following view code:
1732 ///
1733 /// <code lang="none">
1734 /// $Form.CheckboxField('user.disabled')
1735 /// </code>
1736 ///
1737 /// That is going to output:
1738 ///
1739 /// <code lang="none">
1740 /// &lt;input type=&quot;checkbox&quot; id=&quot;user_disabled&quot; name=&quot;user.disabled&quot; value=&quot;true&quot; /&gt;
1741 /// &lt;input type=&quot;hidden&quot; id=&quot;user_disabledH&quot; name=&quot;user.disabled&quot; value=&quot;false&quot; /&gt;
1742 /// </code>
1743 ///
1744 /// </example>
1745 ///
1746 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1747 /// <returns>The generated form element</returns>
1748 public virtual string CheckboxField(string target)
1750 return CheckboxField(target, null);
1753 /// <summary>
1754 /// Generates a checkbox field. In fact it generates two as a
1755 /// way to send a value if the primary checkbox is not checked.
1756 /// This allow the process the be aware of the unchecked value
1757 /// and act accordingly.
1758 ///
1759 /// <para>
1760 /// The checked and unchecked values sent to the server defaults
1761 /// to true and false. You can override them using the
1762 /// parameters <c>trueValue</c> and <c>falseValue</c>, but the DataBinder is prepared only
1763 /// to treat boolean arrays.
1764 /// </para>
1765 ///
1766 /// </summary>
1767 ///
1768 /// <seealso cref="CheckboxField(string)"/>
1769 ///
1770 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1771 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1772 /// <returns>The generated form element</returns>
1773 public virtual string CheckboxField(string target, IDictionary attributes)
1775 target = RewriteTargetIfWithinObjectScope(target);
1777 object value = ObtainValue(target);
1779 string trueValue = CommonUtils.ObtainEntryAndRemove(attributes, "trueValue", "true");
1781 bool isChecked;
1783 if (trueValue != "true")
1785 isChecked = AreEqual(value, trueValue);
1787 else
1789 isChecked = ((value != null && value is bool && ((bool)value)) ||
1790 (!(value is bool) && (value != null) &&
1791 (!(value is string) || ((string) value).ToLower() != "false")));
1794 if (isChecked)
1796 if (attributes == null)
1798 attributes = new HybridDictionary(true);
1801 AddChecked(attributes);
1804 ApplyValidation(InputElementType.Checkbox, target, ref attributes);
1806 string id = CreateHtmlId(attributes, target);
1807 string hiddenElementId = id + "H";
1808 string hiddenElementValue = CommonUtils.ObtainEntryAndRemove(attributes, "falseValue", "false");
1810 string result = CreateInputElement("checkbox", id, target, trueValue, attributes);
1812 result += CreateInputElement("hidden", hiddenElementId, target, hiddenElementValue, null);
1814 return result;
1817 #endregion
1819 #region RadioField
1821 /// <summary>
1822 /// Generates a radio input type with the specified
1823 /// value to send to the served in case the element in checked.
1824 /// It will automatically check the radio if the target
1825 /// evaluated value is equal to the specified <c>valueToSend</c>.
1826 /// </summary>
1827 ///
1828 /// <example>
1829 /// Consider the following action code:
1830 ///
1831 /// <code>
1832 /// public void Index()
1833 /// {
1834 /// PropertyBag["mode"] = FileMode.Truncate;
1835 /// }
1836 /// </code>
1837 ///
1838 /// And the following view code:
1839 ///
1840 /// <code lang="none">
1841 /// $Form.RadioField("mode", "Append") FileMode.Append
1842 /// $Form.RadioField("mode", "Create") FileMode.Create
1843 /// $Form.RadioField("mode", "CreateNew") FileMode.CreateNew
1844 /// $Form.RadioField("mode", "Open") FileMode.Open
1845 /// $Form.RadioField("mode", "OpenOrCreate", "%{id='customhtmlid'}") FileMode.OpenOrCreate
1846 /// $Form.RadioField("mode", "Truncate") FileMode.Truncate
1847 /// </code>
1848 ///
1849 /// That is going to output:
1850 ///
1851 /// <code lang="none">
1852 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Append&quot; /&gt; FileMode.Append
1853 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Create&quot; /&gt; FileMode.Create
1854 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;CreateNew&quot; /&gt; FileMode.CreateNew
1855 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Open&quot; /&gt; FileMode.Open
1856 /// &lt;input type=&quot;radio&quot; id=&quot;customhtmlid&quot; name=&quot;mode&quot; value=&quot;OpenOrCreate&quot; /&gt; FileMode.OpenOrCreate
1857 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Truncate&quot; checked=&quot;checked&quot; /&gt; FileMode.Truncate
1858 /// </code>
1859 ///
1860 /// </example>
1861 ///
1862 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1863 /// <param name="valueToSend"></param>
1864 /// <returns>The generated form element</returns>
1865 public virtual string RadioField(string target, object valueToSend)
1867 return RadioField(target, valueToSend, null);
1870 /// <summary>
1871 /// Generates a radio input type with the specified
1872 /// value to send to the served in case the element in checked.
1873 /// It will automatically check the radio if the target
1874 /// evaluated value is equal to the specified <c>valueToSend</c>.
1875 /// </summary>
1876 ///
1877 /// <seealso cref="RadioField(string,object)"/>
1878 ///
1879 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1880 /// <param name="valueToSend"></param>
1881 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1882 /// <returns>The generated form element</returns>
1883 public virtual string RadioField(string target, object valueToSend, IDictionary attributes)
1885 target = RewriteTargetIfWithinObjectScope(target);
1887 object value = ObtainValue(target);
1889 bool isChecked = AreEqual(valueToSend, value);
1891 if (isChecked)
1893 if (attributes == null)
1895 attributes = new HybridDictionary(true);
1898 AddChecked(attributes);
1901 return CreateInputElement("radio", target, valueToSend, attributes);
1904 #endregion
1906 #region FileField
1908 /// <summary>
1909 /// Generates an input file element.
1910 /// <para>
1911 /// Dirrently than other operations exposed by this helper,
1912 /// no value is extracted for this operation
1913 /// </para>
1914 /// </summary>
1915 /// <param name="target">The object to be based on when creating the element name.</param>
1916 /// <returns>The generated form element</returns>
1917 public virtual string FileField(string target)
1919 return FileField(target, null);
1922 /// <summary>
1923 /// Generates an input file element.
1924 /// <para>
1925 /// Dirrently than other operations exposed by this helper,
1926 /// no value is extracted for this operation
1927 /// </para>
1928 /// </summary>
1929 /// <param name="target">The object to be based on when creating the element name.</param>
1930 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1931 /// <returns>The generated form element</returns>
1932 public virtual string FileField(string target, IDictionary attributes)
1934 target = RewriteTargetIfWithinObjectScope(target);
1936 ApplyValidation(InputElementType.Text, target, ref attributes);
1938 return CreateInputElement("file", target, string.Empty, attributes);
1941 #endregion
1943 #region Select
1945 /// <summary>
1946 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1947 /// If the <c>dataSource</c>
1948 /// elements are complex objects (ie not string or primitives),
1949 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1950 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1951 /// <para>
1952 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1953 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1954 /// <c>firstoptionvalue</c>. The default value is '0'.
1955 /// </para>
1956 /// <para>
1957 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1958 /// a set with multiple items. The element types tend to be the same. If
1959 /// they are not, you might have to specify the <c>suffix</c> parameters on
1960 /// the <c>attributes</c> as it would not be inferred.
1961 /// </para>
1962 /// <para>
1963 /// The target can also be a set. In this case the intersection will be
1964 /// the initially selected elements.
1965 /// </para>
1966 /// </summary>
1967 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1968 /// <param name="dataSource">The set of available elements</param>
1969 /// <returns>The generated form element</returns>
1970 public virtual string Select(string target, IEnumerable dataSource)
1972 return Select(target, dataSource, null);
1975 /// <summary>
1976 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1977 /// If the <c>dataSource</c>
1978 /// elements are complex objects (ie not string or primitives),
1979 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1980 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1981 /// <para>
1982 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1983 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1984 /// <c>firstoptionvalue</c>. The default value is '0'.
1985 /// </para>
1986 /// <para>
1987 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1988 /// a set with multiple items. The element types tend to be the same. If
1989 /// they are not, you might have to specify the <c>suffix</c> parameters on
1990 /// the <c>attributes</c> as it would not be inferred.
1991 /// </para>
1992 /// <para>
1993 /// The target can also be a set. In this case the intersection will be
1994 /// the initially selected elements.
1995 /// </para>
1996 /// </summary>
1997 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1998 /// <param name="dataSource">The set of available elements</param>
1999 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2000 /// <returns>The generated form element</returns>
2001 public virtual string Select(string target, IEnumerable dataSource, IDictionary attributes)
2003 target = RewriteTargetIfWithinObjectScope(target);
2005 object selectedValue = ObtainValue(target);
2007 return Select(target, selectedValue, dataSource, attributes);
2010 /// <summary>
2011 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
2012 /// If the <c>dataSource</c>
2013 /// elements are complex objects (ie not string or primitives),
2014 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
2015 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
2016 /// <para>
2017 /// You can also specify the attribute <c>firstoption</c> to force the first option be
2018 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
2019 /// <c>firstoptionvalue</c>. The default value is '0'.
2020 /// </para>
2021 /// <para>
2022 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
2023 /// a set with multiple items. The element types tend to be the same. If
2024 /// they are not, you might have to specify the <c>suffix</c> parameters on
2025 /// the <c>attributes</c> as it would not be inferred.
2026 /// </para>
2027 /// <para>
2028 /// The target can also be a set. In this case the intersection will be
2029 /// the initially selected elements.
2030 /// </para>
2031 /// </summary>
2032 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2033 /// <param name="selectedValue"></param>
2034 /// <param name="dataSource">The set of available elements</param>
2035 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2036 /// <returns>The generated form element</returns>
2037 public virtual string Select(string target, object selectedValue, IEnumerable dataSource, IDictionary attributes)
2039 return GenerateSelect(target, selectedValue, dataSource, attributes);
2042 /// <summary>
2043 /// Generates the select.
2044 /// </summary>
2045 /// <param name="target">The target.</param>
2046 /// <param name="selectedValue">The selected value.</param>
2047 /// <param name="dataSource">The data source.</param>
2048 /// <param name="attributes">The attributes.</param>
2049 /// <returns></returns>
2050 protected virtual string GenerateSelect(string target, object selectedValue, IEnumerable dataSource, IDictionary attributes)
2052 string id = CreateHtmlId(target);
2054 ApplyValidation(InputElementType.Select, target, ref attributes);
2056 StringBuilder sb = new StringBuilder();
2057 StringWriter sbWriter = new StringWriter(sb);
2058 HtmlTextWriter writer = new HtmlTextWriter(sbWriter);
2060 string firstOption = null;
2061 string firstOptionValue = null;
2062 string name = target;
2064 if (attributes != null)
2066 firstOption = CommonUtils.ObtainEntryAndRemove(attributes, "firstoption");
2067 firstOptionValue = CommonUtils.ObtainEntryAndRemove(attributes, "firstoptionvalue");
2069 if (attributes.Contains("name"))
2071 name = (String) attributes["name"];
2072 attributes.Remove("name");
2075 if (attributes.Contains("id"))
2077 id = (String) attributes["id"];
2078 attributes.Remove("id");
2082 OperationState state = SetOperation.IterateOnDataSource(selectedValue, dataSource, attributes);
2084 writer.WriteBeginTag("select");
2085 writer.WriteAttribute("id", id);
2086 writer.WriteAttribute("name", name);
2087 writer.Write(" ");
2088 writer.Write(GetAttributes(attributes));
2089 writer.Write(HtmlTextWriter.TagRightChar);
2090 writer.WriteLine();
2092 if (firstOption != null)
2094 writer.WriteBeginTag("option");
2095 writer.WriteAttribute("value", (firstOptionValue == null) ? "0" : SafeHtmlEncode(firstOptionValue));
2096 writer.Write(HtmlTextWriter.TagRightChar);
2097 writer.Write(SafeHtmlEncode(firstOption));
2098 writer.WriteEndTag("option");
2099 writer.WriteLine();
2102 foreach(SetItem item in state)
2104 writer.WriteBeginTag("option");
2106 if (item.IsSelected)
2108 writer.Write(" selected=\"selected\"");
2111 writer.WriteAttribute("value", SafeHtmlEncode(item.Value));
2112 writer.Write(HtmlTextWriter.TagRightChar);
2113 writer.Write(SafeHtmlEncode(item.Text));
2114 writer.WriteEndTag("option");
2115 writer.WriteLine();
2118 writer.WriteEndTag("select");
2120 return sbWriter.ToString();
2123 #endregion
2125 #region Field set
2127 /// <summary>
2128 /// Creates a field set element with a legend using the specified name.
2129 /// </summary>
2130 /// <param name="name">The name.</param>
2131 /// <returns></returns>
2132 public virtual string FieldSet(string name)
2134 return "<fieldset><legend>" + name + "</legend>";
2137 /// <summary>
2138 /// Creates an element to close a fieldset element.
2139 /// </summary>
2140 /// <returns></returns>
2141 public virtual string EndFieldSet()
2143 return "</fieldset>";
2146 #endregion
2148 #region Enum
2150 /// <summary>
2151 /// Creates a list of pairs for the enum type.
2152 /// </summary>
2153 /// <param name="enumType">enum type.</param>
2154 /// <returns></returns>
2155 public static Pair<int, string>[] EnumToPairs(Type enumType)
2157 if (enumType == null) throw new ArgumentNullException("enumType");
2158 if (!enumType.IsEnum) throw new ArgumentException("enumType must be an Enum", "enumType");
2160 Array values = Enum.GetValues(enumType);
2161 string[] names = Enum.GetNames(enumType);
2163 List<Pair<int, string>> listOfPairs = new List<Pair<int, string>>();
2164 int index = 0;
2166 foreach(string name in names)
2168 int value = Convert.ToInt32(values.GetValue(index++));
2169 listOfPairs.Add(new Pair<int, string>(value, TextHelper.PascalCaseToWord(name)));
2172 return listOfPairs.ToArray();
2175 #endregion
2177 #region Validation
2179 /// <summary>
2180 /// Configures this FormHelper instance to use the supplied
2181 /// web validator to generate field validation.
2182 /// </summary>
2183 /// <param name="provider">The validation provider.</param>
2184 public virtual void UseWebValidatorProvider(IBrowserValidatorProvider provider)
2186 if (provider == null) throw new ArgumentNullException("provider");
2188 validatorProvider = provider;
2191 /// <summary>
2192 /// Configures this FormHelper instance to use Prototype for form fields validation
2193 /// </summary>
2194 public virtual void UsePrototypeValidation()
2196 UseWebValidatorProvider(new PrototypeWebValidator());
2199 /// <summary>
2200 /// Configures this FormHelper instance to use fValidate for form fields validation
2201 /// </summary>
2202 public virtual void UsefValidate()
2204 UseWebValidatorProvider(new FValidateWebValidator());
2207 /// <summary>
2208 /// Configures this FormHelper instance to use Zebda for form fields validation
2209 /// </summary>
2210 public virtual void UseZebdaValidation()
2212 UseWebValidatorProvider(new ZebdaWebValidator());
2215 /// <summary>
2216 /// Disables the validation.
2217 /// </summary>
2218 public virtual void DisableValidation()
2220 isValidationDisabled = true;
2223 /// <summary>
2224 /// Applies the validation.
2225 /// </summary>
2226 /// <param name="inputType">Type of the input.</param>
2227 /// <param name="target">The target.</param>
2228 /// <param name="attributes">The attributes.</param>
2229 protected virtual void ApplyValidation(InputElementType inputType, string target, ref IDictionary attributes)
2231 bool disableValidation = CommonUtils.ObtainEntryAndRemove(attributes, "disablevalidation", "false") == "true";
2233 if (!IsValidationEnabled || disableValidation)
2235 return;
2238 if (validatorRegistry == null || validationConfig == null)
2240 return;
2243 if (attributes == null)
2245 attributes = new HybridDictionary(true);
2248 IValidator[] validators = CollectValidators(RequestContext.All, target);
2250 IBrowserValidationGenerator generator = validatorProvider.CreateGenerator(validationConfig, inputType, attributes);
2252 foreach(IValidator validator in validators)
2254 if (validator.SupportsBrowserValidation)
2256 validator.ApplyBrowserValidation(validationConfig, inputType, generator, attributes, target);
2261 private IValidator[] CollectValidators(RequestContext requestContext, string target)
2263 List<IValidator> validators = new List<IValidator>();
2265 ObtainTargetProperty(requestContext, target, delegate(PropertyInfo property)
2267 validators.AddRange(validatorRegistry.GetValidators(ValidatorRunner, property.DeclaringType, property, RunWhen.Everytime));
2270 return validators.ToArray();
2273 private bool IsValidationEnabled
2277 if (isValidationDisabled) return false;
2279 if (objectStack.Count == 0) return true;
2281 return ((FormScopeInfo)objectStack.Peek()).IsValidationEnabled;
2285 #endregion
2287 #region protected members
2289 /// <summary>
2290 /// Rewrites the target if within object scope.
2291 /// </summary>
2292 /// <param name="target">The target.</param>
2293 /// <returns></returns>
2294 protected string RewriteTargetIfWithinObjectScope(string target)
2296 if (objectStack.Count == 0)
2298 return target;
2300 else
2302 return ((FormScopeInfo) objectStack.Peek()).RootTarget + "." + target;
2306 /// <summary>
2307 /// Creates the specified input element
2308 /// using the specified parameters to supply the name, value, id and others
2309 /// html attributes.
2310 /// </summary>
2311 /// <param name="type"></param>
2312 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2313 /// <param name="value"></param>
2314 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2315 /// <returns>The generated form element</returns>
2316 protected virtual string CreateInputElement(string type, string target, Object value, IDictionary attributes)
2318 if (value == null)
2320 value = CommonUtils.ObtainEntryAndRemove(attributes, "defaultValue");
2323 string id = CreateHtmlId(attributes, target);
2325 return CreateInputElement(type, id, target, FormatIfNecessary(value, attributes), attributes);
2328 /// <summary>
2329 /// Creates the specified input element
2330 /// using the specified parameters to supply the name, value, id and others
2331 /// html attributes.
2332 /// </summary>
2333 /// <param name="type"></param>
2334 /// <param name="id"></param>
2335 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2336 /// <param name="value"></param>
2337 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2338 /// <returns>The generated form element</returns>
2339 protected virtual string CreateInputElement(string type, string id, string target, string value, IDictionary attributes)
2341 value = FormatIfNecessary(value, attributes);
2343 value = SafeHtmlEncode(value);
2345 if (attributes != null && attributes.Contains("mask"))
2347 string mask = CommonUtils.ObtainEntryAndRemove(attributes, "mask");
2348 string maskSep = CommonUtils.ObtainEntryAndRemove(attributes, "mask_separator", "-");
2350 string onBlur = CommonUtils.ObtainEntryAndRemove(attributes, "onBlur", "void(0)");
2351 string onKeyUp = CommonUtils.ObtainEntryAndRemove(attributes, "onKeyUp", "void(0)");
2353 string js = "return monorail_formhelper_mask(event,this,'" + mask + "','" + maskSep + "');";
2355 attributes["onBlur"] = "javascript:" + onBlur + ";" + js;
2356 attributes["onKeyUp"] = "javascript:" + onKeyUp + ";" + js;
2359 return String.Format("<input type=\"{0}\" id=\"{1}\" name=\"{2}\" value=\"{3}\" {4}/>",
2360 type, id, target, value, GetAttributes(attributes));
2363 /// <summary>
2364 /// Creates the input element.
2365 /// </summary>
2366 /// <param name="type">The type.</param>
2367 /// <param name="value">The value.</param>
2368 /// <param name="attributes">The attributes.</param>
2369 /// <returns></returns>
2370 protected virtual string CreateInputElement(string type, string value, IDictionary attributes)
2372 return String.Format("<input type=\"{0}\" value=\"{1}\" {2}/>",
2373 type, FormatIfNecessary(value, attributes), GetAttributes(attributes));
2376 /// <summary>
2377 /// Formats if necessary.
2378 /// </summary>
2379 /// <param name="value">The value.</param>
2380 /// <param name="attributes">The attributes.</param>
2381 /// <returns></returns>
2382 protected static string FormatIfNecessary(object value, IDictionary attributes)
2384 string formatString = CommonUtils.ObtainEntryAndRemove(attributes, "textformat");
2386 if (value != null && formatString != null)
2388 IFormattable formattable = value as IFormattable;
2390 if (formattable != null)
2392 value = formattable.ToString(formatString, null);
2395 else if (value == null)
2397 value = String.Empty;
2400 return value.ToString();
2403 /// <summary>
2404 /// Obtains the target property.
2405 /// </summary>
2406 /// <param name="context">The context.</param>
2407 /// <param name="target">The target.</param>
2408 /// <param name="action">The action.</param>
2409 /// <returns></returns>
2410 protected PropertyInfo ObtainTargetProperty(RequestContext context, string target, Action<PropertyInfo> action)
2412 string[] pieces;
2414 Type root = ObtainRootType(context, target, out pieces);
2416 if (root != null && pieces.Length > 1)
2418 return QueryPropertyInfoRecursive(root, pieces, 1, action);
2421 return null;
2424 /// <summary>
2425 /// Queries the context for the target value
2426 /// </summary>
2427 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2428 /// <returns>The generated form element</returns>
2429 protected object ObtainValue(string target)
2431 return ObtainValue(RequestContext.All, target);
2434 /// <summary>
2435 /// Queries the context for the target value
2436 /// </summary>
2437 /// <param name="context"></param>
2438 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2439 /// <returns>The generated form element</returns>
2440 protected object ObtainValue(RequestContext context, string target)
2442 string[] pieces;
2444 object rootInstance = ObtainRootInstance(context, target, out pieces);
2446 if (rootInstance != null && pieces.Length > 1)
2448 return QueryPropertyRecursive(rootInstance, pieces, 1);
2451 return rootInstance;
2454 /// <summary>
2455 /// Obtains the root instance.
2456 /// </summary>
2457 /// <param name="context">The context.</param>
2458 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2459 /// <returns>The generated form element</returns>
2460 protected object ObtainRootInstance(RequestContext context, string target)
2462 object rootInstance = null;
2464 if (context == RequestContext.All || context == RequestContext.PropertyBag)
2466 rootInstance = ControllerContext.PropertyBag[target];
2468 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Flash))
2470 rootInstance = Context.Flash[target];
2472 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Session))
2474 rootInstance = Context.Session[target];
2476 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Params))
2478 rootInstance = Context.Request.Params[target];
2480 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Request))
2482 rootInstance = Context.Items[target];
2485 return rootInstance;
2488 /// <summary>
2489 /// Obtains the root instance.
2490 /// </summary>
2491 /// <param name="context">The context.</param>
2492 /// <param name="target">The target.</param>
2493 /// <param name="pieces">The pieces.</param>
2494 /// <returns></returns>
2495 protected object ObtainRootInstance(RequestContext context, string target, out string[] pieces)
2497 pieces = target.Split(new char[] {'.'});
2499 string root = pieces[0];
2501 int index;
2503 bool isIndexed = CheckForExistenceAndExtractIndex(ref root, out index);
2505 object rootInstance = ObtainRootInstance(context, root);
2507 if (rootInstance == null)
2509 return null;
2512 if (isIndexed)
2514 AssertIsValidArray(rootInstance, root, index);
2517 if (!isIndexed && pieces.Length == 1)
2519 return rootInstance;
2521 else if (isIndexed)
2523 rootInstance = GetArrayElement(rootInstance, index);
2526 return rootInstance;
2529 /// <summary>
2530 /// Obtains the type of the root.
2531 /// </summary>
2532 /// <param name="context">The context.</param>
2533 /// <param name="target">The target.</param>
2534 /// <param name="pieces">The pieces.</param>
2535 /// <returns></returns>
2536 private Type ObtainRootType(RequestContext context, string target, out string[] pieces)
2538 pieces = target.Split(new char[] { '.' });
2540 Type foundType = (Type) ControllerContext.PropertyBag[pieces[0] + "type"];
2542 if (foundType == null)
2544 string trimmed = pieces[0].Split('[')[0];
2546 foundType = (Type) ControllerContext.PropertyBag[trimmed + "type"];
2548 if (foundType == null)
2550 object root = ObtainRootInstance(context, target, out pieces);
2552 if (root != null)
2554 foundType = root.GetType();
2559 return foundType;
2562 private static PropertyInfo QueryPropertyInfoRecursive(Type type, string[] propertyPath)
2564 return QueryPropertyInfoRecursive(type, propertyPath, 0, null);
2567 private static PropertyInfo QueryPropertyInfoRecursive(Type type, string[] propertyPath, int piece, Action<PropertyInfo> action)
2569 string property = propertyPath[piece]; int index;
2571 bool isIndexed = CheckForExistenceAndExtractIndex(ref property, out index);
2573 PropertyInfo propertyInfo = type.GetProperty(property, ResolveFlagsToUse(type));
2575 if (propertyInfo == null)
2577 if (logger.IsErrorEnabled)
2579 logger.Error("No public property '{0}' found on type '{1}'", property, type.FullName);
2582 return null;
2585 if (!propertyInfo.CanRead)
2587 throw new BindingException("Property '{0}' for type '{1}' can not be read",
2588 propertyInfo.Name, type.FullName);
2591 if (propertyInfo.GetIndexParameters().Length != 0)
2593 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
2594 propertyInfo.Name, type.FullName);
2597 if (action != null)
2599 action(propertyInfo);
2602 type = propertyInfo.PropertyType;
2604 if (typeof(ICollection).IsAssignableFrom(type))
2606 return null;
2609 if (isIndexed)
2611 if (type.IsGenericType)
2613 Type[] args = type.GetGenericArguments();
2614 if (args.Length != 1)
2615 throw new BindingException("Expected the generic indexed property '{0}' to be of 1 element", type.Name);
2616 type = args[0];
2619 if (type.IsArray)
2621 type = type.GetElementType();
2625 if (piece + 1 == propertyPath.Length)
2627 return propertyInfo;
2630 return QueryPropertyInfoRecursive(type, propertyPath, piece + 1, action);
2633 /// <summary>
2634 /// Query property paths agains the rootInstance type
2635 /// </summary>
2636 /// <param name="rootInstance">the object to query</param>
2637 /// <param name="propertyPath">property path</param>
2638 /// <returns>The generated form element</returns>
2639 protected static object QueryPropertyRecursive(object rootInstance, string[] propertyPath)
2641 return QueryPropertyRecursive(rootInstance, propertyPath, 0);
2644 /// <summary>
2645 /// Query property paths agains the rootInstance type
2646 /// </summary>
2647 /// <param name="rootInstance">the object to query</param>
2648 /// <param name="propertyPath">property path</param>
2649 /// <param name="piece">start index</param>
2650 /// <returns>The generated form element</returns>
2651 protected static object QueryPropertyRecursive(object rootInstance, string[] propertyPath, int piece)
2653 string property = propertyPath[piece]; int index;
2655 Type instanceType = rootInstance.GetType();
2657 bool isIndexed = CheckForExistenceAndExtractIndex(ref property, out index);
2659 PropertyInfo propertyInfo = instanceType.GetProperty(property, ResolveFlagsToUse(instanceType));
2661 object instance = null;
2663 if (propertyInfo == null)
2665 FieldInfo fieldInfo = instanceType.GetField(property, FieldFlags);
2667 if (fieldInfo != null)
2669 instance = fieldInfo.GetValue(rootInstance);
2672 else
2674 if (!propertyInfo.CanRead)
2676 throw new BindingException("Property '{0}' for type '{1}' can not be read",
2677 propertyInfo.Name, instanceType.FullName);
2680 if (propertyInfo.GetIndexParameters().Length != 0)
2682 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
2683 propertyInfo.Name, instanceType.FullName);
2686 instance = propertyInfo.GetValue(rootInstance, null);
2689 if (isIndexed && instance != null)
2691 AssertIsValidArray(instance, property, index);
2693 instance = GetArrayElement(instance, index);
2696 if (instance == null || piece + 1 == propertyPath.Length)
2698 return instance;
2701 return QueryPropertyRecursive(instance, propertyPath, piece + 1);
2704 /// <summary>
2705 /// Creates the HTML id.
2706 /// </summary>
2707 /// <param name="attributes">The attributes.</param>
2708 /// <param name="target">The target.</param>
2709 /// <returns>The generated form element</returns>
2710 protected static string CreateHtmlId(IDictionary attributes, string target)
2712 return CreateHtmlId(attributes, target, true);
2715 /// <summary>
2716 /// Creates the HTML id.
2717 /// </summary>
2718 /// <param name="attributes">The attributes.</param>
2719 /// <param name="target">The target.</param>
2720 /// <param name="removeEntry">if set to <c>true</c> [remove entry].</param>
2721 /// <returns>The generated form element</returns>
2722 protected static string CreateHtmlId(IDictionary attributes, string target, bool removeEntry)
2724 string id;
2726 if (removeEntry)
2728 id = CommonUtils.ObtainEntryAndRemove(attributes, "id");
2730 else
2732 id = CommonUtils.ObtainEntry(attributes, "id");
2735 if (id == null)
2737 id = CreateHtmlId(target);
2740 return id;
2743 #endregion
2745 #region private helpers
2747 private static void ApplyNumberOnlyOptions(IDictionary attributes)
2749 string list = CommonUtils.ObtainEntryAndRemove(attributes, "exceptions", String.Empty);
2750 string forbid = CommonUtils.ObtainEntryAndRemove(attributes, "forbid", String.Empty);
2752 attributes["onKeyPress"] = "return monorail_formhelper_numberonly(event, [" + list + "], [" + forbid + "]);";
2755 private static void ApplyFilterOptions(IDictionary attributes)
2757 string forbid = CommonUtils.ObtainEntryAndRemove(attributes, "forbid", String.Empty);
2759 attributes["onKeyPress"] = "return monorail_formhelper_inputfilter(event, [" + forbid + "]);";
2762 private static void AssertIsValidArray(object instance, string property, int index)
2764 Type instanceType = instance.GetType();
2766 IList list = instance as IList;
2768 bool validList = false;
2770 if (list == null && instanceType.IsGenericType)
2772 Type[] genArgs = instanceType.GetGenericArguments();
2774 Type genList = typeof(System.Collections.Generic.IList<>).MakeGenericType(genArgs);
2775 Type genTypeDef = instanceType.GetGenericTypeDefinition().MakeGenericType(genArgs);
2777 validList = genList.IsAssignableFrom(genTypeDef);
2780 if (!validList && list == null)
2782 throw new MonoRailException("The property {0} is being accessed as " +
2783 "an indexed property but does not seem to implement IList. " +
2784 "In fact the type is {1}", property, instanceType.FullName);
2787 if (index < 0)
2789 throw new MonoRailException("The specified index '{0}' is outside the bounds " +
2790 "of the array. Property {1}", index, property);
2794 private static object GetArrayElement(object instance, int index)
2796 IList list = instance as IList;
2798 if (list == null && instance != null && instance.GetType().IsGenericType)
2800 Type instanceType = instance.GetType();
2802 Type[] genArguments = instanceType.GetGenericArguments();
2804 Type genType = instanceType.GetGenericTypeDefinition().MakeGenericType(genArguments);
2806 // I'm not going to retest for IList implementation as
2807 // if we got here, the AssertIsValidArray has run successfully
2809 PropertyInfo countPropInfo = genType.GetProperty("Count");
2811 int count = (int) countPropInfo.GetValue(instance, null);
2813 if (count == 0 || index + 1 > count)
2815 return null;
2818 PropertyInfo indexerPropInfo = genType.GetProperty("Item");
2820 return indexerPropInfo.GetValue(instance, new object[] { index });
2823 if (list == null || list.Count == 0 || index + 1 > list.Count)
2825 return null;
2828 return list[index];
2831 private static bool CheckForExistenceAndExtractIndex(ref string property, out int index)
2833 bool isIndexed = property.IndexOf('[') != -1;
2835 index = -1;
2837 if (isIndexed)
2839 int start = property.IndexOf('[') + 1;
2840 int len = property.IndexOf(']', start) - start;
2842 string indexStr = property.Substring(start, len);
2846 index = Convert.ToInt32(indexStr);
2848 catch(Exception)
2850 throw new MonoRailException("Could not convert (param {0}) index to Int32. Value is {1}",
2851 property, indexStr);
2854 property = property.Substring(0, start - 1);
2857 return isIndexed;
2860 private static bool AreEqual(object left, object right)
2862 if (left == null || right == null) return false;
2864 if (left is string && right is String)
2866 return String.Compare(left.ToString(), right.ToString()) == 0;
2869 if (left.GetType() == right.GetType())
2871 return right.Equals(left);
2874 IConvertible convertible = left as IConvertible;
2876 if (convertible != null)
2880 object newleft = convertible.ToType(right.GetType(), null);
2881 return (newleft.Equals(right));
2883 catch(Exception)
2885 // Do nothing
2889 return left.ToString().Equals(right.ToString());
2892 private string SafeHtmlEncode(string content)
2894 if (Context != null)
2896 return HtmlEncode(content);
2899 return content;
2902 /// <summary>
2903 /// Determines whether the present value matches the value on
2904 /// the initialSetValue (which can be a single value or a set)
2905 /// </summary>
2906 /// <param name="value">Value from the datasource</param>
2907 /// <param name="initialSetValue">Value from the initial selection set</param>
2908 /// <param name="propertyOnInitialSet">Optional. Property to obtain the value from</param>
2909 /// <param name="isMultiple"><c>true</c> if the initial selection is a set</param>
2910 /// <returns><c>true</c> if it's selected</returns>
2911 protected internal static bool IsPresent(object value, object initialSetValue,
2912 ValueGetter propertyOnInitialSet, bool isMultiple)
2914 if (!isMultiple)
2916 object valueToCompare = initialSetValue;
2918 if (propertyOnInitialSet != null)
2920 // propertyOnInitialSet.GetValue(initialSetValue, null);
2921 valueToCompare = propertyOnInitialSet.GetValue(initialSetValue);
2924 return AreEqual(value, valueToCompare);
2926 else
2928 foreach(object item in (IEnumerable) initialSetValue)
2930 object valueToCompare = item;
2932 if (propertyOnInitialSet != null)
2934 // valueToCompare = propertyOnInitialSet.GetValue(item, null);
2935 valueToCompare = propertyOnInitialSet.GetValue(item);
2938 if (AreEqual(value, valueToCompare))
2940 return true;
2945 return false;
2948 private static void AddChecked(IDictionary attributes)
2950 attributes["checked"] = "checked";
2953 private static void RemoveChecked(IDictionary attributes)
2955 attributes.Remove("checked");
2958 private static string CreateHtmlId(string name)
2960 StringBuilder sb = new StringBuilder(name.Length);
2962 bool canUseUnderline = false;
2964 foreach(char c in name.ToCharArray())
2966 switch(c)
2968 case '.':
2969 case '[':
2970 case ']':
2971 if (canUseUnderline)
2973 sb.Append('_');
2974 canUseUnderline = false;
2976 break;
2977 default:
2978 canUseUnderline = true;
2979 sb.Append(c);
2980 break;
2985 return sb.ToString();
2988 /// <summary>
2989 /// Abstracts the approach to access values on objects.
2990 /// </summary>
2991 public abstract class ValueGetter
2993 /// <summary>
2994 /// Gets the name.
2995 /// </summary>
2996 /// <value>The name.</value>
2997 public abstract string Name { get; }
2999 /// <summary>
3000 /// Gets the value.
3001 /// </summary>
3002 /// <param name="instance">The instance.</param>
3003 /// <returns></returns>
3004 public abstract object GetValue(object instance);
3007 /// <summary>
3008 /// Implementation of <see cref="ValueGetter"/>
3009 /// that uses reflection to access values
3010 /// </summary>
3011 public class ReflectionValueGetter : ValueGetter
3013 private PropertyInfo propInfo;
3015 /// <summary>
3016 /// Initializes a new instance of the <see cref="ReflectionValueGetter"/> class.
3017 /// </summary>
3018 /// <param name="propInfo">The prop info.</param>
3019 public ReflectionValueGetter(PropertyInfo propInfo)
3021 this.propInfo = propInfo;
3024 /// <summary>
3025 /// Gets the name.
3026 /// </summary>
3027 /// <value>The name.</value>
3028 public override string Name
3030 get { return propInfo.Name; }
3033 /// <summary>
3034 /// Gets the value.
3035 /// </summary>
3036 /// <param name="instance">The instance.</param>
3037 /// <returns></returns>
3038 public override object GetValue(object instance)
3042 return propInfo.GetValue(instance, null);
3044 catch(TargetException)
3046 PropertyInfo tempProp = instance.GetType().GetProperty(Name);
3048 if (tempProp == null)
3050 throw;
3053 return tempProp.GetValue(instance, null);
3058 /// <summary>
3059 /// Implementation of <see cref="ValueGetter"/>
3060 /// that uses reflection and recusion to access values
3061 /// </summary>
3062 public class RecursiveReflectionValueGetter : ValueGetter
3064 private readonly string[] keyName;
3065 private readonly string name = string.Empty;
3067 /// <summary>
3068 /// Initializes a new instance of the <see cref="RecursiveReflectionValueGetter"/> class.
3069 /// </summary>
3070 /// <param name="targetType">The target type to query</param>
3071 /// <param name="keyName">the property path</param>
3072 public RecursiveReflectionValueGetter(Type targetType, string keyName)
3074 this.keyName = keyName.Split('.');
3075 name = QueryPropertyInfoRecursive(targetType, this.keyName).Name;
3078 /// <summary>
3079 /// Gets the name.
3080 /// </summary>
3081 /// <value>The name.</value>
3082 public override string Name
3084 get { return name; }
3087 /// <summary>
3088 /// Gets the value.
3089 /// </summary>
3090 /// <param name="instance">The instance.</param>
3091 /// <returns></returns>
3092 public override object GetValue(object instance)
3096 return QueryPropertyRecursive(instance, keyName);
3098 catch (TargetException)
3100 PropertyInfo tempProp = instance.GetType().GetProperty(Name);
3102 if (tempProp == null)
3104 throw;
3107 return tempProp.GetValue(instance, null);
3112 /// <summary>
3113 /// Implementation of <see cref="ValueGetter"/>
3114 /// to access DataRow's value
3115 /// </summary>
3116 public class DataRowValueGetter : ValueGetter
3118 private readonly string columnName;
3120 /// <summary>
3121 /// Initializes a new instance of the <see cref="DataRowValueGetter"/> class.
3122 /// </summary>
3123 /// <param name="columnName">Name of the column.</param>
3124 public DataRowValueGetter(string columnName)
3126 this.columnName = columnName;
3129 /// <summary>
3130 /// Gets the name.
3131 /// </summary>
3132 /// <value>The name.</value>
3133 public override string Name
3135 get { return columnName; }
3138 /// <summary>
3139 /// Gets the value.
3140 /// </summary>
3141 /// <param name="instance">The instance.</param>
3142 /// <returns></returns>
3143 public override object GetValue(object instance)
3145 DataRow row = (DataRow) instance;
3147 return row[columnName];
3151 /// <summary>
3152 /// Implementation of <see cref="ValueGetter"/>
3153 /// to access DataRowView's value
3154 /// </summary>
3155 public class DataRowViewValueGetter : ValueGetter
3157 private readonly string columnName;
3159 /// <summary>
3160 /// Initializes a new instance of the <see cref="DataRowViewValueGetter"/> class.
3161 /// </summary>
3162 /// <param name="columnName">Name of the column.</param>
3163 public DataRowViewValueGetter(string columnName)
3165 this.columnName = columnName;
3168 /// <summary>
3169 /// Gets the name.
3170 /// </summary>
3171 /// <value>The name.</value>
3172 public override string Name
3174 get { return columnName; }
3177 /// <summary>
3178 /// Gets the value.
3179 /// </summary>
3180 /// <param name="instance">The instance.</param>
3181 /// <returns></returns>
3182 public override object GetValue(object instance)
3184 DataRowView row = (DataRowView)instance;
3186 return row[columnName];
3190 /// <summary>
3191 /// Empty implementation of a <see cref="ValueGetter"/>
3192 /// </summary>
3193 public class NoActionGetter : ValueGetter
3195 /// <summary>
3196 /// Gets the name.
3197 /// </summary>
3198 /// <value>The name.</value>
3199 public override string Name
3201 get { return string.Empty; }
3204 /// <summary>
3205 /// Gets the value.
3206 /// </summary>
3207 /// <param name="instance">The instance.</param>
3208 /// <returns></returns>
3209 public override object GetValue(object instance)
3211 return null;
3215 /// <summary>
3216 /// Implementation of <see cref="ValueGetter"/>
3217 /// to access enum fields
3218 /// </summary>
3219 public class EnumValueGetter : ValueGetter
3221 private readonly Type enumType;
3223 /// <summary>
3224 /// Initializes a new instance of the <see cref="EnumValueGetter"/> class.
3225 /// </summary>
3226 /// <param name="enumType">Type of the enum.</param>
3227 public EnumValueGetter(Type enumType)
3229 this.enumType = enumType;
3232 /// <summary>
3233 /// Gets the name.
3234 /// </summary>
3235 /// <value>The name.</value>
3236 public override string Name
3238 get { return string.Empty; }
3241 /// <summary>
3242 /// Gets the value.
3243 /// </summary>
3244 /// <param name="instance">The instance.</param>
3245 /// <returns></returns>
3246 public override object GetValue(object instance)
3248 return Convert.ToDecimal(Enum.Format(enumType, Enum.Parse(enumType, Convert.ToString(instance)), "d"));
3252 /// <summary>
3253 /// Abstract factory for <see cref="ValueGetter"/> implementations
3254 /// </summary>
3255 public class ValueGetterAbstractFactory
3257 /// <summary>
3258 /// Creates the specified target type.
3259 /// </summary>
3260 /// <param name="targetType">Type of the target.</param>
3261 /// <param name="keyName">Name of the key.</param>
3262 /// <returns></returns>
3263 public static ValueGetter Create(Type targetType, string keyName)
3265 if (targetType == null)
3267 return new NoActionGetter();
3269 else if (targetType == typeof(DataRow))
3271 return new DataRowValueGetter(keyName);
3273 else if (targetType == typeof(DataRowView))
3275 return new DataRowViewValueGetter(keyName);
3277 else if (targetType.IsEnum)
3279 return new EnumValueGetter(targetType);
3281 else
3283 PropertyInfo info;
3285 // check for recusion
3286 if(keyName.Contains("."))
3288 info = QueryPropertyInfoRecursive(targetType, keyName.Split('.'));
3290 if (info != null)
3292 return new RecursiveReflectionValueGetter(targetType, keyName);
3295 else
3297 info = targetType.GetProperty(keyName, ResolveFlagsToUse(targetType));
3298 if (info != null)
3300 return new ReflectionValueGetter(info);
3304 return null;
3309 #endregion
3311 #region FormScopeInfo
3313 class FormScopeInfo
3315 private readonly string target;
3316 private readonly bool isValidationEnabled;
3318 /// <summary>
3319 /// Initializes a new instance of the <see cref="FormScopeInfo"/> class.
3320 /// </summary>
3321 /// <param name="target">The target.</param>
3322 /// <param name="isValidationEnabled">if set to <c>true</c> [is validation enabled].</param>
3323 public FormScopeInfo(string target, bool isValidationEnabled)
3325 this.target = target;
3326 this.isValidationEnabled = isValidationEnabled;
3329 /// <summary>
3330 /// Gets the root target.
3331 /// </summary>
3332 /// <value>The root target.</value>
3333 public string RootTarget
3335 get { return target; }
3338 /// <summary>
3339 /// Gets a value indicating whether this instance is validation enabled.
3340 /// </summary>
3341 /// <value>
3342 /// <c>true</c> if this instance is validation enabled; otherwise, <c>false</c>.
3343 /// </value>
3344 public bool IsValidationEnabled
3346 get { return isValidationEnabled; }
3350 #endregion
3352 private static BindingFlags ResolveFlagsToUse(Type type)
3354 if (type.Assembly.FullName.StartsWith("DynamicAssemblyProxyGen") || type.Assembly.FullName.StartsWith("DynamicProxyGenAssembly2"))
3356 return PropertyFlags2;
3359 return PropertyFlags;