根据另一个控件的 ControlId 设置 WebControls TabIndex

发布于 2024-08-28 01:13:25 字数 707 浏览 7 评论 0原文

我有一个 ASP.NET Webforms 站点,经常添加功能。 大多数情况下,新的 WebControl 会添加到页面中,并且我需要将 TabIndex 增加到页面上的所有后续控件。

我更喜欢一个更强大的解决方案,而不是在初始分配的选项卡索引之间选择任意间隙。使用设计器选项卡顺序功能设置选项卡索引是一种选择,但我更愿意保留在源视图中。

理想情况下,例如,如果我有三个复选框,我希望能够根据先前的控件选项卡索引定义选项卡索引。然后我只需要插入新控件并更改一个现有控件。

例如,向 WebControl 添加新属性 TabIndexAfterControlId:

<asp:CheckBox ID="checkBoxA" runat="server" TabIndex="1"/>
<asp:CheckBox ID="checkBoxB" runat="server" TabIndexAfterControlId="checkBoxA"/>
<asp:CheckBox ID="checkBoxC" runat="server" TabIndexAfterControlId="checkBoxB"/>

我的第一个想法是使用新属性扩展 System.Web.UI.WebControls.WebControl,但是 不支持扩展属性

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

天生の放荡 2024-09-04 01:13:25

注意:此方法适用于某些 Web 控件(DropDownList),但不适用于所有 Web 控件(复选框)。我将其留在这里供参考。

我最终得到了一个解决方案,该解决方案使用代码隐藏方法来捕获控件之间的关系。

<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) %>'/>

后面的代码方法最初将执行基本的 TabIndex 分配,当 Tab 键顺序遵循页面上控件的顺序时,该分配效果很好。然后在 PreRender 事件期间,将再次检查选项卡索引顺序。如果 Tab 键顺序不遵循页面的自然流程,这一点很重要。

    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;
        }

    }

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;
        }

    }
寄离 2024-09-04 01:13:25

当某些控件无法绑定 TabIndex (CheckBox) 时,我之前尝试使用数据绑定语法 (<%# ... %>) 为 Web 控件设置 TabIndex 失败。它也不理想,因为我需要将对当前控件的引用传递到代码隐藏方法中。

这次我使用了一个自定义的 ExpressionBuilder ,它接受以下名称:当前控件应遵循 Tab 键顺序的 Web 控件。

TabIndexAfterExpressionBuilder 最初返回短值 -1 作为值。同时注册当前Page的LoadComplete事件。当此事件触发时,将找到两个控件,并根据其相对位置设置选项卡索引。

使用 TabIndex 表达式生成器的示例 WebControl

<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>

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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文