4

Let's say I want to make a class that inherits directly from UIElement and is able to contain one or more [externally added] UIElements as children - like Panels and other container controls. It's obviously easy to have the class house a collection of UIElements in some form or other, but how do I get them to be displayed / rendered along with my class?

I assume they must be added to the visual tree as children of my own UIElement in some way (or, possibly, render them manually à la going via VisualTreeHelper.GetDrawing and do it using OnRender's DrawingContext? But that seems clumsy).

I do not want to know that I can - or should - inherit from more ready-made controls, like FrameworkElement, Panel, ContentControl etc (if anything, I want to know how they are implementing the displaying / rendering of externally added child elements, where applicable).

I have my reasons for wanting to be as high up in the hierarchy as possible, so please don't give me any lectures on why it is a good thing to be XAML / WPF Framework 'compliant' etc.

d7samurai
  • 3,086
  • 2
  • 30
  • 43
  • Inspect `PresentationFramework.dll` with Reflector or something. – Federico Berasategui Dec 02 '13 at 21:24
  • Lol - I was *this* close to adding "you hear that, HighCore?" at the end of the last paragraph (before I noticed there was a comment.. but you beat me to it). – d7samurai Dec 02 '13 at 21:26
  • Don't remember if I said this to you, but yes, you're an experimented developer who really `knows` what he's doing, but for most people out there using WPF, it's quite the opposite. That's why I have this cliche of "delete all your code and learn MVVM" thing constantly coming up again and again. Of course that doesn't apply to you, and other pro WPF/XAML devs – Federico Berasategui Dec 02 '13 at 21:28

1 Answers1

9

The following class provides the absolute minimum in terms of layout and rendering of child elements:

public class UIElementContainer : UIElement
{
    private readonly UIElementCollection children;

    public UIElementContainer()
    {
        children = new UIElementCollection(this, null);
    }

    public void AddChild(UIElement element)
    {
        children.Add(element);
    }

    public void RemoveChild(UIElement element)
    {
        children.Remove(element);
    }

    protected override int VisualChildrenCount
    {
        get { return children.Count; }
    }

    protected override Visual GetVisualChild(int index)
    {
        return children[index];
    }

    protected override Size MeasureCore(Size availableSize)
    {
        foreach (UIElement element in children)
        {
            element.Measure(availableSize);
        }

        return new Size();
    }

    protected override void ArrangeCore(Rect finalRect)
    {
        foreach (UIElement element in children)
        {
            element.Arrange(finalRect);
        }
    }
}

It is not required to have a UIElementCollection. An alternative implementation could look like this:

public class UIElementContainer : UIElement
{
    private readonly List<UIElement> children = new List<UIElement>();

    public void AddChild(UIElement element)
    {
        children.Add(element);
        AddVisualChild(element);
    }

    public void RemoveChild(UIElement element)
    {
        if (children.Remove(element))
        {
            RemoveVisualChild(element);
        }
    }

    // plus the four overrides
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • No, you may as well use a plain list and call Add/RemoveVisualChild to set up the visual tree. The magic is in the four overrides. – Clemens Dec 02 '13 at 22:10
  • Great, this is just what I was looking for (and regarding my previous comment: for a second, I thought maybe the handling of the children was embedded in UIElementCollection, since it gets initialized with `this` - I thought that could mean it did the hooking up with the visual tree etc). How much of this is really stemming from `Visual`? – d7samurai Dec 02 '13 at 22:18
  • All that has Visual in its name: Add/RemoveVisualChild, VisualChildrenCount and GetVisualChild. UIElement adds MeasureCore and ArrangeCore. – Clemens Dec 02 '13 at 22:29
  • ..but inheriting from Visual is useless in practice, right? I read somewhere that its actual rendering mechanisms are `Internal` so they can only be accessed indirectly, via one of its descendants..? – d7samurai Dec 03 '13 at 11:45
  • Well, I've got a very practical use case where I derive from ContainerVisual and DrawingVisual. You may take a look at classes TileContainer and TileLayer in my [XAML Map Control](http://xamlmapcontrol.codeplex.com/) on CodePlex. – Clemens Dec 03 '13 at 12:11
  • Yes, going via DrawingVisual was what I was thinking of - looks like the best access point for building a set of bare-boned custom `Visual`s without the fluff from UIElement (not to mention FrameworkElement). – d7samurai Dec 03 '13 at 12:26
  • Hi, I got the same question for a FrameworkElement instead of an UIElement (as in Silverlight, UIElement's constructor is private). Can you help? – Cœur Sep 11 '14 at 15:00
  • @Cœur Have you tried anything yourselves? If so, and you have any specific problem with your approach, ask a question here on StackOverflow. – Clemens Sep 11 '14 at 15:05