调整画布项目的大小

时间:2014-01-16 07:40:41

标签: wpf canvas

我正在尝试创建画布,当画布本身调整大小时,它会调整其子画面的大小。所以我创建了自己的类,它继承自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;
    }
}

当我改变窗口的宽度时,有时画布看起来像这个图像:

error

然后,如果我改变窗口的高度,项目会移动到正确的位置

我做错了什么? 我试图将SnapsToDevicePixels设置为True,但它对我不起作用。 : - (

3 个答案:

答案 0 :(得分:3)

您的自定义面板应来自Panel而不是Canvas,并覆盖MeasureOverrideArrangeOverride方法。此外,它应该为子元素布局定义自己的附加属性,例如下面显示的四个属性RelativeXRelativeYRelativeWidthRelativeHeight

它将在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之前或由于某种原因我得到这个:

Invalid

答案 2 :(得分:0)

这可能或者可能不完全符合您的要求,但每当我听到/读取“调整大小”时,我会想到为此目的而构建的UI控件; Viewbox。使用这个类,您可以选择如何调整内容的大小,但我认为您需要默认的Uniform方法。

只需将您需要调整大小的内容放入ViewBox,然后看看您喜欢它... 应该为您完成这项工作:

<ViewBox Stretch="Uniform">
    <Canvas ... />
</ViewBox>

您实际上不需要在此处添加Stretch="Uniform",因为这是默认设置......它仅用于演示目的。要了解详情,请参阅MSDN上的Viewbox Class页面。