您的自定义面板应派生自 Panel 而不是 Canvas,并覆盖 MeasureOverride 和 ArrangeOverride 方法。此外,它还应该为子元素布局定义自己的附加属性,如下所示的四个属性RelativeX、RelativeY、RelativeWidth 和RelativeHeight。
它将像这样在 XAML 中使用:
<local:RelativeLayoutPanel>
<Rectangle Fill="Red"
local:RelativeLayoutPanel.RelativeX="0.2"
local:RelativeLayoutPanel.RelativeY="0.1"
local:RelativeLayoutPanel.RelativeWidth="0.6"
local:RelativeLayoutPanel.RelativeHeight="0.8"/>
</local:RelativeLayoutPanel>
这是实现:
public class RelativeLayoutPanel: Panel
{
public static readonly DependencyProperty RelativeXProperty = DependencyProperty.RegisterAttached(
"RelativeX", typeof(double), typeof(RelativeLayoutPanel),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
public static readonly DependencyProperty RelativeYProperty = DependencyProperty.RegisterAttached(
"RelativeY", typeof(double), typeof(RelativeLayoutPanel),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
public static readonly DependencyProperty RelativeWidthProperty = DependencyProperty.RegisterAttached(
"RelativeWidth", typeof(double), typeof(RelativeLayoutPanel),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
public static readonly DependencyProperty RelativeHeightProperty = DependencyProperty.RegisterAttached(
"RelativeHeight", typeof(double), typeof(RelativeLayoutPanel),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
public static double GetRelativeX(UIElement element)
{
return (double)element.GetValue(RelativeXProperty);
}
public static void SetRelativeX(UIElement element, double value)
{
element.SetValue(RelativeXProperty, value);
}
public static double GetRelativeY(UIElement element)
{
return (double)element.GetValue(RelativeYProperty);
}
public static void SetRelativeY(UIElement element, double value)
{
element.SetValue(RelativeYProperty, value);
}
public static double GetRelativeWidth(UIElement element)
{
return (double)element.GetValue(RelativeWidthProperty);
}
public static void SetRelativeWidth(UIElement element, double value)
{
element.SetValue(RelativeWidthProperty, value);
}
public static double GetRelativeHeight(UIElement element)
{
return (double)element.GetValue(RelativeHeightProperty);
}
public static void SetRelativeHeight(UIElement element, double value)
{
element.SetValue(RelativeHeightProperty, value);
}
protected override Size MeasureOverride(Size availableSize)
{
availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
foreach (UIElement element in InternalChildren)
{
element.Measure(availableSize);
}
return new Size();
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement element in InternalChildren)
{
element.Arrange(new Rect(
GetRelativeX(element) * finalSize.Width,
GetRelativeY(element) * finalSize.Height,
GetRelativeWidth(element) * finalSize.Width,
GetRelativeHeight(element) * finalSize.Height));
}
return finalSize;
}
}
如果您不需要四个布局属性可以独立绑定或由样式设置器等设置。您可以将它们替换为Rect 类型的单个附加属性:
<local:RelativeLayoutPanel>
<Rectangle Fill="Red" local:RelativeLayoutPanel.RelativeRect="0.2,0.1,0.6,0.8"/>
</local:RelativeLayoutPanel>
有了这个更短的实现:
public class RelativeLayoutPanel: Panel
{
public static readonly DependencyProperty RelativeRectProperty = DependencyProperty.RegisterAttached(
"RelativeRect", typeof(Rect), typeof(RelativeLayoutPanel),
new FrameworkPropertyMetadata(new Rect(), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
public static Rect GetRelativeRect(UIElement element)
{
return (Rect)element.GetValue(RelativeRectProperty);
}
public static void SetRelativeRect(UIElement element, Rect value)
{
element.SetValue(RelativeRectProperty, value);
}
protected override Size MeasureOverride(Size availableSize)
{
availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
foreach (UIElement element in InternalChildren)
{
element.Measure(availableSize);
}
return new Size();
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement element in InternalChildren)
{
var rect = GetRelativeRect(element);
element.Arrange(new Rect(
rect.X * finalSize.Width,
rect.Y * finalSize.Height,
rect.Width * finalSize.Width,
rect.Height * finalSize.Height));
}
return finalSize;
}
}