Ответ 1
Попробуйте получить ребенка NamingContainer.
У меня есть родительский элемент управления пользователя с меткой. В родительском OnInit я динамически загружаю дочерний элемент управления. Из дочернего элемента управления мне нужно установить родительскую метку.
Использование свойства Parent возвращает непосредственного родителя, который на самом деле является PlaceHolder в моем случае. Теоретически, я могу рекурсивный цикл, чтобы получить ссылку на элемент управления родительским элементом. Я направляюсь в правильном направлении здесь? Есть ли простой способ сделать это?
Попробуйте получить ребенка NamingContainer.
Или вы можете проходить через родителей до тех пор, пока не найдете нужный элемент управления, например, с помощью метода расширения.
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
Для меня правильный способ сделать это - разоблачить метод add в элементе управления. Теперь, если вам нужно обновить ярлык вне его, выведите событие, например OnCollectionChanged (...), и выберете элемент управления, который должен будет отображать информацию о коллекции.
Таким образом, каждый элемент управления делает это частью и все остается SOLID
@Rex M имеет хорошее и простое решение для этого и просто расширяет его, чтобы показать использование:
Этот фрагмент кода используется внутри элемента управления дочернего пользователя для доступа к свойству управления родительским пользователем:
((MyParentUserControlTypeName)NamingContainer).Property1 = "Hello";
Существует метод FindControl, но он не рекурсивный, если я правильно помню. Кроме того, вы не гарантируете, что все иерархии управления существуют на странице_ини, подождите до страницы_load перед доступом к элементам управления. Init для их создания.
Вы можете передать ссылку родителя ребенку и выставить метод родителя, чтобы установить метку, хотя это очень тесно связано с объектами. В противном случае вы можете выставить свойство на дочернем объекте, чтобы родитель мог проверить и установить его собственную метку.
Есть несколько разных способов, которыми вы могли бы пойти... можно было бы добавить свойство родителя к вашему классу Child... а затем выполните:
// in the context of parent loading of child:
child.ParentObject = self;
Я уверен, что кто-то вернется и скажет, что это нарушает какую-то лучшую практику или другую... но пожал плечами. Вы также можете использовать события, если хотите сохранить разделение.
Если вы создаете 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;
}
}
Теперь это тесно связано и может вызвать проблемы позже, но для этого сценария это может сработать.
Лучше всего подождать до тех пор, пока 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);
}
}
Аналогичен решению 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(...)
)