从子控件访问父控件 - ASP.NET C#

时间:2009-03-30 18:24:20

标签: c# asp.net

我有一个带有标签的父用户控件。在父的OnInit上,我动态加载子控件。从子控件中,我需要将父标签设置为某种东西。

使用Parent属性返回直接父级,在我的情况下实际上是PlaceHolder。从理论上讲,我可以递归循环来获取对父用户控件的引用。我在这里朝着正确的方向前进吗?有这么简单的方法吗?

10 个答案:

答案 0 :(得分:10)

尝试获取孩子的NamingContainer

答案 1 :(得分:4)

或者您可以遍历父项,直到找到所需的控件,例如使用扩展方法。

public static Control GetParentOfType(this Control childControl,
                                   Type parentType)
  {
      Control parent = childControl.Parent;
      while(parent.GetType() != parentType)
      {
          parent = parent.Parent;
      }
      if(parent.GetType() == parentType)
            return parent;

     throw new Exception("No control of expected type was found");
  }

此处有关此方法的更多详细信息:http://www.teebot.be/2009/08/extension-method-to-get-controls-parent.html

答案 2 :(得分:1)

对我来说,正确的方法是在控件中公开一个add方法。 现在,如果您需要更新其外部的标签,请公开一个事件,例如OnCollectionChanged(...),并从控件中扣除需要显示该集合的信息。

这样每个控件都可以完成它的所有操作SOLID

答案 3 :(得分:1)

@Rex M为此提供了一个简单易用的解决方案,只是为了扩展它以显示用法:

此子代码片段在子用户控件中用于访问父用户控件属性:

((MyParentUserControlTypeName)NamingContainer).Property1 = "Hello";

答案 4 :(得分:0)

有一个FindControl方法,但如果我没记错的话,它不是递归的。 另外,您并不担心page_init上存在所有控件层次结构,请在访问控件之前等待page_load。 Init用于创建它们。

答案 5 :(得分:0)

您可以将父项的引用传递给子项,并在父项上公开一个方法来设置标签,尽管这会非常紧密地耦合对象。否则,您可以在子项上公开一个属性,然后父项可以检查并设置它自己的标签。

答案 6 :(得分:0)

你可以采用几种不同的方法...一种方法是将一个Parent属性添加到你的Child类......然后执行:

// in the context of parent's loading of child:
child.ParentObject = self;

我确信有人会回来说这违反了一些最佳做法或其他......但耸耸肩。如果你想保持一些分离,你也可以使用事件。

答案 7 :(得分:0)

如果您通过代码创建UserControl,为什么不将强类型父代传递给构造函数。

public class MyUserControl1 : UserControl
{
  public void Init(...)
  {
    var uc2 = new MyUserControl2(this);
  }
}

public class MyUserControl2 : UserControl
{
  private MyUserControl1 parentUserControl;

  public MyUserControl2(MyUserControl1 parent)
  {
    this.parentUserControl = parent;
  }
}

现在这是紧密耦合的,可能会导致您稍后出现问题,但对于这种情况,它可以正常工作。

答案 8 :(得分:0)

你最好的办法是等到page_load完成然后递归搜索Page.Controls。

以下是一些可以帮助您实现这一目标的扩展方法:

var control = Page.GetControl(MyControlID);    

public static class ControlExtensions
    {
        public static IEnumerable<Control> Flatten(this ControlCollection controls)
        {
            List<Control> list = new List<Control>();
            controls.Traverse(c => list.Add(c));
            return list;
        }

        public static IEnumerable<Control> Flatten(this ControlCollection controls, Func<Control, bool> predicate)
        {
            List<Control> list = new List<Control>();
            controls.Traverse(c => { if (predicate(c)) list.Add(c); });
            return list;
        }

        public static void Traverse(this ControlCollection controls, Action<Control> action)
        {
            foreach (Control control in controls)
            {
                action(control);
                if (control.HasControls())
                {
                    control.Controls.Traverse(action);
                }
            }
        }

        public static Control GetControl(this Control control, string id)
        {
            return control.Controls.Flatten(c => c.ID == id).SingleOrDefault();
        }

        public static IEnumerable<Control> GetControls(this Control control)
        {
            return control.Controls.Flatten();
        }

        public static IEnumerable<Control> GetControls(this Control control, Func<Control, bool> predicate)
        {
            return control.Controls.Flatten(predicate);
        }
    }

答案 9 :(得分:0)

类似于teebot的解决方案,但是返回null而不是NullReferenceException,易于使用,并且可以在其他情况下重用:

public static class Extensions
{
    public static IEnumerable<Control> GetAncestors(this Control control)
    {
        if (control == null)
            yield break;
        while ((control = control.Parent) != null)
            yield return control;
    }
}

示例用例和比较:

class Control { public string Name; public Control Parent; }
class Control2 : Control { public string Prop2; }

static class Program
{
    public static Control GetParentOfType(this Control childControl,
                                          Type parentType)
    {
        Control parent = childControl.Parent;
        while(parent.GetType() != parentType) // throws NullReferenceException when "No control of expected type was found" (due to "parent" being null)
        {
            parent = parent.Parent;
        }
        if(parent.GetType() == parentType)
            return parent;

        throw new Exception("No control of expected type was found"); // this line is dead code as null reference throws before this
    }
    public static IEnumerable<Control> GetAncestors(this Control control)
    {
        if (control == null)
            yield break;
        while ((control = control.Parent) != null)
            yield return control;
    }

    static void Main()
    {
        var a = new Control { Name = "A" };
        var b = new Control2 { Name = "B", Parent = a, Prop2 = "B is OK!" };
        var c = new Control { Name = "C", Parent = b };
        var d = new Control { Name = "D",  Parent = c };

        // teebot's 
        var found = d.GetParentOfType(typeof(Control2));
        ((Control2)found).Prop2.Dump(); // properly returns "B is OK!", but needs 2 lines to be clear, and casting to the same type already defined in the line above
        try { b.GetParentOfType(typeof(Control2));
        } catch (Exception e) { e.GetType().Name.Dump(); } // NullReferenceException

        // mine
        d.GetAncestors().OfType<Control2>().First().Prop2.Dump(); // properly returns "B is OK!" (while "yield" and "First()" avoids wasting time looping unneeded ancestors, e.g. "A")
        b.GetAncestors().OfType<Control2>().FirstOrDefault().Dump(); // doesn't throw, just returns null
        d.GetAncestors().Take(2).Select(x => x.Name).ToList().Dump(); // returns a filtered list (instead of a single element) without changing GetAncestors nor wasting performance
    }
}

上面的代码在LinqPad上运行(否则将.Dump()调用替换为Console.WriteLine(...)