我想要一个自定义控件,它包围它的子控件并添加四个 FrameworkElements 作为(边框边),所以我选择了 WPF 的装饰器类,该类和 xaml 都在以下 github gist 中。 装饰器 测量和排列符合预期,但即使设置了对齐方式,子项也始终以装饰器为中心。
我尝试了很多东西,但它们要么破坏了安排/测量,要么根本不起作用,所以也许我对 MeasureOverride 和 ArrangeOverride 或 ??? 的理解存在根本问题。 我希望这个装饰器的功能类似于 Border 类,但具有 FrameworkElements 作为边框。
在 WPF 中扩展像
Decorator
这样的类时,了解 MeasureOverride
和 ArrangeOverride
的工作原理至关重要,因为它们分别决定子元素的大小和位置。在您的实现中,您已经重写了这两种方法,这是正确的,但是您处理子 FrameworkElement
的对齐和排列的方式可能没有考虑 HorizontalAlignment
和 VerticalAlignment
属性。
为了使您的
BorderWrapper
尊重子级的 HorizontalAlignment
和 VerticalAlignment
属性(如标准 Border
类所做的那样),您需要调整将子级放置在 ArrangeOverride
方法中的方式。在为“边框”分配空间后,ArrangeOverride
应考虑子项相对于可用空间的对齐方式。
这是
ArrangeOverride
代码片段的调整版本,旨在尊重子项的对齐属性:
// 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;
}
在此修改版本中,我们通过考虑子级的水平和垂直对齐来确定
childLeft
和 childTop
。子项的 HorizontalAlignment
和 VerticalAlignment
可以是 Left
、Center
、Right
或 Stretch
(与垂直对齐相同)。如果是 Center
,我们将孩子放置在可用空间的中间,减去边框两侧占用的空间。对于 Right
或 Bottom
,我们将其放置在可用空间的末尾。 Left
和 Top
将从边界末端开始,这实际上是距边界 0
的距离。
请务必记住,
HorizontalAlignment
和 VerticalAlignment
的默认行为是 Stretch
。在这种情况下,控件将拉伸以填充可用空间(减去边框占用的空间),如果您实际上在 BorderWrapper
上没有任何空间限制,这可能会使您的边框看起来无效。对于非拉伸对齐,这应该考虑边框占用的空间和所选对齐来定位子项。
确保使用不同的对齐方式和容器大小进行测试,以确保您的控件在各种情况下都能按预期运行。