1 // Copyright 2004-2008 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
;
27 using HtmlTextWriter
= System
.Web
.UI
.HtmlTextWriter
;
30 using Castle
.Core
.Logging
;
31 using Castle
.MonoRail
.Framework
;
32 using Castle
.MonoRail
.Framework
.Helpers
.ValidationStrategy
;
33 using Castle
.MonoRail
.Framework
.Internal
;
34 using Castle
.Components
.Binder
;
35 using Castle
.Components
.Validator
;
38 /// Represents all scopes that the <see cref="FormHelper"/>
39 /// uses to search for root values
41 public enum RequestContext
44 /// All scopes should be searched
48 /// Only PropertyBag should be searched
52 /// Only Flash should be searched
56 /// Only Session should be searched
60 /// Only Request should be searched
64 /// Only Params should be searched
70 /// The FormHelper allows you to output html input elements using the
71 /// conventions necessary to use the DataBinder on the server side. Ultimately it
72 /// allows you do to a bi-directional binding -- if used properly.
75 /// <seealso xref="DataBindAttribute"/>
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 BrowserValidationConfiguration validationConfig
;
260 private IBrowserValidatorProvider validatorProvider
= new PrototypeWebValidator();
261 private IValidatorRegistry validatorRegistry
;
262 private ValidatorRunner validatorRunner
;
266 /// Initializes a new instance of the <see cref="FormHelper"/> class.
270 validatorRegistry
= new CachedValidationRegistry();
271 validatorRunner
= new ValidatorRunner(false, validatorRegistry
);
275 /// Initializes a new instance of the <see cref="FormHelper"/> class.
276 /// setting the Controller, Context and ControllerContext.
278 /// <param name="engineContext">The engine context.</param>
279 public FormHelper(IEngineContext engineContext
) : base(engineContext
)
281 validatorRegistry
= new CachedValidationRegistry();
282 validatorRunner
= new ValidatorRunner(false, validatorRegistry
);
290 protected static ILogger logger
= NullLogger
.Instance
;
292 #region IServiceEnabledComponent implementation
295 /// Invoked by the framework in order to give a chance to
296 /// obtain other services
298 /// <param name="provider">The service proviver</param>
299 public virtual void Service(IServiceProvider provider
)
301 ILoggerFactory loggerFactory
= (ILoggerFactory
) provider
.GetService(typeof(ILoggerFactory
));
303 if (loggerFactory
!= null)
305 logger
= loggerFactory
.Create(typeof(FormHelper
));
308 IMonoRailConfiguration config
= (IMonoRailConfiguration
) provider
.GetService(typeof(IMonoRailConfiguration
));
312 LibraryConfiguration jsLibConfig
= config
.JSGeneratorConfiguration
.DefaultLibrary
;
314 if (jsLibConfig
!= null)
316 if (jsLibConfig
.BrowserValidatorProvider
!= null)
318 validatorProvider
= (IBrowserValidatorProvider
)
319 Activator
.CreateInstance(jsLibConfig
.BrowserValidatorProvider
);
324 validatorRegistry
= (IValidatorRegistry
) provider
.GetService(typeof(IValidatorRegistry
));
326 if (validatorRegistry
!= null)
328 validatorRunner
= new ValidatorRunner(false, validatorRegistry
);
332 validatorRunner
= new ValidatorRunner(false, new CachedValidationRegistry());
339 /// Gets or sets the validator provider.
341 /// <value>The validator provider.</value>
342 public IBrowserValidatorProvider ValidatorProvider
344 get { return validatorProvider; }
345 set { validatorProvider = value; }
349 /// Gets or sets the validator runner.
351 /// <value>The validator runner.</value>
352 public ValidatorRunner ValidatorRunner
354 get { return validatorRunner; }
355 set { validatorRunner = value; }
359 /// Renders a Javascript library inside a single script tag.
361 /// <returns></returns>
362 public virtual string InstallScripts()
364 return RenderScriptBlockToSource("/MonoRail/Files/FormHelperScript");
367 #region FormTag related
370 /// Creates a form tag based on the parameters.
372 /// Javascript validation can also be bound to
373 /// the form and|or elements nested as long as the helper is
374 /// able to reach the <see cref="Type"/> of the object used on your view code
377 /// The action attribute generation will use <see cref="UrlHelper"/>
381 /// <seealso cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>
385 /// <code lang="none">
386 /// $Form.FormTag("%{action='Save',id='productform'}")
391 /// <code lang="xml">
392 /// <form method='post' action='/[appdir]/[controller]/Save.[extension]' id='productform'>
398 /// The parameters are used to build a url and also to form the tag. For notes on the url
399 /// see <see cref="DefaultUrlBuilder.BuildUrl(UrlInfo,IDictionary)"/>. The other parameters supported
402 /// <list type="table">
404 /// <term>noaction</term>
405 /// <description>boolean. Disables the generation of an action</description>
408 /// <term>method</term>
409 /// <description>string. The http method to use. Defaults to <c>post</c></description>
413 /// <description>string. The form id.</description>
417 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
421 /// <param name="parameters">The parameters for the tag or for action and form validation generation.</param>
422 /// <returns></returns>
423 public virtual string FormTag(IDictionary parameters
)
427 // Creates action attribute
428 if (CommonUtils
.ObtainEntryAndRemove(parameters
, "noaction", "false") == "false")
430 url
= UrlHelper
.For(parameters
);
433 return FormTag(url
, parameters
);
437 /// Creates a form tag based on the parameters.
439 /// Javascript validation can also be bound to
440 /// the form and|or elements nested as long as the helper is
441 /// able to reach the <see cref="Type"/> of the object used on your view code
444 /// The action attribute generation will use <see cref="UrlHelper"/>
450 /// <code lang="none">
451 /// $Form.FormTag('mytarget.castle', "%{id='productform'}")
456 /// <code lang="xml">
457 /// <form method='post' action='mytarget.castle' id='productform'>
463 /// The following parameters are accepted.
465 /// <list type="table">
467 /// <term>method</term>
468 /// <description>string. The http method to use. Defaults to <c>post</c></description>
472 /// <description>string. The form id.</description>
476 /// More parameters can be accepted depending on the form validation strategy you are using (if any).
480 /// <param name="url">The hardcoded url.</param>
481 /// <param name="parameters">The parameters for the tag or for action and form validation generation.</param>
482 /// <returns></returns>
483 public virtual string FormTag(string url
, IDictionary parameters
)
485 string method
= CommonUtils
.ObtainEntryAndRemove(parameters
, "method", "post");
486 currentFormId
= CommonUtils
.ObtainEntryAndRemove(parameters
, "id", "form" + ++formCount
);
488 validationConfig
= validatorProvider
.CreateConfiguration(parameters
);
490 string afterFormTag
= IsValidationEnabled
?
491 validationConfig
.CreateAfterFormOpened(currentFormId
) :
498 formContent
= "<form action='" + url
+ "' method='" + method
+ "' " +
499 "id='" + currentFormId
+ "' " + GetAttributes(parameters
) + ">";
503 formContent
= "<form method='" + method
+ "' id='" + currentFormId
+ "' " + GetAttributes(parameters
) + ">";
506 return formContent
+ afterFormTag
;
510 /// Generate Ajax form tag for ajax based form submission. Experimental.
512 /// <param name="parameters"></param>
513 /// <returns></returns>
514 public virtual string AjaxFormTag(IDictionary parameters
)
516 currentFormId
= CommonUtils
.ObtainEntryAndRemove(parameters
, "id", "form" + ++formCount
);
518 validationConfig
= validatorProvider
.CreateConfiguration(parameters
);
520 string afterFormTag
= IsValidationEnabled
?
521 validationConfig
.CreateAfterFormOpened(currentFormId
) :
524 string url
= UrlHelper
.For(parameters
);
526 parameters
["form"] = true;
528 if (parameters
.Contains("onsubmit"))
530 string onSubmitFunc
= CommonUtils
.ObtainEntryAndRemove(parameters
, "onsubmit");
531 //remove return to make it compatible for ajax condition
532 if (onSubmitFunc
.StartsWith("return ", StringComparison
.InvariantCultureIgnoreCase
))
534 onSubmitFunc
= onSubmitFunc
.Substring(7);
536 if (onSubmitFunc
.EndsWith(";", StringComparison
.InvariantCultureIgnoreCase
))
538 onSubmitFunc
= onSubmitFunc
.Remove(onSubmitFunc
.Length
- 1);
540 string conditionFunc
= CommonUtils
.ObtainEntryAndRemove(parameters
, "condition", string.Empty
);
541 if (!string.IsNullOrEmpty(conditionFunc
))
543 conditionFunc
+= " && ";
545 conditionFunc
+= onSubmitFunc
;
547 parameters
["condition"] = conditionFunc
;
549 bool isMethodAssigned
= parameters
.Contains("method");
551 string method
= CommonUtils
.ObtainEntryAndRemove(parameters
, "method", "post");
553 parameters
["url"] = url
;
555 // reassign method so in case if there is no value the default is assigned.
557 if (isMethodAssigned
)
559 parameters
["method"] = method
;
562 String remoteFunc
= new AjaxHelper().RemoteFunction(parameters
);
564 string formContent
= String
.Format("<form id='{1}' method='{2}' {3} onsubmit=\"{0}; return false;\" enctype=\"multipart/form-data\">", remoteFunc
, currentFormId
, method
,GetAttributes(parameters
));
566 return formContent
+ afterFormTag
;
570 /// Renders an end form element.
573 /// Should be used if you are using form validation. Some validation approaches
574 /// uses the end form before or after appending a javascript snippet.
576 /// <returns></returns>
577 public virtual string EndFormTag()
579 string beforeEndTag
= string.Empty
;
581 if (validationConfig
!= null)
583 beforeEndTag
= IsValidationEnabled
?
584 validationConfig
.CreateBeforeFormClosed(currentFormId
) :
588 return beforeEndTag
+ "</form>";
593 #region Object scope related
596 /// Pushes the specified target. Experimental.
598 /// <param name="target">The target.</param>
599 public virtual void Push(string target
)
605 /// Pushes the specified target. Experimental.
607 /// <param name="target">The target.</param>
608 /// <param name="parameters">The parameters.</param>
609 public virtual void Push(string target
, IDictionary parameters
)
611 string disableValidation
= CommonUtils
.ObtainEntryAndRemove(parameters
, "disablevalidation", "false");
612 object value = ObtainValue(target
);
616 objectStack
.Push(new FormScopeInfo(target
, disableValidation
!= "true"));
620 value = ObtainValue(target
+ "type");
624 objectStack
.Push(new FormScopeInfo(target
, disableValidation
!= "true"));
628 throw new ArgumentException("target could not be evaluated during Push operation. Target: " + target
);
634 /// Pops this instance. Experimental.
636 public virtual void Pop()
643 #region Submit and Button related
646 /// Generates an input submit element.
648 /// <param name="value">The value/caption for the button.</param>
649 /// <returns>The element tag</returns>
650 public virtual string Submit(string value)
652 return Submit(value, null);
656 /// Generates an input submit element.
658 /// <param name="value">The value/caption for the button.</param>
659 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
660 /// <returns>The element tag</returns>
661 public virtual string Submit(string value, IDictionary attributes
)
663 return CreateInputElement("submit", value, attributes
);
667 /// Generates an graphical submit element.
669 /// <param name="imgsrc">The path the image file.</param>
670 /// <param name="alttext">The alt text displayed by screenreaders, or when images are not enabled.</param>
671 /// <returns>The element tag</returns>
672 public virtual string ImageSubmit(string imgsrc
, string alttext
)
674 return ImageSubmit(imgsrc
, alttext
, null);
678 /// Generates an input submit element.
680 /// <param name="imgsrc">The path the image file.</param>
681 /// <param name="alttext">The alt text displayed by screenreaders, or when images are not enabled.</param>
682 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
683 /// <returns>The element tag</returns>
684 public virtual string ImageSubmit(string imgsrc
, string alttext
, IDictionary attributes
)
686 attributes
= attributes
!= null ? attributes
: new Hashtable();
687 attributes
["src"] = imgsrc
;
688 attributes
["alt"] = alttext
;
689 return CreateInputElement("image", alttext
, attributes
);
693 /// Generates an input button element.
695 /// <param name="value">The value.</param>
696 /// <returns>The element tag</returns>
697 public virtual string Button(string value)
699 return Button(value, null);
703 /// Generates an input button element.
705 /// <param name="value">The value.</param>
706 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
707 /// <returns>The element tag</returns>
708 public virtual string Button(string value, IDictionary attributes
)
710 return CreateInputElement("button", value, attributes
);
714 /// Creates a basic button element of type submit.
716 /// <param name="innerText">The inner text of the button element.</param>
717 /// <returns>The generated button element.</returns>
718 public virtual string ButtonElement(string innerText
)
720 return ButtonElement(innerText
, "submit", null);
724 /// Creates a basic button element of the specified type.
726 /// <param name="innerText">The inner text of the button element.</param>
727 /// <param name="type">The type of the button.</param>
728 /// <returns>The generated button element.</returns>
729 public virtual string ButtonElement(string innerText
, string type
)
731 return ButtonElement(innerText
, type
, null);
735 /// Creates a basic button element of the specified type and with specified attributes.
737 /// <param name="innerText">The inner text of the button element.</param>
738 /// <param name="type">The type of the button.</param>
739 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
740 /// <returns>The generated button element.</returns>
741 public virtual string ButtonElement(string innerText
, string type
, IDictionary attributes
)
743 return String
.Format("<button type=\"{0}\" {1}>{2}</button>", type
, GetAttributes(attributes
), innerText
);
748 #region TextFieldValue
751 /// Generates an input text form element
752 /// with the supplied value
754 /// <param name="target">The string to be used to create the element name.</param>
755 /// <param name="value">Value to supply to the element (instead of querying the target)</param>
756 /// <returns>The generated form element</returns>
757 public string TextFieldValue(string target
, object value)
759 return TextFieldValue(target
, value, null);
763 /// Generates an input text form element
764 /// with the supplied value
766 /// <param name="target">The string to be used to create the element name.</param>
767 /// <param name="value">Value to supply to the element (instead of querying the target)</param>
768 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
769 /// <returns>The generated form element</returns>
770 public string TextFieldValue(string target
, object value, IDictionary attributes
)
772 return CreateInputElement("text", target
, value, attributes
);
780 /// Generates an input text element.
782 /// The value is extracted from the target (if available)
787 /// The following example assumes that an entry <c>username</c> exists on the
788 /// <see cref="Controller.PropertyBag"/> or <see cref="Controller.Flash"/> or <see cref="Controller.Session"/>
790 /// <code lang="none">
791 /// $Form.TextField('username')
794 /// <code lang="xml">
795 /// <input type='text' name='username' id='username' value='John Doe' />
799 /// The following example assumes that an entry <c>user</c> exists on the
800 /// <see cref="Controller.PropertyBag"/> or <see cref="Controller.Flash"/> or <see cref="Controller.Session"/>
803 /// <code lang="none">
804 /// $Form.TextField('user.name')
807 /// <code lang="xml">
808 /// <input type='text' name='user.name' id='user_name' value='John Doe' />
812 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
813 /// <returns>The generated form element</returns>
814 public virtual string TextField(string target
)
816 return TextField(target
, null);
820 /// Generates an input text element.
822 /// The value is extracted from the target (if available)
826 /// <seealso cref="TextField(string)"/>
828 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
829 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
830 /// <returns>The generated form element</returns>
831 public virtual string TextField(string target
, IDictionary attributes
)
833 target
= RewriteTargetIfWithinObjectScope(target
);
835 object value = ObtainValue(target
);
837 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
839 return CreateInputElement("text", target
, value, attributes
);
843 /// Combines <see cref="AjaxHelper.InputTextWithAutoCompletion(string,string,IDictionary,IDictionary)"/>
844 /// with <see cref="TextField(string,IDictionary)"/> to achieve an TextField that offers
845 /// both input completion and databinding support.
847 /// <param name="target">The target of the text field (analogous to <see cref="TextField(string,IDictionary)"/>)</param>
848 /// <param name="url">The url to call for completion options (analogous to <see cref="AjaxHelper.InputTextWithAutoCompletion(string,string,IDictionary,IDictionary)"/>)</param>
849 /// <param name="tagAttributes">The attributes to apply to the text input field. If this dictionary does not contain a value for browser-based autocompletion (autocomplete="off|on"), it will default to "off" to prevent interference of AJAX-based and browser-based autocompletion.</param>
850 /// <param name="completionOptions">The options for the AJAX-call analogous to <see cref="AjaxHelper.InputTextWithAutoCompletion(string,string,IDictionary,IDictionary)"/>.</param>
851 /// <returns>The generated form elements.</returns>
852 /// <remarks>Please note that this function requires Ajax scripts (Prototype/Scriptaculous)</remarks>
853 public virtual string TextFieldAutoComplete(string target
, string url
, IDictionary tagAttributes
, IDictionary completionOptions
)
855 if (!tagAttributes
.Contains("autocomplete")) tagAttributes
.Add("autocomplete", "off");
857 StringBuilder sb
= new StringBuilder();
858 sb
.Append(TextField(target
, tagAttributes
));
860 string textFieldId
= CreateHtmlId(tagAttributes
, target
);
861 sb
.AppendFormat("<div id=\"{0}\" class=\"auto_complete\"></div>", textFieldId
+ "autocomplete");
862 sb
.Append(new AjaxHelper().AutoCompleteInputText(textFieldId
, url
, completionOptions
));
864 return sb
.ToString();
870 #region FilteredTextField
873 /// Generates an input text element with a javascript that prevents the
874 /// chars listed in the forbid attribute from being entered.
877 /// You must pass an <c>forbid</c> value through the dictionary.
878 /// It must be a comma separated list of chars that cannot be accepted on the field.
882 /// FormHelper.FilteredTextField("product.price", {forbid='46'})
884 /// In this case the key code 46 (period) will not be accepted on the field.
886 /// The value is extracted from the target (if available).
888 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
889 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
890 /// <returns>The generated form element.</returns>
892 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it.
894 public virtual string FilteredTextField(string target
, IDictionary attributes
)
896 target
= RewriteTargetIfWithinObjectScope(target
);
898 object value = ObtainValue(target
);
900 attributes
= attributes
!= null ? attributes
: new Hashtable();
902 ApplyFilterOptions(attributes
);
903 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
905 return CreateInputElement("text", target
, value, attributes
);
913 /// Generates an input text element with a javascript that prevents
914 /// chars other than numbers from being entered.
916 /// The value is extracted from the target (if available)
920 /// <seealso cref="InstallScripts"/>
921 /// <seealso cref="NumberField(string,IDictionary)"/>
924 /// You must include the formhelper javascript functions to use the NumberField.
925 /// See <see cref="InstallScripts"/>
928 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
929 /// <returns>The generated form element</returns>
930 public virtual string NumberField(string target
)
932 return NumberField(target
, null);
936 /// Generates an input text element with a javascript that prevents
937 /// chars other than numbers from being entered.
939 /// The value is extracted from the target (if available)
943 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
944 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
945 /// <returns>The generated form element</returns>
948 /// You must include the formhelper javascript functions to use the NumberField.
949 /// See <see cref="InstallScripts"/>
951 /// You can optionally pass an <c>exceptions</c> value through the dictionary.
952 /// It must be a comma separated list of chars that can be accepted on the field.
956 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
958 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
960 /// You can aslo optionally pass an <c>forbid</c> value through the dictionary.
961 /// It must be a comma separated list of chars that cannot be accepted on the field.
965 /// FormHelper.NumberField("product.price", {forbid='46'})
967 /// In this case the key code 46 (period) will not be accepted on the field.
969 public virtual string NumberField(string target
, IDictionary attributes
)
971 target
= RewriteTargetIfWithinObjectScope(target
);
973 object value = ObtainValue(target
);
975 attributes
= attributes
!= null ? attributes
: new Hashtable();
977 ApplyNumberOnlyOptions(attributes
);
978 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
980 return CreateInputElement("text", target
, value, attributes
);
985 #region NumberFieldValue
988 /// Generates an input text element with a javascript that prevents
989 /// chars other than numbers from being entered. The value is not gathered
990 /// from the context, instead you specify it on the second argument
993 /// <seealso cref="InstallScripts"/>
994 /// <seealso cref="NumberField(string,IDictionary)"/>
996 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
997 /// <param name="value">The current value to output.</param>
998 /// <returns>The generated form element</returns>
1001 /// You must include the formhelper javascript functions to use the NumberField.
1002 /// See <see cref="InstallScripts"/>
1004 public virtual string NumberFieldValue(string target
, object value)
1006 return NumberFieldValue(target
, value, null);
1010 /// Generates an input text element with a javascript that prevents
1011 /// chars other than numbers from being entered. The value is not gathered
1012 /// from the context, instead you specify it on the second argument
1015 /// <seealso cref="InstallScripts"/>
1016 /// <seealso cref="NumberField(string,IDictionary)"/>
1018 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1019 /// <param name="value">The current value to output.</param>
1020 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1021 /// <returns>The generated form element</returns>
1024 /// You must include the formhelper javascript functions to use the NumberField.
1025 /// See <see cref="InstallScripts"/>
1027 public virtual string NumberFieldValue(string target
, object value, IDictionary attributes
)
1029 target
= RewriteTargetIfWithinObjectScope(target
);
1031 attributes
= attributes
?? new Hashtable();
1033 ApplyNumberOnlyOptions(attributes
);
1034 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
1036 return CreateInputElement("text", target
, value, attributes
);
1044 /// Generates a textarea element.
1046 /// The value is extracted from the target (if available)
1049 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1050 /// <returns>The generated form element</returns>
1051 public virtual string TextArea(string target
)
1053 return TextArea(target
, null);
1057 /// Generates a textarea element.
1059 /// The value is extracted from the target (if available)
1062 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1063 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1064 /// <returns>The generated form element</returns>
1065 public virtual string TextArea(string target
, IDictionary attributes
)
1067 string targetForValue
= RewriteTargetIfWithinObjectScope(target
);
1068 object value = ObtainValue(targetForValue
);
1069 return TextAreaValue(target
, value, attributes
);
1073 /// Generates a textarea element with a specified value.
1075 /// <param name="target">The target to base the element name on.</param>
1076 /// <param name="value">The value to apply to the field.</param>
1077 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1078 /// <returns>The generated form element</returns>
1079 public virtual string TextAreaValue(string target
, object value, IDictionary attributes
)
1081 target
= RewriteTargetIfWithinObjectScope(target
);
1083 value = value == null ? "" : HtmlEncode(value.ToString());
1085 string id
= CreateHtmlId(target
);
1087 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
1089 return String
.Format("<textarea id=\"{0}\" name=\"{1}\" {2}>{3}</textarea>",
1090 id
, target
, GetAttributes(attributes
), FormatIfNecessary(value, attributes
));
1095 #region PasswordField
1098 /// Generates a password input field.
1100 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1101 /// <returns>The generated form element</returns>
1102 public virtual string PasswordField(string target
)
1104 return PasswordField(target
, null);
1108 /// Generates a password input field.
1110 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1111 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1112 /// <returns>The generated form element</returns>
1113 public virtual string PasswordField(string target
, IDictionary attributes
)
1115 target
= RewriteTargetIfWithinObjectScope(target
);
1117 object value = ObtainValue(target
);
1119 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
1121 return CreateInputElement("password", target
, value, attributes
);
1126 #region PasswordNumberField
1129 /// Generates an input password element with a javascript that prevents
1130 /// chars other than numbers from being entered.
1132 /// The value is extracted from the target (if available)
1136 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
1138 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1139 /// <returns>The generated form element</returns>
1140 public virtual string PasswordNumberField(string target
)
1142 return PasswordNumberField(target
, null);
1146 /// Generates an input password element with a javascript that prevents
1147 /// chars other than numbers from being entered.
1149 /// The value is extracted from the target (if available)
1152 /// You can optionally pass an <c>exceptions</c> value through the dictionary.
1153 /// It must be a comma separated list of chars that can be accepted on the field.
1157 /// FormHelper.NumberField("product.price", {exceptions='13,10,11'})
1159 /// In this case the key codes 13, 10 and 11 will be accepted on the field.
1161 /// You can aslo optionally pass an <c>forbid</c> value through the dictionary.
1162 /// It must be a comma separated list of chars that cannot be accepted on the field.
1166 /// FormHelper.NumberField("product.price", {forbid='46'})
1168 /// In this case the key code 46 (period) will not be accepted on the field.
1171 /// You must invoke <see cref="FormHelper.InstallScripts"/> before using it
1173 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1174 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1175 /// <returns>The generated form element</returns>
1176 public virtual string PasswordNumberField(string target
, IDictionary attributes
)
1178 target
= RewriteTargetIfWithinObjectScope(target
);
1180 object value = ObtainValue(target
);
1182 attributes
= attributes
!= null ? attributes
: new Hashtable();
1184 ApplyNumberOnlyOptions(attributes
);
1185 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
1187 return CreateInputElement("password", target
, value, attributes
);
1194 ///<summary>Returns the value for specified target with no additional markup. If no value is obtained
1195 /// an empty string is returned.
1197 ///<param name="target">The object to get the value from.</param>
1198 ///<returns>The value or an empty string if none is found.</returns>
1199 public virtual string LiteralFor(string target
)
1201 target
= RewriteTargetIfWithinObjectScope(target
);
1202 object value = ObtainValue(target
);
1205 return string.Empty
;
1207 return value.ToString();
1215 /// Generates a label element.
1217 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1218 /// <param name="label">Legend</param>
1219 /// <returns>The generated form element</returns>
1220 public virtual string LabelFor(string target
, string label
)
1222 return LabelFor(target
, label
, null);
1226 /// Generates a label element.
1228 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1229 /// <param name="label">Legend</param>
1230 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1231 /// <returns>The generated form element</returns>
1232 public virtual string LabelFor(string target
, string label
, IDictionary attributes
)
1234 target
= RewriteTargetIfWithinObjectScope(target
);
1236 string id
= CreateHtmlId(attributes
, target
);
1238 StringBuilder sb
= new StringBuilder();
1239 StringWriter sbWriter
= new StringWriter(sb
);
1240 HtmlTextWriter writer
= new HtmlTextWriter(sbWriter
);
1242 writer
.WriteBeginTag("label");
1243 writer
.WriteAttribute("for", id
);
1244 string strAttributes
= GetAttributes(attributes
);
1245 if (strAttributes
!= String
.Empty
)
1247 writer
.Write(HtmlTextWriter
.SpaceChar
);
1250 writer
.Write(strAttributes
);
1251 writer
.Write(HtmlTextWriter
.TagRightChar
);
1252 writer
.Write(label
);
1253 writer
.WriteEndTag("label");
1255 return sbWriter
.ToString();
1263 /// Generates a hidden form element.
1265 /// The value is extracted from the target (if available)
1268 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1269 /// <returns>The generated form element</returns>
1270 public virtual string HiddenField(string target
)
1272 target
= RewriteTargetIfWithinObjectScope(target
);
1274 object value = ObtainValue(target
);
1276 return CreateInputElement("hidden", target
, value, null);
1280 /// Generates a hidden form element with the specified value
1282 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1283 /// <param name="value">The value for the hidden field</param>
1284 /// <returns>The generated form element</returns>
1285 public virtual string HiddenField(string target
, object value)
1287 return CreateInputElement("hidden", target
, value, null);
1291 /// Generates a hidden form element.
1293 /// The value is extracted from the target (if available)
1296 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1297 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1298 /// <returns>The generated form element</returns>
1299 public virtual string HiddenField(string target
, IDictionary attributes
)
1301 target
= RewriteTargetIfWithinObjectScope(target
);
1303 object value = ObtainValue(target
);
1305 string id
= CreateHtmlId(attributes
, target
);
1307 value = value != null ? value : String
.Empty
;
1309 return CreateInputElement("hidden", id
, target
, value.ToString(), attributes
);
1313 /// Generates a hidden form element with the specified value
1315 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1316 /// <param name="value">The value for the hidden field</param>
1317 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1318 /// <returns>The generated form element</returns>
1319 public virtual string HiddenField(string target
, object value, IDictionary attributes
)
1321 return CreateInputElement("hidden", target
, value, attributes
);
1326 #region CheckboxList
1329 /// Creates a <see cref="CheckboxList"/> instance
1330 /// which is enumerable. For each interaction you can invoke
1331 /// <see cref="CheckboxList.Item()"/> which will correctly render
1332 /// a checkbox input element for the current element on the supplied set (<c>dataSource</c>).
1334 /// The enumerable item will be an element of the <c>dataSource</c>.
1336 /// If the <c>dataSource</c>
1337 /// elements are complex objects (ie not string or primitives),
1338 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1339 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1341 /// Usually both the <c>target</c> and obviously the <c>dataSource</c> are sets
1342 /// with multiple items. The element types tend to be the same. If
1343 /// they are not, you might have to specify the <c>suffix</c> parameters on
1344 /// the <c>attributes</c> as it would not be inferred.
1349 /// Consider the following action code:
1351 /// public void Index()
1354 /// PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
1356 /// // initial selection
1357 /// PropertyBag["selectedPrimes"] = new int[] { 11, 19 };
1361 /// And the respective view code
1363 /// <code lang="none">
1364 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1366 /// #foreach($elem in $items)
1367 /// $items.Item() $elem
1371 /// That will generates the following html:
1373 /// <code lang="none">
1374 /// <input type="checkbox" id="selectedPrimes_0_" name="selectedPrimes[0]" value="2" /> 2
1375 /// <input type="checkbox" id="selectedPrimes_1_" name="selectedPrimes[1]" value="3" /> 3
1376 /// <input type="checkbox" id="selectedPrimes_2_" name="selectedPrimes[2]" value="5" /> 5
1377 /// <input type="checkbox" id="selectedPrimes_3_" name="selectedPrimes[3]" value="7" /> 7
1378 /// <input type="checkbox" id="selectedPrimes_4_" name="selectedPrimes[4]" value="11" checked="checked" /> 11
1379 /// <input type="checkbox" id="selectedPrimes_5_" name="selectedPrimes[5]" value="13" /> 13
1380 /// <input type="checkbox" id="selectedPrimes_6_" name="selectedPrimes[6]" value="17" /> 17
1381 /// <input type="checkbox" id="selectedPrimes_7_" name="selectedPrimes[7]" value="19" checked="checked" /> 19
1382 /// <input type="checkbox" id="selectedPrimes_8_" name="selectedPrimes[8]" value="23" /> 23
1386 /// To customize the id, you can call the <see cref="CheckboxList.Item(string)"/> overload:
1389 /// <code lang="none">
1390 /// #set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))
1392 /// #foreach($elem in $items)
1393 /// $items.Item("myId${velocityCount}") $Form.LabelFor("myId${velocityCount}", $elem.ToString()) <br/>
1398 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1399 /// <param name="dataSource">The set of available elements</param>
1400 /// <returns>The generated form element</returns>
1401 public virtual CheckboxList
CreateCheckboxList(string target
, IEnumerable dataSource
)
1403 return CreateCheckboxList(target
, dataSource
, null);
1407 /// Creates a <see cref="CheckboxList"/> instance
1408 /// which is enumerable. For each interaction you can invoke
1409 /// <see cref="CheckboxList.Item()"/> which will correctly render
1410 /// a checkbox input element for the current element on the supplied set (<c>dataSource</c>).
1412 /// The enumerable item will be an element of the <c>dataSource</c>.
1414 /// If the <c>dataSource</c>
1415 /// elements are complex objects (ie not string or primitives),
1416 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1417 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1419 /// Usually both the <c>target</c> and obviously the <c>dataSource</c> are sets
1420 /// with multiple items. The element types tend to be the same. If
1421 /// they are not, you might have to specify the <c>suffix</c> parameters on
1422 /// the <c>attributes</c> as it would not be inferred.
1426 /// <seealso cref="CreateCheckboxList(string,IEnumerable)"/>
1429 /// Consider the following action code:
1431 /// public void Index()
1433 /// Category[] categories = new Category[] { new Category(1, "Music"), new Category(2, "Humor"), new Category(3, "Politics") };
1434 /// PropertyBag["categories"] = categories; // datasource
1436 /// Blog blog = new Blog();
1437 /// blog.Categories = new Category[] { new Category(2, "Humor") }; // initial selection
1438 /// PropertyBag["blog"] = blog;
1442 /// And the respective view code
1444 /// <code lang="none">
1445 /// #set($items = $Form.CreateCheckboxList("blog.categories", $categories, "%{value='Id'}"))
1447 /// #foreach($elem in $items)
1448 /// $items.Item() $elem
1452 /// That will generates the following html:
1454 /// <code lang="none">
1455 /// <input type="checkbox" id="blog_categories_0_" name="blog.categories[0].Id" value="1" /> Music
1456 /// <input type="checkbox" id="blog_categories_1_" name="blog.categories[1].Id" value="2" checked="checked" /> Humor
1457 /// <input type="checkbox" id="blog_categories_2_" name="blog.categories[2].Id" value="3" /> Politics
1462 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1463 /// <param name="dataSource">The set of available elements</param>
1464 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1465 /// <returns>The generated form element</returns>
1466 public virtual CheckboxList
CreateCheckboxList(string target
, IEnumerable dataSource
, IDictionary attributes
)
1468 target
= RewriteTargetIfWithinObjectScope(target
);
1470 object value = ObtainValue(target
);
1472 return new CheckboxList(this, target
, value, dataSource
, attributes
);
1476 /// Outputs a checkbox element (for internal use)
1478 /// <param name="index"></param>
1479 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1480 /// <param name="suffix"></param>
1481 /// <param name="item"></param>
1482 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1483 /// <returns>The generated form element</returns>
1484 internal string CheckboxItem(int index
, string target
, string suffix
, SetItem item
, IDictionary attributes
)
1486 if (item
.IsSelected
)
1488 AddChecked(attributes
);
1492 RemoveChecked(attributes
);
1495 target
= String
.Format("{0}[{1}]", target
, index
);
1497 string elementId
= CreateHtmlId(attributes
, target
, true);
1499 string computedTarget
= target
;
1501 if (suffix
!= null && suffix
!= String
.Empty
)
1503 computedTarget
+= "." + suffix
;
1506 return CreateInputElement("checkbox", elementId
, computedTarget
, item
.Value
, attributes
);
1510 /// Creates a label for an item of the checkbox list (internal use). The method mirrors
1511 /// <see cref="CheckboxItem"/> to ensure that the HTML ID is created consistently.
1513 /// <param name="index">Index for creating HTML ID</param>
1514 /// <param name="target">Target object for which the HTML ID is needed</param>
1515 /// <param name="suffix"></param>
1516 /// <param name="label">The label to display</param>
1517 /// <param name="attributes">additional attributes that influence HTML ID creation</param>
1518 /// <returns></returns>
1519 internal string CheckboxLabel(int index
, string target
, string suffix
, string label
, IDictionary attributes
)
1521 target
= String
.Format("{0}[{1}]", target
, index
);
1523 string elementId
= CreateHtmlId(attributes
, target
, true);
1525 return "<label for=\"" + elementId
+ "\" " + GetAttributes(attributes
) + ">" + label
+ "</label>";
1529 /// This class is an enumerable list of checkboxes.
1530 /// It uses the <see cref="OperationState"/> to manage the sets
1531 /// and to control the check/uncheck state.
1533 public sealed class CheckboxList
: IEnumerable
, IEnumerator
1535 private readonly FormHelper helper
;
1536 private readonly string target
;
1537 private readonly IDictionary attributes
;
1538 private readonly OperationState operationState
;
1539 private readonly IEnumerator enumerator
;
1540 private bool hasMovedNext
, hasItem
;
1541 private int index
= -1;
1544 /// Initializes a new instance of the <see cref="CheckboxList"/> class.
1546 /// <param name="helper">The helper.</param>
1547 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1548 /// <param name="initialSelectionSet">The initial selection set.</param>
1549 /// <param name="dataSource">The set of available elements</param>
1550 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1551 public CheckboxList(FormHelper helper
, string target
,
1552 object initialSelectionSet
, IEnumerable dataSource
, IDictionary attributes
)
1554 if (dataSource
== null) throw new ArgumentNullException("dataSource");
1556 this.helper
= helper
;
1557 this.target
= target
;
1558 this.attributes
= attributes
?? new HybridDictionary(true);
1560 operationState
= SetOperation
.IterateOnDataSource(initialSelectionSet
, dataSource
, attributes
);
1561 enumerator
= operationState
.GetEnumerator();
1565 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1567 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1569 /// <returns>The generated input element</returns>
1570 public string Item()
1576 /// Outputs the Checkbox in the correct state (checked/unchecked) based
1578 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1580 /// <param name="id">The element id</param>
1581 /// <returns>The generated input element</returns>
1582 public string Item(string id
)
1586 throw new InvalidOperationException("Before rendering a checkbox item, you must use MoveNext");
1591 // Nothing to render
1592 return String
.Empty
;
1597 attributes
["id"] = id
;
1600 return helper
.CheckboxItem(index
, target
, operationState
.TargetSuffix
, CurrentSetItem
, attributes
);
1604 /// Outputs a label for the current checkbox element based on the generated id.
1605 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1607 /// <param name="label">The text to display</param>
1608 /// <returns>The generated label element</returns>
1609 public string LabelFor(string label
)
1611 return LabelFor(null, label
, null);
1615 /// Outputs a label for the current checkbox element based on the generated id.
1616 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1618 /// <param name="label">The text to display</param>
1619 /// <param name="attributes">The attributes.</param>
1620 /// <returns>The generated label element</returns>
1621 public string LabelFor(string label
, IDictionary attributes
)
1623 return LabelFor(null, label
, attributes
);
1627 /// Outputs a label for the current checkbox element based on the given id.
1628 /// <seealso cref="FormHelper.CreateCheckboxList(string,IEnumerable,IDictionary)"/>
1630 /// <param name="id">The id to use within the label</param>
1631 /// <param name="label">The text to display</param>
1632 /// <param name="attributes">The attributes.</param>
1633 /// <returns>The generated label element</returns>
1634 public string LabelFor(string id
, string label
, IDictionary attributes
)
1638 throw new InvalidOperationException("Before rendering a checkbox item, you must use MoveNext");
1641 if (attributes
== null)
1643 attributes
= new HybridDictionary(true);
1648 // Nothing to render
1649 return String
.Empty
;
1654 attributes
["id"] = id
;
1657 return helper
.CheckboxLabel(index
, target
, operationState
.TargetSuffix
, label
, attributes
);
1661 /// Returns an enumerator that iterates through a collection.
1664 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
1666 public IEnumerator
GetEnumerator()
1672 /// Advances the enumerator to the next element of the collection.
1675 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
1677 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1678 public bool MoveNext()
1680 hasMovedNext
= true;
1681 hasItem
= enumerator
.MoveNext();
1683 if (hasItem
) index
++;
1689 /// Sets the enumerator to its initial position, which is before the first element in the collection.
1691 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
1699 /// Gets the current element in the collection.
1702 /// <returns>The current element in the collection.</returns>
1703 /// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element. </exception>
1704 public object Current
1706 get { return CurrentSetItem.Item; }
1710 /// Gets the current set item.
1712 /// <value>The current set item.</value>
1713 public SetItem CurrentSetItem
1715 get { return enumerator.Current as SetItem; }
1721 #region CheckboxField
1724 /// Generates a checkbox field. In fact it generates two as a
1725 /// way to send a value if the primary checkbox is not checked.
1726 /// This allow the process the be aware of the unchecked value
1727 /// and act accordingly.
1731 /// Consider the following view code:
1733 /// <code lang="none">
1734 /// $Form.CheckboxField('user.disabled')
1737 /// That is going to output:
1739 /// <code lang="none">
1740 /// <input type="checkbox" id="user_disabled" name="user.disabled" value="true" />
1741 /// <input type="hidden" id="user_disabledH" name="user.disabled" value="false" />
1746 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1747 /// <returns>The generated form element</returns>
1748 public virtual string CheckboxField(string target
)
1750 return CheckboxField(target
, null);
1754 /// Generates a checkbox field. In fact it generates two as a
1755 /// way to send a value if the primary checkbox is not checked.
1756 /// This allow the process the be aware of the unchecked value
1757 /// and act accordingly.
1760 /// The checked and unchecked values sent to the server defaults
1761 /// to true and false. You can override them using the
1762 /// parameters <c>trueValue</c> and <c>falseValue</c>, but the DataBinder is prepared only
1763 /// to treat boolean arrays.
1768 /// <seealso cref="CheckboxField(string)"/>
1770 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1771 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1772 /// <returns>The generated form element</returns>
1773 public virtual string CheckboxField(string target
, IDictionary attributes
)
1775 target
= RewriteTargetIfWithinObjectScope(target
);
1777 object value = ObtainValue(target
);
1779 string trueValue
= CommonUtils
.ObtainEntryAndRemove(attributes
, "trueValue", "true");
1783 if (trueValue
!= "true")
1785 isChecked
= AreEqual(value, trueValue
);
1789 isChecked
= ((value != null && value is bool && ((bool)value)) ||
1790 (!(value is bool) && (value != null) &&
1791 (!(value is string) || ((string) value).ToLower() != "false")));
1796 if (attributes
== null)
1798 attributes
= new HybridDictionary(true);
1801 AddChecked(attributes
);
1804 ApplyValidation(InputElementType
.Checkbox
, target
, ref attributes
);
1806 string id
= CreateHtmlId(attributes
, target
);
1807 string hiddenElementId
= id
+ "H";
1808 string hiddenElementValue
= CommonUtils
.ObtainEntryAndRemove(attributes
, "falseValue", "false");
1810 string result
= CreateInputElement("checkbox", id
, target
, trueValue
, attributes
);
1812 result
+= CreateInputElement("hidden", hiddenElementId
, target
, hiddenElementValue
, null);
1822 /// Generates a radio input type with the specified
1823 /// value to send to the served in case the element in checked.
1824 /// It will automatically check the radio if the target
1825 /// evaluated value is equal to the specified <c>valueToSend</c>.
1829 /// Consider the following action code:
1832 /// public void Index()
1834 /// PropertyBag["mode"] = FileMode.Truncate;
1838 /// And the following view code:
1840 /// <code lang="none">
1841 /// $Form.RadioField("mode", "Append") FileMode.Append
1842 /// $Form.RadioField("mode", "Create") FileMode.Create
1843 /// $Form.RadioField("mode", "CreateNew") FileMode.CreateNew
1844 /// $Form.RadioField("mode", "Open") FileMode.Open
1845 /// $Form.RadioField("mode", "OpenOrCreate", "%{id='customhtmlid'}") FileMode.OpenOrCreate
1846 /// $Form.RadioField("mode", "Truncate") FileMode.Truncate
1849 /// That is going to output:
1851 /// <code lang="none">
1852 /// <input type="radio" id="mode" name="mode" value="Append" /> FileMode.Append
1853 /// <input type="radio" id="mode" name="mode" value="Create" /> FileMode.Create
1854 /// <input type="radio" id="mode" name="mode" value="CreateNew" /> FileMode.CreateNew
1855 /// <input type="radio" id="mode" name="mode" value="Open" /> FileMode.Open
1856 /// <input type="radio" id="customhtmlid" name="mode" value="OpenOrCreate" /> FileMode.OpenOrCreate
1857 /// <input type="radio" id="mode" name="mode" value="Truncate" checked="checked" /> FileMode.Truncate
1862 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1863 /// <param name="valueToSend"></param>
1864 /// <returns>The generated form element</returns>
1865 public virtual string RadioField(string target
, object valueToSend
)
1867 return RadioField(target
, valueToSend
, null);
1871 /// Generates a radio input type with the specified
1872 /// value to send to the served in case the element in checked.
1873 /// It will automatically check the radio if the target
1874 /// evaluated value is equal to the specified <c>valueToSend</c>.
1877 /// <seealso cref="RadioField(string,object)"/>
1879 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1880 /// <param name="valueToSend"></param>
1881 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1882 /// <returns>The generated form element</returns>
1883 public virtual string RadioField(string target
, object valueToSend
, IDictionary attributes
)
1885 target
= RewriteTargetIfWithinObjectScope(target
);
1887 object value = ObtainValue(target
);
1889 bool isChecked
= AreEqual(valueToSend
, value);
1893 if (attributes
== null)
1895 attributes
= new HybridDictionary(true);
1898 AddChecked(attributes
);
1901 return CreateInputElement("radio", target
, valueToSend
, attributes
);
1909 /// Generates an input file element.
1911 /// Dirrently than other operations exposed by this helper,
1912 /// no value is extracted for this operation
1915 /// <param name="target">The object to be based on when creating the element name.</param>
1916 /// <returns>The generated form element</returns>
1917 public virtual string FileField(string target
)
1919 return FileField(target
, null);
1923 /// Generates an input file element.
1925 /// Dirrently than other operations exposed by this helper,
1926 /// no value is extracted for this operation
1929 /// <param name="target">The object to be based on when creating the element name.</param>
1930 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
1931 /// <returns>The generated form element</returns>
1932 public virtual string FileField(string target
, IDictionary attributes
)
1934 target
= RewriteTargetIfWithinObjectScope(target
);
1936 ApplyValidation(InputElementType
.Text
, target
, ref attributes
);
1938 return CreateInputElement("file", target
, string.Empty
, attributes
);
1946 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1947 /// If the <c>dataSource</c>
1948 /// elements are complex objects (ie not string or primitives),
1949 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1950 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1952 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1953 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1954 /// <c>firstoptionvalue</c>. The default value is '0'.
1957 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1958 /// a set with multiple items. The element types tend to be the same. If
1959 /// they are not, you might have to specify the <c>suffix</c> parameters on
1960 /// the <c>attributes</c> as it would not be inferred.
1963 /// The target can also be a set. In this case the intersection will be
1964 /// the initially selected elements.
1967 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1968 /// <param name="dataSource">The set of available elements</param>
1969 /// <returns>The generated form element</returns>
1970 public virtual string Select(string target
, IEnumerable dataSource
)
1972 return Select(target
, dataSource
, null);
1976 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
1977 /// If the <c>dataSource</c>
1978 /// elements are complex objects (ie not string or primitives),
1979 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
1980 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
1982 /// You can also specify the attribute <c>firstoption</c> to force the first option be
1983 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
1984 /// <c>firstoptionvalue</c>. The default value is '0'.
1987 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
1988 /// a set with multiple items. The element types tend to be the same. If
1989 /// they are not, you might have to specify the <c>suffix</c> parameters on
1990 /// the <c>attributes</c> as it would not be inferred.
1993 /// The target can also be a set. In this case the intersection will be
1994 /// the initially selected elements.
1997 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
1998 /// <param name="dataSource">The set of available elements</param>
1999 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2000 /// <returns>The generated form element</returns>
2001 public virtual string Select(string target
, IEnumerable dataSource
, IDictionary attributes
)
2003 target
= RewriteTargetIfWithinObjectScope(target
);
2005 object selectedValue
= ObtainValue(target
);
2007 return Select(target
, selectedValue
, dataSource
, attributes
);
2011 /// Creates a <c>select</c> element and its <c>option</c>s based on the <c>dataSource</c>.
2012 /// If the <c>dataSource</c>
2013 /// elements are complex objects (ie not string or primitives),
2014 /// supply the parameters <c>value</c> and <c>text</c> to the dictionary to make
2015 /// the helper use the specified properties to extract the <c>option</c> value and content respectively.
2017 /// You can also specify the attribute <c>firstoption</c> to force the first option be
2018 /// something like 'please select'. You can set the value of <c>firstoption</c> by specifying the attribute
2019 /// <c>firstoptionvalue</c>. The default value is '0'.
2022 /// Usually the <c>target</c> is a single value and the <c>dataSource</c> is obviously
2023 /// a set with multiple items. The element types tend to be the same. If
2024 /// they are not, you might have to specify the <c>suffix</c> parameters on
2025 /// the <c>attributes</c> as it would not be inferred.
2028 /// The target can also be a set. In this case the intersection will be
2029 /// the initially selected elements.
2032 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2033 /// <param name="selectedValue"></param>
2034 /// <param name="dataSource">The set of available elements</param>
2035 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2036 /// <returns>The generated form element</returns>
2037 public virtual string Select(string target
, object selectedValue
, IEnumerable dataSource
, IDictionary attributes
)
2039 return GenerateSelect(target
, selectedValue
, dataSource
, attributes
);
2043 /// Generates the select.
2045 /// <param name="target">The target.</param>
2046 /// <param name="selectedValue">The selected value.</param>
2047 /// <param name="dataSource">The data source.</param>
2048 /// <param name="attributes">The attributes.</param>
2049 /// <returns></returns>
2050 protected virtual string GenerateSelect(string target
, object selectedValue
, IEnumerable dataSource
, IDictionary attributes
)
2052 string id
= CreateHtmlId(target
);
2054 ApplyValidation(InputElementType
.Select
, target
, ref attributes
);
2056 StringBuilder sb
= new StringBuilder();
2057 StringWriter sbWriter
= new StringWriter(sb
);
2058 HtmlTextWriter writer
= new HtmlTextWriter(sbWriter
);
2060 string firstOption
= null;
2061 string firstOptionValue
= null;
2062 string name
= target
;
2064 if (attributes
!= null)
2066 firstOption
= CommonUtils
.ObtainEntryAndRemove(attributes
, "firstoption");
2067 firstOptionValue
= CommonUtils
.ObtainEntryAndRemove(attributes
, "firstoptionvalue");
2069 if (attributes
.Contains("name"))
2071 name
= (String
) attributes
["name"];
2072 attributes
.Remove("name");
2075 if (attributes
.Contains("id"))
2077 id
= (String
) attributes
["id"];
2078 attributes
.Remove("id");
2082 OperationState state
= SetOperation
.IterateOnDataSource(selectedValue
, dataSource
, attributes
);
2084 writer
.WriteBeginTag("select");
2085 writer
.WriteAttribute("id", id
);
2086 writer
.WriteAttribute("name", name
);
2088 writer
.Write(GetAttributes(attributes
));
2089 writer
.Write(HtmlTextWriter
.TagRightChar
);
2092 if (firstOption
!= null)
2094 writer
.WriteBeginTag("option");
2095 writer
.WriteAttribute("value", (firstOptionValue
== null) ? "0" : SafeHtmlEncode(firstOptionValue
));
2096 writer
.Write(HtmlTextWriter
.TagRightChar
);
2097 writer
.Write(SafeHtmlEncode(firstOption
));
2098 writer
.WriteEndTag("option");
2102 foreach(SetItem item
in state
)
2104 writer
.WriteBeginTag("option");
2106 if (item
.IsSelected
)
2108 writer
.Write(" selected=\"selected\"");
2111 writer
.WriteAttribute("value", SafeHtmlEncode(item
.Value
));
2112 writer
.Write(HtmlTextWriter
.TagRightChar
);
2113 writer
.Write(SafeHtmlEncode(item
.Text
));
2114 writer
.WriteEndTag("option");
2118 writer
.WriteEndTag("select");
2120 return sbWriter
.ToString();
2128 /// Creates a field set element with a legend using the specified name.
2130 /// <param name="name">The name.</param>
2131 /// <returns></returns>
2132 public virtual string FieldSet(string name
)
2134 return "<fieldset><legend>" + name
+ "</legend>";
2138 /// Creates an element to close a fieldset element.
2140 /// <returns></returns>
2141 public virtual string EndFieldSet()
2143 return "</fieldset>";
2151 /// Creates a list of pairs for the enum type.
2153 /// <param name="enumType">enum type.</param>
2154 /// <returns></returns>
2155 public static Pair
<int, string>[] EnumToPairs(Type enumType
)
2157 if (enumType
== null) throw new ArgumentNullException("enumType");
2158 if (!enumType
.IsEnum
) throw new ArgumentException("enumType must be an Enum", "enumType");
2160 Array values
= Enum
.GetValues(enumType
);
2161 string[] names
= Enum
.GetNames(enumType
);
2163 List
<Pair
<int, string>> listOfPairs
= new List
<Pair
<int, string>>();
2166 foreach(string name
in names
)
2168 int value = Convert
.ToInt32(values
.GetValue(index
++));
2169 listOfPairs
.Add(new Pair
<int, string>(value, TextHelper
.PascalCaseToWord(name
)));
2172 return listOfPairs
.ToArray();
2180 /// Configures this FormHelper instance to use the supplied
2181 /// web validator to generate field validation.
2183 /// <param name="provider">The validation provider.</param>
2184 public virtual void UseWebValidatorProvider(IBrowserValidatorProvider provider
)
2186 if (provider
== null) throw new ArgumentNullException("provider");
2188 validatorProvider
= provider
;
2192 /// Configures this FormHelper instance to use Prototype for form fields validation
2194 public virtual void UsePrototypeValidation()
2196 UseWebValidatorProvider(new PrototypeWebValidator());
2200 /// Configures this FormHelper instance to use fValidate for form fields validation
2202 public virtual void UsefValidate()
2204 UseWebValidatorProvider(new FValidateWebValidator());
2208 /// Configures this FormHelper instance to use Zebda for form fields validation
2210 public virtual void UseZebdaValidation()
2212 UseWebValidatorProvider(new ZebdaWebValidator());
2216 /// Disables the validation.
2218 public virtual void DisableValidation()
2220 isValidationDisabled
= true;
2224 /// Applies the validation.
2226 /// <param name="inputType">Type of the input.</param>
2227 /// <param name="target">The target.</param>
2228 /// <param name="attributes">The attributes.</param>
2229 protected virtual void ApplyValidation(InputElementType inputType
, string target
, ref IDictionary attributes
)
2231 bool disableValidation
= CommonUtils
.ObtainEntryAndRemove(attributes
, "disablevalidation", "false") == "true";
2233 if (!IsValidationEnabled
|| disableValidation
)
2238 if (validatorRegistry
== null || validationConfig
== null)
2243 if (attributes
== null)
2245 attributes
= new HybridDictionary(true);
2248 IValidator
[] validators
= CollectValidators(RequestContext
.All
, target
);
2250 IBrowserValidationGenerator generator
= validatorProvider
.CreateGenerator(validationConfig
, inputType
, attributes
);
2252 foreach(IValidator validator
in validators
)
2254 if (validator
.SupportsBrowserValidation
)
2256 validator
.ApplyBrowserValidation(validationConfig
, inputType
, generator
, attributes
, target
);
2261 private IValidator
[] CollectValidators(RequestContext requestContext
, string target
)
2263 List
<IValidator
> validators
= new List
<IValidator
>();
2265 ObtainTargetProperty(requestContext
, target
, delegate(PropertyInfo property
)
2267 validators
.AddRange(validatorRegistry
.GetValidators(ValidatorRunner
, property
.DeclaringType
, property
, RunWhen
.Everytime
));
2270 return validators
.ToArray();
2273 private bool IsValidationEnabled
2277 if (isValidationDisabled
) return false;
2279 if (objectStack
.Count
== 0) return true;
2281 return ((FormScopeInfo
)objectStack
.Peek()).IsValidationEnabled
;
2287 #region protected members
2290 /// Rewrites the target if within object scope.
2292 /// <param name="target">The target.</param>
2293 /// <returns></returns>
2294 protected string RewriteTargetIfWithinObjectScope(string target
)
2296 if (objectStack
.Count
== 0)
2302 return ((FormScopeInfo
) objectStack
.Peek()).RootTarget
+ "." + target
;
2307 /// Creates the specified input element
2308 /// using the specified parameters to supply the name, value, id and others
2309 /// html attributes.
2311 /// <param name="type"></param>
2312 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2313 /// <param name="value"></param>
2314 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2315 /// <returns>The generated form element</returns>
2316 protected virtual string CreateInputElement(string type
, string target
, Object
value, IDictionary attributes
)
2320 value = CommonUtils
.ObtainEntryAndRemove(attributes
, "defaultValue");
2323 string id
= CreateHtmlId(attributes
, target
);
2325 return CreateInputElement(type
, id
, target
, FormatIfNecessary(value, attributes
), attributes
);
2329 /// Creates the specified input element
2330 /// using the specified parameters to supply the name, value, id and others
2331 /// html attributes.
2333 /// <param name="type"></param>
2334 /// <param name="id"></param>
2335 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2336 /// <param name="value"></param>
2337 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
2338 /// <returns>The generated form element</returns>
2339 protected virtual string CreateInputElement(string type
, string id
, string target
, string value, IDictionary attributes
)
2341 value = FormatIfNecessary(value, attributes
);
2343 value = SafeHtmlEncode(value);
2345 if (attributes
!= null && attributes
.Contains("mask"))
2347 string mask
= CommonUtils
.ObtainEntryAndRemove(attributes
, "mask");
2348 string maskSep
= CommonUtils
.ObtainEntryAndRemove(attributes
, "mask_separator", "-");
2350 string onBlur
= CommonUtils
.ObtainEntryAndRemove(attributes
, "onBlur", "void(0)");
2351 string onKeyUp
= CommonUtils
.ObtainEntryAndRemove(attributes
, "onKeyUp", "void(0)");
2353 string js
= "return monorail_formhelper_mask(event,this,'" + mask
+ "','" + maskSep
+ "');";
2355 attributes
["onBlur"] = "javascript:" + onBlur
+ ";" + js
;
2356 attributes
["onKeyUp"] = "javascript:" + onKeyUp
+ ";" + js
;
2359 return String
.Format("<input type=\"{0}\" id=\"{1}\" name=\"{2}\" value=\"{3}\" {4}/>",
2360 type
, id
, target
, value, GetAttributes(attributes
));
2364 /// Creates the input element.
2366 /// <param name="type">The type.</param>
2367 /// <param name="value">The value.</param>
2368 /// <param name="attributes">The attributes.</param>
2369 /// <returns></returns>
2370 protected virtual string CreateInputElement(string type
, string value, IDictionary attributes
)
2372 return String
.Format("<input type=\"{0}\" value=\"{1}\" {2}/>",
2373 type
, FormatIfNecessary(value, attributes
), GetAttributes(attributes
));
2377 /// Formats if necessary.
2379 /// <param name="value">The value.</param>
2380 /// <param name="attributes">The attributes.</param>
2381 /// <returns></returns>
2382 protected static string FormatIfNecessary(object value, IDictionary attributes
)
2384 string formatString
= CommonUtils
.ObtainEntryAndRemove(attributes
, "textformat");
2386 if (value != null && formatString
!= null)
2388 IFormattable formattable
= value as IFormattable
;
2390 if (formattable
!= null)
2392 value = formattable
.ToString(formatString
, null);
2395 else if (value == null)
2397 value = String
.Empty
;
2400 return value.ToString();
2404 /// Obtains the target property.
2406 /// <param name="context">The context.</param>
2407 /// <param name="target">The target.</param>
2408 /// <param name="action">The action.</param>
2409 /// <returns></returns>
2410 protected PropertyInfo
ObtainTargetProperty(RequestContext context
, string target
, Action
<PropertyInfo
> action
)
2414 Type root
= ObtainRootType(context
, target
, out pieces
);
2416 if (root
!= null && pieces
.Length
> 1)
2418 return QueryPropertyInfoRecursive(root
, pieces
, 1, action
);
2425 /// Queries the context for the target value
2427 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2428 /// <returns>The generated form element</returns>
2429 protected object ObtainValue(string target
)
2431 return ObtainValue(RequestContext
.All
, target
);
2435 /// Queries the context for the target value
2437 /// <param name="context"></param>
2438 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2439 /// <returns>The generated form element</returns>
2440 protected object ObtainValue(RequestContext context
, string target
)
2444 object rootInstance
= ObtainRootInstance(context
, target
, out pieces
);
2446 if (rootInstance
!= null && pieces
.Length
> 1)
2448 return QueryPropertyRecursive(rootInstance
, pieces
, 1);
2451 return rootInstance
;
2455 /// Obtains the root instance.
2457 /// <param name="context">The context.</param>
2458 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
2459 /// <returns>The generated form element</returns>
2460 protected object ObtainRootInstance(RequestContext context
, string target
)
2462 object rootInstance
= null;
2464 if (context
== RequestContext
.All
|| context
== RequestContext
.PropertyBag
)
2466 rootInstance
= ControllerContext
.PropertyBag
[target
];
2468 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Flash
))
2470 rootInstance
= Context
.Flash
[target
];
2472 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Session
))
2474 rootInstance
= Context
.Session
[target
];
2476 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Params
))
2478 rootInstance
= Context
.Request
.Params
[target
];
2480 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Request
))
2482 rootInstance
= Context
.Items
[target
];
2485 return rootInstance
;
2489 /// Obtains the root instance.
2491 /// <param name="context">The context.</param>
2492 /// <param name="target">The target.</param>
2493 /// <param name="pieces">The pieces.</param>
2494 /// <returns></returns>
2495 protected object ObtainRootInstance(RequestContext context
, string target
, out string[] pieces
)
2497 pieces
= target
.Split(new char[] {'.'}
);
2499 string root
= pieces
[0];
2503 bool isIndexed
= CheckForExistenceAndExtractIndex(ref root
, out index
);
2505 object rootInstance
= ObtainRootInstance(context
, root
);
2507 if (rootInstance
== null)
2514 AssertIsValidArray(rootInstance
, root
, index
);
2517 if (!isIndexed
&& pieces
.Length
== 1)
2519 return rootInstance
;
2523 rootInstance
= GetArrayElement(rootInstance
, index
);
2526 return rootInstance
;
2530 /// Obtains the type of the root.
2532 /// <param name="context">The context.</param>
2533 /// <param name="target">The target.</param>
2534 /// <param name="pieces">The pieces.</param>
2535 /// <returns></returns>
2536 private Type
ObtainRootType(RequestContext context
, string target
, out string[] pieces
)
2538 pieces
= target
.Split(new char[] { '.' }
);
2540 Type foundType
= (Type
) ControllerContext
.PropertyBag
[pieces
[0] + "type"];
2542 if (foundType
== null)
2544 string trimmed
= pieces
[0].Split('[')[0];
2546 foundType
= (Type
) ControllerContext
.PropertyBag
[trimmed
+ "type"];
2548 if (foundType
== null)
2550 object root
= ObtainRootInstance(context
, target
, out pieces
);
2554 foundType
= root
.GetType();
2562 private static PropertyInfo
QueryPropertyInfoRecursive(Type type
, string[] propertyPath
)
2564 return QueryPropertyInfoRecursive(type
, propertyPath
, 0, null);
2567 private static PropertyInfo
QueryPropertyInfoRecursive(Type type
, string[] propertyPath
, int piece
, Action
<PropertyInfo
> action
)
2569 string property
= propertyPath
[piece
]; int index
;
2571 bool isIndexed
= CheckForExistenceAndExtractIndex(ref property
, out index
);
2573 PropertyInfo propertyInfo
= type
.GetProperty(property
, ResolveFlagsToUse(type
));
2575 if (propertyInfo
== null)
2577 if (logger
.IsErrorEnabled
)
2579 logger
.Error("No public property '{0}' found on type '{1}'", property
, type
.FullName
);
2585 if (!propertyInfo
.CanRead
)
2587 throw new BindingException("Property '{0}' for type '{1}' can not be read",
2588 propertyInfo
.Name
, type
.FullName
);
2591 if (propertyInfo
.GetIndexParameters().Length
!= 0)
2593 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
2594 propertyInfo
.Name
, type
.FullName
);
2599 action(propertyInfo
);
2602 type
= propertyInfo
.PropertyType
;
2604 if (typeof(ICollection
).IsAssignableFrom(type
))
2611 if (type
.IsGenericType
)
2613 Type
[] args
= type
.GetGenericArguments();
2614 if (args
.Length
!= 1)
2615 throw new BindingException("Expected the generic indexed property '{0}' to be of 1 element", type
.Name
);
2621 type
= type
.GetElementType();
2625 if (piece
+ 1 == propertyPath
.Length
)
2627 return propertyInfo
;
2630 return QueryPropertyInfoRecursive(type
, propertyPath
, piece
+ 1, action
);
2634 /// Query property paths agains the rootInstance type
2636 /// <param name="rootInstance">the object to query</param>
2637 /// <param name="propertyPath">property path</param>
2638 /// <returns>The generated form element</returns>
2639 protected static object QueryPropertyRecursive(object rootInstance
, string[] propertyPath
)
2641 return QueryPropertyRecursive(rootInstance
, propertyPath
, 0);
2645 /// Query property paths agains the rootInstance type
2647 /// <param name="rootInstance">the object to query</param>
2648 /// <param name="propertyPath">property path</param>
2649 /// <param name="piece">start index</param>
2650 /// <returns>The generated form element</returns>
2651 protected static object QueryPropertyRecursive(object rootInstance
, string[] propertyPath
, int piece
)
2653 string property
= propertyPath
[piece
]; int index
;
2655 Type instanceType
= rootInstance
.GetType();
2657 bool isIndexed
= CheckForExistenceAndExtractIndex(ref property
, out index
);
2659 PropertyInfo propertyInfo
= instanceType
.GetProperty(property
, ResolveFlagsToUse(instanceType
));
2661 object instance
= null;
2663 if (propertyInfo
== null)
2665 FieldInfo fieldInfo
= instanceType
.GetField(property
, FieldFlags
);
2667 if (fieldInfo
!= null)
2669 instance
= fieldInfo
.GetValue(rootInstance
);
2674 if (!propertyInfo
.CanRead
)
2676 throw new BindingException("Property '{0}' for type '{1}' can not be read",
2677 propertyInfo
.Name
, instanceType
.FullName
);
2680 if (propertyInfo
.GetIndexParameters().Length
!= 0)
2682 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
2683 propertyInfo
.Name
, instanceType
.FullName
);
2686 instance
= propertyInfo
.GetValue(rootInstance
, null);
2689 if (isIndexed
&& instance
!= null)
2691 AssertIsValidArray(instance
, property
, index
);
2693 instance
= GetArrayElement(instance
, index
);
2696 if (instance
== null || piece
+ 1 == propertyPath
.Length
)
2701 return QueryPropertyRecursive(instance
, propertyPath
, piece
+ 1);
2705 /// Creates the HTML id.
2707 /// <param name="attributes">The attributes.</param>
2708 /// <param name="target">The target.</param>
2709 /// <returns>The generated form element</returns>
2710 protected static string CreateHtmlId(IDictionary attributes
, string target
)
2712 return CreateHtmlId(attributes
, target
, true);
2716 /// Creates the HTML id.
2718 /// <param name="attributes">The attributes.</param>
2719 /// <param name="target">The target.</param>
2720 /// <param name="removeEntry">if set to <c>true</c> [remove entry].</param>
2721 /// <returns>The generated form element</returns>
2722 protected static string CreateHtmlId(IDictionary attributes
, string target
, bool removeEntry
)
2728 id
= CommonUtils
.ObtainEntryAndRemove(attributes
, "id");
2732 id
= CommonUtils
.ObtainEntry(attributes
, "id");
2737 id
= CreateHtmlId(target
);
2745 #region private helpers
2747 private static void ApplyNumberOnlyOptions(IDictionary attributes
)
2749 string list
= CommonUtils
.ObtainEntryAndRemove(attributes
, "exceptions", String
.Empty
);
2750 string forbid
= CommonUtils
.ObtainEntryAndRemove(attributes
, "forbid", String
.Empty
);
2752 attributes
["onKeyPress"] = "return monorail_formhelper_numberonly(event, [" + list
+ "], [" + forbid
+ "]);";
2755 private static void ApplyFilterOptions(IDictionary attributes
)
2757 string forbid
= CommonUtils
.ObtainEntryAndRemove(attributes
, "forbid", String
.Empty
);
2759 attributes
["onKeyPress"] = "return monorail_formhelper_inputfilter(event, [" + forbid
+ "]);";
2762 private static void AssertIsValidArray(object instance
, string property
, int index
)
2764 Type instanceType
= instance
.GetType();
2766 IList list
= instance
as IList
;
2768 bool validList
= false;
2770 if (list
== null && instanceType
.IsGenericType
)
2772 Type
[] genArgs
= instanceType
.GetGenericArguments();
2774 Type genList
= typeof(System
.Collections
.Generic
.IList
<>).MakeGenericType(genArgs
);
2775 Type genTypeDef
= instanceType
.GetGenericTypeDefinition().MakeGenericType(genArgs
);
2777 validList
= genList
.IsAssignableFrom(genTypeDef
);
2780 if (!validList
&& list
== null)
2782 throw new MonoRailException("The property {0} is being accessed as " +
2783 "an indexed property but does not seem to implement IList. " +
2784 "In fact the type is {1}", property
, instanceType
.FullName
);
2789 throw new MonoRailException("The specified index '{0}' is outside the bounds " +
2790 "of the array. Property {1}", index
, property
);
2794 private static object GetArrayElement(object instance
, int index
)
2796 IList list
= instance
as IList
;
2798 if (list
== null && instance
!= null && instance
.GetType().IsGenericType
)
2800 Type instanceType
= instance
.GetType();
2802 Type
[] genArguments
= instanceType
.GetGenericArguments();
2804 Type genType
= instanceType
.GetGenericTypeDefinition().MakeGenericType(genArguments
);
2806 // I'm not going to retest for IList implementation as
2807 // if we got here, the AssertIsValidArray has run successfully
2809 PropertyInfo countPropInfo
= genType
.GetProperty("Count");
2811 int count
= (int) countPropInfo
.GetValue(instance
, null);
2813 if (count
== 0 || index
+ 1 > count
)
2818 PropertyInfo indexerPropInfo
= genType
.GetProperty("Item");
2820 return indexerPropInfo
.GetValue(instance
, new object[] { index }
);
2823 if (list
== null || list
.Count
== 0 || index
+ 1 > list
.Count
)
2831 private static bool CheckForExistenceAndExtractIndex(ref string property
, out int index
)
2833 bool isIndexed
= property
.IndexOf('[') != -1;
2839 int start
= property
.IndexOf('[') + 1;
2840 int len
= property
.IndexOf(']', start
) - start
;
2842 string indexStr
= property
.Substring(start
, len
);
2846 index
= Convert
.ToInt32(indexStr
);
2850 throw new MonoRailException("Could not convert (param {0}) index to Int32. Value is {1}",
2851 property
, indexStr
);
2854 property
= property
.Substring(0, start
- 1);
2860 private static bool AreEqual(object left
, object right
)
2862 if (left
== null || right
== null) return false;
2864 if (left
is string && right
is String
)
2866 return String
.Compare(left
.ToString(), right
.ToString()) == 0;
2869 if (left
.GetType() == right
.GetType())
2871 return right
.Equals(left
);
2874 IConvertible convertible
= left
as IConvertible
;
2876 if (convertible
!= null)
2880 object newleft
= convertible
.ToType(right
.GetType(), null);
2881 return (newleft
.Equals(right
));
2889 return left
.ToString().Equals(right
.ToString());
2892 private string SafeHtmlEncode(string content
)
2894 if (Context
!= null)
2896 return HtmlEncode(content
);
2903 /// Determines whether the present value matches the value on
2904 /// the initialSetValue (which can be a single value or a set)
2906 /// <param name="value">Value from the datasource</param>
2907 /// <param name="initialSetValue">Value from the initial selection set</param>
2908 /// <param name="propertyOnInitialSet">Optional. Property to obtain the value from</param>
2909 /// <param name="isMultiple"><c>true</c> if the initial selection is a set</param>
2910 /// <returns><c>true</c> if it's selected</returns>
2911 protected internal static bool IsPresent(object value, object initialSetValue
,
2912 ValueGetter propertyOnInitialSet
, bool isMultiple
)
2916 object valueToCompare
= initialSetValue
;
2918 if (propertyOnInitialSet
!= null)
2920 // propertyOnInitialSet.GetValue(initialSetValue, null);
2921 valueToCompare
= propertyOnInitialSet
.GetValue(initialSetValue
);
2924 return AreEqual(value, valueToCompare
);
2928 foreach(object item
in (IEnumerable
) initialSetValue
)
2930 object valueToCompare
= item
;
2932 if (propertyOnInitialSet
!= null)
2934 // valueToCompare = propertyOnInitialSet.GetValue(item, null);
2935 valueToCompare
= propertyOnInitialSet
.GetValue(item
);
2938 if (AreEqual(value, valueToCompare
))
2948 private static void AddChecked(IDictionary attributes
)
2950 attributes
["checked"] = "checked";
2953 private static void RemoveChecked(IDictionary attributes
)
2955 attributes
.Remove("checked");
2958 private static string CreateHtmlId(string name
)
2960 StringBuilder sb
= new StringBuilder(name
.Length
);
2962 bool canUseUnderline
= false;
2964 foreach(char c
in name
.ToCharArray())
2971 if (canUseUnderline
)
2974 canUseUnderline
= false;
2978 canUseUnderline
= true;
2985 return sb
.ToString();
2989 /// Abstracts the approach to access values on objects.
2991 public abstract class ValueGetter
2996 /// <value>The name.</value>
2997 public abstract string Name { get; }
3002 /// <param name="instance">The instance.</param>
3003 /// <returns></returns>
3004 public abstract object GetValue(object instance
);
3008 /// Implementation of <see cref="ValueGetter"/>
3009 /// that uses reflection to access values
3011 public class ReflectionValueGetter
: ValueGetter
3013 private PropertyInfo propInfo
;
3016 /// Initializes a new instance of the <see cref="ReflectionValueGetter"/> class.
3018 /// <param name="propInfo">The prop info.</param>
3019 public ReflectionValueGetter(PropertyInfo propInfo
)
3021 this.propInfo
= propInfo
;
3027 /// <value>The name.</value>
3028 public override string Name
3030 get { return propInfo.Name; }
3036 /// <param name="instance">The instance.</param>
3037 /// <returns></returns>
3038 public override object GetValue(object instance
)
3042 return propInfo
.GetValue(instance
, null);
3044 catch(TargetException
)
3046 PropertyInfo tempProp
= instance
.GetType().GetProperty(Name
);
3048 if (tempProp
== null)
3053 return tempProp
.GetValue(instance
, null);
3059 /// Implementation of <see cref="ValueGetter"/>
3060 /// that uses reflection and recusion to access values
3062 public class RecursiveReflectionValueGetter
: ValueGetter
3064 private readonly string[] keyName
;
3065 private readonly string name
= string.Empty
;
3068 /// Initializes a new instance of the <see cref="RecursiveReflectionValueGetter"/> class.
3070 /// <param name="targetType">The target type to query</param>
3071 /// <param name="keyName">the property path</param>
3072 public RecursiveReflectionValueGetter(Type targetType
, string keyName
)
3074 this.keyName
= keyName
.Split('.');
3075 name
= QueryPropertyInfoRecursive(targetType
, this.keyName
).Name
;
3081 /// <value>The name.</value>
3082 public override string Name
3084 get { return name; }
3090 /// <param name="instance">The instance.</param>
3091 /// <returns></returns>
3092 public override object GetValue(object instance
)
3096 return QueryPropertyRecursive(instance
, keyName
);
3098 catch (TargetException
)
3100 PropertyInfo tempProp
= instance
.GetType().GetProperty(Name
);
3102 if (tempProp
== null)
3107 return tempProp
.GetValue(instance
, null);
3113 /// Implementation of <see cref="ValueGetter"/>
3114 /// to access DataRow's value
3116 public class DataRowValueGetter
: ValueGetter
3118 private readonly string columnName
;
3121 /// Initializes a new instance of the <see cref="DataRowValueGetter"/> class.
3123 /// <param name="columnName">Name of the column.</param>
3124 public DataRowValueGetter(string columnName
)
3126 this.columnName
= columnName
;
3132 /// <value>The name.</value>
3133 public override string Name
3135 get { return columnName; }
3141 /// <param name="instance">The instance.</param>
3142 /// <returns></returns>
3143 public override object GetValue(object instance
)
3145 DataRow row
= (DataRow
) instance
;
3147 return row
[columnName
];
3152 /// Implementation of <see cref="ValueGetter"/>
3153 /// to access DataRowView's value
3155 public class DataRowViewValueGetter
: ValueGetter
3157 private readonly string columnName
;
3160 /// Initializes a new instance of the <see cref="DataRowViewValueGetter"/> class.
3162 /// <param name="columnName">Name of the column.</param>
3163 public DataRowViewValueGetter(string columnName
)
3165 this.columnName
= columnName
;
3171 /// <value>The name.</value>
3172 public override string Name
3174 get { return columnName; }
3180 /// <param name="instance">The instance.</param>
3181 /// <returns></returns>
3182 public override object GetValue(object instance
)
3184 DataRowView row
= (DataRowView
)instance
;
3186 return row
[columnName
];
3191 /// Empty implementation of a <see cref="ValueGetter"/>
3193 public class NoActionGetter
: ValueGetter
3198 /// <value>The name.</value>
3199 public override string Name
3201 get { return string.Empty; }
3207 /// <param name="instance">The instance.</param>
3208 /// <returns></returns>
3209 public override object GetValue(object instance
)
3216 /// Implementation of <see cref="ValueGetter"/>
3217 /// to access enum fields
3219 public class EnumValueGetter
: ValueGetter
3221 private readonly Type enumType
;
3224 /// Initializes a new instance of the <see cref="EnumValueGetter"/> class.
3226 /// <param name="enumType">Type of the enum.</param>
3227 public EnumValueGetter(Type enumType
)
3229 this.enumType
= enumType
;
3235 /// <value>The name.</value>
3236 public override string Name
3238 get { return string.Empty; }
3244 /// <param name="instance">The instance.</param>
3245 /// <returns></returns>
3246 public override object GetValue(object instance
)
3248 return Convert
.ToDecimal(Enum
.Format(enumType
, Enum
.Parse(enumType
, Convert
.ToString(instance
)), "d"));
3253 /// Abstract factory for <see cref="ValueGetter"/> implementations
3255 public class ValueGetterAbstractFactory
3258 /// Creates the specified target type.
3260 /// <param name="targetType">Type of the target.</param>
3261 /// <param name="keyName">Name of the key.</param>
3262 /// <returns></returns>
3263 public static ValueGetter
Create(Type targetType
, string keyName
)
3265 if (targetType
== null)
3267 return new NoActionGetter();
3269 else if (targetType
== typeof(DataRow
))
3271 return new DataRowValueGetter(keyName
);
3273 else if (targetType
== typeof(DataRowView
))
3275 return new DataRowViewValueGetter(keyName
);
3277 else if (targetType
.IsEnum
)
3279 return new EnumValueGetter(targetType
);
3285 // check for recusion
3286 if(keyName
.Contains("."))
3288 info
= QueryPropertyInfoRecursive(targetType
, keyName
.Split('.'));
3292 return new RecursiveReflectionValueGetter(targetType
, keyName
);
3297 info
= targetType
.GetProperty(keyName
, ResolveFlagsToUse(targetType
));
3300 return new ReflectionValueGetter(info
);
3311 #region FormScopeInfo
3315 private readonly string target
;
3316 private readonly bool isValidationEnabled
;
3319 /// Initializes a new instance of the <see cref="FormScopeInfo"/> class.
3321 /// <param name="target">The target.</param>
3322 /// <param name="isValidationEnabled">if set to <c>true</c> [is validation enabled].</param>
3323 public FormScopeInfo(string target
, bool isValidationEnabled
)
3325 this.target
= target
;
3326 this.isValidationEnabled
= isValidationEnabled
;
3330 /// Gets the root target.
3332 /// <value>The root target.</value>
3333 public string RootTarget
3335 get { return target; }
3339 /// Gets a value indicating whether this instance is validation enabled.
3342 /// <c>true</c> if this instance is validation enabled; otherwise, <c>false</c>.
3344 public bool IsValidationEnabled
3346 get { return isValidationEnabled; }
3352 private static BindingFlags
ResolveFlagsToUse(Type type
)
3354 if (type
.Assembly
.FullName
.StartsWith("DynamicAssemblyProxyGen") || type
.Assembly
.FullName
.StartsWith("DynamicProxyGenAssembly2"))
3356 return PropertyFlags2
;
3359 return PropertyFlags
;