Setting a WebControls TabIndex based on the ControlId of another control
我有一个ASP.NET网站,它经常添加一些功能。大多数时候,一个新的WebControl被添加到页面上,我需要将tabindex增加到页面上所有后续控件。
比起在最初分配的标签索引之间选择一个任意的间隙,我更喜欢一个更健壮的解决方案。使用设计器选项卡顺序功能设置选项卡索引是一个选项,但我更喜欢留在源代码视图中。
理想情况下,例如,如果我有三个复选框,我希望能够基于以前的控件tabindex定义tabindex。然后我只需要插入新控件并更改一个现有控件。
例如,将新属性tabindexAfterControlID添加到WebControl:
1 |
我的第一个想法是使用新属性扩展System.Web.UI.WebControls.WebControl,但不支持扩展属性。
我先前尝试使用数据绑定语法(<%…%>)当某些控件无法绑定TabIndex(复选框)时,为Web控件设置TabIndex失败。它也不理想,因为我需要将对当前控件的引用传递到代码隐藏方法中。
这一次,我使用了一个自定义的ExpressionBuilder,它接受当前控件应按选项卡顺序跟随的Web控件的名称。
TabIndexAfterExpressionBuilder最初返回short-1作为值。同时,它向当前页面的LoadComplete事件注册。当此事件激发时,将找到两个控件,并根据它们的相对位置设置选项卡索引。
使用TabIndex表达式生成器的WebControls示例
1 2 3 | <br /> " /><br /> " /> |
tabIndexExpressionBuilder.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 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(禁用或取消控制)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 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
1 2 3 4 5 6 7 | <system.web> <compilation debug="true" targetFramework="4.0"> <expressionBuilders> </expressionBuilders> </compilation> </system.web> |
注意:这种方法适用于某些Web控件(DropDownList),但并非所有控件(复选框)。我把它留在这里作为参考。
我最后得到了一个解决方案,它使用代码隐藏方法来捕获控件之间的关系。
1 2 | '/> '/> |
代码隐藏方法最初将执行基本的tabindex分配,当选项卡顺序遵循页面上控件的顺序时,该方法工作良好。然后在预渲染事件期间,将再次检查选项卡索引顺序。如果标签顺序不遵循页面的自然流动,这一点很重要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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; } } |