GorillaApe
GorillaApe

Reputation: 3641

WPF canvas and placing items

I want an image as background that would uniform stretch. Then i want my canvas to have size equal to uniform stretched image.(A workaround i found is to have inside a grid the image and canvas and bind canvas width/height to image actual height/width).

Then i want to have items inside the canvas that will be absolutely positioned.But here is the problem.If an item X is 50 and image is 200px width then its ok. But if image gets 100px width item X should be 25.Also i want a small scale transform. Is there a way to achieve this via binding ? Do i have to implement my custom panel usercontrol because i see no other clean solution.

Upvotes: 3

Views: 738

Answers (1)

Clemens
Clemens

Reputation: 128146

You could certainly achieve all this by binding, but i guess that it would be a lot easier and cleaner to create a derived Panel that arranges children at positions relative to the Panel's ActualWidth and Actualheight.

The derived Panel class could define attached properties for child positions, similar to Canvas.Left and Canvas.Top, but with fractional values ranging from 0 to 1.

It could also maintain the background image as dependency property. You would override the OnRender method to directly draw this image.

The Panel could also calculate a current scale factor from dividing e.g. its ActualWidth by the image's width. An appropriate ScaleTransform property would be easily attached to each child's RenderTransform.

EDIT: Except the uniformity of the Panel's dimension relative to the background image, the following might give you an idea of how that panel could be implemented:

public class ImagePanel : Panel
{
    public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached(
        "Left", typeof(double), typeof(ImagePanel),
        new FrameworkPropertyMetadata(PositionChanged));

    public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached(
        "Top", typeof(double), typeof(ImagePanel),
        new FrameworkPropertyMetadata(PositionChanged));

    public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(
        "Image", typeof(ImageSource), typeof(ImagePanel),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

    private ScaleTransform scaleTransform = new ScaleTransform();

    public static double GetLeft(DependencyObject obj)
    {
        return (double)obj.GetValue(LeftProperty);
    }

    public static void SetLeft(DependencyObject obj, double value)
    {
        obj.SetValue(LeftProperty, value);
    }

    public static double GetTop(DependencyObject obj)
    {
        return (double)obj.GetValue(TopProperty);
    }

    public static void SetTop(DependencyObject obj, double value)
    {
        obj.SetValue(TopProperty, value);
    }

    public ImageSource Image
    {
        get { return (ImageSource)GetValue(ImageProperty); }
        set { SetValue(ImageProperty, value); }
    }

    protected override void OnRender(DrawingContext dc)
    {
        if (Image != null)
        {
            dc.DrawImage(Image, new Rect(0d, 0d, ActualWidth, ActualHeight));
        }

        base.OnRender(dc);
    }

    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);

        if (Image != null)
        {
            scaleTransform.ScaleX = sizeInfo.NewSize.Width / Image.Width;
            scaleTransform.ScaleY = sizeInfo.NewSize.Height / Image.Height;
        }
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        foreach (UIElement element in InternalChildren)
        {
            element.Measure(availableSize);
        }

        return new Size();
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement element in InternalChildren)
        {
            element.RenderTransform = scaleTransform;
            ArrangeElement(element, finalSize.Width, finalSize.Height);
        }

        return finalSize;
    }

    private static void PositionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var element = obj as UIElement;
        var panel = VisualTreeHelper.GetParent(obj) as ImagePanel;

        if (element != null && panel != null)
        {
            ArrangeElement(element, panel.ActualWidth, panel.ActualHeight);
        }
    }

    private static void ArrangeElement(UIElement element, double width, double height)
    {
        var left = GetLeft(element);
        var top = GetLeft(element);
        var rect = new Rect(new Point(left * width, top * height), element.DesiredSize);

        element.Arrange(rect);
    }
}

Upvotes: 4

Related Questions