【问题标题】:C#: Get all the controls with runat="server"C#:使用 runat="server" 获取所有控件
【发布时间】:2015-07-16 15:26:15
【问题描述】:

我有很多带有很多控件的 .aspx 页面。这些控件都以类似的方式声明:

<input type="text" runat="server" id="txtLatitude" />

现在我必须检查用户是谁,如果不允许进行更改,请将所有这些控件设为只读。通常我会做一些像

txtLatitude.Attributes.Add("readonly", "readonly")

但我需要永远手动为每个页面中的每个控件执行此操作。我想知道是否有一种方法可以使用 runat="server" 获取列表或所有控件的内容。 我尝试使用

ControlCollection myControls = Page.Controls

得到它们,但我在调试模式下查看了 myControls,它似乎得到了少量控件,可能只有用 asp 特定符号声明的控件 ,不确定。

使用上述列表,我只需执行一个 foreach 循环并为每个循环添加 readonly 属性,只需几行代码。想法? (或者也许我只是愚蠢,无法以正确的方式导航和搜索 myControls =D)

【问题讨论】:

  • 你在递归函数中尝试过 FindControl 吗?
  • C# Get all web controls on page 的可能重复项
  • @FranciscoGoldenstein 我还没有意识到这一点......但是我应该如何管理该方法所需的 id 参数?您是否建议尝试输入所有可能的 id?我可以通过添加不同的前缀来优化它,但它似乎很耗费资源。
  • @RobertMcKee 我尝试了该解决方案,但我认为它不起作用
  • 请注意,要让 ASP.NET Web 表单识别控制服务器端,它必须具有 runat="server"。如果内存服务正确,则客户端 ID 位于 ClientID 属性中。

标签: c# asp.net


【解决方案1】:

取自:Get All Web Controls of a Specific Type on a Page

/// <summary>
/// Provide utilities methods related to <see cref="Control"/> objects
/// </summary>
public static class ControlUtilities
{
    /// <summary>
    /// Find the first ancestor of the selected control in the control tree
    /// </summary>
    /// <typeparam name="TControl">Type of the ancestor to look for</typeparam>
    /// <param name="control">The control to look for its ancestors</param>
    /// <returns>The first ancestor of the specified type, or null if no ancestor is found.</returns>
    public static TControl FindAncestor<TControl>(this Control control) where TControl : Control
    {
        if (control == null) throw new ArgumentNullException("control");

        Control parent = control;
        do
        {
            parent = parent.Parent;
            var candidate = parent as TControl;
            if (candidate != null)
            {
                return candidate;
            }
        } while (parent != null);
        return null;
    }

    /// <summary>
    /// Finds all descendants of a certain type of the specified control.
    /// </summary>
    /// <typeparam name="TControl">The type of descendant controls to look for.</typeparam>
    /// <param name="parent">The parent control where to look into.</param>
    /// <returns>All corresponding descendants</returns>
    public static IEnumerable<TControl> FindDescendants<TControl>(this Control parent) where TControl : Control
    {
        if (parent == null) throw new ArgumentNullException("control");

        if (parent.HasControls())
        {
            foreach (Control childControl in parent.Controls)
            {
                var candidate = childControl as TControl;
                if (candidate != null) yield return candidate;

                foreach (var nextLevel in FindDescendants<TControl>(childControl))
                {
                    yield return nextLevel;
                }
            }
        }
    }
}

然后执行以下操作:

foreach(var ctrl in Page.FindDescendants<HtmlInputText>())
{
    ctrl.Attributes.Add("readonly","readonly");
}

【讨论】:

  • 谢谢,它可以工作:) 看来我所缺少的只是识别 HTML 控件的类型
【解决方案2】:

在您的页面上添加此功能:

void whatYouWannaDo (Control con)
{
    foreach (Control c in con.Controls)
    {
      if (c.Controls.Count > 0)
          whatYouWannaDo(c);
      else
         {
           //Do stuff here
         }
    }
}

你可以在这个递归函数中做任何你想做的事情。并通过输入whatYouWannaDo(Page.Controls) 来调用它

【讨论】:

  • 不起作用,无效参数错误。假设这是因为 Page.Controls 是一个集合,而 Control 是一次出现。我会尽量适应这个...
  • void DoWhatYouWannaDo2(ControlCollection controls) { foreach (Control c in controls) { if (c.Controls.Count &gt; 0) DoWhatYouWannaDo2(c.Controls); else { **StartToDoWhatYouWannaDoHere** } } }
【解决方案3】:

为 VB.NET 道歉。我用一些扩展方法来做到这一点。您还需要从 nuget 获取 IEnumerable 上的 Each 扩展方法(尝试 MoreLinq):

    ''' <summary>
    ''' Returns the control and all descendants as a sequence
    ''' </summary>
    <Extension>
    Public Iterator Function AsEnumerable(control As Control) As IEnumerable(Of Control)
        Dim queue = New Queue(Of Control)
        queue.Enqueue(control)

        Do While queue.Count <> 0
            control = queue.Dequeue()
            Yield control
            For Each c As Control In control.Controls
                queue.Enqueue(c)
            Next
        Loop
    End Function

    <Extension>
    Public Sub SetInputControlsToReadonly(control As Control)
        control.AsEnumerable().Each(Sub(c)
                                        If TypeOf c Is TextBox Then DirectCast(c, TextBox).ReadOnly = True
                                        If TypeOf c Is ListControl Then DirectCast(c, ListControl).Enabled = False
                                    End Sub)
    End Sub

这允许您调用 control.SetInputControlsToReadonly()。有时您不想将所有控件设置为只读,只设置页面上的一个部分。在这种情况下,将控件包装在一个(或使用面板)中,并在其上调用 SetInputControlsToReadonly。

【讨论】:

  • stackoverflow.com/questions/7362482/… 中给出的扩展方法比你的要好得多,因为它实现了 IEnumerable 而不是在返回之前构建整个队列。
  • 我列出的扩展方法返回 IEnumerable,并且使用了一个迭代器块,所以它仍然是惰性的。它不会“在返回之前建立整个队列”。一个使用递归,我的使用迭代。它们只是走树的不同方式。
  • 哦,我明白了。非常有趣,抱歉我错过了。如果 DOM 被延迟解析,它可能会有所不同,因为您在返回第一个之前将所有孩子排入队列,但由于我确实相信 DOM 已经被解析为对象(不是延迟解析),所以应该没有任何显着差异。递归方法是深度优先,你的方法是广度优先。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多