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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/698434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 13:55:03  来源:igfitidea点击:

Accessing parent control from child control - ASP.NET C#

c#asp.net

提问by DotnetDude

I have a parent user control with a label. On the parent's OnInit, I dynamically load the child control. From the child control, I will need to set the parent's label to something.

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

Using the Parent property returns the immediate parent which is actually a PlaceHolder in my case. Theoretically, I can recursively loop to get the reference to the Parent User control. Am I heading in the right direction here? Is there a straightforward way of doing this?

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

采纳答案by Rex M

Try getting the child's NamingContainer.

尝试获取孩子的NamingContainer

回答by AndreasN

There is a FindControl method, but its not recursive if i remember correctly. Also you're not guarantied that all the control hierarchies exist on page_init, wait til page_load before accessing the controls. Init is for creating them.

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

回答by Quintin Robinson

You could pass a reference of the parent to the child and expose a method on the parent to set the label, although this would very tightly couple the objects. Otherwise you could expose a property on the child that the parent could then check and set it's own label.

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

回答by Jonas

There's a few different ways you could go...one would be to add a Parent property to your Child class...and then do:

您可以采用几种不同的方法……一种是将 Parent 属性添加到您的 Child 类中……然后执行以下操作:

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

I'm sure somebody will come back and say this violates some best practice or another...but shrug. You could use events too, if you wanted to maintain some separation.

我敢肯定有人会回来说这违反了某些最佳实践……但耸耸肩。如果你想保持一些分离,你也可以使用事件。

回答by bendewey

If your creating the UserControl via code, why not pass in the strongly typed parent to the constructor.

如果您通过代码创建 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;
  }
}

Now this is tightly coupled and could cause you issues later, but for this scenario it could work.

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

回答by andleer

You best bet is to wait until page_load completes and then recursively search on Page.Controls.

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

Here are some extension methods that will help you accompish that:

这里有一些扩展方法可以帮助你实现:

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

回答by teebot

Or you could iterate through the parents until you find the desired control, such as with an extension method.

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

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

More details about this method here: http://www.teebot.be/2009/08/extension-method-to-get-controls-parent.html

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

回答by Aridane álamo

To me the right way to do this is by exposing an add method in the control. Now if you need to update a label outside it, expose an event, something like OnCollectionChanged(...) and suscribe from the control that will need to show info about the collection.

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

This way each control does it's part and all stays SOLID

这样每个控件都完成它的一部分,并且所有控件都保持SOLID

回答by Art

@Rex M has a good and easy solution for this and just to expand on it to show the usage:

@Rex M 对此有一个很好且简单的解决方案,只是扩展它以显示用法:

This code snippet is used from within the child user control to access parent user control property:

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

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

回答by Mariano Desanze

Similar to teebot's solution, but returning null instead of NullReferenceException, easier to use, and reusable in other contexts:

类似于 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;
    }
}

Sample use cases and comparison:

示例用例和比较:

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

Code above runs on LinqPad (otherwise replace the .Dump()calls with Console.WriteLine(...))

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