获取页面上特定类型的所有 Web 控件

Posted

技术标签:

【中文标题】获取页面上特定类型的所有 Web 控件【英文标题】:Get All Web Controls of a Specific Type on a Page 【发布时间】:2011-11-13 19:37:46 【问题描述】:

我一直在思考如何获取页面上的所有控件,然后在这个相关问题中对它们执行任务:

How to Search Through a C# DropDownList Programmatically

我需要可以扫描页面、获取所有 DropDownList 控件并在列表中返回它们的代码。

我目前不得不编辑每个单独的控件,我希望能够动态循环每个控件以执行我的任务。

【问题讨论】:

【参考方案1】:

检查my previous SO answer。

基本上,这个想法是使用以下方法包装遍历控件集合的递归:

private void GetControlList<T>(ControlCollection controlCollection, List<T> resultCollection)
where T : Control

    foreach (Control control in controlCollection)
    
        //if (control.GetType() == typeof(T))
        if (control is T) // This is cleaner
            resultCollection.Add((T)control);

        if (control.HasControls())
            GetControlList(control.Controls, resultCollection);
    

并使用它:

List<DropDownList> allControls = new List<DropDownList>();
GetControlList<DropDownList>(Page.Controls, allControls )
foreach (var childControl in allControls )

//     call for all controls of the page

[2013 年 11 月 26 日编辑]:这是实现此目标的更优雅的方法。我写了两个扩展方法,可以在两个方向上遍历控制树。这些方法以更 Linq 的方式编写,因为它产生一个可枚举:

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

感谢this关键字,这些方法是扩展方法,可以简化代码。

例如,要查找页面中的所有DropDownList,您可以简单地调用:

var allDropDowns = this.Page.FindControl<DropDownList>();

由于使用了yield 关键字,并且由于 Linq 足够聪明,可以推迟枚举的执行,您可以调用(例如):

var allDropDowns = this.Page.FindDescendants<DropDownList>();
var firstDropDownWithCustomClass = allDropDowns.First(
    ddl=>ddl.CssClass == "customclass"
    );

只要满足First 方法中的谓词,枚举就会停止。不会遍历整个控制树。

【讨论】:

最全面的答案,它就像一个魅力欢呼的伙伴。 msdn.microsoft.com/en-us/library/yt340bh4.aspxmsdn上的好文章也不错。 我喜欢。很好,我不再需要使用 yield 关键字,这使得递归函数始终运行,即使我声明 Visual Studio 跳过它。 如果您添加实际使用此扩展方法的示例会更好。例如,foreach (var tbox in Page.GetAllControls&lt;TextBox&gt;()) tbox.Text = box.Text.Trim(); 直接回答原始问题。 @Fernando68:你应该问一个特定的问题...我展示的代码来自一个更大的项目...我无法发布它【参考方案2】:
foreach (DropDownList dr in this.Page.Form.Controls.OfType<DropDownList>())



【讨论】:

是的!您需要迭代每个控件 Controls 集合 - 递归。但我认为这些扩展方法真的很有用,因为它们让你的代码更紧凑——用更少的行做更多的工作。 我看不到这个答案对于包含其他控件的控件的任何页面如何正确工作。正如@JamesJohnson 指出的那样,要正确找到所有 DropDownList,您需要递归搜索所有控件(正如许多其他答案所做的那样),而不仅仅是***控件。在我的实现中,这个答案没有返回我页面上存在的所有控件。我错过了什么吗?【参考方案3】:

这是一个递归版本,它返回请求类型的控件集合,而不是使用另一个参数:

using System.Collections.Generic;
using System.Web.UI;
// ...
public static List<T> GetControls<T>(ControlCollection Controls)
where T : Control 
  List<T> results = new List<T>();
  foreach (Control c in Controls) 
    if (c is T) results.Add((T)c);
    if (c.HasControls()) results.AddRange(GetControls<T>(c.Controls));
  
  return results;

插入你的类(静态可选)。

【讨论】:

【参考方案4】:

有这个问题,虽然我发现史蒂夫 B 的回答很有用,但我想要一个扩展方法,所以重新分解它:

    public static IEnumerable<T> GetControlList<T>(this ControlCollection controlCollection) where T : Control
    
        foreach (Control control in controlCollection)
        
            if (control is T)
            
                yield return (T)control;
            

            if (control.HasControls())
            
                foreach (T childControl in control.Controls.GetControlList<T>())
                
                    yield return childControl;
                
            
        
    

【讨论】:

我在“this”上出现智能感知错误,提示“Type expected”。我只是将它添加到 app_code 文件夹中的一个类中。【参考方案5】:

循环浏览页面上的控件并不难 - 您只需在每个控件中查找更多控件即可。

你可以做类似的事情

foreach(var control in Page)

    if(control is DropDownList)
    
        //Do whatever
    
    else
    
        //Call this function again to search for controls within this control
    

【讨论】:

这就是 else 的作用...//再次调用这个函数来搜索这个控件中的控件...这就是调用递归的东西...【参考方案6】:

您可以使用递归逻辑来获取所有控件,如下所示:

private void PopulateSelectList(Control parentCtrl, List<DropDownList> selectList)

    foreach (Control ctrl in parentCtrl.Controls)
    
        if (ctrl is DropDownList)
        
            selectList.Add(((DropDownList)ctrl);
            continue;
        
        FindAllControls(ctrl, selectList);
    

【讨论】:

【参考方案7】:
        var dropDownLists = new List<DropDownList>();
        foreach (var control in this.Controls)
        
            if (control is DropDownList)
            
                dropDownLists.Add( (DropDownList)control );
            
        

【讨论】:

我很确定你需要递归。【参考方案8】:

如果您使用 system.web.ui 中的表单组件,这将有效 但是,当您从 system.web.mvc 使用它们时,这显然不起作用,所以我想出了以下解决方法。

for (Int32 idx = 0; idx < formCollection.Count; idx += 1)
                    
                    String Name = formCollection.Keys[idx];
                    String value = formCollection[idx];

                    if (Name.Substring(0, 3).ToLower() == "chk")

                        
                        Response.Write(Name + " is a checkbox <br/>");
                        
                    else if (Name.Substring(0, 5).ToLower() == "txtar")
                        
                        Response.Write(Name + " is a text area <br/>");
                        
                    else if (Name.Substring(0, 2).ToLower() == "rd")
                        
                        Response.Write(Name + " is a RadioButton <br/>");
                        

                    

这对我有用,但是我发现如果未选中单选按钮为空,因此不会返回任何内容,如果它为空,我不必向数据库写入任何内容

【讨论】:

以上是关于获取页面上特定类型的所有 Web 控件的主要内容,如果未能解决你的问题,请参考以下文章

检查控件类型

如何获取特定类型(按钮/文本框)的Windows窗体表单的所有子控件?

如何从 ASP.NET 网站(不是 Web 应用程序)中的用户控件获取父页面

菜单显示在所有页面上。有没有办法只显示在 React 的特定页面上?

使用 UpdatePanel 从页面上的 Web 控件注册样式表

Sharepoint:如何找到承载特定 Web 部件的所有页面?