1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Collections
.Specialized
;
23 using System
.Reflection
;
26 using HtmlTextWriter
= System
.Web
.UI
.HtmlTextWriter
;
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
;
37 /// Represents all scopes that the <see cref="FormHelper"/>
38 /// uses to search for root values
40 public enum RequestContext
43 /// All scopes should be searched
47 /// Only PropertyBag should be searched
51 /// Only Flash should be searched
55 /// Only Session should be searched
59 /// Only Request should be searched
63 /// Only Params should be searched
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.
74 /// <seealso cref="DataBindAttribute"/>
75 /// <seealso cref="Castle.Components.Common"/>
78 /// Using simple values:
79 /// <para>On the controller:</para>
82 /// public void MyAction()
84 /// PropertyBag["name"] = "John Doe";
88 /// <para>On the view (using NVelocity syntax)</para>
90 /// <code lang="none">
91 /// $Form.TextField('name') // Renders an input text with value "John Doe"
95 /// Using complex objects:
99 /// public void MyAction()
101 /// PropertyBag["user"] = new User("John Doe");
105 /// <para>On the view (using NVelocity syntax)</para>
107 /// <code lang="none">
108 /// $Form.TextField('user.name') // Renders an input text with value "John Doe"
113 /// <b>Elements generation</b> <br/>
115 /// <list type="table">
117 /// <term>Buttons</term>
119 /// <see cref="Submit(string)"/> <br/>
120 /// <see cref="Button(string)" /> <br/>
121 /// <see cref="ButtonElement(string)" />
126 /// <term>Select</term>
128 /// <see cref="Select(string,IEnumerable)" />
133 /// <term>Text area</term>
135 /// <see cref="TextArea(string)" />
140 /// <term>Hidden field</term>
142 /// <see cref="HiddenField(string)" />
147 /// <term>Checkbox field</term>
149 /// <see cref="CheckboxField(string)" /> <br/>
150 /// <see cref="CreateCheckboxList(string,IEnumerable)" />
155 /// <term>Radio field</term>
157 /// <see cref="RadioField(string,object)" />
162 /// <term>File upload</term>
164 /// <see cref="FileField(string)" />
169 /// <term>Text field</term>
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)"/>
179 /// <term>Password field</term>
181 /// <see cref="PasswordField(string)" /> <br/>
182 /// <see cref="PasswordNumberField(string)" />
187 /// <term>Labels</term>
189 /// <see cref="LabelFor(string,string)" /> <br/>
190 /// <see cref="LabelFor(string,string,IDictionary)"/>
198 /// <b>FormValidation</b> <br/>
199 /// The following operations are related to the Form Validation support:
202 /// <list type="table">
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>
208 /// <term><see cref="DisableValidation"/> </term>
209 /// <description>Disables validation altogether</description>
212 /// <term><see cref="UseWebValidatorProvider"/> </term>
213 /// <description>Sets a custom Browser validator provider</description>
216 /// <term><see cref="UsePrototypeValidation"/> </term>
217 /// <description>Configures the helper to use the prototype easy field validation. Must be invoked before FormTag</description>
220 /// <term><see cref="UsefValidate"/> </term>
221 /// <description>Configures the helper to use the fValidate. Deprecated.</description>
224 /// <term><see cref="UseZebdaValidation"/> </term>
225 /// <description>Configures the helper to use the Zebda. Must be invoked before FormTag</description>
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
237 /// For example: mask='2,5',mask_separator='/' will mask the content to '12/34/1234'
240 public class FormHelper
: AbstractHelper
, IServiceEnabledComponent
243 /// Common property flags for reflection
245 protected static readonly BindingFlags PropertyFlags
= BindingFlags
.GetProperty
|BindingFlags
.Public
|BindingFlags
.Instance
|BindingFlags
.IgnoreCase
;
247 /// Common property flags for reflection (with declared only)
249 protected static readonly BindingFlags PropertyFlags2
= BindingFlags
.GetProperty
|BindingFlags
.Public
|BindingFlags
.Instance
|BindingFlags
.IgnoreCase
|BindingFlags
.DeclaredOnly
;
251 /// Common field flags for reflection
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
;
265 protected static ILogger logger
= NullLogger
.Instance
;
267 #region IServiceEnabledComponent implementation
270 /// Invoked by the framework in order to give a chance to
271 /// obtain other services
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
;
295 /// Renders a Javascript library inside a single script tag.
297 /// <returns></returns>
298 public string InstallScripts()
300 return RenderScriptBlockToSource("/MonoRail/Files/FormHelperScript");
303 #region FormTag related
306 /// Creates a form tag based on the parameters.
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
313 /// The action attribute generation will use <see cref="UrlHelper"/>
317 /// <seealso cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>
321 /// <code lang="none">
322 /// $Form.FormTag("%{action='Save',id='productform'}")
327 /// <code lang="xml">
328 /// <form method='post' action='/[appdir]/[controller]/Save.[extension]' id='productform'>
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
338 /// <list type="table">
340 /// <term>noaction</term>
341 /// <description>boolean. Disables the generation of an action</description>
344 /// <term>method</term>
345 /// <description>string. The http method to use. Defaults to <c>post</c></description>
349 /// <description>string. The form id.</description>
353 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
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
)
363 // Creates action attribute
364 if (CommonUtils
.ObtainEntryAndRemove(parameters
, "noaction", "false") == "false")
366 url
= UrlHelper
.For(parameters
);
369 return FormTag(url
, parameters
);
373 /// Creates a form tag based on the parameters.
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
380 /// The action attribute generation will use <see cref="UrlHelper"/>
386 /// <code lang="none">
387 /// $Form.FormTag('mytarget.castle', "%{id='productform'}")
392 /// <code lang="xml">
393 /// <form method='post' action='mytarget.castle' id='productform'>
399 /// The following parameters are accepted.
401 /// <list type="table">
403 /// <term>method</term>
404 /// <description>string. The http method to use. Defaults to <c>post</c></description>
408 /// <description>string. The form id.</description>
412 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
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
) :
434 formContent
= "<form action='" + url
+ "' method='" + method
+ "' " +
435 "id='" + currentFormId
+ "' " + GetAttributes(parameters
) + ">";
439 formContent
= "<form method='" + method
+ "' id='" + currentFormId
+ "' " + GetAttributes(parameters
) + ">";
442 return formContent
+ afterFormTag
;
446 /// Generate Ajax form tag for ajax based form submission. Experimental.
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
) :
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
;
506 /// Renders an end form element.
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.
512 /// <returns></returns>
513 public string EndFormTag()
515 string beforeEndTag
= string.Empty
;
517 if (validationConfig
!= null)
519 beforeEndTag
= IsValidationEnabled
?
520 validationConfig
.CreateBeforeFormClosed(currentFormId
) :
524 return beforeEndTag
+ "</form>";
529 #region Object scope related
532 /// Pushes the specified target. Experimental.
534 /// <param name="target">The target.</param>
535 public void Push(string target
)
541 /// Pushes the specified target. Experimental.
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
);
552 objectStack
.Push(new FormScopeInfo(target
, disableValidation
!= "true"));
556 value = ObtainValue(target
+ "type");
560 objectStack
.Push(new FormScopeInfo(target
, disableValidation
!= "true"));
564 throw new ArgumentException("target could not be evaluated during Push operation. Target: " + target
);
570 /// Pops this instance. Experimental.
579 #region Submit and Button related
582 /// Generates an input submit element.
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);
592 /// Generates an input submit element.
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
);
603 /// Generates an graphical submit element.
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);
614 /// Generates an input submit element.
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
);
629 /// Generates an input button element.
631 /// <param name="value">The value.</param>
632 /// <returns>The element tag</returns>
633 public string Button(string value)
635 return Button(value, null);
639 /// Generates an input button element.
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
);
650 /// Creates a basic button element of type submit.
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);
660 /// Creates a basic button element of the specified type.
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);
671 /// Creates a basic button element of the specified type and with specified attributes.
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
);
684 #region TextFieldValue
687 /// Generates an input text form element
688 /// with the supplied value
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);
699 /// Generates an input text form element
700 /// with the supplied value
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
);
716 /// Generates an input text element.
718 /// The value is extracted from the target (if available)
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"/>
726 /// <code lang="none">
727 /// $Form.TextField('username')
730 /// <code lang="xml">
731 /// <input type='text' name='username' id='username' value='John Doe' />
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"/>
739 /// <code lang="none">
740 /// $Form.TextField('user.name')
743 /// <code lang="xml">
744 /// <input type='text' name='user.name' id='user_name' value='John Doe' />
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);
756 /// Generates an input text element.
758 /// The value is extracted from the target (if available)
762 /// <seealso cref="TextField(string)"/>
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
);
780 #region FilteredTextField
783 /// Generates an input text element with a javascript that prevents the
784 /// chars listed in the forbid attribute from being entered.
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.
792 /// FormHelper.FilteredTextField("product.price", {forbid='46'})
794 /// In this case the key code 46 (period) will not be accepted on the field.
796 /// The value is extracted from the target (if available).
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>
802 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it.
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
);
823 /// Generates an input text element with a javascript that prevents
824 /// chars other than numbers from being entered.
826 /// The value is extracted from the target (if available)
830 /// <seealso cref="InstallScripts"/>
831 /// <seealso cref="NumberField(string,IDictionary)"/>
834 /// You must include the formhelper javascript functions to use the NumberField.
835 /// See <see cref="InstallScripts"/>
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);
846 /// Generates an input text element with a javascript that prevents
847 /// chars other than numbers from being entered.
849 /// The value is extracted from the target (if available)
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>
858 /// You must include the formhelper javascript functions to use the NumberField.
859 /// See <see cref="InstallScripts"/>
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.
866 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
868 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
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.
875 /// FormHelper.NumberField("product.price", {forbid='46'})
877 /// In this case the key code 46 (period) will not be accepted on the field.
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
);
895 #region NumberFieldValue
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
903 /// <seealso cref="InstallScripts"/>
904 /// <seealso cref="NumberField(string,IDictionary)"/>
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>
911 /// You must include the formhelper javascript functions to use the NumberField.
912 /// See <see cref="InstallScripts"/>
914 public string NumberFieldValue(string target
, object value)
916 return NumberFieldValue(target
, value, null);
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
925 /// <seealso cref="InstallScripts"/>
926 /// <seealso cref="NumberField(string,IDictionary)"/>
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>
934 /// You must include the formhelper javascript functions to use the NumberField.
935 /// See <see cref="InstallScripts"/>
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
);
954 /// Generates a textarea element.
956 /// The value is extracted from the target (if available)
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);
967 /// Generates a textarea element.
969 /// The value is extracted from the target (if available)
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
);
983 /// Generates a textarea element with a specified value.
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
));
1005 #region PasswordField
1008 /// Generates a password input field.
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);
1018 /// Generates a password input field.
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
);
1036 #region PasswordNumberField
1039 /// Generates an input password element with a javascript that prevents
1040 /// chars other than numbers from being entered.
1042 /// The value is extracted from the target (if available)
1046 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
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);
1056 /// Generates an input password element with a javascript that prevents
1057 /// chars other than numbers from being entered.
1059 /// The value is extracted from the target (if available)
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.
1067 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
1069 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
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.
1076 /// FormHelper.NumberField("product.price", {forbid='46'})
1078 /// In this case the key code 46 (period) will not be accepted on the field.
1081 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
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
);
1105 /// Generates a label element.
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);
1116 /// Generates a label element.
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();
1150 /// Generates a hidden form element.
1152 /// The value is extracted from the target (if available)
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);
1167 /// Generates a hidden form element with the specified value
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);
1178 /// Generates a hidden form element.
1180 /// The value is extracted from the target (if available)
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
);
1200 /// Generates a hidden form element with the specified value
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
);
1213 #region CheckboxList
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>).
1221 /// The enumerable item will be an element of the <c>dataSource</c>.
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.
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.
1236 /// Consider the following action code:
1238 /// public void Index()
1241 /// PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
1243 /// // initial selection
1244 /// PropertyBag["selectedPrimes"] = new int[] { 11, 19 };
1248 /// And the respective view code
1250 /// <code lang="none">
1251 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1253 /// #foreach($elem in $items)
1254 /// $items.Item() $elem
1258 /// That will generates the following html:
1260 /// <code lang="none">
1261 /// <input type="checkbox" id="selectedPrimes_0_" name="selectedPrimes[0]" value="2" /> 2
1262 /// <input type="checkbox" id="selectedPrimes_1_" name="selectedPrimes[1]" value="3" /> 3
1263 /// <input type="checkbox" id="selectedPrimes_2_" name="selectedPrimes[2]" value="5" /> 5
1264 /// <input type="checkbox" id="selectedPrimes_3_" name="selectedPrimes[3]" value="7" /> 7
1265 /// <input type="checkbox" id="selectedPrimes_4_" name="selectedPrimes[4]" value="11" checked="checked" /> 11
1266 /// <input type="checkbox" id="selectedPrimes_5_" name="selectedPrimes[5]" value="13" /> 13
1267 /// <input type="checkbox" id="selectedPrimes_6_" name="selectedPrimes[6]" value="17" /> 17
1268 /// <input type="checkbox" id="selectedPrimes_7_" name="selectedPrimes[7]" value="19" checked="checked" /> 19
1269 /// <input type="checkbox" id="selectedPrimes_8_" name="selectedPrimes[8]" value="23" /> 23
1273 /// To customize the id, you can call the <see cref="CheckboxList.Item(string)"/> overload:
1276 /// <code lang="none">
1277 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1279 /// #foreach($elem in $items)
1280 /// $items.Item("myId${velocityCount}") $Form.LabelFor("myId${velocityCount}", $elem.ToString()) <br/>
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);
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>).
1299 /// The enumerable item will be an element of the <c>dataSource</c>.
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.
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.
1313 /// <seealso cref="CreateCheckboxList(string,IEnumerable)"/>
1316 /// Consider the following action code:
1318 /// public void Index()
1320 /// Category[] categories = new Category[] { new Category(1, "Music"), new Category(2, "Humor"), new Category(3, "Politics") };
1321 /// PropertyBag["categories"] = categories; // datasource
1323 /// Blog blog = new Blog();
1324 /// blog.Categories = new Category[] { new Category(2, "Humor") }; // initial selection
1325 /// PropertyBag["blog"] = blog;
1329 /// And the respective view code
1331 /// <code lang="none">
1332 /// #set($items = $Form.CreateCheckboxList("blog.categories", $categories, "%{value='Id'}"))
1334 /// #foreach($elem in $items)
1335 /// $items.Item() $elem
1339 /// That will generates the following html:
1341 /// <code lang="none">
1342 /// <input type="checkbox" id="blog_categories_0_" name="blog.categories[0].Id" value="1" /> Music
1343 /// <input type="checkbox" id="blog_categories_1_" name="blog.categories[1].Id" value="2" checked="checked" /> Humor
1344 /// <input type="checkbox" id="blog_categories_2_" name="blog.categories[2].Id" value="3" /> Politics
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
);
1363 /// Outputs a checkbox element (for internal use)
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
);
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
);
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.
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;
1412 /// Initializes a new instance of the <see cref="CheckboxList"/> class.
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();
1433 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1435 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1437 /// <returns>The generated input element</returns>
1438 public string Item()
1444 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1446 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1448 /// <param name="id">The element id</param>
1449 /// <returns>The generated input element</returns>
1450 public string Item(string id
)
1454 throw new InvalidOperationException("Before rendering a checkbox item, you must use MoveNext");
1459 // Nothing to render
1460 return String
.Empty
;
1465 attributes
["id"] = id
;
1468 return helper
.CheckboxItem(index
, target
, operationState
.TargetSuffix
, CurrentSetItem
, attributes
);
1472 /// Returns an enumerator that iterates through a collection.
1475 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
1477 public IEnumerator
GetEnumerator()
1483 /// Advances the enumerator to the next element of the collection.
1486 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
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
++;
1500 /// Sets the enumerator to its initial position, which is before the first element in the collection.
1502 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1510 /// Gets the current element in the collection.
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; }
1521 /// Gets the current set item.
1523 /// <value>The current set item.</value>
1524 public SetItem CurrentSetItem
1526 get { return enumerator.Current as SetItem; }
1532 #region CheckboxField
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.
1542 /// Consider the following view code:
1544 /// <code lang="none">
1545 /// $Form.CheckboxField('user.disabled')
1548 /// That is going to output:
1550 /// <code lang="none">
1551 /// <input type="checkbox" id="user_disabled" name="user.disabled" value="true" />
1552 /// <input type="hidden" id="user_disabledH" name="user.disabled" value="false" />
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);
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.
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.
1579 /// <seealso cref="CheckboxField(string)"/>
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");
1594 if (trueValue
!= "true")
1596 isChecked
= AreEqual(value, trueValue
);
1600 isChecked
= ((value != null && value is bool && ((bool)value)) ||
1601 (!(value is bool) && (value != null)));
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);
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>.
1639 /// Consider the following action code:
1642 /// public void Index()
1644 /// PropertyBag["mode"] = FileMode.Truncate;
1648 /// And the following view code:
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
1659 /// That is going to output:
1661 /// <code lang="none">
1662 /// <input type="radio" id="mode" name="mode" value="Append" /> FileMode.Append
1663 /// <input type="radio" id="mode" name="mode" value="Create" /> FileMode.Create
1664 /// <input type="radio" id="mode" name="mode" value="CreateNew" /> FileMode.CreateNew
1665 /// <input type="radio" id="mode" name="mode" value="Open" /> FileMode.Open
1666 /// <input type="radio" id="customhtmlid" name="mode" value="OpenOrCreate" /> FileMode.OpenOrCreate
1667 /// <input type="radio" id="mode" name="mode" value="Truncate" checked="checked" /> FileMode.Truncate
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);
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>.
1687 /// <seealso cref="RadioField(string,object)"/>
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);
1703 if (attributes
== null)
1705 attributes
= new HybridDictionary(true);
1708 AddChecked(attributes
);
1711 return CreateInputElement("radio", target
, valueToSend
, attributes
);
1719 /// Generates an input file element.
1721 /// Dirrently than other operations exposed by this helper,
1722 /// no value is extracted for this operation
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);
1733 /// Generates an input file element.
1735 /// Dirrently than other operations exposed by this helper,
1736 /// no value is extracted for this operation
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
);
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.
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'.
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.
1773 /// The target can also be a set. In this case the intersection will be
1774 /// the initially selected elements.
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);
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.
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'.
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.
1803 /// The target can also be a set. In this case the intersection will be
1804 /// the initially selected elements.
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
);
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.
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'.
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.
1838 /// The target can also be a set. In this case the intersection will be
1839 /// the initially selected elements.
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
);
1853 /// Generates the select.
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
);
1898 writer
.Write(GetAttributes(attributes
));
1899 writer
.Write(HtmlTextWriter
.TagRightChar
);
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");
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");
1928 writer
.WriteEndTag("select");
1930 return sbWriter
.ToString();
1938 /// Creates a field set element with a legend using the specified name.
1940 /// <param name="name">The name.</param>
1941 /// <returns></returns>
1942 public string FieldSet(string name
)
1944 return "<fieldset><legend>" + name
+ "</legend>";
1948 /// Creates an element to close a fieldset element.
1950 /// <returns></returns>
1951 public string EndFieldSet()
1953 return "</fieldset>";
1961 /// Creates a list of pairs for the enum type.
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>>();
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();
1990 /// Configures this FormHelper instance to use the supplied
1991 /// web validator to generate field validation.
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
;
2002 /// Configures this FormHelper instance to use Prototype for form fields validation
2004 public void UsePrototypeValidation()
2006 UseWebValidatorProvider(new PrototypeWebValidator());
2010 /// Configures this FormHelper instance to use fValidate for form fields validation
2012 public void UsefValidate()
2014 UseWebValidatorProvider(new FValidateWebValidator());
2018 /// Configures this FormHelper instance to use Zebda for form fields validation
2020 public void UseZebdaValidation()
2022 UseWebValidatorProvider(new ZebdaWebValidator());
2026 /// Disables the validation.
2028 public void DisableValidation()
2030 isValidationDisabled
= true;
2034 /// Applies the validation.
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
)
2048 if (Controller
.Validator
== null || validationConfig
== null)
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
;
2097 #region protected members
2100 /// Rewrites the target if within object scope.
2102 /// <param name="target">The target.</param>
2103 /// <returns></returns>
2104 protected string RewriteTargetIfWithinObjectScope(string target
)
2106 if (objectStack
.Count
== 0)
2112 return ((FormScopeInfo
) objectStack
.Peek()).RootTarget
+ "." + target
;
2117 /// Creates the specified input element
2118 /// using the specified parameters to supply the name, value, id and others
2119 /// html attributes.
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
)
2130 value = CommonUtils
.ObtainEntryAndRemove(attributes
, "defaultValue");
2133 string id
= CreateHtmlId(attributes
, target
);
2135 return CreateInputElement(type
, id
, target
, FormatIfNecessary(value, attributes
), attributes
);
2139 /// Creates the specified input element
2140 /// using the specified parameters to supply the name, value, id and others
2141 /// html attributes.
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
));
2174 /// Creates the input element.
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
));
2187 /// Formats if necessary.
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();
2214 /// Obtains the target property.
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
)
2224 Type root
= ObtainRootType(context
, target
, out pieces
);
2226 if (root
!= null && pieces
.Length
> 1)
2228 return QueryPropertyInfoRecursive(root
, pieces
, 1, action
);
2235 /// Queries the context for the target value
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
);
2245 /// Queries the context for the target value
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
)
2254 object rootInstance
= ObtainRootInstance(context
, target
, out pieces
);
2256 if (rootInstance
!= null && pieces
.Length
> 1)
2258 return QueryPropertyRecursive(rootInstance
, pieces
, 1);
2261 return rootInstance
;
2265 /// Obtains the root instance.
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
;
2299 /// Obtains the root instance.
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];
2313 bool isIndexed
= CheckForExistenceAndExtractIndex(ref root
, out index
);
2315 object rootInstance
= ObtainRootInstance(context
, root
);
2317 if (rootInstance
== null)
2324 AssertIsValidArray(rootInstance
, root
, index
);
2327 if (!isIndexed
&& pieces
.Length
== 1)
2329 return rootInstance
;
2333 rootInstance
= GetArrayElement(rootInstance
, index
);
2336 return rootInstance
;
2340 /// Obtains the type of the root.
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
);
2358 foundType
= root
.GetType();
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
);
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
);
2402 action(propertyInfo
);
2405 type
= propertyInfo
.PropertyType
;
2407 if (typeof(ICollection
).IsAssignableFrom(type
))
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
);
2424 type
= type
.GetElementType();
2428 if (piece
+ 1 == propertyPath
.Length
)
2430 return propertyInfo
;
2433 return QueryPropertyInfoRecursive(type
, propertyPath
, piece
+ 1, action
);
2437 /// Query property paths agains the rootInstance type
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);
2448 /// Query property paths agains the rootInstance type
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
);
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
)
2504 return QueryPropertyRecursive(instance
, propertyPath
, piece
+ 1);
2508 /// Creates the HTML id.
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);
2519 /// Creates the HTML id.
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
)
2531 id
= CommonUtils
.ObtainEntryAndRemove(attributes
, "id");
2535 id
= CommonUtils
.ObtainEntry(attributes
, "id");
2540 id
= CreateHtmlId(target
);
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
);
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
)
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
)
2634 private static bool CheckForExistenceAndExtractIndex(ref string property
, out int index
)
2636 bool isIndexed
= property
.IndexOf('[') != -1;
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
);
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);
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
));
2692 return left
.ToString().Equals(right
.ToString());
2695 private string SafeHtmlEncode(string content
)
2697 if (Controller
.Context
!= null)
2699 return HtmlEncode(content
);
2706 /// Determines whether the present value matches the value on
2707 /// the initialSetValue (which can be a single value or a set)
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
)
2719 object valueToCompare
= initialSetValue
;
2721 if (propertyOnInitialSet
!= null)
2723 // propertyOnInitialSet.GetValue(initialSetValue, null);
2724 valueToCompare
= propertyOnInitialSet
.GetValue(initialSetValue
);
2727 return AreEqual(value, valueToCompare
);
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
))
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())
2774 if (canUseUnderline
)
2777 canUseUnderline
= false;
2781 canUseUnderline
= true;
2788 return sb
.ToString();
2792 /// Abstracts the approach to access values on objects.
2794 public abstract class ValueGetter
2799 /// <value>The name.</value>
2800 public abstract string Name { get; }
2805 /// <param name="instance">The instance.</param>
2806 /// <returns></returns>
2807 public abstract object GetValue(object instance
);
2811 /// Implementation of <see cref="ValueGetter"/>
2812 /// that uses reflection to access values
2814 public class ReflectionValueGetter
: ValueGetter
2816 private PropertyInfo propInfo
;
2819 /// Initializes a new instance of the <see cref="ReflectionValueGetter"/> class.
2821 /// <param name="propInfo">The prop info.</param>
2822 public ReflectionValueGetter(PropertyInfo propInfo
)
2824 this.propInfo
= propInfo
;
2830 /// <value>The name.</value>
2831 public override string Name
2833 get { return propInfo.Name; }
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)
2856 return tempProp
.GetValue(instance
, null);
2862 /// Implementation of <see cref="ValueGetter"/>
2863 /// that uses reflection and recusion to access values
2865 public class RecursiveReflectionValueGetter
: ValueGetter
2867 private readonly string[] keyName
;
2868 private readonly string name
= string.Empty
;
2871 /// Initializes a new instance of the <see cref="RecursiveReflectionValueGetter"/> class.
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
;
2884 /// <value>The name.</value>
2885 public override string Name
2887 get { return name; }
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)
2910 return tempProp
.GetValue(instance
, null);
2916 /// Implementation of <see cref="ValueGetter"/>
2917 /// to access DataRow's value
2919 public class DataRowValueGetter
: ValueGetter
2921 private readonly string columnName
;
2924 /// Initializes a new instance of the <see cref="DataRowValueGetter"/> class.
2926 /// <param name="columnName">Name of the column.</param>
2927 public DataRowValueGetter(string columnName
)
2929 this.columnName
= columnName
;
2935 /// <value>The name.</value>
2936 public override string Name
2938 get { return columnName; }
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
];
2955 /// Implementation of <see cref="ValueGetter"/>
2956 /// to access DataRowView's value
2958 public class DataRowViewValueGetter
: ValueGetter
2960 private readonly string columnName
;
2963 /// Initializes a new instance of the <see cref="DataRowViewValueGetter"/> class.
2965 /// <param name="columnName">Name of the column.</param>
2966 public DataRowViewValueGetter(string columnName
)
2968 this.columnName
= columnName
;
2974 /// <value>The name.</value>
2975 public override string Name
2977 get { return columnName; }
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
];
2994 /// Empty implementation of a <see cref="ValueGetter"/>
2996 public class NoActionGetter
: ValueGetter
3001 /// <value>The name.</value>
3002 public override string Name
3004 get { return string.Empty; }
3010 /// <param name="instance">The instance.</param>
3011 /// <returns></returns>
3012 public override object GetValue(object instance
)
3019 /// Implementation of <see cref="ValueGetter"/>
3020 /// to access enum fields
3022 public class EnumValueGetter
: ValueGetter
3024 private readonly Type enumType
;
3027 /// Initializes a new instance of the <see cref="EnumValueGetter"/> class.
3029 /// <param name="enumType">Type of the enum.</param>
3030 public EnumValueGetter(Type enumType
)
3032 this.enumType
= enumType
;
3038 /// <value>The name.</value>
3039 public override string Name
3041 get { return string.Empty; }
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");
3056 /// Abstract factory for <see cref="ValueGetter"/> implementations
3058 public class ValueGetterAbstractFactory
3061 /// Creates the specified target type.
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
);
3088 // check for recusion
3089 if(keyName
.Contains("."))
3091 info
= QueryPropertyInfoRecursive(targetType
, keyName
.Split('.'));
3095 return new RecursiveReflectionValueGetter(targetType
, keyName
);
3100 info
= targetType
.GetProperty(keyName
, ResolveFlagsToUse(targetType
));
3103 return new ReflectionValueGetter(info
);
3114 #region FormScopeInfo
3118 private readonly string target
;
3119 private readonly bool isValidationEnabled
;
3122 /// Initializes a new instance of the <see cref="FormScopeInfo"/> class.
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
;
3133 /// Gets the root target.
3135 /// <value>The root target.</value>
3136 public string RootTarget
3138 get { return target; }
3142 /// Gets a value indicating whether this instance is validation enabled.
3145 /// <c>true</c> if this instance is validation enabled; otherwise, <c>false</c>.
3147 public bool IsValidationEnabled
3149 get { return isValidationEnabled; }
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
;