Доступ к родительскому элементу управления из дочернего элемента управления - ASP.NET С#

У меня есть родительский элемент управления пользователя с меткой. В родительском OnInit я динамически загружаю дочерний элемент управления. Из дочернего элемента управления мне нужно установить родительскую метку.

Использование свойства Parent возвращает непосредственного родителя, который на самом деле является PlaceHolder в моем случае. Теоретически, я могу рекурсивный цикл, чтобы получить ссылку на элемент управления родительским элементом. Я направляюсь в правильном направлении здесь? Есть ли простой способ сделать это?

Ответ 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

Для меня правильный способ сделать это - разоблачить метод add в элементе управления. Теперь, если вам нужно обновить ярлык вне его, выведите событие, например OnCollectionChanged (...), и выберете элемент управления, который должен будет отображать информацию о коллекции.

Таким образом, каждый элемент управления делает это частью и все остается SOLID

Ответ 4

@Rex M имеет хорошее и простое решение для этого и просто расширяет его, чтобы показать использование:

Этот фрагмент кода используется внутри элемента управления дочернего пользователя для доступа к свойству управления родительским пользователем:

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

Ответ 5

Существует метод FindControl, но он не рекурсивный, если я правильно помню. Кроме того, вы не гарантируете, что все иерархии управления существуют на странице_ини, подождите до страницы_load перед доступом к элементам управления. Init для их создания.

Ответ 6

Вы можете передать ссылку родителя ребенку и выставить метод родителя, чтобы установить метку, хотя это очень тесно связано с объектами. В противном случае вы можете выставить свойство на дочернем объекте, чтобы родитель мог проверить и установить его собственную метку.

Ответ 7

Есть несколько разных способов, которыми вы могли бы пойти... можно было бы добавить свойство родителя к вашему классу Child... а затем выполните:

// in the context of parent 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 не завершится, а затем рекурсивного поиска на странице. Контроль.

Вот несколько методов расширения, которые помогут вам в этом:

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 
        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(...))