我正在尝试创建画布,当画布本身调整大小时,它会调整其子画面的大小。所以我创建了自己的类,它继承自canvas和覆盖方法ArrangeOverride,我在其中为canvas中定义的所有子项设置位置和大小。 一切看起来都很好,但是当我调整应用程序窗口的大小时,项目的大小或位置都没有调整大小。
这是一个简化的示例,它尝试将其元素捕捉到画布的右边界:
Xaml代码:
<Border BorderBrush="Black" BorderThickness="1" Margin="10" SnapsToDevicePixels="True">
<l:CustomPanel>
<Rectangle Fill="Red" />
<Button>a</Button>
</l:CustomPanel>
</Border>
CustomPanel:
public class CustomPanel : Canvas
{
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
var ret = base.ArrangeOverride(arrangeSize);
var top = 0;
foreach(UIElement child in Children)
{
Canvas.SetLeft(child, arrangeSize.Width - 20.0);
child.SetValue(WidthProperty, arrangeSize.Width - Canvas.GetLeft(child));
Canvas.SetTop(child, top);
child.SetValue(HeightProperty, 20.0);
top += 30;
}
return ret;
}
}
当我改变窗口的宽度时,有时画布看起来像这个图像:
然后,如果我改变窗口的高度,项目会移动到正确的位置
我做错了什么? 我试图将SnapsToDevicePixels设置为True,但它对我不起作用。 : - (
答案 0 :(得分:3)
您的自定义面板应来自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;
}
}
答案 1 :(得分:1)
好的,所以我试着搞乱这个,我发现了一些让它运作起来的hacky方式。我真的不明白它为什么会这样(但是),但这对我有用:
public class CustomPanel : Canvas
{
private bool isFirstArrange = true;
protected override Size ArrangeOverride(Size arrangeSize)
{
var ret = new Size();
bool isFirstArrangeLocal = isFirstArrange;
if (isFirstArrangeLocal)
{
ret = base.ArrangeOverride(arrangeSize);
isFirstArrange = false;
}
var top = 0;
foreach (UIElement child in Children)
{
Canvas.SetLeft(child, arrangeSize.Width - 20.0);
child.SetValue(WidthProperty, arrangeSize.Width - Canvas.GetLeft(child));
Canvas.SetTop(child, top);
child.SetValue(HeightProperty, 20.0);
top += 30;
}
if (!isFirstArrangeLocal)
{
ret = base.ArrangeOverride(arrangeSize);
}
return ret;
}
}
所以我的想法是在除了第一次调用之外的所有情况下将ArrangeOverride()
放在foreach循环之后。
第一次电话必须在foreach之前或由于某种原因我得到这个:
答案 2 :(得分:0)
这可能或者可能不完全符合您的要求,但每当我听到/读取“调整大小”时,我会想到为此目的而构建的UI控件; Viewbox
。使用这个类,您可以选择如何调整内容的大小,但我认为您需要默认的Uniform
方法。
只需将您需要调整大小的内容放入ViewBox
,然后看看您喜欢它... 应该为您完成这项工作:
<ViewBox Stretch="Uniform">
<Canvas ... />
</ViewBox>
您实际上不需要在此处添加Stretch="Uniform"
,因为这是默认设置......它仅用于演示目的。要了解详情,请参阅MSDN上的Viewbox
Class页面。