#1,062 – Scaling a Canvas Using a ViewBox

ViewBox is typically used to scale a panel containing other elements.  One common use of a ViewBox is to scale the contents of a Canvas panel.

We might include several elements within a Canvas that has an explicit size.

1062-001

If we re-size the window, however, the canvas stays the same size.

1062-002

We could have had the Canvas stretch to fill the remaining area, but its elements would still be the same size.

We can get the elements within the Canvas to scale by wrapping the Canvas in a ViewBox.

    <DockPanel>
        <Label DockPanel.Dock="Top" Background="LightGray"
               Content="Stuff at top of window here"
               VerticalAlignment="Top"/>
        <Label DockPanel.Dock="Bottom" Background="AliceBlue"
               Content="Bottom stuff down here"
               VerticalAlignment="Bottom"/>
        <Viewbox>
            <Canvas Background="Bisque" Width="200" Height="100">
                <Line X1="5" Y1="5" X2="195" Y2="95"
                        Stroke="Black"/>
                <Label Canvas.Left="80" Canvas.Top="5" Content="Howdy"/>
                <Ellipse Height="30" Width="50" Stroke="Blue" StrokeThickness="2"
                            Canvas.Left="140" Canvas.Top="5"/>
            </Canvas>
        </Viewbox>
    </DockPanel>

Now when we resize the window, everything within the Canvas is scaled.
1062-003

 

Advertisement

#1,061 – Scaling Content Using a ViewBox

You can scale entire panels by embedding the panel to be scaled within a ViewBox.

Assume that you start with a simple StackPanel in a Window (the StackPanel scales up to fit in the Window).

1061-001

If you want to scale these elements larger as the window gets larger, you can place the StackPanel in a ViewBox.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Scaling" Width="220" Height="140">
    <Viewbox>
        <StackPanel Background="Bisque">
            <Label Margin="5" Background="AliceBlue"
              Content="Alice Isn't Blue Anymore"/>
            <Button Content="Got It"
              HorizontalAlignment="Center"/>
            <TextBox Margin="5"
                  Text="Enter something here"/>
        </StackPanel>
    </Viewbox>
</Window>

Everything is a little bigger now, within the original window. This is because the StackPanel is no longer stretching to fill the Window, but sizing to fit its contents. And the ViewBox is then scaling the StackPanel to fit into the Window.

1061-002

Now scaling the Window larger causes everything within the ViewBox to scale up.

1061-003

 

#1,060 – Clipping in Grid Happens Before Render Transforms

Layout and render transforms fit into the layout logic for panels as follows:

  • Layout transforms are performed
  • Panel calculates layout of child elements (and clips or resizes)
  • Render transforms are performed

Clipping and/or resizing of elements happens after layout transforms have been applied.  This means that the layout-transformed object is clipped/resized to fit into the Grid.  You can, however, applied a render transform to an object to allow it to extend outside the bounds of the Grid, because this step happens after clipping/resizing.

Below, the first button is rotated before layout, so the rotated button is sized to fit into the Grid cell.  The second button is rotated after layout, so is not resized.

    <Grid ShowGridLines="True" Background="Bisque"
          Margin="10" ClipToBounds="false">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Button Content="Click Here Please"
                HorizontalAlignment="Center"
                VerticalAlignment="Center">
            <Button.LayoutTransform>
                <RotateTransform Angle="30"/>
            </Button.LayoutTransform>
        </Button>

        <Button Grid.Row="1" Content="Click Here Please"
                Width="100"
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <RotateTransform Angle="30"/>
            </Button.RenderTransform>
        </Button>
    </Grid>

1060-001

#1,059 – Clipping vs. Resizing in a Grid

Grid appears to clip its child elements, in that the elements are constrained to fit within a particular grid cell.

In reality, however, a Grid is often resizing, rather than clipping, the child elements.  If the child element does not specify an explicit size, the Grid will ask the child to be a smaller size, rather than simply clipping it at the grid cell boundary.

Below, the top Button does not have an explicit width, but the bottom Button does.  As we shrink the Grid, the top button will be resized, while the bottom button will be clipped.  The labels indicate the actual button widths.

    <Grid ShowGridLines="True" Background="Bisque"
          Margin="10" ClipToBounds="false">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Label Content="{Binding ElementName=myButton, Path=ActualWidth}"/>
        <Button Name="myButton" Margin="2"
                Grid.Column="1" Content="Click Here Please"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"/>

        <Label Grid.Row="1" Content="{Binding ElementName=myButton2, Path=ActualWidth}"/>
        <Button Name="myButton2" Margin="2"
                Grid.Row="1" Grid.Column="1" Content="Click Here Please"
                Width="100"
                VerticalAlignment="Center"/>
    </Grid>


1059-001
1059-002

#1,058 – Translation Makes No Sense within Layout Transforms

