ASP.Net FindControl не работает. Почему?

Я использовал FindControl в прошлом, до .NET 2.0/3.0. Похоже, теперь по какой-то причине идентификаторы моих элементов управления получают причудливое имя. Например, я присвоил флажку идентификатор "cbSelect", но FindControl его не находит. Когда я просматриваю HTML, ему было присвоено значение ctl00_bodyPlaceHolder_ctl02_cbSelect.

Я не нашел ни одного примера FindControl, в котором бы это упоминалось. На самом деле все, кажется, просто используют управление поиском, как обычно.

Итак, я делаю что-то не так? Изменился ли .Net? Может ли кто-нибудь пролить свет на это для меня, это действительно расстраивает!


person LilMoke    schedule 28.04.2009    source источник


Ответы (7)


Вы, вероятно, используете MasterPage или пользовательские элементы управления (ascx), и это причина изменения идентификаторов клиентов. Представьте, что у вас есть элемент управления на главной странице с тем же идентификатором, что и на странице. Это приведет к столкновениям. Изменение идентификатора гарантирует, что все свойства ClientID будут уникальными на странице.

FindControl требует особого внимания при работе с мастер-страницами. Взгляните на ASP.NET 2.0 MasterPages и FindControl(). FindControl работает внутри контейнера имен. MastePage и страница — это разные контейнеры имен.

person Aleris    schedule 28.04.2009
comment
То, как Microsoft реализовала это, такая шутка, оно должно работать. О, у тебя есть мастер-страница? Как насчет 500 вложенных мастер-страниц? Метод должен понять это и сделать все необходимое, чтобы найти контроль, и точка. - person The Muffin Man; 19.05.2013
comment
Знаете ли вы, что используете «должен» и «Майкрософт» в одной и той же мысли? - person user1566694; 13.12.2016

Вы можете написать расширитель, чтобы найти любой элемент управления на странице, используя рекурсию. Это может быть в каком-то классе Util/Helper.

 public static Control FindAnyControl(this Page page, string controlId)
    {
        return FindControlRecursive(controlId, page.Form);
    }

    public static Control FindAnyControl(this UserControl control, string controlId)
    {
        return FindControlRecursive(controlId, control);
    }

    public static Control FindControlRecursive(string controlId, Control parent)
    {
        foreach (Control control in parent.Controls)
        {
            Control result = FindControlRecursive(controlId, control);
            if (result != null)
            {
                return result;
            }
        }
        return parent.FindControl(controlId);
    }
person nemke    schedule 27.01.2010

Мне очень повезло с решением этой проблемы в «большинстве» случаев с помощью простого метода расширения.

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

private static Control FindControlIterative(this Control control, string id)
{
    Control ctl = control;

    LinkedList<Control> controls = new LinkedList<Control>();

    while(ctl != null)
    {
        if(ctl.ID == id)
        {
            return ctl;
        }

        foreach(Control child in ctl.Controls)
        {
            if(child.ID == id)
            {
                return child;
            }

            if(child.HasControls())
            {
                controls.AddLast(child);
            }
        }

        ctl = controls.First.Value;
        controls.Remove(ctl);
    }

    return null;
}
person Stephen M. Redd    schedule 28.04.2009

При поиске элемента управления в коллекции элементов управления всегда используйте идентификатор, назначенный элементу управления, а не тот, который вы видите в исходном пост-рендеринге. Если FindControl() не находит элемент управления, который, как вы знаете, существует, велика вероятность, что вы ищете не в нужной ветви иерархии элементов управления. Рекурсивная функция была успешной для меня.

Вот мой пример того, что я использую для VB.NET 3.5:

Function FindControlRecursive(ByVal ctrl As Control, ByVal id As String) As Control
    Dim c As Control = Nothing

    If ctrl.ID = id Then
        c = ctrl
    Else
        For Each childCtrl In ctrl.Controls
            Dim resCtrl As Control = FindControlRecursive(childCtrl, id)
            If resCtrl IsNot Nothing Then c = resCtrl
        Next
    End If

    Return c
End Function

Вот пример того, как я бы реализовал эту функцию в своем классе базовой страницы:

Dim form HtmlForm = CType(FindControlRecursive(Me, "Form"), HtmlForm)
person Community    schedule 02.03.2010
comment
+1, потому что, не видя вашего, я уже реализовал его на С# с тем же подходом: public static System.Web.UI.Control FindControlRecursive(Control ctl, string id){ if (ctl == null) return null; если (ctl.ID == id) вернуть ctl; if (ctl.Controls.Count › 0) foreach(Control sc в ctl.Controls) { Control fnd = FindControlRecursive(sc, id); если (fnd != null) вернуть fnd; } вернуть ноль; }` - person gkakas; 17.11.2013

Это код VB.NET, который работал у меня:

<Extension()> _
Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control
    If controlToStartWith Is Nothing Then Return Nothing
    If controlToStartWith.ID = controlIdToFind Then Return controlToStartWith
    For Each childControl As Control In controlToStartWith.Controls
        Dim resCtrl As Control = FindChildControlById(childControl, controlIdToFind)
        If resCtrl IsNot Nothing Then Return resCtrl
    Next childControl
    Return Nothing
End Function ' Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control

Первоначальный код VB.NET принадлежит Джорджу. Я лишь немного изменил его с двумя функциональными изменениями: мой не выдает ошибку, если/когда null/Nothing передается в качестве элемента управления вводом, а мой реализован как расширение. Другие 3 моих незначительных изменения не влияют на функциональность, но для меня они были упрощением кода. Но я знаю, что это очень субъективно.

Таким образом, этот метод можно использовать с:

Dim c1 As Control = Page.FindChildControlById("aspControlID")

И если вы хотите преобразовать его в определенный дочерний класс элемента управления, например:

Dim c1 As Control = Page.FindChildControlById("aspControlID")
Dim c As HyperLink = TryCast(c1, HyperLink)

Обновление: моя функция теперь называется «FindChildControlById» (ранее называлась «FindMiControl»). Мне больше понравилось предложение SpeedNet.

person Shawn Kovac    schedule 01.03.2016
comment
Это идеально, лучше, чем другие решения, спасибо! Я переименовал в FindChildControlByID(). - person Speednet; 23.03.2017

При рендеринге html ASP.NET добавит ко всем идентификаторам элементов управления идентификаторы контейнеров именования (пользовательские элементы управления и т. д.) в иерархии, восходящей к корню документа. Это гарантирует, что все идентификаторы уникальны для обратных сообщений и т. д.

Это не влияет на использование FindControl, где вы должны использовать идентификатор в исходной разметке.

person Nick    schedule 28.04.2009

Вот ссылка на то, как называются элементы управления веб-формой...

Идентификация управления веб-формами

person George    schedule 28.04.2009