#1,191 – Custom Element with a Single Child Element
October 30, 2014 1 Comment
Below is an example of a simple custom element that derives from FrameworkElement and includes a single child element (UIElement), set using the Child dependency property.
Here’s the full code for the custom element. We do the following:
- Override both Measure and Layout
- Override VisualChildrenCount and GetVisualChild to respond indicating that we have a single child
- Override Render to render the graphical portion of the control
- Use AddVisualChild / AddLogicalChild to indicate that the child element belongs to the parent element
public class MyElement : FrameworkElement { private static FrameworkPropertyMetadata childMetadata = new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsParentArrange, new PropertyChangedCallback(OnChildChanged)); public static void OnChildChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { MyElement thisElement = obj as MyElement; if (thisElement == null) throw new Exception("Child property must be attached to MyElement"); // Remove old one Visual oldChild = e.OldValue as Visual; if (oldChild != null) { thisElement.RemoveVisualChild(oldChild); thisElement.RemoveLogicalChild(oldChild); } // Attach new one Visual newChild = e.NewValue as Visual; if (newChild != null) { thisElement.AddVisualChild(newChild); thisElement.AddLogicalChild(newChild); } } public static readonly DependencyProperty ChildProperty = DependencyProperty.RegisterAttached("Child", typeof(UIElement), typeof(MyElement), childMetadata); public static void SetChild(DependencyObject depObj, UIElement value) { depObj.SetValue(ChildProperty, value); } protected override int VisualChildrenCount { get { UIElement childElement = (UIElement)GetValue(ChildProperty); return childElement != null ? 1 : 0; } } protected override Visual GetVisualChild(int index) { // (ignoring index) return (UIElement)GetValue(ChildProperty); } protected override Size MeasureOverride(Size availableSize) { UIElement childElement = (UIElement)GetValue(ChildProperty); if (childElement != null) childElement.Measure(availableSize); // "X" and child both use all of the available space return availableSize; } protected override Size ArrangeOverride(Size finalSize) { UIElement childElement = (UIElement)GetValue(ChildProperty); if (childElement != null) childElement.Arrange(new Rect(new Point(0.0, 0.0), finalSize)); return finalSize; } // Render a big "X" protected override void OnRender(DrawingContext dc) { dc.DrawLine(new Pen(Brushes.Blue, 2.0), new Point(0.0, 0.0), new Point(ActualWidth, ActualHeight)); dc.DrawLine(new Pen(Brushes.Green, 2.0), new Point(ActualWidth, 0.0), new Point(0.0, ActualHeight)); } }
We can now use this element as follows:
<Grid> <loc:MyElement> <loc:MyElement.Child> <Label Content="I'm the child" HorizontalAlignment="Center"/> </loc:MyElement.Child> </loc:MyElement> </Grid>
When rendered, it looks like this: