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

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

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

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

Ответы

Ответ 1

Разве не проще расширить

System.Windows.Controls.UIElementCollection 

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

protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)

?

Ответ 2

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

Ответ 3

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

Ответ 4

EDIT:

Я только что подумал. Если вы можете установить обработчик 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 handler, который можно использовать с некоторыми настройками стиля/шаблона.

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

Ответ 5

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

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