Rating control in Ajax control toolkit will not raise the event if a user clicks on current rating star because in behaviour js it has checked
if (this._ratingValue != this._currentRating), so I want to override this method without changing js and building toolkit. How can I do it, can I extend rating control and override RatingBehavior.js or any other solution.
_onStarClick : function(e) {
/// <summary>
/// Handler for a star's click event
/// </summary>
/// <param name="e" type="Sys.UI.DomEvent">
/// Event info
/// </param>
if (this._readOnly) {
return;
}
if (this._ratingValue != this._currentRating) {
this.set_Rating(this._currentRating);
}
},
I have solved it by putting the code just above
function AddNewRatingHandler() {
AjaxControlToolkit.RatingBehavior.prototype._onStarClick =
function(e) {
if (this._readOnly) {
return;
}
// if (this._ratingValue != this._currentRating) {
this.set_Rating(this._currentRating);
// }
};
AjaxControlToolkit.RatingBehavior.prototype.set_Rating = function(value) {
// if (this._ratingValue != value) {
this._ratingValue = value;
this._currentRating = value;
if (this.get_isInitialized()) {
if ((value < 0) || (value > this._maxRatingValue)) {
return;
}
this._update();
AjaxControlToolkit.RatingBehavior.callBaseMethod(this, 'set_ClientState', [this._ratingValue]);
this.raisePropertyChanged('Rating');
this.raiseRated(this._currentRating);
this._waitingMode(true);
var args = this._currentRating + ";" + this._tag;
var id = this._callbackID;
if (this._autoPostBack) {
__doPostBack(id, args);
}
else {
WebForm_DoCallback(id, args, this._receiveServerData, this, this._onError, true)
}
}
// }
};
}
AddNewRatingHandler();
</script>
</div>
</form>
Related
I use the following code to add "error class" to invalid controls.
function WebForm_OnSubmit() {
if (typeof (ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) {
var prevControl = "";
for (var i in Page_Validators) {
try {
var control = document.getElementById(Page_Validators[i].controltovalidate);
if (!Page_Validators[i].isvalid) {
control.className = "error";
prevControl = control;
} else {
control.className = "";
}
if (control = prevControl) {
control.className = "error";
}
} catch (e) { }
}
return false;
}
return true;
}
With this approach, when I put the valid input the error message disappears but the error class is still applied to the control.
I want that when the user modifies the input, the control should resume to its normal still immediately after the blur even, not when the user clicks the button again.
My problem is a bit more complicated than the title says:
I made a user control (I called it editor) for editing Data base data into another user control I also made(I called it GridView).
The editor is use for each row (a row is also a usercontrol and the editor is only inside row even for insert) inside my gridview and work perfectly but not when I try to use it to insert.
The only difference between insert and edit is this Field:
#region Field
/// <summary>
///
/// </summary>
//public Field Field { get { return DataItem as Field; } }
private Field _field;
[Bindable(true)]
public Field Field
{
get
{
if (IsInsert && _field == null)
{
_field = SubscriptionController.CreateField();
}
return _field;
}
set { _field = value; }
}
#endregion
Inside this Field I've the collection I bind to the repeater
the SubscriptionController.CreateField(); method just create an instance of Field class and all collection inside here is the code:
public Field CreateField()
{
Field field = new Field();
field.Type = GetFieldTypes().First();
field.Label = new LocalizedStringCollection();
field.Values = new FieldValueCollection();
field.Selections = new FieldSelectionCollection();
foreach (Models.TrainingGroup trainingGroup in GetTrainingGroup())
{
foreach (Models.Division division in GetDivisions())
{
foreach (Models.ProfilStatusGroup profilStatusGroup in GetProfilStatusGroup())
{
field.Selections.Add(new Models.FieldSelection() { Selected = false, DivisionId = division.Id, ProfilStatusGroupId = profilStatusGroup.Id, TrainingGroupId = trainingGroup.Id });
}
}
}
}
the collection I bind is stored in viewstate :
#region FieldValues
/// <summary>
/// Get/Set FieldValues from Viewstate
/// </summary>
public FieldValueCollection FieldValues
{
get
{
if (ViewState["FieldValues"] == null)
{
if (Field != null && Field.Values != null)
ViewState.Add("FieldValues", Field.Values);
else
ViewState.Add("FieldValues", new FieldValueCollection());
}
if (ViewState["FieldValues"] != null)
{
return (FieldValueCollection)ViewState["FieldValues"];
}
return null;
}
set
{
if (ViewState["FieldValues"] == null)
{
ViewState.Add("FieldValues", value);
}
else
{
ViewState["FieldValues"] = value;
}
}
}
#endregion
but when I get on postback all textboxes inside my repeater are empty.
I've been using my own Error reporting module which was combination of simple c# and jQueryUI Dialog. Problem is that once error or success occurs i do write it's value to session. It does work pretty good on pages with Responce.Redirect on error but not on pages where i catch an error and then return to same form.
My question is why does session which added pre-postback fails to load in pages where i have return statement on some condition.
And if there another way to save errors and success message except in session ? Maybe global variables or something like that ...
CODE EXAMPLES
this is Error class
public static string getMessage()
{
HttpContext c = HttpContext.Current;
string messageType = "";
if (c.Session["errorMessage"] != null)
{
messageType = "errorMessage";
}
else if (c.Session["successMessage"] != null)
{
messageType = "successMessage";
}
if (!string.IsNullOrEmpty(messageType))
{
string[] messageBody = c.Session[messageType].ToString().Split('|');
StringBuilder userMessageSb = new StringBuilder();
userMessageSb.Append(string.Format("<div id=\"{0}\" title=\"{1}\">{2}</div>", messageType, messageBody[0], messageBody[1]));
// fix so message will not re-appear
c.Session.Remove(messageType);
messageType = userMessageSb.ToString();
}
return messageType;
}
public static void setSuccess(string successMessage)
{
HttpContext.Current.Session["successMessage"] = setMessage("success", successMessage);
}
public static void setError(string errorMessage)
{
HttpContext.Current.Session["errorMessage"] = setMessage("error", errorMessage);
}
private static string setMessage(string messageTitle, string messageBody)
{
return string.Format("{0}|{1}", messageTitle, messageBody);
}
i set message like this prior to redirect or return
Errors.setError(my error is");
i get error on bottom of my masterpage like this
<%= Errors.getMessage() %>
and this is JS
$(function () {
$("#errorMessage").dialog("destroy");
$("#successMessage").dialog("destroy");
if ($("#errorMessage").length != 0) {
$("#errorMessage").dialog({
modal: true,
height: 300,
width: 400,
buttons: {
Ok: function () {
$(this).dialog('close');
}
}
});
}
if ($("#successMessage").length != 0) {
$("#successMessage").dialog({
modal: true,
height: 300,
width: 400,
buttons: {
Ok: function () {
$(this).dialog('close');
}
}
});
}
});
There is a possibility that <%= Errors.getMessage() %> executes before you call Errors.setError(my error is") in case when you are not redirecting.
Hope below answer helps.
Create a property in your master page code behind
public string MessagePlaceholder
{
get { return messagePlaceholder.InnerHtml; }
set { messagePlaceholder.InnerHtml = value; }
}
Replace <%= Errors.getMessage() %> with a div place holder like below
<div id="messagePlaceholder" runat="server"></div>
And here is your setError method
public static void setError(string errorMessage, bool redirecting)
{
HttpContext.Current.Session["errorMessage"] = setMessage("error", errorMessage);
if (!redirecting)
{
((HttpContext.Current.Handler as System.Web.UI.Page).Master as YourMasterPageType).MessagePlaceholder = getMessage();
}
}
EDIT
Sorry I forgot this
In Page_Load event of your master page
if(!IsPostBack)
{
messagePlaceholder.InnerHtml = Errors.getMessage();
}
I have two validation groups: parent and child
I have an add button that needs to only validate the child validation group which is easily done. The save button needs to validate against the parent and child validation groups, both client side and server side. I think I know how to do it server side by calling the Page.Validate("groupname") method for each group, but how can it be done client side?
You should be able to accomplish this by creating a javascript function that uses Page_ClientValidate and then having the button call that function
<asp:Button ID="btnSave" Text="Save" OnClientClick="return validate()" runat="server" />
<script type="text/javascript">
function validate() {
var t1 = Page_ClientValidate("parent");
var t2 = Page_ClientValidate("child");
if (!t1 || !t2) return false;
return true;
}
</script>
The problem with CAbbott's answer is that validation errors that occur in the "parent" group will not be displayed after the call to validate the "child" group. The more minor problem with Oleg's answer is that validation of the "child" group will not occur until the "parent" group is ready.
All we really need to do to allow client-side validation of more than one group at the same time is to override the Javascript IsValidationGroupMatch method which determines whether or not a control is to be included in the current set being validated.
For example:
(function replaceValidationGroupMatch() {
// If this is true, IsValidationGroupMatch doesn't exist - oddness is afoot!
if (!IsValidationGroupMatch) throw "WHAT? IsValidationGroupmatch not found!";
// Replace ASP.net's IsValidationGroupMatch method with our own...
IsValidationGroupMatch = function(control, validationGroup) {
if (!validationGroup) return true;
var controlGroup = '';
if (typeof(control.validationGroup) === 'string') controlGroup = control.validationGroup;
// Deal with potential multiple space-delimited groups being validated
var validatingGroups = validationGroup.split(' ');
for (var i = 0; i < validatingGroups.length; i++) {
if (validatingGroups[i] === controlGroup) return true;
}
// Control's group not in any being validated, return false
return false;
};
} ());
// You can now validate against multiple groups at once, for example:
// space-delimited list. This would validate against the Decline group:
//
// Page_ClientValidate('Decline');
//
// while this would validate against the Decline, Open and Complete groups:
//
// Page_ClientValidate('Open Decline Complete');
//
// so if you wanted to validate all three upon click of a button, you'd do:
<asp:Button ID="yourButton" runat="server"
OnClick="ButtonSave_Click" CausesValidation="false"
OnClientClick="return Page_ClientValidate('Open Decline Complete');" />
If you call Page_ClientValidate(..) twice, only the last validation result will be shown and it can be OK while the first is not. So the second call should be made only if the first has returned true
<script type="text/javascript">
var parentOk= Page_ClientValidate('parent');
var childOk = false;
if (parentOk) {
childOk = Page_ClientValidate('child');
}
return parentOk && childOk;
</script>
Whatever way you do it requires some hacking to get round ASP.Net's assumption that you wouldn't try to do this. I favour a reusable approach which is explicit about the hackery involved.
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebSandbox.Validators
{
/// <summary>
/// <para>
/// Validates a different validation group. Among the use cases envisioned are
/// <list type="">
/// <item>
/// Validating one set of rules when the user clicks "Save draft" and validating those rules plus some
/// extra consistency checks when they click "Send".
/// </item>
/// <item>
/// Grouping controls in a <code>fieldset</code> into a validation group with a
/// <code>ValidationSummary</code> and then having a final <code>ValidationSummary</code> which tells the
/// user which groups still have errors.
/// </item>
/// </list>
/// </para>
/// <para>
/// We include checks against setting <code>GroupToValidate</code> to the same value as
/// <code>ValidationGroup</code>, but we don't yet include checks for infinite recursion with one validator
/// in group A which validates group B and another in group B which validates group A. Caveat utilitor.
/// </para>
/// </summary>
public class ValidationGroupValidator : BaseValidator
{
public string GroupToValidate
{
get { return ViewState["G2V"] as string; }
set { ViewState["G2V"] = value; }
}
protected override bool ControlPropertiesValid()
{
if (string.IsNullOrEmpty(GroupToValidate)) throw new HttpException("GroupToValidate not specified");
if (GroupToValidate == ValidationGroup) throw new HttpException("Circular dependency");
// Don't call the base, because we don't want a "control to validate"
return true;
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute("evaluationfunction", "ValidateValidationGroup");
writer.AddAttribute("GroupToValidate", GroupToValidate);
}
protected override void OnPreRender(EventArgs e)
{
// The standard validation JavaScript is too restrictive for this validator to work, so we have to replace a key function.
// Fortunately this runs later than the standard JS, so we can simply overwrite the existing value of Page_ClientValidate.
Page.ClientScript.RegisterStartupScript(typeof(ValidationGroupValidator), "validationJS", _ValidationJS);
base.OnPreRender(e);
}
protected override bool EvaluateIsValid()
{
if (string.IsNullOrEmpty(GroupToValidate)) return false;
bool groupValid = true;
foreach (IValidator validator in Page.GetValidators(GroupToValidate))
{
validator.Validate();
groupValid &= validator.IsValid;
}
return groupValid;
}
private const string _ValidationJS = #"<script type=""text/javascript"">
function ValidateValidationGroup(val) {
if (typeof(val.GroupToValidate) == ""string"") {
val.valid = PageMod_DoValidation(val.GroupToValidate);
}
}
function Page_ClientValidate(validationGroup) {
Page_InvalidControlToBeFocused = null;
if (!Page_Validators) return true;
var i, ctrl;
// Mark everything as valid.
for (i = 0; i < Page_Validators.length; i++) {
Page_Validators[i].finalValid = true;
}
if (Page_ValidationSummaries) {
for (i = 0; i < Page_ValidationSummaries.length; i++) {
Page_ValidationSummaries[i].finalDisplay = ""none"";
}
}
// Validate.
var groupValid = PageMod_DoValidation(validationGroup);
// Update displays once.
for (i = 0; i < Page_Validators.length; i++) {
ctrl = Page_Validators[i];
ctrl.isvalid = ctrl.finalValid;
ValidatorUpdateDisplay(ctrl);
}
if (Page_ValidationSummaries) {
for (i = 0; i < Page_ValidationSummaries.length; i++) {
ctrl = Page_ValidationSummaries[i];
ctrl.style.display = ctrl.finalDisplay;
}
}
ValidatorUpdateIsValid();
Page_BlockSubmit = !Page_IsValid;
return Page_IsValid;
}
function PageMod_DoValidation(validationGroup) {
var groupValid = true, validator, i;
for (i = 0; i < Page_Validators.length; i++) {
validator = Page_Validators[i];
ValidatorValidate(validator, validationGroup, null);
validator.finalValid &= validator.isvalid;
groupValid &= validator.isvalid;
}
if (Page_ValidationSummaries) {
ValidationSummaryOnSubmit(validationGroup, groupValid);
var summary;
for (i = 0; i < Page_ValidationSummaries.length; i++) {
summary = Page_ValidationSummaries[i];
if (summary.style.display !== ""none"") summary.finalDisplay = summary.style.display;
}
}
return groupValid;
}
</script>";
}
}
I have an ASP.NET Webforms site that is regularly having features added.
The majority of time a new WebControl is added to the page and I need to increment the TabIndex to all subsequent controls on the page.
I'd prefer a more robust solution than choosing an arbitrary gap between the initial assigned tab indexes. Setting the tab indexes using the designer tab order functionality is one option but I'd prefer to stay in the source view.
Ideally, if I had, for example, three check boxes I'd like to be able to define the tabindex based off the previous controls tabindex. Then I'd only need to insert the new control and change one existing control.
For example, add a new property TabIndexAfterControlId to WebControl:
<asp:CheckBox ID="checkBoxA" runat="server" TabIndex="1"/>
<asp:CheckBox ID="checkBoxB" runat="server" TabIndexAfterControlId="checkBoxA"/>
<asp:CheckBox ID="checkBoxC" runat="server" TabIndexAfterControlId="checkBoxB"/>
My first thought was to extend System.Web.UI.WebControls.WebControl with a new property, but extension properties aren't supported.
Note: This approach worked for some webcontrols (DropDownLists) but not all of them (CheckBoxes). I've left it here for reference.
I've ended up with a solution that uses a code behind method to capture the relationship between controls.
<asp:CheckBox ID="checkBoxA" runat="server" TabIndex="1"/>
<asp:CheckBox ID="checkBoxB" runat="server" TabIndex='<%# TabIndexAfter(checkBoxB, checkBoxA) %>'/>
<asp:CheckBox ID="checkBoxC" runat="server" TabIndex='<%# TabIndexAfter(checkBoxC, checkBoxB) %>'/>
The code behind method will initially do a basic TabIndex assignment, which works well when the tab order follows the order of the controls on a page. Then during the PreRender event the tab index order will be checked again. This is important if the tab order doesn't not follow the natural flow of the page.
private LinkedList<WebControl> _webControlTabOrder;
/// <summary>
/// Assign the current WebControl TabIndex a value greater than the prior WebControl.
/// </summary>
/// <param name="currentWebControl">The current WebControl to set the TabIndex for</param>
/// <param name="priorWebControl">The prior WebControl to get the previous TabIndex from.</param>
/// <returns>The new TabIndex for the control</returns>
public int TabIndexAfter(WebControl currentWebControl, WebControl priorWebControl)
{
if (_webControlTabOrder == null)
{
_webControlTabOrder = new LinkedList<WebControl>();
this.PreRender += new EventHandler(UserControlBase_PreRender);
}
LinkedListNode<WebControl> priorNode = _webControlTabOrder.Find(currentWebControl);
if (priorNode == null)
{
priorNode = _webControlTabOrder.AddLast(priorWebControl);
}
_webControlTabOrder.AddAfter(priorNode, currentWebControl);
return priorWebControl.TabIndex + 1;
}
void UserControlBase_PreRender(object sender, EventArgs e)
{
LinkedListNode<WebControl> currentNode = _webControlTabOrder.First;
while(currentNode.Next != null)
{
LinkedListNode<WebControl> nextNode = currentNode.Next;
if (nextNode.Value.TabIndex <= currentNode.Value.TabIndex)
{
nextNode.Value.TabIndex = (short)(currentNode.Value.TabIndex + 1);
}
currentNode = nextNode;
}
}
My prior attempt using the data-binding syntax (<%# ... %>) to set the TabIndex for web controls failed when some controls wouldn't bind the TabIndex (CheckBox). It also wasn't ideal as I needed to pass a reference to the current control into the code behind method.
This time around I went with a custom ExpressionBuilder that accepts the name of the web control that the current control should follow in the tab order.
The TabIndexAfterExpressionBuilder initially returns the short -1 as the value. At the same time it registers with the LoadComplete event of the current Page. When this event fires both controls are found and the tab indexes set according to their relative positions.
Example WebControls using the TabIndex Expression Builder
<asp:TextBox ID="txtTextBox0" runat="server" TabIndex="1" /><br />
<asp:TextBox ID="txtTextBox1" runat="server" TabIndex="<%$ TabIndex:txtTextBox0 %>" /><br />
<asp:TextBox ID="txtTextBox2" runat="server" TabIndex="<%$ TabIndex:txtTextBox1 %>" />
TabIndexExpressionBuilder.cs
namespace ExpressionBuilders
{
public class TabIndexExpressionBuilder : ExpressionBuilder
{
public override System.CodeDom.CodeExpression GetCodeExpression(System.Web.UI.BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
{
string priorControlId = entry.Expression.Trim();
string currentControlId = entry.ControlID;
CodeExpression[] inputParams = new CodeExpression[] { new CodePrimitiveExpression(priorControlId),
new CodePrimitiveExpression(currentControlId),
new CodeTypeOfExpression(entry.DeclaringType),
new CodePrimitiveExpression(entry.PropertyInfo.Name) };
// Return a CodeMethodInvokeExpression that will invoke the GetRequestedValue method using the specified input parameters
return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(this.GetType()),
"GetRequestedValue",
inputParams);
}
public static object GetRequestedValue(string priorControlId, string currentControlId, Type targetType, string propertyName)
{
if (HttpContext.Current == null)
{
return null;
}
Page page = HttpContext.Current.Handler as Page;
if (page != null)
{
page.LoadComplete += delegate(object sender, EventArgs e)
{
WebControl currentWebControl = FindControlRecursive(page, currentControlId);
WebControl priorWebControl = FindControlRecursive(page, priorControlId);
if (currentWebControl != null && priorWebControl != null)
{
TabIndexAfter(page, currentWebControl, priorWebControl);
}
};
}
// Default TabIndex
short value = (short)-1;
return value;
}
private static WebControl FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) { return rootControl as WebControl; }
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn = FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null)
{
return controlToReturn as WebControl;
}
}
return null;
}
#region Tabbing
/// <summary>
/// Assign the current WebControl TabIndex a value greater than the prior WebControl.
/// </summary>
/// <param name="currentWebControl">The current Control to set the TabIndex for</param>
/// <param name="priorWebControl">The prior Control to get the previous TabIndex from.</param>
/// <returns>The new TabIndex for the current control</returns>
private static short TabIndexAfter(Page page, WebControl currentWebControl, object prior)
{
TabOrderWebControl tabOrderWebControl = page.FindControl("TabOrderWebControl") as TabOrderWebControl;
if (tabOrderWebControl == null)
{
tabOrderWebControl = new TabOrderWebControl();
page.Controls.Add(tabOrderWebControl);
}
WebControl priorWebControl = prior as WebControl;
if (priorWebControl == null)
{
string priorWebControlId = prior as string;
priorWebControl = page.FindControl(priorWebControlId) as WebControl;
}
if (currentWebControl == null) { throw new ArgumentNullException("currentWebControl"); }
if (priorWebControl == null) { throw new ArgumentNullException("priorWebControl"); }
if (currentWebControl == priorWebControl) { throw new ArgumentException("priorWebControl is the same as the currentWebControl", "priorWebControl"); }
tabOrderWebControl.TabIndexAfter(currentWebControl, priorWebControl);
return currentWebControl.TabIndex;
}
#endregion
}
}
TabOrderWebControl.cs
namespace ExpressionBuilders
{
public class TabOrderWebControl :
WebControl
{
LinkedList<WebControl> _webControlTabOrder;
internal void TabIndexAfter(System.Web.UI.WebControls.WebControl currentWebControl, System.Web.UI.WebControls.WebControl priorWebControl)
{
if (_webControlTabOrder == null)
{
_webControlTabOrder = new LinkedList<WebControl>();
this.Page.PreRender += new EventHandler(PageBase_PreRender);
}
LinkedListNode<WebControl> priorNode = _webControlTabOrder.Find(priorWebControl);
LinkedListNode<WebControl> currentNode = _webControlTabOrder.Find(currentWebControl);
if (currentNode != null)
{
//The current node is already in the list (it must preceed some other control)
//Add the prior node before it.
if (priorNode == null)
{
priorNode = _webControlTabOrder.AddBefore(currentNode, priorWebControl);
}
else
{
//Both nodes are already in the list. Ensure the ordering is correct.
bool foundPriorNode = false;
foreach (WebControl controlNode in _webControlTabOrder)
{
if (controlNode == priorWebControl)
{
foundPriorNode = true;
}
else if (controlNode == currentWebControl)
{
if (foundPriorNode)
{
//Ordering is correct
break;
}
else
{
throw new ApplicationException(string.Format("WebControl ordering is incorrect. Found {1} before {0}", currentWebControl.ID, priorWebControl.ID));
}
}
}
}
}
else if (priorNode == null)
{
//Neither control is in the list yet.
priorNode = _webControlTabOrder.AddLast(priorWebControl);
currentNode = _webControlTabOrder.AddAfter(priorNode, currentWebControl);
}
else
{
//Prior node is already in the list but the current node isn't
currentNode = _webControlTabOrder.AddAfter(priorNode, currentWebControl);
}
}
/// <summary>
/// Once all the controls have been added to the linked list ensure the tab ordering is correct.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void PageBase_PreRender(object sender, EventArgs e)
{
AssignTabIndexes();
}
/// <summary>
/// Reassign tab indexes for all known controls.
/// </summary>
protected void AssignTabIndexes()
{
LinkedListNode<WebControl> currentNode = _webControlTabOrder.First;
while (currentNode.Next != null)
{
LinkedListNode<WebControl> nextNode = currentNode.Next;
WebControl currentControl = currentNode.Value;
WebControl nextControl = nextNode.Value;
if (currentControl == nextControl)
{
throw new ApplicationException("Control added twice");
}
short currentTabIndex = currentControl.TabIndex;
short nextTabIndex = nextControl.TabIndex;
if (nextTabIndex <= currentTabIndex)
{
nextControl.TabIndex = (short)(currentTabIndex + 1);
}
currentNode = nextNode;
}
}
}
}
web.config
<system.web>
<compilation debug="true" targetFramework="4.0">
<expressionBuilders>
<add expressionPrefix="TabIndex" type="ExpressionBuilders.TabIndexExpressionBuilder, ExpressionBuilders"/>
</expressionBuilders>
</compilation>
</system.web>