【问题标题】:Accessing parent control from child control - ASP.NET C#从子控件访问父控件 - ASP.NET C#
【发布时间】:2009-03-30 18:24:19
【问题描述】:

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

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

【问题讨论】:

    标签: c# asp.net


    【解决方案1】:

    尝试获取孩子的NamingContainer。

    【讨论】:

      【解决方案2】:

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

      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

      【讨论】:

      【解决方案3】:

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

      此代码 sn-p 用于从子用户控件中访问父用户控件属性:

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

      【讨论】:

        【解决方案4】:

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

        这样每个控件都是它的一部分并且全部保持SOLID

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              【解决方案7】:

              您可以采用几种不同的方法...一种是将 Parent 属性添加到您的 Child 类...然后这样做:

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

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

              【讨论】:

                【解决方案8】:

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

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

                【讨论】:

                  【解决方案9】:

                  最好的办法是等到 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);
                          }
                      }
                  

                  【讨论】:

                    【解决方案10】:

                    类似于 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(...))

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-05-04
                      • 1970-01-01
                      • 2017-02-04
                      • 2013-11-09
                      • 2018-01-16
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多