wpfencapsulationframeworkelementdynamic-ui

WPF element that dynamically creates (encapsulated) children at runtime


I want to create a WPF element that, at runtime, is in full control of its child elements -- adding and removing child UI when its properties change. Something a bit like what ItemsControl does when you modify its ItemsSource property, though in my case there'll only be one child.

This will be a view container for MVVM -- when you give it a Model or a ViewModel, it will magically create the correct View and wire everything up. There's no need for my view container to be templatable (since it creates user-defined views, which are UserControls and have their own templates), and I'd prefer that it encapsulate as much as possible. I could probably do this easily by descending from something like Grid, and adding child controls when my own properties change; but Grid publicly exposes its collection of child elements and lets anyone add and remove stuff.

Which WPF class should I descend from for maximum encapsulation, and how do I add child elements to it at runtime?

Based on my understanding of the docs, I tried using FrameworkElement and AddVisualChild, just to see if I could create child controls at runtime. I'm not clear on whether the AddLogicalChild is necessary, but I put it in just in case:

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;
    }
}

When I put a ViewContainer into a Window, and run this, I expect to see a TextBlock saying "ViewContainer". But instead, I just see a blank window. So obviously I'm missing something.

How can I fix the above code so that the "child" control does appear at runtime, but isn't exposed for others to mess with (any more than can be avoided)?


Solution

  • To answer your specific question, you'll also need to override GetVisualChild and VisualChildrenCount properties to enable your child element to be displayed.