8 votes

Comment faire fonctionner la fonction ClipToBounds de WPF ?

J'ai une application qui affiche une image à l'intérieur d'un objet Image dans WPF. L'image est contenue dans un contrôle dont le xaml ressemble à ceci :

<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>

Deux de ces contrôles sont contenus dans une grille dans une fenêtre, comme suit :

    <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>

J'estime que l'écrêtage est vrai sur toute la ligne.

L'utilisateur peut zoomer sur l'image en utilisant le bouton de défilement de la souris, ce qui appelle une ScaleTransform sur l'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;
                }
            }
        }
    }

Une fois que cette transformation d'échelle est appelée, l'image n'est plus contenue dans les limites de son canevas ou de la sélection de la grille. En fait, la fonction ClipToBounds est ignorée. Comment puis-je faire en sorte que cette transformation tienne compte de ClipToBounds ?

22voto

John Bowen Points 14985

Le canevas est unique en ce sens qu'il ne participe pas vraiment au système de mise en page comme d'autres éléments. Il agit essentiellement comme un espace de taille infinie avec des enfants à position fixe, ce qui lui permet d'ignorer complètement le clipping. Je ne peux pas le dire avec certitude sans voir plus de code, mais si vous voulez appliquer le clipping à l'objet mis à l'échelle, déplacer la mise à l'échelle vers un élément différent pourrait faire ce que vous voulez. La chose la plus simple à faire serait d'entourer votre Canvas d'un Border et d'appliquer la ScaleTransform à cet élément. La bordure devrait vous permettre d'obtenir un meilleur comportement en matière d'écrêtage.

<Border x:Name="border" Background="Black" ClipToBounds="True">
    <Canvas x:Name="imageHost">
    ...
    </Canvas>
</Border>

0voto

Gerard Sexton Points 1569

Le commentaire ci-dessus m'a aidé. Emboîter un canevas dans un autre, ajouter ClipToBounds="True" au parent et lie la hauteur et la largeur imbriquées aux propriétés du parent respectivement.
De cette manière, il n'est pas nécessaire d'effectuer des transformations sur le parent.

<Canvas ClipToBounds="True" Name="Outer">
     <Canvas x:Name="Inner" 
         Height="{Binding ActualHeight, ElementName=Outer, Mode=OneWay}"
         Width="{Binding ActualWidth, ElementName=Outer, Mode=OneWay}" />
</Canvas>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X