Установка TabIndex WebControls на основе ControlId другого элемента управления - PullRequest
1 голос
/ 01 апреля 2010

У меня есть сайт ASP.NET Webforms, в который регулярно добавляются новые функции. Большую часть времени новый WebControl добавляется на страницу, и мне нужно увеличить TabIndex для всех последующих элементов управления на странице.

Я бы предпочел более надежное решение, чем выбор произвольного промежутка между начальными назначенными индексами вкладок. Настройка индексов вкладок с использованием функциональности порядка вкладок дизайнера - один из вариантов, но я бы предпочел остаться в исходном виде.

В идеале, если бы у меня было, например, три флажка, я бы хотел иметь возможность определить tabindex на основе предыдущего tabindex элементов управления. Тогда мне нужно будет только вставить новый элемент управления и изменить один существующий элемент управления.

Например, добавьте новое свойство TabIndexAfterControlId в 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"/>

Моей первой мыслью было расширение System.Web.UI.WebControls.WebControl новым свойством, но свойства расширения не поддерживаются .

Ответы [ 2 ]

0 голосов
/ 01 июня 2010

Моя предыдущая попытка использовать синтаксис привязки данных (<% # ...%>) для установки TabIndex для веб-элементов управления не удалась, когда некоторые элементы управления не связывали TabIndex (CheckBox). Это также не было идеальным, так как мне нужно было передать ссылку на текущий элемент управления в метод кода.

На этот раз я использовал пользовательский ExpressionBuilder , который принимает имя веб-элемента управления, которому должен следовать текущий элемент управления в порядке табуляции.

TabIndexAfterExpressionBuilder изначально возвращает шорт -1 в качестве значения. В то же время он регистрируется с событием LoadComplete текущей страницы. При возникновении этого события оба элемента управления обнаруживаются, и индексы вкладок устанавливаются в соответствии с их относительными позициями.

Пример WebControls с использованием построителя выражений TabIndex

<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>
0 голосов
/ 18 мая 2010

Примечание. Этот подход работал для некоторых веб-контролей (DropDownLists), но не для всех (CheckBoxes). Я оставил это здесь для справки.

В итоге я нашел решение, использующее метод кода для фиксации отношений между элементами управления.

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

Метод code code изначально выполняет базовое назначение TabIndex, которое хорошо работает, когда порядок табуляции соответствует порядку элементов управления на странице. Затем во время события PreRender порядок индекса вкладки будет проверен снова. Это важно, если порядок вкладок не соответствует естественному течению страницы.

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

    }
...