Элемент WPF, который динамически создает (инкапсулирует) дочерние элементы во время выполнения

Я хочу создать элемент WPF, который во время выполнения полностью контролирует свои дочерние элементы — добавляя и удаляя дочерний пользовательский интерфейс при изменении его свойств. Что-то вроде того, что делает ItemsControl, когда вы изменяете его свойство ItemsSource, хотя в моем случае будет только один дочерний элемент.

Это будет контейнер представления для MVVM — когда вы дадите ему модель или модель представления, он волшебным образом создаст правильное представление и подключит все. Нет необходимости, чтобы мой контейнер представления был шаблонным (поскольку он создает определяемые пользователем представления, которые являются пользовательскими элементами управления и имеют свои собственные шаблоны), и я бы предпочел, чтобы он инкапсулировал как можно больше. Вероятно, я мог бы легко сделать это, наследуя что-то вроде Grid и добавляя дочерние элементы управления при изменении моих собственных свойств; но Grid публично раскрывает свою коллекцию дочерних элементов и позволяет любому добавлять и удалять элементы.

От какого класса WPF следует исходить для максимальной инкапсуляции и как добавить к нему дочерние элементы во время выполнения?

Основываясь на моем понимании документов, я попытался использовать FrameworkElement и AddVisualChild, просто чтобы посмотреть, смогу ли я создавать дочерние элементы управления во время выполнения. Мне не ясно, нужен ли AddLogicalChild, но на всякий случай я его поставил:

public class ViewContainer : FrameworkElement {
    private TextBlock _child;

    public ViewContainer() {
        _child = new TextBlock { Text = "ViewContainer" };
        AddLogicalChild(_child);
        AddVisualChild(_child);
        InvalidateMeasure();
    }

    public object Content { get; set; }

    protected override Size ArrangeOverride(Size finalSize) {
        _child.Arrange(new Rect(finalSize));
        return finalSize;
    }
    protected override Size MeasureOverride(Size availableSize) {
        _child.Measure(availableSize);
        return _child.DesiredSize;
    }
}

Когда я помещаю ViewContainer в окно и запускаю его, я ожидаю увидеть TextBlock с надписью «ViewContainer». Но вместо этого я просто вижу пустое окно. Так что, очевидно, я что-то упускаю.

Как я могу исправить приведенный выше код, чтобы «дочерний» элемент управления отображался во время выполнения, но не был доступен для других (больше, чем можно было бы избежать)?


person Joe White    schedule 23.06.2010    source источник


Ответы (2)


Чтобы ответить на ваш конкретный вопрос, вам также потребуется переопределить GetVisualChild и VisualChildrenCount свойства, чтобы ваш дочерний элемент отображался.

person Samuel Jack    schedule 23.06.2010
comment
Круто - получилось. Хотя я немного озадачен тем, почему существует AddVisualChild, если он на самом деле не добавляет его ни к чему. - person Joe White; 24.06.2010
comment
Ах я вижу. Он настраивает ребенка на то, чтобы он знал о своем родителе; но многим родителям не нужен список дочерних элементов (отсутствующих или только один), поэтому выбор — и ответственность — за хранение остается за родителем. Агрессивное разделение ответственности: одна из вещей, которые мне очень нравятся в WPF. Но это означает, что имя AddVisualChild вводит в заблуждение, так как предполагает, что он добавляет его в какой-то список, а это не так. - person Joe White; 24.06.2010

Думали ли вы о том, чтобы воспользоваться преимуществами поддержки неявных шаблонов данных в WPF?

Я справился с требованием, подобным вашему, с помощью ContentControl. Я привязываю свойство Content к моей ViewModel. Затем я удостоверяюсь, что в словарях ресурсов, на которые есть ссылки где-то в дереве над ContentControl, у меня есть DataTemplates, определенные для всех типов ViewModels, которые могут быть назначены свойству Content.

Таким образом, WPF позаботится о подключении правильного представления для моей ViewModel.

person Samuel Jack    schedule 23.06.2010
comment
Да, я делал это раньше, но на этот раз я хочу сделать еще один шаг — я хочу, чтобы мой ViewContainer мог принимать модель, автоматически обнаруживать и создавать для нее правильную ViewModel и переходить оттуда к Вид. Я не думаю, что DataTemplate.DataType доставит меня туда без посторонней помощи. - person Joe White; 24.06.2010
comment
конечно, DataTemplate.DataType не подойдет, но есть DataTemplateSelector, который может. - person Romain Hautefeuille; 03.11.2015