#1,174 – Custom Panel, part VI (Attached Properties)

You can define an attached dependency property in a custom panel.  The attached property can be used by child elements of the panel and in a way that affects how the child elements are laid out.  In the example below, we define a boolean SecondColumn property.  If set, this property indicates that a child element should appear in a second column.

    public class TwoColPanel : Panel
    {
        private static FrameworkPropertyMetadata secColMetadata =
            new FrameworkPropertyMetadata(false,
                FrameworkPropertyMetadataOptions.AffectsParentArrange);

        public static readonly DependencyProperty SecondColumnProperty =
            DependencyProperty.RegisterAttached("SecondColumn", typeof(bool),
                typeof(TwoColPanel), secColMetadata);

        public static void SetSecondColumn(DependencyObject depObj, bool value)
        {
            depObj.SetValue(SecondColumnProperty, value);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement elem in InternalChildren)
                elem.Measure(availableSize);

            return availableSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            double topCol1 = 0.0;
            double topCol2 = 0.0;

            for (int i = 0; i < InternalChildren.Count; i++)
            {
                bool col2 = (bool)InternalChildren[i].GetValue(SecondColumnProperty);

                double left = col2 ? (finalSize.Width / 2.0) : 0.0;
                double top = col2 ? topCol2 : topCol1;

                Rect r = new Rect(new Point(left, top),
                                  InternalChildren[i].DesiredSize);

                InternalChildren[i].Arrange(r);

                if (col2)
                    topCol2 += InternalChildren[i].DesiredSize.Height;
                else
                    topCol1 += InternalChildren[i].DesiredSize.Height;
            }

            return finalSize;
        }
    }

We use the attached property as follows:

    <loc:TwoColPanel Margin="5">
        <Label Content="I'm child #1" loc:TwoColPanel.SecondColumn="True"
               Background="Thistle" />
        <Label Content="I'm child #2" loc:TwoColPanel.SecondColumn="False"
               Background="Lavender" />
        <Label Content="Third kid"
               Background="Honeydew" />
    </loc:TwoColPanel>

1174-001