- Fixed MR-84
[castle.git] / MonoRail / Castle.MonoRail.Framework / Helpers / FormHelper.cs
blob35cb76ee4478eb5df9779263a2bbe2607f88fbf0
1 // Copyright 2004-2007 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 Services;
26 using HtmlTextWriter = System.Web.UI.HtmlTextWriter;
28 using Castle.Core;
29 using Castle.Core.Logging;
30 using Castle.MonoRail.Framework;
31 using Castle.MonoRail.Framework.Helpers.ValidationStrategy;
32 using Castle.MonoRail.Framework.Internal;
33 using Castle.Components.Binder;
34 using Castle.Components.Validator;
36 /// <summary>
37 /// Represents all scopes that the <see cref="FormHelper"/>
38 /// uses to search for root values
39 /// </summary>
40 public enum RequestContext
42 /// <summary>
43 /// All scopes should be searched
44 /// </summary>
45 All,
46 /// <summary>
47 /// Only PropertyBag should be searched
48 /// </summary>
49 PropertyBag,
50 /// <summary>
51 /// Only Flash should be searched
52 /// </summary>
53 Flash,
54 /// <summary>
55 /// Only Session should be searched
56 /// </summary>
57 Session,
58 /// <summary>
59 /// Only Request should be searched
60 /// </summary>
61 Request,
62 /// <summary>
63 /// Only Params should be searched
64 /// </summary>
65 Params
68 /// <summary>
69 /// The FormHelper allows you to output html input elements using the
70 /// conventions necessary to use the DataBinder on the server side. Ultimately it
71 /// allows you do to a bi-directional binding -- if used properly.
72 /// </summary>
73 ///
74 /// <seealso cref="DataBindAttribute"/>
75 /// <seealso cref="Castle.Components.Common"/>
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 IBrowserValidatorProvider validatorProvider = new PrototypeWebValidator();
260 private BrowserValidationConfiguration validationConfig;
262 /// <summary>
263 /// Logger instance
264 /// </summary>
265 protected static ILogger logger = NullLogger.Instance;
267 #region IServiceEnabledComponent implementation
269 /// <summary>
270 /// Invoked by the framework in order to give a chance to
271 /// obtain other services
272 /// </summary>
273 /// <param name="provider">The service proviver</param>
274 public void Service(IServiceProvider provider)
276 ILoggerFactory loggerFactory = (ILoggerFactory) provider.GetService(typeof(ILoggerFactory));
278 if (loggerFactory != null)
280 logger = loggerFactory.Create(typeof(FormHelper));
283 IBrowserValidatorProvider validatorProv = (IBrowserValidatorProvider)
284 provider.GetService(typeof(IBrowserValidatorProvider));
286 if (validatorProv != null)
288 validatorProvider = validatorProv;
292 #endregion
294 /// <summary>
295 /// Renders a Javascript library inside a single script tag.
296 /// </summary>
297 /// <returns></returns>
298 public string InstallScripts()
300 return RenderScriptBlockToSource("/MonoRail/Files/FormHelperScript");
303 #region FormTag related
305 /// <summary>
306 /// Creates a form tag based on the parameters.
307 /// <para>
308 /// Javascript validation can also be bound to
309 /// the form and|or elements nested as long as the helper is
310 /// able to reach the <see cref="Type"/> of the object used on your view code
311 /// </para>
312 /// <para>
313 /// The action attribute generation will use <see cref="UrlHelper"/>
314 /// </para>
315 /// </summary>
316 ///
317 /// <seealso cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>
318 ///
319 /// <example>
320 ///
321 /// <code lang="none">
322 /// $Form.FormTag("%{action='Save',id='productform'}")
323 /// </code>
324 ///
325 /// Outputs:
326 ///
327 /// <code lang="xml">
328 /// &lt;form method='post' action='/[appdir]/[controller]/Save.[extension]' id='productform'&gt;
329 /// </code>
331 /// </example>
332 ///
333 /// <remarks>
334 /// The parameters are used to build a url and also to form the tag. For notes on the url
335 /// see <see cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>. The other parameters supported
336 /// follows
337 ///
338 /// <list type="table">
339 /// <term>
340 /// <term>noaction</term>
341 /// <description>boolean. Disables the generation of an action</description>
342 /// </term>
343 /// <term>
344 /// <term>method</term>
345 /// <description>string. The http method to use. Defaults to <c>post</c></description>
346 /// </term>
347 /// <term>
348 /// <term>id</term>
349 /// <description>string. The form id.</description>
350 /// </term>
351 /// </list>
352 ///
353 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
354 ///
355 /// </remarks>
356 ///
357 /// <param name="parameters">The parameters for the tag or for action and form validation generation.</param>
358 /// <returns></returns>
359 public string FormTag(IDictionary parameters)
361 string url = null;
363 // Creates action attribute
364 if (CommonUtils.ObtainEntryAndRemove(parameters, "noaction", "false") == "false")
366 url = UrlHelper.For(parameters);
369 return FormTag(url, parameters);
372 /// <summary>
373 /// Creates a form tag based on the parameters.
374 /// <para>
375 /// Javascript validation can also be bound to
376 /// the form and|or elements nested as long as the helper is
377 /// able to reach the <see cref="Type"/> of the object used on your view code
378 /// </para>
379 /// <para>
380 /// The action attribute generation will use <see cref="UrlHelper"/>
381 /// </para>
382 /// </summary>
383 ///
384 /// <example>
385 ///
386 /// <code lang="none">
387 /// $Form.FormTag('mytarget.castle', "%{id='productform'}")
388 /// </code>
389 ///
390 /// Outputs:
391 ///
392 /// <code lang="xml">
393 /// &lt;form method='post' action='mytarget.castle' id='productform'&gt;
394 /// </code>
396 /// </example>
397 ///
398 /// <remarks>
399 /// The following parameters are accepted.
400 ///
401 /// <list type="table">
402 /// <term>
403 /// <term>method</term>
404 /// <description>string. The http method to use. Defaults to <c>post</c></description>
405 /// </term>
406 /// <term>
407 /// <term>id</term>
408 /// <description>string. The form id.</description>
409 /// </term>
410 /// </list>
411 ///
412 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
413 ///
414 /// </remarks>
415 ///
416 /// <param name="url">The hardcoded url.</param>
417 /// <param name="parameters">The parameters for the tag or for action and form validation generation.</param>
418 /// <returns></returns>
419 public string FormTag(string url, IDictionary parameters)
421 string method = CommonUtils.ObtainEntryAndRemove(parameters, "method", "post");
422 currentFormId = CommonUtils.ObtainEntryAndRemove(parameters, "id", "form" + ++formCount);
424 validationConfig = validatorProvider.CreateConfiguration(parameters);
426 string afterFormTag = IsValidationEnabled ?
427 validationConfig.CreateAfterFormOpened(currentFormId) :
428 String.Empty;
430 string formContent;
432 if (url != null)
434 formContent = "<form action='" + url + "' method='" + method + "' " +
435 "id='" + currentFormId + "' " + GetAttributes(parameters) + ">";
437 else
439 formContent = "<form method='" + method + "' id='" + currentFormId + "' " + GetAttributes(parameters) + ">";
442 return formContent + afterFormTag;
445 /// <summary>
446 /// Generate Ajax form tag for ajax based form submission. Experimental.
447 /// </summary>
448 /// <param name="parameters"></param>
449 /// <returns></returns>
450 public string AjaxFormTag(IDictionary parameters)
452 currentFormId = CommonUtils.ObtainEntryAndRemove(parameters, "id", "form" + ++formCount);
454 validationConfig = validatorProvider.CreateConfiguration(parameters);
456 string afterFormTag = IsValidationEnabled ?
457 validationConfig.CreateAfterFormOpened(currentFormId) :
458 String.Empty;
460 string url = UrlHelper.For(parameters);
462 parameters["form"] = true;
464 if (parameters.Contains("onsubmit"))
466 string onSubmitFunc = CommonUtils.ObtainEntryAndRemove(parameters, "onsubmit");
467 //remove return to make it compatible for ajax condition
468 if (onSubmitFunc.StartsWith("return ", StringComparison.InvariantCultureIgnoreCase))
470 onSubmitFunc = onSubmitFunc.Substring(7);
472 if (onSubmitFunc.EndsWith(";", StringComparison.InvariantCultureIgnoreCase))
474 onSubmitFunc = onSubmitFunc.Remove(onSubmitFunc.Length - 1);
476 string conditionFunc = CommonUtils.ObtainEntryAndRemove(parameters, "condition", string.Empty);
477 if (!string.IsNullOrEmpty(conditionFunc))
479 conditionFunc += " && ";
481 conditionFunc += onSubmitFunc;
483 parameters["condition"] = conditionFunc;
485 bool isMethodAssigned = parameters.Contains("method");
487 string method = CommonUtils.ObtainEntryAndRemove(parameters, "method", "post");
489 parameters["url"] = url;
491 // reassign method so in case if there is no value the default is assigned.
493 if (isMethodAssigned)
495 parameters["method"] = method;
498 String remoteFunc = new AjaxHelper().RemoteFunction(parameters);
500 string formContent = String.Format("<form id='{1}' method='{2}' {3} onsubmit=\"{0}; return false;\" enctype=\"multipart/form-data\">", remoteFunc, currentFormId, method,GetAttributes(parameters));
502 return formContent + afterFormTag;
505 /// <summary>
506 /// Renders an end form element.
507 /// </summary>
508 /// <remarks>
509 /// Should be used if you are using form validation. Some validation approaches
510 /// uses the end form before or after appending a javascript snippet.
511 /// </remarks>
512 /// <returns></returns>
513 public string EndFormTag()
515 string beforeEndTag = string.Empty;
517 if (validationConfig != null)
519 beforeEndTag = IsValidationEnabled ?
520 validationConfig.CreateBeforeFormClosed(currentFormId) :
521 String.Empty;
524 return beforeEndTag + "</form>";
527 #endregion
529 #region Object scope related
531 /// <summary>
532 /// Pushes the specified target. Experimental.
533 /// </summary>
534 /// <param name="target">The target.</param>
535 public void Push(string target)
537 Push(target, null);
540 /// <summary>
541 /// Pushes the specified target. Experimental.
542 /// </summary>
543 /// <param name="target">The target.</param>
544 /// <param name="parameters">The parameters.</param>
545 public void Push(string target, IDictionary parameters)
547 string disableValidation = CommonUtils.ObtainEntryAndRemove(parameters, "disablevalidation", "false");
548 object value = ObtainValue(target);
550 if (value != null)
552 objectStack.Push(new FormScopeInfo(target, disableValidation != "true"));
554 else
556 value = ObtainValue(target + "type");
558 if (value != null)
560 objectStack.Push(new FormScopeInfo(target, disableValidation != "true"));
562 else
564 throw new ArgumentException("target could not be evaluated during Push operation. Target: " + target);
569 /// <summary>
570 /// Pops this instance. Experimental.
571 /// </summary>
572 public void Pop()
574 objectStack.Pop();
577 #endregion
579 #region Submit and Button related
581 /// <summary>
582 /// Generates an input submit element.
583 /// </summary>
584 /// <param name="value">The value/caption for the button.</param>
585 /// <returns>The element tag</returns>
586 public string Submit(string value)
588 return Submit(value, null);
591 /// <summary>
592 /// Generates an input submit element.
593 /// </summary>
594 /// <param name="value">The value/caption for the button.</param>
595 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
596 /// <returns>The element tag</returns>
597 public string Submit(string value, IDictionary attributes)
599 return CreateInputElement("submit", value, attributes);
602 /// <summary>
603 /// Generates an graphical submit element.
604 /// </summary>
605 /// <param name="imgsrc">The path the image file.</param>
606 /// <param name="alttext">The alt text displayed by screenreaders, or when images are not enabled.</param>
607 /// <returns>The element tag</returns>
608 public string ImageSubmit(string imgsrc, string alttext)
610 return ImageSubmit(imgsrc, alttext, null);
613 /// <summary>
614 /// Generates an input submit element.
615 /// </summary>
616 /// <param name="imgsrc">The path the image file.</param>
617 /// <param name="alttext">The alt text displayed by screenreaders, or when images are not enabled.</param>
618 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
619 /// <returns>The element tag</returns>
620 public string ImageSubmit(string imgsrc, string alttext, IDictionary attributes)
622 attributes = attributes != null ? attributes : new Hashtable();
623 attributes["src"] = imgsrc;
624 attributes["alt"] = alttext;
625 return CreateInputElement("image", alttext, attributes);
628 /// <summary>
629 /// Generates an input button element.
630 /// </summary>
631 /// <param name="value">The value.</param>
632 /// <returns>The element tag</returns>
633 public string Button(string value)
635 return Button(value, null);
638 /// <summary>
639 /// Generates an input button element.
640 /// </summary>
641 /// <param name="value">The value.</param>
642 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
643 /// <returns>The element tag</returns>
644 public string Button(string value, IDictionary attributes)
646 return CreateInputElement("button", value, attributes);
649 /// <summary>
650 /// Creates a basic button element of type submit.
651 /// </summary>
652 /// <param name="innerText">The inner text of the button element.</param>
653 /// <returns>The generated button element.</returns>
654 public string ButtonElement(string innerText)
656 return ButtonElement(innerText, "submit", null);
659 /// <summary>
660 /// Creates a basic button element of the specified type.
661 /// </summary>
662 /// <param name="innerText">The inner text of the button element.</param>
663 /// <param name="type">The type of the button.</param>
664 /// <returns>The generated button element.</returns>
665 public string ButtonElement(string innerText, string type)
667 return ButtonElement(innerText, type, null);
670 /// <summary>
671 /// Creates a basic button element of the specified type and with specified attributes.
672 /// </summary>
673 /// <param name="innerText">The inner text of the button element.</param>
674 /// <param name="type">The type of the button.</param>
675 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
676 /// <returns>The generated button element.</returns>
677 public string ButtonElement(string innerText, string type, IDictionary attributes)
679 return String.Format("<button type=\"{0}\" {1}>{2}</button>", type, GetAttributes(attributes), innerText);
682 #endregion
684 #region TextFieldValue
686 /// <summary>
687 /// Generates an input text form element
688 /// with the supplied value
689 /// </summary>
690 /// <param name="target">The string to be used to create the element name.</param>
691 /// <param name="value">Value to supply to the element (instead of querying the target)</param>
692 /// <returns>The generated form element</returns>
693 public string TextFieldValue(string target, object value)
695 return TextFieldValue(target, value, null);
698 /// <summary>
699 /// Generates an input text form element
700 /// with the supplied value
701 /// </summary>
702 /// <param name="target">The string to be used to create the element name.</param>
703 /// <param name="value">Value to supply to the element (instead of querying the target)</param>
704 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
705 /// <returns>The generated form element</returns>
706 public string TextFieldValue(string target, object value, IDictionary attributes)
708 return CreateInputElement("text", target, value, attributes);
711 #endregion
713 #region TextField
715 /// <summary>
716 /// Generates an input text element.
717 /// <para>
718 /// The value is extracted from the target (if available)
719 /// </para>
720 /// </summary>
721 ///
722 /// <example>
723 /// The following example assumes that an entry <c>username</c> exists on the
724 /// <see cref="Controller.PropertyBag"/> or <see cref="Controller.Flash"/> or <see cref="Controller.Session"/>
725 ///
726 /// <code lang="none">
727 /// $Form.TextField('username')
728 /// </code>
729 /// Outputs:
730 /// <code lang="xml">
731 /// &lt;input type='text' name='username' id='username' value='John Doe' /&gt;
732 /// </code>
733 ///
734 /// <para>
735 /// The following example assumes that an entry <c>user</c> exists on the
736 /// <see cref="Controller.PropertyBag"/> or <see cref="Controller.Flash"/> or <see cref="Controller.Session"/>
737 /// </para>
738 ///
739 /// <code lang="none">
740 /// $Form.TextField('user.name')
741 /// </code>
742 /// Outputs:
743 /// <code lang="xml">
744 /// &lt;input type='text' name='user.name' id='user_name' value='John Doe' /&gt;
745 /// </code>
746 /// </example>
747 ///
748 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
749 /// <returns>The generated form element</returns>
750 public string TextField(string target)
752 return TextField(target, null);
755 /// <summary>
756 /// Generates an input text element.
757 /// <para>
758 /// The value is extracted from the target (if available)
759 /// </para>
760 /// </summary>
761 ///
762 /// <seealso cref="TextField(string)"/>
763 ///
764 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
765 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
766 /// <returns>The generated form element</returns>
767 public string TextField(string target, IDictionary attributes)
769 target = RewriteTargetIfWithinObjectScope(target);
771 object value = ObtainValue(target);
773 ApplyValidation(InputElementType.Text, target, ref attributes);
775 return CreateInputElement("text", target, value, attributes);
778 #endregion
780 #region FilteredTextField
782 /// <summary>
783 /// Generates an input text element with a javascript that prevents the
784 /// chars listed in the forbid attribute from being entered.
785 /// </summary>
786 /// <para>
787 /// You must pass an <c>forbid</c> value through the dictionary.
788 /// It must be a comma separated list of chars that cannot be accepted on the field.
789 /// For example:
790 /// </para>
791 /// <code>
792 /// FormHelper.FilteredTextField("product.price", {forbid='46'})
793 /// </code>
794 /// In this case the key code 46 (period) will not be accepted on the field.
795 /// <para>
796 /// The value is extracted from the target (if available).
797 /// </para>
798 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
799 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
800 /// <returns>The generated form element.</returns>
801 /// <remarks>
802 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it.
803 /// </remarks>
804 public string FilteredTextField(string target, IDictionary attributes)
806 target = RewriteTargetIfWithinObjectScope(target);
808 object value = ObtainValue(target);
810 attributes = attributes != null ? attributes : new Hashtable();
812 ApplyFilterOptions(attributes);
813 ApplyValidation(InputElementType.Text, target, ref attributes);
815 return CreateInputElement("text", target, value, attributes);
818 #endregion
820 #region NumberField
822 /// <summary>
823 /// Generates an input text element with a javascript that prevents
824 /// chars other than numbers from being entered.
825 /// <para>
826 /// The value is extracted from the target (if available)
827 /// </para>
828 /// </summary>
829 ///
830 /// <seealso cref="InstallScripts"/>
831 /// <seealso cref="NumberField(string,IDictionary)"/>
832 ///
833 /// <remarks>
834 /// You must include the formhelper javascript functions to use the NumberField.
835 /// See <see cref="InstallScripts"/>
836 /// </remarks>
837 ///
838 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
839 /// <returns>The generated form element</returns>
840 public string NumberField(string target)
842 return NumberField(target, null);
845 /// <summary>
846 /// Generates an input text element with a javascript that prevents
847 /// chars other than numbers from being entered.
848 /// <para>
849 /// The value is extracted from the target (if available)
850 /// </para>
851 /// </summary>
852 ///
853 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
854 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
855 /// <returns>The generated form element</returns>
856 ///
857 /// <remarks>
858 /// You must include the formhelper javascript functions to use the NumberField.
859 /// See <see cref="InstallScripts"/>
860 /// <para>
861 /// You can optionally pass an <c>exceptions</c> value through the dictionary.
862 /// It must be a comma separated list of chars that can be accepted on the field.
863 /// For example:
864 /// </para>
865 /// <code>
866 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
867 /// </code>
868 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
869 /// <para>
870 /// You can aslo optionally pass an <c>forbid</c> value through the dictionary.
871 /// It must be a comma separated list of chars that cannot be accepted on the field.
872 /// For example:
873 /// </para>
874 /// <code>
875 /// FormHelper.NumberField("product.price", {forbid='46'})
876 /// </code>
877 /// In this case the key code 46 (period) will not be accepted on the field.
878 /// </remarks>
879 public string NumberField(string target, IDictionary attributes)
881 target = RewriteTargetIfWithinObjectScope(target);
883 object value = ObtainValue(target);
885 attributes = attributes != null ? attributes : new Hashtable();
887 ApplyNumberOnlyOptions(attributes);
888 ApplyValidation(InputElementType.Text, target, ref attributes);
890 return CreateInputElement("text", target, value, attributes);
893 #endregion
895 #region NumberFieldValue
897 /// <summary>
898 /// Generates an input text element with a javascript that prevents
899 /// chars other than numbers from being entered. The value is not gathered
900 /// from the context, instead you specify it on the second argument
901 /// </summary>
902 ///
903 /// <seealso cref="InstallScripts"/>
904 /// <seealso cref="NumberField(string,IDictionary)"/>
905 ///
906 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
907 /// <param name="value">The current value to output.</param>
908 /// <returns>The generated form element</returns>
909 ///
910 /// <remarks>
911 /// You must include the formhelper javascript functions to use the NumberField.
912 /// See <see cref="InstallScripts"/>
913 /// </remarks>
914 public string NumberFieldValue(string target, object value)
916 return NumberFieldValue(target, value, null);
919 /// <summary>
920 /// Generates an input text element with a javascript that prevents
921 /// chars other than numbers from being entered. The value is not gathered
922 /// from the context, instead you specify it on the second argument
923 /// </summary>
924 ///
925 /// <seealso cref="InstallScripts"/>
926 /// <seealso cref="NumberField(string,IDictionary)"/>
927 ///
928 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
929 /// <param name="value">The current value to output.</param>
930 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
931 /// <returns>The generated form element</returns>
932 ///
933 /// <remarks>
934 /// You must include the formhelper javascript functions to use the NumberField.
935 /// See <see cref="InstallScripts"/>
936 /// </remarks>
937 public string NumberFieldValue(string target, object value, IDictionary attributes)
939 target = RewriteTargetIfWithinObjectScope(target);
941 attributes = attributes ?? new Hashtable();
943 ApplyNumberOnlyOptions(attributes);
944 ApplyValidation(InputElementType.Text, target, ref attributes);
946 return CreateInputElement("text", target, value, attributes);
949 #endregion
951 #region TextArea
953 /// <summary>
954 /// Generates a textarea element.
955 /// <para>
956 /// The value is extracted from the target (if available)
957 /// </para>
958 /// </summary>
959 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
960 /// <returns>The generated form element</returns>
961 public string TextArea(string target)
963 return TextArea(target, null);
966 /// <summary>
967 /// Generates a textarea element.
968 /// <para>
969 /// The value is extracted from the target (if available)
970 /// </para>
971 /// </summary>
972 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
973 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
974 /// <returns>The generated form element</returns>
975 public string TextArea(string target, IDictionary attributes)
977 string targetForValue = RewriteTargetIfWithinObjectScope(target);
978 object value = ObtainValue(targetForValue);
979 return TextAreaValue(target, value, attributes);
982 /// <summary>
983 /// Generates a textarea element with a specified value.
984 /// </summary>
985 /// <param name="target">The target to base the element name on.</param>
986 /// <param name="value">The value to apply to the field.</param>
987 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
988 /// <returns>The generated form element</returns>
989 public string TextAreaValue(string target, object value, IDictionary attributes)
991 target = RewriteTargetIfWithinObjectScope(target);
993 value = value == null ? "" : HtmlEncode(value.ToString());
995 string id = CreateHtmlId(target);
997 ApplyValidation(InputElementType.Text, target, ref attributes);
999 return String.Format("<textarea id=\"{0}\" name=\"{1}\" {2}>{3}</textarea>",
1000 id, target, GetAttributes(attributes), FormatIfNecessary(value, attributes));
1003 #endregion
1005 #region PasswordField
1007 /// <summary>
1008 /// Generates a password input field.
1009 /// </summary>
1010 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1011 /// <returns>The generated form element</returns>
1012 public string PasswordField(string target)
1014 return PasswordField(target, null);
1017 /// <summary>
1018 /// Generates a password input field.
1019 /// </summary>
1020 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1021 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1022 /// <returns>The generated form element</returns>
1023 public string PasswordField(string target, IDictionary attributes)
1025 target = RewriteTargetIfWithinObjectScope(target);
1027 object value = ObtainValue(target);
1029 ApplyValidation(InputElementType.Text, target, ref attributes);
1031 return CreateInputElement("password", target, value, attributes);
1034 #endregion
1036 #region PasswordNumberField
1038 /// <summary>
1039 /// Generates an input password element with a javascript that prevents
1040 /// chars other than numbers from being entered.
1041 /// <para>
1042 /// The value is extracted from the target (if available)
1043 /// </para>
1044 /// </summary>
1045 /// <remarks>
1046 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
1047 /// </remarks>
1048 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1049 /// <returns>The generated form element</returns>
1050 public string PasswordNumberField(string target)
1052 return PasswordNumberField(target, null);
1055 /// <summary>
1056 /// Generates an input password element with a javascript that prevents
1057 /// chars other than numbers from being entered.
1058 /// <para>
1059 /// The value is extracted from the target (if available)
1060 /// </para>
1061 /// <para>
1062 /// You can optionally pass an <c>exceptions</c> value through the dictionary.
1063 /// It must be a comma separated list of chars that can be accepted on the field.
1064 /// For example:
1065 /// </para>
1066 /// <code>
1067 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
1068 /// </code>
1069 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
1070 /// <para>
1071 /// You can aslo optionally pass an <c>forbid</c> value through the dictionary.
1072 /// It must be a comma separated list of chars that cannot be accepted on the field.
1073 /// For example:
1074 /// </para>
1075 /// <code>
1076 /// FormHelper.NumberField("product.price", {forbid='46'})
1077 /// </code>
1078 /// In this case the key code 46 (period) will not be accepted on the field.
1079 /// </summary>
1080 /// <remarks>
1081 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
1082 /// </remarks>
1083 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1084 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1085 /// <returns>The generated form element</returns>
1086 public string PasswordNumberField(string target, IDictionary attributes)
1088 target = RewriteTargetIfWithinObjectScope(target);
1090 object value = ObtainValue(target);
1092 attributes = attributes != null ? attributes : new Hashtable();
1094 ApplyNumberOnlyOptions(attributes);
1095 ApplyValidation(InputElementType.Text, target, ref attributes);
1097 return CreateInputElement("password", target, value, attributes);
1100 #endregion
1102 #region LabelFor
1104 /// <summary>
1105 /// Generates a label element.
1106 /// </summary>
1107 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1108 /// <param name="label">Legend</param>
1109 /// <returns>The generated form element</returns>
1110 public string LabelFor(string target, string label)
1112 return LabelFor(target, label, null);
1115 /// <summary>
1116 /// Generates a label element.
1117 /// </summary>
1118 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1119 /// <param name="label">Legend</param>
1120 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1121 /// <returns>The generated form element</returns>
1122 public string LabelFor(string target, string label, IDictionary attributes)
1124 target = RewriteTargetIfWithinObjectScope(target);
1126 string id = CreateHtmlId(attributes, target);
1128 StringBuilder sb = new StringBuilder();
1129 StringWriter sbWriter = new StringWriter(sb);
1130 HtmlTextWriter writer = new HtmlTextWriter(sbWriter);
1132 writer.WriteBeginTag("label");
1133 writer.WriteAttribute("for", id);
1134 string strAttributes = GetAttributes(attributes);
1135 if (strAttributes != String.Empty) writer.Write(HtmlTextWriter.SpaceChar);
1137 writer.Write(strAttributes);
1138 writer.Write(HtmlTextWriter.TagRightChar);
1139 writer.Write(label);
1140 writer.WriteEndTag("label");
1142 return sbWriter.ToString();
1145 #endregion
1147 #region HiddenField
1149 /// <summary>
1150 /// Generates a hidden form element.
1151 /// <para>
1152 /// The value is extracted from the target (if available)
1153 /// </para>
1154 /// </summary>
1155 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1156 /// <returns>The generated form element</returns>
1157 public string HiddenField(string target)
1159 target = RewriteTargetIfWithinObjectScope(target);
1161 object value = ObtainValue(target);
1163 return CreateInputElement("hidden", target, value, null);
1166 /// <summary>
1167 /// Generates a hidden form element with the specified value
1168 /// </summary>
1169 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1170 /// <param name="value">The value for the hidden field</param>
1171 /// <returns>The generated form element</returns>
1172 public string HiddenField(string target, object value)
1174 return CreateInputElement("hidden", target, value, null);
1177 /// <summary>
1178 /// Generates a hidden form element.
1179 /// <para>
1180 /// The value is extracted from the target (if available)
1181 /// </para>
1182 /// </summary>
1183 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1184 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1185 /// <returns>The generated form element</returns>
1186 public string HiddenField(string target, IDictionary attributes)
1188 target = RewriteTargetIfWithinObjectScope(target);
1190 object value = ObtainValue(target);
1192 string id = CreateHtmlId(attributes, target);
1194 value = value != null ? value : String.Empty;
1196 return CreateInputElement("hidden", id, target, value.ToString(), attributes);
1199 /// <summary>
1200 /// Generates a hidden form element with the specified value
1201 /// </summary>
1202 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1203 /// <param name="value">The value for the hidden field</param>
1204 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1205 /// <returns>The generated form element</returns>
1206 public string HiddenField(string target, object value, IDictionary attributes)
1208 return CreateInputElement("hidden", target, value, attributes);
1211 #endregion
1213 #region CheckboxList
1215 /// <summary>
1216 /// Creates a <see cref="CheckboxList"/> instance
1217 /// which is enumerable. For each interaction you can invoke
1218 /// <see cref="CheckboxList.Item()"/> which will correctly render
1219 /// a checkbox input element for the current element on the supplied set (<c>dataSource</c>).
1220 /// <para>
1221 /// The enumerable item will be an element of the <c>dataSource</c>.
1222 /// </para>
1223 /// If the <c>dataSource</c>
1224 /// elements are complex objects (ie not string or primitives),
1225 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1226 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1227 /// <para>
1228 /// Usually both the <c>target</c> and obviously the <c>dataSource</c> are sets
1229 /// with multiple items. The element types tend to be the same. If
1230 /// they are not, you might have to specify the <c>suffix</c> parameters on
1231 /// the <c>attributes</c> as it would not be inferred.
1232 /// </para>
1233 /// </summary>
1234 ///
1235 /// <example>
1236 /// Consider the following action code:
1237 /// <code>
1238 /// public void Index()
1239 /// {
1240 /// // data source
1241 /// PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
1242 ///
1243 /// // initial selection
1244 /// PropertyBag["selectedPrimes"] = new int[] { 11, 19 };
1245 /// }
1246 /// </code>
1247 ///
1248 /// And the respective view code
1249 ///
1250 /// <code lang="none">
1251 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1252 ///
1253 /// #foreach($elem in $items)
1254 /// $items.Item() $elem
1255 /// #end
1256 /// </code>
1257 ///
1258 /// That will generates the following html:
1259 ///
1260 /// <code lang="none">
1261 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_0_&quot; name=&quot;selectedPrimes[0]&quot; value=&quot;2&quot; /&gt; 2
1262 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_1_&quot; name=&quot;selectedPrimes[1]&quot; value=&quot;3&quot; /&gt; 3
1263 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_2_&quot; name=&quot;selectedPrimes[2]&quot; value=&quot;5&quot; /&gt; 5
1264 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_3_&quot; name=&quot;selectedPrimes[3]&quot; value=&quot;7&quot; /&gt; 7
1265 /// &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
1266 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_5_&quot; name=&quot;selectedPrimes[5]&quot; value=&quot;13&quot; /&gt; 13
1267 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_6_&quot; name=&quot;selectedPrimes[6]&quot; value=&quot;17&quot; /&gt; 17
1268 /// &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
1269 /// &lt;input type=&quot;checkbox&quot; id=&quot;selectedPrimes_8_&quot; name=&quot;selectedPrimes[8]&quot; value=&quot;23&quot; /&gt; 23
1270 /// </code>
1271 ///
1272 /// <para>
1273 /// To customize the id, you can call the <see cref="CheckboxList.Item(string)"/> overload:
1274 /// </para>
1275 ///
1276 /// <code lang="none">
1277 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1278 ///
1279 /// #foreach($elem in $items)
1280 /// $items.Item("myId${velocityCount}") $Form.LabelFor("myId${velocityCount}", $elem.ToString()) <br/>
1281 /// #end
1282 /// </code>
1283 /// </example>
1284 ///
1285 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1286 /// <param name="dataSource">The set of available elements</param>
1287 /// <returns>The generated form element</returns>
1288 public CheckboxList CreateCheckboxList(string target, IEnumerable dataSource)
1290 return CreateCheckboxList(target, dataSource, null);
1293 /// <summary>
1294 /// Creates a <see cref="CheckboxList"/> instance
1295 /// which is enumerable. For each interaction you can invoke
1296 /// <see cref="CheckboxList.Item()"/> which will correctly render
1297 /// a checkbox input element for the current element on the supplied set (<c>dataSource</c>).
1298 /// <para>
1299 /// The enumerable item will be an element of the <c>dataSource</c>.
1300 /// </para>
1301 /// If the <c>dataSource</c>
1302 /// elements are complex objects (ie not string or primitives),
1303 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1304 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1305 /// <para>
1306 /// Usually both the <c>target</c> and obviously the <c>dataSource</c> are sets
1307 /// with multiple items. The element types tend to be the same. If
1308 /// they are not, you might have to specify the <c>suffix</c> parameters on
1309 /// the <c>attributes</c> as it would not be inferred.
1310 /// </para>
1311 /// </summary>
1312 ///
1313 /// <seealso cref="CreateCheckboxList(string,IEnumerable)"/>
1315 /// <example>
1316 /// Consider the following action code:
1317 /// <code>
1318 /// public void Index()
1319 /// {
1320 /// Category[] categories = new Category[] { new Category(1, "Music"), new Category(2, "Humor"), new Category(3, "Politics") };
1321 /// PropertyBag["categories"] = categories; // datasource
1322 ///
1323 /// Blog blog = new Blog();
1324 /// blog.Categories = new Category[] { new Category(2, "Humor") }; // initial selection
1325 /// PropertyBag["blog"] = blog;
1326 /// }
1327 /// </code>
1328 ///
1329 /// And the respective view code
1330 ///
1331 /// <code lang="none">
1332 /// #set($items = $Form.CreateCheckboxList("blog.categories", $categories, "%{value='Id'}"))
1333 ///
1334 /// #foreach($elem in $items)
1335 /// $items.Item() $elem
1336 /// #end
1337 /// </code>
1338 ///
1339 /// That will generates the following html:
1340 ///
1341 /// <code lang="none">
1342 /// &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
1343 /// &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
1344 /// &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
1345 /// </code>
1346 ///
1347 /// </example>
1348 ///
1349 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1350 /// <param name="dataSource">The set of available elements</param>
1351 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1352 /// <returns>The generated form element</returns>
1353 public CheckboxList CreateCheckboxList(string target, IEnumerable dataSource, IDictionary attributes)
1355 target = RewriteTargetIfWithinObjectScope(target);
1357 object value = ObtainValue(target);
1359 return new CheckboxList(this, target, value, dataSource, attributes);
1362 /// <summary>
1363 /// Outputs a checkbox element (for internal use)
1364 /// </summary>
1365 /// <param name="index"></param>
1366 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1367 /// <param name="suffix"></param>
1368 /// <param name="item"></param>
1369 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1370 /// <returns>The generated form element</returns>
1371 internal string CheckboxItem(int index, string target, string suffix, SetItem item, IDictionary attributes)
1373 if (item.IsSelected)
1375 AddChecked(attributes);
1377 else
1379 RemoveChecked(attributes);
1382 target = String.Format("{0}[{1}]", target, index);
1384 string elementId = CreateHtmlId(attributes, target, true);
1386 string computedTarget = target;
1388 if (suffix != null && suffix != String.Empty)
1390 computedTarget += "." + suffix;
1393 return CreateInputElement("checkbox", elementId, computedTarget, item.Value, attributes);
1396 /// <summary>
1397 /// This class is an enumerable list of checkboxes.
1398 /// It uses the <see cref="OperationState"/> to manage the sets
1399 /// and to control the check/uncheck state.
1400 /// </summary>
1401 public sealed class CheckboxList : IEnumerable, IEnumerator
1403 private readonly FormHelper helper;
1404 private readonly string target;
1405 private readonly IDictionary attributes;
1406 private readonly OperationState operationState;
1407 private readonly IEnumerator enumerator;
1408 private bool hasMovedNext, hasItem;
1409 private int index = -1;
1411 /// <summary>
1412 /// Initializes a new instance of the <see cref="CheckboxList"/> class.
1413 /// </summary>
1414 /// <param name="helper">The helper.</param>
1415 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1416 /// <param name="initialSelectionSet">The initial selection set.</param>
1417 /// <param name="dataSource">The set of available elements</param>
1418 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1419 public CheckboxList(FormHelper helper, string target,
1420 object initialSelectionSet, IEnumerable dataSource, IDictionary attributes)
1422 if (dataSource == null) throw new ArgumentNullException("dataSource");
1424 this.helper = helper;
1425 this.target = target;
1426 this.attributes = attributes ?? new HybridDictionary(true);
1428 operationState = SetOperation.IterateOnDataSource(initialSelectionSet, dataSource, attributes);
1429 enumerator = operationState.GetEnumerator();
1432 /// <summary>
1433 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1434 /// on the Set.
1435 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1436 /// </summary>
1437 /// <returns>The generated input element</returns>
1438 public string Item()
1440 return Item(null);
1443 /// <summary>
1444 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1445 /// on the Set.
1446 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1447 /// </summary>
1448 /// <param name="id">The element id</param>
1449 /// <returns>The generated input element</returns>
1450 public string Item(string id)
1452 if (!hasMovedNext)
1454 throw new InvalidOperationException("Before rendering a checkbox item, you must use MoveNext");
1457 if (!hasItem)
1459 // Nothing to render
1460 return String.Empty;
1463 if (id != null)
1465 attributes["id"] = id;
1468 return helper.CheckboxItem(index, target, operationState.TargetSuffix, CurrentSetItem, attributes);
1471 /// <summary>
1472 /// Returns an enumerator that iterates through a collection.
1473 /// </summary>
1474 /// <returns>
1475 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
1476 /// </returns>
1477 public IEnumerator GetEnumerator()
1479 return this;
1482 /// <summary>
1483 /// Advances the enumerator to the next element of the collection.
1484 /// </summary>
1485 /// <returns>
1486 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
1487 /// </returns>
1488 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1489 public bool MoveNext()
1491 hasMovedNext = true;
1492 hasItem = enumerator.MoveNext();
1494 if (hasItem) index++;
1496 return hasItem;
1499 /// <summary>
1500 /// Sets the enumerator to its initial position, which is before the first element in the collection.
1501 /// </summary>
1502 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1503 public void Reset()
1505 index = -1;
1506 enumerator.Reset();
1509 /// <summary>
1510 /// Gets the current element in the collection.
1511 /// </summary>
1512 /// <value></value>
1513 /// <returns>The current element in the collection.</returns>
1514 /// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element. </exception>
1515 public object Current
1517 get { return CurrentSetItem.Item; }
1520 /// <summary>
1521 /// Gets the current set item.
1522 /// </summary>
1523 /// <value>The current set item.</value>
1524 public SetItem CurrentSetItem
1526 get { return enumerator.Current as SetItem; }
1530 #endregion
1532 #region CheckboxField
1534 /// <summary>
1535 /// Generates a checkbox field. In fact it generates two as a
1536 /// way to send a value if the primary checkbox is not checked.
1537 /// This allow the process the be aware of the unchecked value
1538 /// and act accordingly.
1539 /// </summary>
1540 ///
1541 /// <example>
1542 /// Consider the following view code:
1543 ///
1544 /// <code lang="none">
1545 /// $Form.CheckboxField('user.disabled')
1546 /// </code>
1547 ///
1548 /// That is going to output:
1549 ///
1550 /// <code lang="none">
1551 /// &lt;input type=&quot;checkbox&quot; id=&quot;user_disabled&quot; name=&quot;user.disabled&quot; value=&quot;true&quot; /&gt;
1552 /// &lt;input type=&quot;hidden&quot; id=&quot;user_disabledH&quot; name=&quot;user.disabled&quot; value=&quot;false&quot; /&gt;
1553 /// </code>
1554 ///
1555 /// </example>
1556 ///
1557 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1558 /// <returns>The generated form element</returns>
1559 public string CheckboxField(string target)
1561 return CheckboxField(target, null);
1564 /// <summary>
1565 /// Generates a checkbox field. In fact it generates two as a
1566 /// way to send a value if the primary checkbox is not checked.
1567 /// This allow the process the be aware of the unchecked value
1568 /// and act accordingly.
1569 ///
1570 /// <para>
1571 /// The checked and unchecked values sent to the server defaults
1572 /// to true and false. You can override them using the
1573 /// parameters <c>trueValue</c> and <c>falseValue</c>, but the DataBinder is prepared only
1574 /// to treat boolean arrays.
1575 /// </para>
1576 ///
1577 /// </summary>
1578 ///
1579 /// <seealso cref="CheckboxField(string)"/>
1580 ///
1581 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1582 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1583 /// <returns>The generated form element</returns>
1584 public string CheckboxField(string target, IDictionary attributes)
1586 target = RewriteTargetIfWithinObjectScope(target);
1588 object value = ObtainValue(target);
1590 string trueValue = CommonUtils.ObtainEntryAndRemove(attributes, "trueValue", "true");
1592 bool isChecked;
1594 if (trueValue != "true")
1596 isChecked = AreEqual(value, trueValue);
1598 else
1600 isChecked = ((value != null && value is bool && ((bool)value)) ||
1601 (!(value is bool) && (value != null)));
1604 if (isChecked)
1606 if (attributes == null)
1608 attributes = new HybridDictionary(true);
1611 AddChecked(attributes);
1614 ApplyValidation(InputElementType.Checkbox, target, ref attributes);
1616 string id = CreateHtmlId(attributes, target);
1617 string hiddenElementId = id + "H";
1618 string hiddenElementValue = CommonUtils.ObtainEntryAndRemove(attributes, "falseValue", "false");
1620 string result = CreateInputElement("checkbox", id, target, trueValue, attributes);
1622 result += CreateInputElement("hidden", hiddenElementId, target, hiddenElementValue, null);
1624 return result;
1627 #endregion
1629 #region RadioField
1631 /// <summary>
1632 /// Generates a radio input type with the specified
1633 /// value to send to the served in case the element in checked.
1634 /// It will automatically check the radio if the target
1635 /// evaluated value is equal to the specified <c>valueToSend</c>.
1636 /// </summary>
1637 ///
1638 /// <example>
1639 /// Consider the following action code:
1640 ///
1641 /// <code>
1642 /// public void Index()
1643 /// {
1644 /// PropertyBag["mode"] = FileMode.Truncate;
1645 /// }
1646 /// </code>
1647 ///
1648 /// And the following view code:
1649 ///
1650 /// <code lang="none">
1651 /// $Form.RadioField("mode", "Append") FileMode.Append
1652 /// $Form.RadioField("mode", "Create") FileMode.Create
1653 /// $Form.RadioField("mode", "CreateNew") FileMode.CreateNew
1654 /// $Form.RadioField("mode", "Open") FileMode.Open
1655 /// $Form.RadioField("mode", "OpenOrCreate", "%{id='customhtmlid'}") FileMode.OpenOrCreate
1656 /// $Form.RadioField("mode", "Truncate") FileMode.Truncate
1657 /// </code>
1658 ///
1659 /// That is going to output:
1660 ///
1661 /// <code lang="none">
1662 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Append&quot; /&gt; FileMode.Append
1663 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Create&quot; /&gt; FileMode.Create
1664 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;CreateNew&quot; /&gt; FileMode.CreateNew
1665 /// &lt;input type=&quot;radio&quot; id=&quot;mode&quot; name=&quot;mode&quot; value=&quot;Open&quot; /&gt; FileMode.Open
1666 /// &lt;input type=&quot;radio&quot; id=&quot;customhtmlid&quot; name=&quot;mode&quot; value=&quot;OpenOrCreate&quot; /&gt; FileMode.OpenOrCreate
1667 /// &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
1668 /// </code>
1669 ///
1670 /// </example>
1671 ///
1672 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1673 /// <param name="valueToSend"></param>
1674 /// <returns>The generated form element</returns>
1675 public string RadioField(string target, object valueToSend)
1677 return RadioField(target, valueToSend, null);
1680 /// <summary>
1681 /// Generates a radio input type with the specified
1682 /// value to send to the served in case the element in checked.
1683 /// It will automatically check the radio if the target
1684 /// evaluated value is equal to the specified <c>valueToSend</c>.
1685 /// </summary>
1686 ///
1687 /// <seealso cref="RadioField(string,object)"/>
1688 ///
1689 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1690 /// <param name="valueToSend"></param>
1691 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1692 /// <returns>The generated form element</returns>
1693 public string RadioField(string target, object valueToSend, IDictionary attributes)
1695 target = RewriteTargetIfWithinObjectScope(target);
1697 object value = ObtainValue(target);
1699 bool isChecked = AreEqual(valueToSend, value);
1701 if (isChecked)
1703 if (attributes == null)
1705 attributes = new HybridDictionary(true);
1708 AddChecked(attributes);
1711 return CreateInputElement("radio", target, valueToSend, attributes);
1714 #endregion
1716 #region FileField
1718 /// <summary>
1719 /// Generates an input file element.
1720 /// <para>
1721 /// Dirrently than other operations exposed by this helper,
1722 /// no value is extracted for this operation
1723 /// </para>
1724 /// </summary>
1725 /// <param name="target">The object to be based on when creating the element name.</param>
1726 /// <returns>The generated form element</returns>
1727 public string FileField(string target)
1729 return FileField(target, null);
1732 /// <summary>
1733 /// Generates an input file element.
1734 /// <para>
1735 /// Dirrently than other operations exposed by this helper,
1736 /// no value is extracted for this operation
1737 /// </para>
1738 /// </summary>
1739 /// <param name="target">The object to be based on when creating the element name.</param>
1740 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1741 /// <returns>The generated form element</returns>
1742 public string FileField(string target, IDictionary attributes)
1744 target = RewriteTargetIfWithinObjectScope(target);
1746 ApplyValidation(InputElementType.Text, target, ref attributes);
1748 return CreateInputElement("file", target, string.Empty, attributes);
1751 #endregion
1753 #region Select
1755 /// <summary>
1756 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1757 /// If the <c>dataSource</c>
1758 /// elements are complex objects (ie not string or primitives),
1759 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1760 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1761 /// <para>
1762 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1763 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1764 /// <c>firstoptionvalue</c>. The default value is '0'.
1765 /// </para>
1766 /// <para>
1767 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1768 /// a set with multiple items. The element types tend to be the same. If
1769 /// they are not, you might have to specify the <c>suffix</c> parameters on
1770 /// the <c>attributes</c> as it would not be inferred.
1771 /// </para>
1772 /// <para>
1773 /// The target can also be a set. In this case the intersection will be
1774 /// the initially selected elements.
1775 /// </para>
1776 /// </summary>
1777 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1778 /// <param name="dataSource">The set of available elements</param>
1779 /// <returns>The generated form element</returns>
1780 public string Select(string target, IEnumerable dataSource)
1782 return Select(target, dataSource, null);
1785 /// <summary>
1786 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1787 /// If the <c>dataSource</c>
1788 /// elements are complex objects (ie not string or primitives),
1789 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1790 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1791 /// <para>
1792 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1793 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1794 /// <c>firstoptionvalue</c>. The default value is '0'.
1795 /// </para>
1796 /// <para>
1797 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1798 /// a set with multiple items. The element types tend to be the same. If
1799 /// they are not, you might have to specify the <c>suffix</c> parameters on
1800 /// the <c>attributes</c> as it would not be inferred.
1801 /// </para>
1802 /// <para>
1803 /// The target can also be a set. In this case the intersection will be
1804 /// the initially selected elements.
1805 /// </para>
1806 /// </summary>
1807 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1808 /// <param name="dataSource">The set of available elements</param>
1809 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1810 /// <returns>The generated form element</returns>
1811 public string Select(string target, IEnumerable dataSource, IDictionary attributes)
1813 target = RewriteTargetIfWithinObjectScope(target);
1815 object selectedValue = ObtainValue(target);
1817 return Select(target, selectedValue, dataSource, attributes);
1820 /// <summary>
1821 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1822 /// If the <c>dataSource</c>
1823 /// elements are complex objects (ie not string or primitives),
1824 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1825 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1826 /// <para>
1827 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1828 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1829 /// <c>firstoptionvalue</c>. The default value is '0'.
1830 /// </para>
1831 /// <para>
1832 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1833 /// a set with multiple items. The element types tend to be the same. If
1834 /// they are not, you might have to specify the <c>suffix</c> parameters on
1835 /// the <c>attributes</c> as it would not be inferred.
1836 /// </para>
1837 /// <para>
1838 /// The target can also be a set. In this case the intersection will be
1839 /// the initially selected elements.
1840 /// </para>
1841 /// </summary>
1842 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1843 /// <param name="selectedValue"></param>
1844 /// <param name="dataSource">The set of available elements</param>
1845 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1846 /// <returns>The generated form element</returns>
1847 public string Select(string target, object selectedValue, IEnumerable dataSource, IDictionary attributes)
1849 return GenerateSelect(target, selectedValue, dataSource, attributes);
1852 /// <summary>
1853 /// Generates the select.
1854 /// </summary>
1855 /// <param name="target">The target.</param>
1856 /// <param name="selectedValue">The selected value.</param>
1857 /// <param name="dataSource">The data source.</param>
1858 /// <param name="attributes">The attributes.</param>
1859 /// <returns></returns>
1860 protected virtual string GenerateSelect(string target, object selectedValue, IEnumerable dataSource, IDictionary attributes)
1862 string id = CreateHtmlId(target);
1864 ApplyValidation(InputElementType.Select, target, ref attributes);
1866 StringBuilder sb = new StringBuilder();
1867 StringWriter sbWriter = new StringWriter(sb);
1868 HtmlTextWriter writer = new HtmlTextWriter(sbWriter);
1870 string firstOption = null;
1871 string firstOptionValue = null;
1872 string name = target;
1874 if (attributes != null)
1876 firstOption = CommonUtils.ObtainEntryAndRemove(attributes, "firstoption");
1877 firstOptionValue = CommonUtils.ObtainEntryAndRemove(attributes, "firstoptionvalue");
1879 if (attributes.Contains("name"))
1881 name = (String) attributes["name"];
1882 attributes.Remove("name");
1885 if (attributes.Contains("id"))
1887 id = (String) attributes["id"];
1888 attributes.Remove("id");
1892 OperationState state = SetOperation.IterateOnDataSource(selectedValue, dataSource, attributes);
1894 writer.WriteBeginTag("select");
1895 writer.WriteAttribute("id", id);
1896 writer.WriteAttribute("name", name);
1897 writer.Write(" ");
1898 writer.Write(GetAttributes(attributes));
1899 writer.Write(HtmlTextWriter.TagRightChar);
1900 writer.WriteLine();
1902 if (firstOption != null)
1904 writer.WriteBeginTag("option");
1905 writer.WriteAttribute("value", (firstOptionValue == null) ? "0" : SafeHtmlEncode(firstOptionValue));
1906 writer.Write(HtmlTextWriter.TagRightChar);
1907 writer.Write(SafeHtmlEncode(firstOption));
1908 writer.WriteEndTag("option");
1909 writer.WriteLine();
1912 foreach(SetItem item in state)
1914 writer.WriteBeginTag("option");
1916 if (item.IsSelected)
1918 writer.Write(" selected=\"selected\"");
1921 writer.WriteAttribute("value", SafeHtmlEncode(item.Value));
1922 writer.Write(HtmlTextWriter.TagRightChar);
1923 writer.Write(SafeHtmlEncode(item.Text));
1924 writer.WriteEndTag("option");
1925 writer.WriteLine();
1928 writer.WriteEndTag("select");
1930 return sbWriter.ToString();
1933 #endregion
1935 #region Field set
1937 /// <summary>
1938 /// Creates a field set element with a legend using the specified name.
1939 /// </summary>
1940 /// <param name="name">The name.</param>
1941 /// <returns></returns>
1942 public string FieldSet(string name)
1944 return "<fieldset><legend>" + name + "</legend>";
1947 /// <summary>
1948 /// Creates an element to close a fieldset element.
1949 /// </summary>
1950 /// <returns></returns>
1951 public string EndFieldSet()
1953 return "</fieldset>";
1956 #endregion
1958 #region Enum
1960 /// <summary>
1961 /// Creates a list of pairs for the enum type.
1962 /// </summary>
1963 /// <param name="enumType">enum type.</param>
1964 /// <returns></returns>
1965 public static Pair<int, string>[] EnumToPairs(Type enumType)
1967 if (enumType == null) throw new ArgumentNullException("enumType");
1968 if (!enumType.IsEnum) throw new ArgumentException("enumType must be an Enum", "enumType");
1970 Array values = Enum.GetValues(enumType);
1971 string[] names = Enum.GetNames(enumType);
1973 List<Pair<int, string>> listOfPairs = new List<Pair<int, string>>();
1974 int index = 0;
1976 foreach(string name in names)
1978 int value = Convert.ToInt32(values.GetValue(index++));
1979 listOfPairs.Add(new Pair<int, string>(value, TextHelper.PascalCaseToWord(name)));
1982 return listOfPairs.ToArray();
1985 #endregion
1987 #region Validation
1989 /// <summary>
1990 /// Configures this FormHelper instance to use the supplied
1991 /// web validator to generate field validation.
1992 /// </summary>
1993 /// <param name="provider">The validation provider.</param>
1994 public void UseWebValidatorProvider(IBrowserValidatorProvider provider)
1996 if (provider == null) throw new ArgumentNullException("provider");
1998 validatorProvider = provider;
2001 /// <summary>
2002 /// Configures this FormHelper instance to use Prototype for form fields validation
2003 /// </summary>
2004 public void UsePrototypeValidation()
2006 UseWebValidatorProvider(new PrototypeWebValidator());
2009 /// <summary>
2010 /// Configures this FormHelper instance to use fValidate for form fields validation
2011 /// </summary>
2012 public void UsefValidate()
2014 UseWebValidatorProvider(new FValidateWebValidator());
2017 /// <summary>
2018 /// Configures this FormHelper instance to use Zebda for form fields validation
2019 /// </summary>
2020 public void UseZebdaValidation()
2022 UseWebValidatorProvider(new ZebdaWebValidator());
2025 /// <summary>
2026 /// Disables the validation.
2027 /// </summary>
2028 public void DisableValidation()
2030 isValidationDisabled = true;
2033 /// <summary>
2034 /// Applies the validation.
2035 /// </summary>
2036 /// <param name="inputType">Type of the input.</param>
2037 /// <param name="target">The target.</param>
2038 /// <param name="attributes">The attributes.</param>
2039 protected virtual void ApplyValidation(InputElementType inputType, string target, ref IDictionary attributes)
2041 bool disableValidation = CommonUtils.ObtainEntryAndRemove(attributes, "disablevalidation", "false") == "true";
2043 if (!IsValidationEnabled && disableValidation)
2045 return;
2048 if (Controller.Validator == null || validationConfig == null)
2050 return;
2053 if (attributes == null)
2055 attributes = new HybridDictionary(true);
2058 IValidator[] validators = CollectValidators(RequestContext.All, target);
2060 IBrowserValidationGenerator generator = validatorProvider.CreateGenerator(validationConfig, inputType, attributes);
2062 foreach(IValidator validator in validators)
2064 if (validator.SupportsBrowserValidation)
2066 validator.ApplyBrowserValidation(validationConfig, inputType, generator, attributes, target);
2071 private IValidator[] CollectValidators(RequestContext requestContext, string target)
2073 List<IValidator> validators = new List<IValidator>();
2075 ObtainTargetProperty(requestContext, target, delegate(PropertyInfo property)
2077 validators.AddRange(Controller.Validator.GetValidators(property.DeclaringType, property));
2080 return validators.ToArray();
2083 private bool IsValidationEnabled
2087 if (isValidationDisabled) return false;
2089 if (objectStack.Count == 0) return true;
2091 return ((FormScopeInfo)objectStack.Peek()).IsValidationEnabled;
2095 #endregion
2097 #region protected members
2099 /// <summary>
2100 /// Rewrites the target if within object scope.
2101 /// </summary>
2102 /// <param name="target">The target.</param>
2103 /// <returns></returns>
2104 protected string RewriteTargetIfWithinObjectScope(string target)
2106 if (objectStack.Count == 0)
2108 return target;
2110 else
2112 return ((FormScopeInfo) objectStack.Peek()).RootTarget + "." + target;
2116 /// <summary>
2117 /// Creates the specified input element
2118 /// using the specified parameters to supply the name, value, id and others
2119 /// html attributes.
2120 /// </summary>
2121 /// <param name="type"></param>
2122 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2123 /// <param name="value"></param>
2124 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2125 /// <returns>The generated form element</returns>
2126 protected virtual string CreateInputElement(string type, string target, Object value, IDictionary attributes)
2128 if (value == null)
2130 value = CommonUtils.ObtainEntryAndRemove(attributes, "defaultValue");
2133 string id = CreateHtmlId(attributes, target);
2135 return CreateInputElement(type, id, target, FormatIfNecessary(value, attributes), attributes);
2138 /// <summary>
2139 /// Creates the specified input element
2140 /// using the specified parameters to supply the name, value, id and others
2141 /// html attributes.
2142 /// </summary>
2143 /// <param name="type"></param>
2144 /// <param name="id"></param>
2145 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2146 /// <param name="value"></param>
2147 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2148 /// <returns>The generated form element</returns>
2149 protected virtual string CreateInputElement(string type, string id, string target, string value, IDictionary attributes)
2151 value = FormatIfNecessary(value, attributes);
2153 value = SafeHtmlEncode(value);
2155 if (attributes != null && attributes.Contains("mask"))
2157 string mask = CommonUtils.ObtainEntryAndRemove(attributes, "mask");
2158 string maskSep = CommonUtils.ObtainEntryAndRemove(attributes, "mask_separator", "-");
2160 string onBlur = CommonUtils.ObtainEntryAndRemove(attributes, "onBlur", "void(0)");
2161 string onKeyUp = CommonUtils.ObtainEntryAndRemove(attributes, "onKeyUp", "void(0)");
2163 string js = "return monorail_formhelper_mask(event,this,'" + mask + "','" + maskSep + "');";
2165 attributes["onBlur"] = "javascript:" + onBlur + ";" + js;
2166 attributes["onKeyUp"] = "javascript:" + onKeyUp + ";" + js;
2169 return String.Format("<input type=\"{0}\" id=\"{1}\" name=\"{2}\" value=\"{3}\" {4}/>",
2170 type, id, target, value, GetAttributes(attributes));
2173 /// <summary>
2174 /// Creates the input element.
2175 /// </summary>
2176 /// <param name="type">The type.</param>
2177 /// <param name="value">The value.</param>
2178 /// <param name="attributes">The attributes.</param>
2179 /// <returns></returns>
2180 protected virtual string CreateInputElement(string type, string value, IDictionary attributes)
2182 return String.Format("<input type=\"{0}\" value=\"{1}\" {2}/>",
2183 type, FormatIfNecessary(value, attributes), GetAttributes(attributes));
2186 /// <summary>
2187 /// Formats if necessary.
2188 /// </summary>
2189 /// <param name="value">The value.</param>
2190 /// <param name="attributes">The attributes.</param>
2191 /// <returns></returns>
2192 protected static string FormatIfNecessary(object value, IDictionary attributes)
2194 string formatString = CommonUtils.ObtainEntryAndRemove(attributes, "textformat");
2196 if (value != null && formatString != null)
2198 IFormattable formattable = value as IFormattable;
2200 if (formattable != null)
2202 value = formattable.ToString(formatString, null);
2205 else if (value == null)
2207 value = String.Empty;
2210 return value.ToString();
2213 /// <summary>
2214 /// Obtains the target property.
2215 /// </summary>
2216 /// <param name="context">The context.</param>
2217 /// <param name="target">The target.</param>
2218 /// <param name="action">The action.</param>
2219 /// <returns></returns>
2220 protected PropertyInfo ObtainTargetProperty(RequestContext context, string target, Action<PropertyInfo> action)
2222 string[] pieces;
2224 Type root = ObtainRootType(context, target, out pieces);
2226 if (root != null && pieces.Length > 1)
2228 return QueryPropertyInfoRecursive(root, pieces, 1, action);
2231 return null;
2234 /// <summary>
2235 /// Queries the context for the target value
2236 /// </summary>
2237 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2238 /// <returns>The generated form element</returns>
2239 protected object ObtainValue(string target)
2241 return ObtainValue(RequestContext.All, target);
2244 /// <summary>
2245 /// Queries the context for the target value
2246 /// </summary>
2247 /// <param name="context"></param>
2248 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2249 /// <returns>The generated form element</returns>
2250 protected object ObtainValue(RequestContext context, string target)
2252 string[] pieces;
2254 object rootInstance = ObtainRootInstance(context, target, out pieces);
2256 if (rootInstance != null && pieces.Length > 1)
2258 return QueryPropertyRecursive(rootInstance, pieces, 1);
2261 return rootInstance;
2264 /// <summary>
2265 /// Obtains the root instance.
2266 /// </summary>
2267 /// <param name="context">The context.</param>
2268 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2269 /// <returns>The generated form element</returns>
2270 protected object ObtainRootInstance(RequestContext context, string target)
2272 object rootInstance = null;
2274 if (context == RequestContext.All || context == RequestContext.PropertyBag)
2276 rootInstance = Controller.PropertyBag[target];
2278 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Flash))
2280 rootInstance = Controller.Context.Flash[target];
2282 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Session))
2284 rootInstance = Controller.Context.Session[target];
2286 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Params))
2288 rootInstance = Controller.Params[target];
2290 if (rootInstance == null && (context == RequestContext.All || context == RequestContext.Request))
2292 rootInstance = Controller.Context.Items[target];
2295 return rootInstance;
2298 /// <summary>
2299 /// Obtains the root instance.
2300 /// </summary>
2301 /// <param name="context">The context.</param>
2302 /// <param name="target">The target.</param>
2303 /// <param name="pieces">The pieces.</param>
2304 /// <returns></returns>
2305 protected object ObtainRootInstance(RequestContext context, string target, out string[] pieces)
2307 pieces = target.Split(new char[] {'.'});
2309 string root = pieces[0];
2311 int index;
2313 bool isIndexed = CheckForExistenceAndExtractIndex(ref root, out index);
2315 object rootInstance = ObtainRootInstance(context, root);
2317 if (rootInstance == null)
2319 return null;
2322 if (isIndexed)
2324 AssertIsValidArray(rootInstance, root, index);
2327 if (!isIndexed && pieces.Length == 1)
2329 return rootInstance;
2331 else if (isIndexed)
2333 rootInstance = GetArrayElement(rootInstance, index);
2336 return rootInstance;
2339 /// <summary>
2340 /// Obtains the type of the root.
2341 /// </summary>
2342 /// <param name="context">The context.</param>
2343 /// <param name="target">The target.</param>
2344 /// <param name="pieces">The pieces.</param>
2345 /// <returns></returns>
2346 private Type ObtainRootType(RequestContext context, string target, out string[] pieces)
2348 pieces = target.Split(new char[] { '.' });
2350 Type foundType = (Type) Controller.PropertyBag[pieces[0] + "type"];
2352 if (foundType == null)
2354 object root = ObtainRootInstance(context, target, out pieces);
2356 if (root != null)
2358 foundType = root.GetType();
2362 return foundType;
2365 private static PropertyInfo QueryPropertyInfoRecursive(Type type, string[] propertyPath)
2367 return QueryPropertyInfoRecursive(type, propertyPath, 0, null);
2370 private static PropertyInfo QueryPropertyInfoRecursive(Type type, string[] propertyPath, int piece, Action<PropertyInfo> action)
2372 string property = propertyPath[piece]; int index;
2374 bool isIndexed = CheckForExistenceAndExtractIndex(ref property, out index);
2376 PropertyInfo propertyInfo = type.GetProperty(property, ResolveFlagsToUse(type));
2378 if (propertyInfo == null)
2380 if (logger.IsErrorEnabled)
2382 logger.Error("No public property '{0}' found on type '{1}'", property, type.FullName);
2385 return null;
2388 if (!propertyInfo.CanRead)
2390 throw new BindingException("Property '{0}' for type '{1}' can not be read",
2391 propertyInfo.Name, type.FullName);
2394 if (propertyInfo.GetIndexParameters().Length != 0)
2396 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
2397 propertyInfo.Name, type.FullName);
2400 if (action != null)
2402 action(propertyInfo);
2405 type = propertyInfo.PropertyType;
2407 if (typeof(ICollection).IsAssignableFrom(type))
2409 return null;
2412 if (isIndexed)
2414 if (type.IsGenericType)
2416 Type[] args = type.GetGenericArguments();
2417 if (args.Length != 1)
2418 throw new BindingException("Expected the generic indexed property '{0}' to be of 1 element", type.Name);
2419 type = args[0];
2422 if (type.IsArray)
2424 type = type.GetElementType();
2428 if (piece + 1 == propertyPath.Length)
2430 return propertyInfo;
2433 return QueryPropertyInfoRecursive(type, propertyPath, piece + 1, action);
2436 /// <summary>
2437 /// Query property paths agains the rootInstance type
2438 /// </summary>
2439 /// <param name="rootInstance">the object to query</param>
2440 /// <param name="propertyPath">property path</param>
2441 /// <returns>The generated form element</returns>
2442 protected static object QueryPropertyRecursive(object rootInstance, string[] propertyPath)
2444 return QueryPropertyRecursive(rootInstance, propertyPath, 0);
2447 /// <summary>
2448 /// Query property paths agains the rootInstance type
2449 /// </summary>
2450 /// <param name="rootInstance">the object to query</param>
2451 /// <param name="propertyPath">property path</param>
2452 /// <param name="piece">start index</param>
2453 /// <returns>The generated form element</returns>
2454 protected static object QueryPropertyRecursive(object rootInstance, string[] propertyPath, int piece)
2456 string property = propertyPath[piece]; int index;
2458 Type instanceType = rootInstance.GetType();
2460 bool isIndexed = CheckForExistenceAndExtractIndex(ref property, out index);
2462 PropertyInfo propertyInfo = instanceType.GetProperty(property, ResolveFlagsToUse(instanceType));
2464 object instance = null;
2466 if (propertyInfo == null)
2468 FieldInfo fieldInfo = instanceType.GetField(property, FieldFlags);
2470 if (fieldInfo != null)
2472 instance = fieldInfo.GetValue(rootInstance);
2475 else
2477 if (!propertyInfo.CanRead)
2479 throw new BindingException("Property '{0}' for type '{1}' can not be read",
2480 propertyInfo.Name, instanceType.FullName);
2483 if (propertyInfo.GetIndexParameters().Length != 0)
2485 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
2486 propertyInfo.Name, instanceType.FullName);
2489 instance = propertyInfo.GetValue(rootInstance, null);
2492 if (isIndexed && instance != null)
2494 AssertIsValidArray(instance, property, index);
2496 instance = GetArrayElement(instance, index);
2499 if (instance == null || piece + 1 == propertyPath.Length)
2501 return instance;
2504 return QueryPropertyRecursive(instance, propertyPath, piece + 1);
2507 /// <summary>
2508 /// Creates the HTML id.
2509 /// </summary>
2510 /// <param name="attributes">The attributes.</param>
2511 /// <param name="target">The target.</param>
2512 /// <returns>The generated form element</returns>
2513 protected static string CreateHtmlId(IDictionary attributes, string target)
2515 return CreateHtmlId(attributes, target, true);
2518 /// <summary>
2519 /// Creates the HTML id.
2520 /// </summary>
2521 /// <param name="attributes">The attributes.</param>
2522 /// <param name="target">The target.</param>
2523 /// <param name="removeEntry">if set to <c>true</c> [remove entry].</param>
2524 /// <returns>The generated form element</returns>
2525 protected static string CreateHtmlId(IDictionary attributes, string target, bool removeEntry)
2527 string id;
2529 if (removeEntry)
2531 id = CommonUtils.ObtainEntryAndRemove(attributes, "id");
2533 else
2535 id = CommonUtils.ObtainEntry(attributes, "id");
2538 if (id == null)
2540 id = CreateHtmlId(target);
2543 return id;
2546 #endregion
2548 #region private helpers
2550 private static void ApplyNumberOnlyOptions(IDictionary attributes)
2552 string list = CommonUtils.ObtainEntryAndRemove(attributes, "exceptions", String.Empty);
2553 string forbid = CommonUtils.ObtainEntryAndRemove(attributes, "forbid", String.Empty);
2555 attributes["onKeyPress"] = "return monorail_formhelper_numberonly(event, [" + list + "], [" + forbid + "]);";
2558 private static void ApplyFilterOptions(IDictionary attributes)
2560 string forbid = CommonUtils.ObtainEntryAndRemove(attributes, "forbid", String.Empty);
2562 attributes["onKeyPress"] = "return monorail_formhelper_inputfilter(event, [" + forbid + "]);";
2565 private static void AssertIsValidArray(object instance, string property, int index)
2567 Type instanceType = instance.GetType();
2569 IList list = instance as IList;
2571 bool validList = false;
2573 if (list == null && instanceType.IsGenericType)
2575 Type[] genArgs = instanceType.GetGenericArguments();
2577 Type genList = typeof(System.Collections.Generic.IList<>).MakeGenericType(genArgs);
2578 Type genTypeDef = instanceType.GetGenericTypeDefinition().MakeGenericType(genArgs);
2580 validList = genList.IsAssignableFrom(genTypeDef);
2583 if (!validList && list == null)
2585 throw new MonoRailException("The property {0} is being accessed as " +
2586 "an indexed property but does not seem to implement IList. " +
2587 "In fact the type is {1}", property, instanceType.FullName);
2590 if (index < 0)
2592 throw new MonoRailException("The specified index '{0}' is outside the bounds " +
2593 "of the array. Property {1}", index, property);
2597 private static object GetArrayElement(object instance, int index)
2599 IList list = instance as IList;
2601 if (list == null && instance != null && instance.GetType().IsGenericType)
2603 Type instanceType = instance.GetType();
2605 Type[] genArguments = instanceType.GetGenericArguments();
2607 Type genType = instanceType.GetGenericTypeDefinition().MakeGenericType(genArguments);
2609 // I'm not going to retest for IList implementation as
2610 // if we got here, the AssertIsValidArray has run successfully
2612 PropertyInfo countPropInfo = genType.GetProperty("Count");
2614 int count = (int) countPropInfo.GetValue(instance, null);
2616 if (count == 0 || index + 1 > count)
2618 return null;
2621 PropertyInfo indexerPropInfo = genType.GetProperty("Item");
2623 return indexerPropInfo.GetValue(instance, new object[] { index });
2626 if (list == null || list.Count == 0 || index + 1 > list.Count)
2628 return null;
2631 return list[index];
2634 private static bool CheckForExistenceAndExtractIndex(ref string property, out int index)
2636 bool isIndexed = property.IndexOf('[') != -1;
2638 index = -1;
2640 if (isIndexed)
2642 int start = property.IndexOf('[') + 1;
2643 int len = property.IndexOf(']', start) - start;
2645 string indexStr = property.Substring(start, len);
2649 index = Convert.ToInt32(indexStr);
2651 catch(Exception)
2653 throw new MonoRailException("Could not convert (param {0}) index to Int32. Value is {1}",
2654 property, indexStr);
2657 property = property.Substring(0, start - 1);
2660 return isIndexed;
2663 private static bool AreEqual(object left, object right)
2665 if (left == null || right == null) return false;
2667 if (left is string && right is String)
2669 return String.Compare(left.ToString(), right.ToString()) == 0;
2672 if (left.GetType() == right.GetType())
2674 return right.Equals(left);
2677 IConvertible convertible = left as IConvertible;
2679 if (convertible != null)
2683 object newleft = convertible.ToType(right.GetType(), null);
2684 return (newleft.Equals(right));
2686 catch(Exception)
2688 // Do nothing
2692 return left.ToString().Equals(right.ToString());
2695 private string SafeHtmlEncode(string content)
2697 if (Controller.Context != null)
2699 return HtmlEncode(content);
2702 return content;
2705 /// <summary>
2706 /// Determines whether the present value matches the value on
2707 /// the initialSetValue (which can be a single value or a set)
2708 /// </summary>
2709 /// <param name="value">Value from the datasource</param>
2710 /// <param name="initialSetValue">Value from the initial selection set</param>
2711 /// <param name="propertyOnInitialSet">Optional. Property to obtain the value from</param>
2712 /// <param name="isMultiple"><c>true</c> if the initial selection is a set</param>
2713 /// <returns><c>true</c> if it's selected</returns>
2714 protected internal static bool IsPresent(object value, object initialSetValue,
2715 ValueGetter propertyOnInitialSet, bool isMultiple)
2717 if (!isMultiple)
2719 object valueToCompare = initialSetValue;
2721 if (propertyOnInitialSet != null)
2723 // propertyOnInitialSet.GetValue(initialSetValue, null);
2724 valueToCompare = propertyOnInitialSet.GetValue(initialSetValue);
2727 return AreEqual(value, valueToCompare);
2729 else
2731 foreach(object item in (IEnumerable) initialSetValue)
2733 object valueToCompare = item;
2735 if (propertyOnInitialSet != null)
2737 // valueToCompare = propertyOnInitialSet.GetValue(item, null);
2738 valueToCompare = propertyOnInitialSet.GetValue(item);
2741 if (AreEqual(value, valueToCompare))
2743 return true;
2748 return false;
2751 private static void AddChecked(IDictionary attributes)
2753 attributes["checked"] = "checked";
2756 private static void RemoveChecked(IDictionary attributes)
2758 attributes.Remove("checked");
2761 private static string CreateHtmlId(string name)
2763 StringBuilder sb = new StringBuilder(name.Length);
2765 bool canUseUnderline = false;
2767 foreach(char c in name.ToCharArray())
2769 switch(c)
2771 case '.':
2772 case '[':
2773 case ']':
2774 if (canUseUnderline)
2776 sb.Append('_');
2777 canUseUnderline = false;
2779 break;
2780 default:
2781 canUseUnderline = true;
2782 sb.Append(c);
2783 break;
2788 return sb.ToString();
2791 /// <summary>
2792 /// Abstracts the approach to access values on objects.
2793 /// </summary>
2794 public abstract class ValueGetter
2796 /// <summary>
2797 /// Gets the name.
2798 /// </summary>
2799 /// <value>The name.</value>
2800 public abstract string Name { get; }
2802 /// <summary>
2803 /// Gets the value.
2804 /// </summary>
2805 /// <param name="instance">The instance.</param>
2806 /// <returns></returns>
2807 public abstract object GetValue(object instance);
2810 /// <summary>
2811 /// Implementation of <see cref="ValueGetter"/>
2812 /// that uses reflection to access values
2813 /// </summary>
2814 public class ReflectionValueGetter : ValueGetter
2816 private PropertyInfo propInfo;
2818 /// <summary>
2819 /// Initializes a new instance of the <see cref="ReflectionValueGetter"/> class.
2820 /// </summary>
2821 /// <param name="propInfo">The prop info.</param>
2822 public ReflectionValueGetter(PropertyInfo propInfo)
2824 this.propInfo = propInfo;
2827 /// <summary>
2828 /// Gets the name.
2829 /// </summary>
2830 /// <value>The name.</value>
2831 public override string Name
2833 get { return propInfo.Name; }
2836 /// <summary>
2837 /// Gets the value.
2838 /// </summary>
2839 /// <param name="instance">The instance.</param>
2840 /// <returns></returns>
2841 public override object GetValue(object instance)
2845 return propInfo.GetValue(instance, null);
2847 catch(TargetException)
2849 PropertyInfo tempProp = instance.GetType().GetProperty(Name);
2851 if (tempProp == null)
2853 throw;
2856 return tempProp.GetValue(instance, null);
2861 /// <summary>
2862 /// Implementation of <see cref="ValueGetter"/>
2863 /// that uses reflection and recusion to access values
2864 /// </summary>
2865 public class RecursiveReflectionValueGetter : ValueGetter
2867 private readonly string[] keyName;
2868 private readonly string name = string.Empty;
2870 /// <summary>
2871 /// Initializes a new instance of the <see cref="RecursiveReflectionValueGetter"/> class.
2872 /// </summary>
2873 /// <param name="targetType">The target type to query</param>
2874 /// <param name="keyName">the property path</param>
2875 public RecursiveReflectionValueGetter(Type targetType, string keyName)
2877 this.keyName = keyName.Split('.');
2878 name = QueryPropertyInfoRecursive(targetType, this.keyName).Name;
2881 /// <summary>
2882 /// Gets the name.
2883 /// </summary>
2884 /// <value>The name.</value>
2885 public override string Name
2887 get { return name; }
2890 /// <summary>
2891 /// Gets the value.
2892 /// </summary>
2893 /// <param name="instance">The instance.</param>
2894 /// <returns></returns>
2895 public override object GetValue(object instance)
2899 return QueryPropertyRecursive(instance, keyName);
2901 catch (TargetException)
2903 PropertyInfo tempProp = instance.GetType().GetProperty(Name);
2905 if (tempProp == null)
2907 throw;
2910 return tempProp.GetValue(instance, null);
2915 /// <summary>
2916 /// Implementation of <see cref="ValueGetter"/>
2917 /// to access DataRow's value
2918 /// </summary>
2919 public class DataRowValueGetter : ValueGetter
2921 private readonly string columnName;
2923 /// <summary>
2924 /// Initializes a new instance of the <see cref="DataRowValueGetter"/> class.
2925 /// </summary>
2926 /// <param name="columnName">Name of the column.</param>
2927 public DataRowValueGetter(string columnName)
2929 this.columnName = columnName;
2932 /// <summary>
2933 /// Gets the name.
2934 /// </summary>
2935 /// <value>The name.</value>
2936 public override string Name
2938 get { return columnName; }
2941 /// <summary>
2942 /// Gets the value.
2943 /// </summary>
2944 /// <param name="instance">The instance.</param>
2945 /// <returns></returns>
2946 public override object GetValue(object instance)
2948 DataRow row = (DataRow) instance;
2950 return row[columnName];
2954 /// <summary>
2955 /// Implementation of <see cref="ValueGetter"/>
2956 /// to access DataRowView's value
2957 /// </summary>
2958 public class DataRowViewValueGetter : ValueGetter
2960 private readonly string columnName;
2962 /// <summary>
2963 /// Initializes a new instance of the <see cref="DataRowViewValueGetter"/> class.
2964 /// </summary>
2965 /// <param name="columnName">Name of the column.</param>
2966 public DataRowViewValueGetter(string columnName)
2968 this.columnName = columnName;
2971 /// <summary>
2972 /// Gets the name.
2973 /// </summary>
2974 /// <value>The name.</value>
2975 public override string Name
2977 get { return columnName; }
2980 /// <summary>
2981 /// Gets the value.
2982 /// </summary>
2983 /// <param name="instance">The instance.</param>
2984 /// <returns></returns>
2985 public override object GetValue(object instance)
2987 DataRowView row = (DataRowView)instance;
2989 return row[columnName];
2993 /// <summary>
2994 /// Empty implementation of a <see cref="ValueGetter"/>
2995 /// </summary>
2996 public class NoActionGetter : ValueGetter
2998 /// <summary>
2999 /// Gets the name.
3000 /// </summary>
3001 /// <value>The name.</value>
3002 public override string Name
3004 get { return string.Empty; }
3007 /// <summary>
3008 /// Gets the value.
3009 /// </summary>
3010 /// <param name="instance">The instance.</param>
3011 /// <returns></returns>
3012 public override object GetValue(object instance)
3014 return null;
3018 /// <summary>
3019 /// Implementation of <see cref="ValueGetter"/>
3020 /// to access enum fields
3021 /// </summary>
3022 public class EnumValueGetter : ValueGetter
3024 private readonly Type enumType;
3026 /// <summary>
3027 /// Initializes a new instance of the <see cref="EnumValueGetter"/> class.
3028 /// </summary>
3029 /// <param name="enumType">Type of the enum.</param>
3030 public EnumValueGetter(Type enumType)
3032 this.enumType = enumType;
3035 /// <summary>
3036 /// Gets the name.
3037 /// </summary>
3038 /// <value>The name.</value>
3039 public override string Name
3041 get { return string.Empty; }
3044 /// <summary>
3045 /// Gets the value.
3046 /// </summary>
3047 /// <param name="instance">The instance.</param>
3048 /// <returns></returns>
3049 public override object GetValue(object instance)
3051 return Enum.Format(enumType, Enum.Parse(enumType, Convert.ToString(instance)), "d");
3055 /// <summary>
3056 /// Abstract factory for <see cref="ValueGetter"/> implementations
3057 /// </summary>
3058 public class ValueGetterAbstractFactory
3060 /// <summary>
3061 /// Creates the specified target type.
3062 /// </summary>
3063 /// <param name="targetType">Type of the target.</param>
3064 /// <param name="keyName">Name of the key.</param>
3065 /// <returns></returns>
3066 public static ValueGetter Create(Type targetType, string keyName)
3068 if (targetType == null)
3070 return new NoActionGetter();
3072 else if (targetType == typeof(DataRow))
3074 return new DataRowValueGetter(keyName);
3076 else if (targetType == typeof(DataRowView))
3078 return new DataRowViewValueGetter(keyName);
3080 else if (targetType.IsEnum)
3082 return new EnumValueGetter(targetType);
3084 else
3086 PropertyInfo info;
3088 // check for recusion
3089 if(keyName.Contains("."))
3091 info = QueryPropertyInfoRecursive(targetType, keyName.Split('.'));
3093 if (info != null)
3095 return new RecursiveReflectionValueGetter(targetType, keyName);
3098 else
3100 info = targetType.GetProperty(keyName, ResolveFlagsToUse(targetType));
3101 if (info != null)
3103 return new ReflectionValueGetter(info);
3107 return null;
3112 #endregion
3114 #region FormScopeInfo
3116 class FormScopeInfo
3118 private readonly string target;
3119 private readonly bool isValidationEnabled;
3121 /// <summary>
3122 /// Initializes a new instance of the <see cref="FormScopeInfo"/> class.
3123 /// </summary>
3124 /// <param name="target">The target.</param>
3125 /// <param name="isValidationEnabled">if set to <c>true</c> [is validation enabled].</param>
3126 public FormScopeInfo(string target, bool isValidationEnabled)
3128 this.target = target;
3129 this.isValidationEnabled = isValidationEnabled;
3132 /// <summary>
3133 /// Gets the root target.
3134 /// </summary>
3135 /// <value>The root target.</value>
3136 public string RootTarget
3138 get { return target; }
3141 /// <summary>
3142 /// Gets a value indicating whether this instance is validation enabled.
3143 /// </summary>
3144 /// <value>
3145 /// <c>true</c> if this instance is validation enabled; otherwise, <c>false</c>.
3146 /// </value>
3147 public bool IsValidationEnabled
3149 get { return isValidationEnabled; }
3153 #endregion
3155 private static BindingFlags ResolveFlagsToUse(Type type)
3157 if (type.Assembly.FullName.StartsWith("DynamicAssemblyProxyGen") || type.Assembly.FullName.StartsWith("DynamicProxyGenAssembly2"))
3159 return PropertyFlags2;
3162 return PropertyFlags;