Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get WPF's ClipToBounds to work?

I have an application that displays an image inside of an Image object in WPF. The image is contained in a control whose xaml looks like:

<UserControl x:Class="MyProgram.NativeImageDisplay"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Loaded="UserControl_Loaded">
    <Canvas Name="border" Background="Black" >
        <Image Name="image" StretchDirection="Both" Stretch="Uniform" ClipToBounds="True"
                SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="HighQuality"></Image>
    </Canvas>
</UserControl>

Two of these controls are contained in a grid in a window, like so:

    <Grid  Grid.Row="2" Name="DisplayCanvas">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <l:NativeImageDisplay x:Name="imageDisplay2" Grid.Column="1" ClipToBounds="True"/>
        <l:NativeImageDisplay x:Name="imageDisplay" Grid.Column="0" ClipToBounds="True"/>           
    </Grid>

I'm calling clipping to be true all the way through here.

The user can zoom on the image by using the scroll button on the mouse, and that ultimately calls a ScaleTransform on the image:

    private void image_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (!thumbnail)
        {
            TransformGroup transformGroup = (TransformGroup)border.RenderTransform;
            ScaleTransform transform = (ScaleTransform)transformGroup.Children[0];

            double oldScaleX = transform.ScaleX;
            double oldScaleY = transform.ScaleY;

            double zoom = e.Delta;
            transform.ScaleX += zoom / 10000;
            transform.ScaleY += zoom / 10000;
            if (transform.ScaleX > maxZoom || transform.ScaleY > maxZoom)
            {
                transform.ScaleX = maxZoom;
                transform.ScaleY = maxZoom;
            }
            if (transform.ScaleX < minZoom || transform.ScaleY < minZoom)
            {
                transform.ScaleX = minZoom;
                transform.ScaleY = minZoom;
            }
            Point thePoint = e.GetPosition(border);
            transform.CenterY = 0;
            transform.CenterX = 0;

            foreach (UIElement child in border.Children)
            {
                if (child is Anchor)
                {
                    TransformGroup group = (TransformGroup)child.RenderTransform;
                    ScaleTransform t = (ScaleTransform)group.Children[0];
                    t.ScaleX *= oldScaleX / transform.ScaleX;
                    t.ScaleY *= oldScaleY / transform.ScaleY;
                }
            }
        }
    }

Once that scale transform is called, the image is no longer contained in the boundary of its canvas or the grid selection. Essentially, ClipToBounds is being ignored. How can I make this transform pay attention to ClipToBounds?

like image 977
mmr Avatar asked Jul 13 '11 20:07

mmr


People also ask

What is Clipstobounds in Swift?

A Boolean value that determines whether subviews are confined to the bounds of the view.

What does Maskstobounds mean?

A Boolean indicating whether sublayers are clipped to the layer's bounds.


1 Answers

Canvas is kind of unique in that it doesn't really participate in the layout system like other elements. It basically acts as an infinite size space with fixed position children so generally ignores clipping completely. I can't tell for sure without seeing more of the code but if you want to apply the clipping to the scaled object moving the scaling to a different element might do what you want. The simplest thing to do would be to wrap a Border around your Canvas and apply the ScaleTransform to that instead. The Border should give you better clipping behavior.

<Border x:Name="border" Background="Black" ClipToBounds="True">
    <Canvas x:Name="imageHost">
    ...
    </Canvas>
</Border>
like image 136
John Bowen Avatar answered Oct 02 '22 12:10

John Bowen