Nathan Friend
Nathan Friend

Reputation: 12804

Custom Panel implementation: Take into account the size of the Panel's children when arranging

I'm working on a custom WPF Panel that works similar to a Canvas except that the placements of its children are expressed as percentages instead of absolute offsets. The Panel, which I'm calling PercentageCanvas would be used like so:

<Window>
  <local:PercentageCanvas>
    <ChildElement local:PercentageCanvas.Left=".30" local:PercentageCanvas.Top=".50" />
    <ChildElement local:PercentageCanvas.Left="0" local:PercentageCanvas.Top=".9" />
      ... etc ...
  </local:PercentageCanvas>
</Window>

I have the PercentageCanvas working, although not quite as I expected. The children of the PercentageCanvas are positioned relative to the center of the child, rather than the top-left edge, as I had expected. Here's the ArrangeOverride method I use that gives me this behavior:

protected override Size ArrangeOverride(Size finalSize)
{
    int currentIndex = 0;

    for (int index = InternalChildren.Count - 1; index >= 0; index--)
    {
        Rect rect = new Rect(finalSize);

        rect.Location = new Point()
        {
            X = finalSize.Width * PercentagePanel.GetLeft(InternalChildren[index]) - finalSize.Width / 2,
            Y = finalSize.Height * PercentagePanel.GetTop(InternalChildren[index]) - finalSize.Height / 2
        };

        InternalChildren[index].Arrange(rect);
        currentIndex++;
    }

    return finalSize;
}

It seems that to counteract this center-positioning behavior, I'll need to know each child's dimensions and compensate for the child's size while computing the Location of the Rect. However, I don't seem to have access to the child's size in order to compute the appropriate offset.

How can I take into account the size of a Panel's child while computing the child's layout in the ArrangeOverride method?

Upvotes: 0

Views: 81

Answers (1)

Nathan Friend
Nathan Friend

Reputation: 12804

Found the answer while in the middle of writing up the question. All UIElements have a DesiredSize property which gives me exactly what I'm looking for. My ArrangeOverride method now looks like this (you'll have to scroll to the right to see the difference):

protected override Size ArrangeOverride(Size finalSize)
{
    int currentIndex = 0;

    for (int index = InternalChildren.Count - 1; index >= 0; index--)
    {
        Rect rect = new Rect(finalSize);

        rect.Location = new Point()
        {
            X = finalSize.Width * PercentagePanel.GetLeft(InternalChildren[index]) - finalSize.Width / 2 + (InternalChildren[index].DesiredSize.Width / 2),
            Y = finalSize.Height * PercentagePanel.GetTop(InternalChildren[index]) - finalSize.Height / 2 + (InternalChildren[index].DesiredSize.Height / 2)
        };

        InternalChildren[index].Arrange(rect);
        currentIndex++;
    }

    return finalSize;
}

The elements now position themselves relative to their top-left corner instead of their center.

Upvotes: 1

Related Questions