Ian Ian - 1 month ago 22
C# Question

WPF Canvas Scaling/Transform to Fit

I'm reposting this question as I didn't get much of a response last time, hopefully a bit of re-wording might help...

Essentially what I'm trying to do is to create a databound canvas, that will automatically scale its contents to 'fill' up the available space. Sort of like a zoom to fit operation. Unfortunately my WPF skills aren't yet very strong, and I'm struggling to work out how to do this last part. I've followed some databinding examples to get the canvas bound, but not sure if maybe its wrong and hindering me.

I've got two basic problems at the moment depending on the way I try and tackle the solution, either:


  • I don't know how to make the
    canvas re-scale automatically
    through XAML if its possible using a
    transform.

  • I can't seem to
    reference the canvas in the behind
    code, I'm guessing because its part
    of an ItemsControl?



An example of what I'm trying to achieve, I've got A I want to try and get B:

(removed expired link to an img)

The code I'm currently using is pretty simple, just creating 4 dots with a given co-ordinate, and a another view model to wrap these up in.

public class PointCollectionViewModel
{
private List<PointViewModel> viewModels;
public PointCollectionViewModel()
{
this.viewModels = new List<PointViewModel>();
this.viewModels.Add(new PointViewModel(new Point(1, 1)));
this.viewModels.Add(new PointViewModel(new Point(9, 9)));
this.viewModels.Add(new PointViewModel(new Point(1, 9)));
this.viewModels.Add(new PointViewModel(new Point(9, 1)));
}

public List<PointViewModel> Models
{
get { return this.viewModels; }
}
}

public class PointViewModel
{
private Point point;
public PointViewModel(Point point)
{
this.point = point;
}

public Double X { get { return point.X; } }
public Double Y { get { return point.Y; } }
}


Then the PointCollectionViewModel is used as the DataContent for my AutoResizingCanvas, which has the following XAML to implement the binding:

<UserControl x:Class="WpfCanvasTransform.AutoResizingCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCanvasTransform"
x:Name="parent">
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Path=Models}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas" Background="DarkSeaGreen" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Canvas.LayoutTransform>
<ScaleTransform ScaleY="-1" />
</Canvas.LayoutTransform>

</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:PointViewModel}">
<Ellipse Width="3" Height="3" Fill="Red"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}"/>
<Setter Property="Canvas.Left" Value="{Binding Path=X}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</UserControl>

Answer

As your Canvas doesn't seem to have fixed width and height, I would include it into a Viewbox:

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <Viewbox Stretch="Uniform">
            <Canvas x:Name="canvas" Background="DarkSeaGreen">
                <Canvas.LayoutTransform>
                <ScaleTransform ScaleY="-1" />
                </Canvas.LayoutTransform>
            </Canvas>
        </Viewbox>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

Alternatively, place your entire UserControl into a ViewBox.