Search code examples
wpfuielement

Adding and removing WPF UIElements at runtime


Is there a way to logically group or tag UIElements like shapes and controls added at runtime for easy removal?

For eg., I have a Grid with some (design-time) child elements and add Ellipses and TextBlocks to it at runtime. When I want to draw a different set of Ellipses and TextBlocks, I'd like to remove the original set I added. What would be an easy way to logically group these while adding them so I can just have a children.clear() or some way to identify them to remove them?

It is possible to add a tag value but there is no way to retrieve or read this while iterating through children of a control because they are of type UIElement which does not have a tag property.

Thoughts?


Solution

  • A very good place to use an Attached Property.

    Example:

    // Create an attached property named `GroupID`
    public static class UIElementExtensions
    {
        public static Int32 GetGroupID(DependencyObject obj)
        {
            return (Int32)obj.GetValue(GroupIDProperty);
        }
    
        public static void SetGroupID(DependencyObject obj, Int32 value)
        {
            obj.SetValue(GroupIDProperty, value);
        }
    
        // Using a DependencyProperty as the backing store for GroupID.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GroupIDProperty =
            DependencyProperty.RegisterAttached("GroupID", typeof(Int32), typeof(UIElementExtensions), new UIPropertyMetadata(null));
    }
    

    Usage:

    public void AddChild(UIElement element, Int32 groupID)
    {
        UIElementExtensions.SetGroupID(element, groupID);
        rootPanel.Children.Add(element);
    }
    
    public void RemoveChildrenWithGroupID(Int32 groupID)
    {
        var childrenToRemove = rootPanel.Children.OfType<UIElement>().
                               Where(c => UIElementExtensions.GetGroupID(c) == groupID);
    
        foreach (var child in childrenToRemove)
        {
            rootPanel.Children.Remove(child);
        }
    }