layout transform is a transformation applied before a container’s child elements are laid out.

Because the container will position child elements after doing the layout transform, it doesn’t make sense to include a translation as part of a layout transform.  The container positions its child elements based on its layout rules, deciding on the X and Y position of each element after you’ve done the layout transform.

If you want to translate an element relative to where the container wants to put it, you can do that by using the Margin property of the element or by applying a render transform.

Below, the second label uses a translate transform that is ignored.  The third label does the translation in the render transform.

    <StackPanel>
        <Label Content="Flavius" Background="AliceBlue"
               HorizontalAlignment="Left"/>
        <Label Content="Lucius" Background="Bisque"
               HorizontalAlignment="Left">
            <Label.LayoutTransform>
                <TranslateTransform X="100"/>
            </Label.LayoutTransform>
        </Label>
        <Label Content="Constantine" Background="Gainsboro"
               HorizontalAlignment="Left">
            <Label.RenderTransform>
                <TranslateTransform X="100"/>
            </Label.RenderTransform>
        </Label>
    </StackPanel>

1058-001

#1,057 – Preventing a Grid from Clipping a Child Element

Grid will normally clip child elements within each grid cell so that the element will not extend beyond the bounds of the cell.  In the example below, the Label in the second column is clipped to the right side of the column.

    <Grid Background="AliceBlue" Margin="25" ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Label Grid.Column="1"
                Content="Each man delights in the work that suits him best." Background="Olive"
                VerticalAlignment="Center"
                Margin="10"/>
    </Grid>

1057-001
If we want to prevent the Grid from clipping the element, we can do this by placing the element in a Canvas, which we then put into the Grid.  Because a Canvas does not clip child elements,  this will allow the element to extend beyond the boundaries of the Grid.

    <Grid Background="AliceBlue" Margin="25" ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Canvas Grid.Column="1">
            <Label Content="Each man delights in the work that suits him best." Background="Olive"
                   VerticalAlignment="Center"
                   Margin="10"/>
        </Canvas>
    </Grid>

1057-002

#1,056 – Grid Clips Child Elements

While a Canvas panel does not clip its child elements, a Grid does clip child elements.  The child elements are clipped even if you set the ClipToBounds property of the Grid to false.

        <Grid Background="AliceBlue" Margin="25" >
            <Label Content="Each man delights in the work that suits him best." Background="Olive"
                   VerticalAlignment="Center" 
                   Margin="10"/>
        </Grid>

1056-001
1056-002

#1,055 – Canvas Does Not Clip Child Elements

By default, the Canvas panel does not clip its child elements at the boundaries of the Canvas.  If the child element does not fit entirely within the Canvas, it will extend beyond the edge of the Canvas.

For example, assume that we put a Label on a Canvas:

    <Canvas Background="AliceBlue" Margin="25">
        <Label Content="Each man delights in the work that suits him best." Background="Olive"
               Margin="10"/>
    </Canvas>

If we place the Canvas in a large enough Window, the Canvas will be large enough to contain the Label.

1055-001

If we now make the window smaller, the Canvas will become smaller and the Label will extend beyond the boundaries of the Canvas.  Note, however that the Label will not extend past the edge of the Window.

1055-002

1055-003

#1,054 – TabPanel Wraps and Evenly Spaces

Recall that a WrapPanel stacks controls horizontally until it fills the available space and then wraps to a new line.  (Or stacks vertically and wraps to a new column, if the orientation is vertical).

1054-001

The TabPanel, normally used to display the tabs in a TabControl, behaves a little like the WrapPanel.  But when it decides to wrap to a new line, it evenly spaces the items within each line.

We can demonstrate this by starting with a wide window and then narrowing it to cause the items to wrap.

1054-002

1054-003

1054-004

We can better visualize what’s going on here by changing the HorizontalAlignment of the Labels to Center so that they don’t stretch to fill the available space.  We can then see the gaps introduced between elements by the TabPanel.

1054-005

1054-006

1054-007

1054-008

Notice that the TabPanel also attempts to place roughly the same number of items on each row.

#1,053 – How SharedSizeGroup Relates to Column Sizing

Recall that you can set the width of a column in a Grid (or height of a row) in three different ways: auto, explicit size, or star sizing.

When you’re using the SharedSizeGroup property to set multiple columns (or rows) to the same width (or height), the method that you use for setting the column width (or row height) affects the final size as follows:

  • Star sizing – not honored, treated as Auto
  • Absolute sizing – takes priority over Auto, columns are set to maximum explicit width
  • Auto sizing – if all columns are Auto, size is set to fit the largest content.  If any columns use explicit width, the explicit width value takes precedence

In the example below, we’ve set the first and third columns’ widths to be Auto and put them in the same SharedSizeGroup.

1053-001

Setting the width of the first column to 20 causes both columns to use that width.

1053-002