Получать уведомления о добавлении/удалении визуального/логического дочернего элемента

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

Я знаю о методе Visual::OnVisualChildrenChanged, но он не применим ко мне, так как я не всегда могу наследовать и переопределять эту функцию. Я ищу событие.

Итак, есть ли способ уведомить владельца FrameworkElement/Visual о добавлении дочернего элемента?


person decasteljau    schedule 14.10.2008    source источник


Ответы (5)


Не проще ли продлить

System.Windows.Controls.UIElementCollection 

сделать уведомление и использовать

protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)

?

person Bram    schedule 19.10.2011

Я считаю, что FrameworkElement.Loaded и FrameworkElement.Unloaded запускается, когда элемент управления добавлены в визуальное дерево и удалены из него соответственно. Однако несколько раз, когда я пытался что-то с ними сделать, мне не удавалось заставить их работать последовательно (в то время я использовал обработчики событий класса, так что это могло иметь какое-то отношение к этому).

person Andy    schedule 18.10.2008
comment
Ну, сам ребенок получает FrameworkElement.Loaded, но родитель не знает, что ребенок был фактически создан и добавлен к нему. - person decasteljau; 18.10.2008

Может стоит добавить в дочернее поле ссылку на родительское и отправлять уведомление после изменения обратно в него?

person Maciej    schedule 10.07.2009

РЕДАКТИРОВАТЬ:

Я просто кое о чем подумал. Если вы можете установить обработчик Loaded для каждого элемента, который появляется в вашем визуальном дереве, возможно, это можно сделать с помощью такой техники (считайте, что это ОЧЕНЬ основная идея, просто как начало возможного решения):

public class ElementChildrenChangedEventArgs
{
    public ElementChildrenChangedEventArgs(FrameworkElement parent, FrameworkElement child)
    {
        Parent = parent;
        Child = child;
    }

    public FrameworkElement Parent { get; private set;}
    public FrameworkElement Child { get; private set;}
}

public delegate void ChildrenChanged(ElementChildrenChangedEventArgs args);

public static class ElementLoadedManager
{
    private static readonly HashSet<FrameworkElement> elements = new HashSet<FrameworkElement>();

    public static void Process_Loaded(FrameworkElement fe)
    {
        FrameworkElement feParent = VisualTreeHelper.GetParent(fe) as FrameworkElement;

        if (feParent != null)
        {
            if (elements.Contains(feParent))
            {
                InvokeChildrenChanged(feParent, fe);
            }
        }

        if (!elements.Contains(fe))
        {
            elements.Add(fe);
        }

        fe.Unloaded += Element_Unloaded;           
    }

    static void Element_Unloaded(object sender, RoutedEventArgs e)
    {
        FrameworkElement fe = sender as FrameworkElement;
        elements.Remove(fe);
        ClearUnloaded(fe);
    }

    static void ClearUnloaded(FrameworkElement fe)
    {
        fe.Unloaded -= Element_Unloaded;
    }

    public static event ChildrenChanged ChildrenChanged;

    private static void InvokeChildrenChanged(FrameworkElement parent, FrameworkElement child)
    {
        ElementChildrenChangedEventArgs args = new ElementChildrenChangedEventArgs(parent, child);

        ChildrenChanged changed = ChildrenChanged;

        if (changed != null) changed(args);
    }
}

Идея (и требование) заключалась бы в том, чтобы вызвать метод Process_Loaded(FrameworkElement) в обработчике Loaded каждого элемента, что можно сделать с некоторой настройкой стиля/шаблона.

А так как Loaded — это маршрутизируемое событие, вы можете установить обработчик только в родительском окне и проверить наличие e.OriginalSource.

person Kenan E. K.    schedule 11.07.2009
comment
Отредактированный ответ с некоторыми идеями. - person Kenan E. K.; 12.07.2009
comment
Согласно MSDN, стратегия маршрутизации события Loaded не пузырьковая, а прямая. По этой причине мне было бы очень интересно прочитать ваши отзывы о том, работает ли ваше решение (выше). - person Mark; 14.07.2009

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

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

person hbarck    schedule 07.06.2012