WPF, Custom Control based on a (Decorator Class), The Decorator's child's Vertical nor Horizontal alignments takes effect when set

47 Views Asked by At

I wanted a Custom Control that wraps around its child and adds a four FrameworkElements as (border sides), so I chose a WPF's Decorator Class, both the class and the xaml are in the following github gist. Decorator The Measurements and Arrangements are as expected, yet the child is always centered to the Decorator even when the its alignments are set.

I tried many things, yet they either ruin the arrangements/measurements or don't work at all, so maybe there is a fundamental problem with my understanding of MeasureOverride & ArrangeOverride or ???. I want this decorator to function like the Border Class yet has FrameworkElements as a border.

1

There are 1 best solutions below

0
Ali Yousefi On

When extending a class like Decorator in WPF, understanding how MeasureOverride and ArrangeOverride work is crucial because they determine how the child elements are sized and positioned, respectively. In your implementation, you've overridden these two methods, which is correct, but the way you've handled the alignments and arrangement of the child FrameworkElement may not be taking into account the HorizontalAlignment and VerticalAlignment properties.

To make your BorderWrapper respect the child's HorizontalAlignment and VerticalAlignment properties (like the standard Border class does), you need to adjust how you place the child inside the ArrangeOverride method. The ArrangeOverride should account for the child's alignment relative to the available space, after allocating space for the "borders".

Here's an adjusted version of your ArrangeOverride code snippet that aims to respect the child's alignment properties:

// The default size & the boundaries of an object
protected override Size ArrangeOverride(Size arrangeSize)
{
    // ... (previous code remains the same)

    // Calculate the top-left point where the Child should be arranged, taking alignment into account
    double childLeft = Left != null ? Left.DesiredSize.Width : 0;
    double childTop = Top != null ? Top.DesiredSize.Height : 0;

    // Determine the child's final alignment within the available space
    childLeft += child.HorizontalAlignment switch
    {
        HorizontalAlignment.Center => (childArrangeSize.Width - childDesiredSize.Width) / 2,
        HorizontalAlignment.Right => childArrangeSize.Width - childDesiredSize.Width,
        _ => 0 // Left or Stretch
    };

    childTop += child.VerticalAlignment switch
    {
        VerticalAlignment.Center => (childArrangeSize.Height - childDesiredSize.Height) / 2,
        VerticalAlignment.Bottom => childArrangeSize.Height - childDesiredSize.Height,
        _ => 0 // Top or Stretch
    };

    // Arrange the Child with alignment taken into account
    Point childPos = new Point(childLeft, childTop);
    child.Arrange(new Rect(childPos, childDesiredSize));

    // ... (arranging sides - code remains the same)

    return arrangementSize;
}

In this modified version, we determine childLeft and childTop by considering the horizontal and vertical alignments of the child. The HorizontalAlignment and VerticalAlignment of the child can be Left, Center, Right or Stretch (same for vertical alignment). If it's Center, we place the child in the middle of the available space, minus the space taken by the border sides. For Right or Bottom, we place it at the end of the available space. Left and Top will start from the border end, which is effectively 0 distance from the border.

It's essential to remember that the default behavior for HorizontalAlignment and VerticalAlignment is Stretch. In that case, the control will stretch to fill the available space (minus the space taken by borders), which may make your borders seem non-effective if you don't actually have any space constraints on the BorderWrapper. For non-stretch alignments, this should position the child considering the space occupied by the borders and the chosen alignment.

Make sure to test with different alignments and container sizes to ensure that your control behaves as expected in a variety of scenarios.