En développant un module de visualisation de photos (silverlight 4), Il me fallait un outil capable de redimmensioner une image avec la roulette de la souris et de la repositionner sur une surface via un "drag and drop". J'ai pu obtenir le résultat escompté en lisant le contenu de ce lien , moyennant quelques adaptations propres à mes besoins.

  • la solution proposée utilise certaines fonctions propres à wpf, heureusement elle ne sont pas très complexes à réécrire (substraction de vecteur...)
  • La grande différence entre mon implémentation et celle proposée par l'utilisateur est que j'ai préféré une gestion des événements au niveau de la bordure qui contient l'image. De cette façon, le contrôle sur l'image est possible partout à l'intérieur de la bordure (c'est le comportement d'un calque dans Photoshop). Notons que pour permettre une gestion des événements à un conteneur quelqu'il soit (une bordure ici), il faut impérativement lui imposer une couleur d'arrière plan si son contenu ne prend pas toute la surface disponible. Raison intuitive de cette obligation : les évènements ne se produisent que s'ils sont propagés via un contenu (--> conteneur != contenu).
  • Outre cette différence, j'ai souhaité que le resizing lors de l'événement "MouseWheel" (roulette de la souris) préserve la position de la souris sur l'image. Par exemple, si je zoome avec la souris sur le volant d'une voiture, mon objectif est de préserver le volant de cette voiture sous ma souris.
  • Une dernière fonctionnalité intéressante, retrouvée sur ce lien, permet de s'assurer que le moteur de rendu silverlight n'affiche pas le contenu de la bordure qui se retrouverait en dehors de sa zone d'affichage. Cette fonctionnalité est présente nativement dans WPF et est activée par la propriété "ClipToBound"

Code Xaml

<Border Grid.Row="1" Layout:Clip.ToBounds="True" Background="WhiteSmoke"
        MouseWheel="OnBorderMouseWheel"
        MouseLeftButtonDown="OnBorderMouseLeftButtonDown"
        MouseLeftButtonUp="OnBorderMouseLeftButtonUp"
        MouseMove="OnBorderMouseMove"
        >
    <Image x:Name="imgLarge" RenderTransformOrigin="0.5,0.5"      
        HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" Source="{Binding Value.FileWebUrl}">
    <Image.RenderTransform>
        <TransformGroup>
            <ScaleTransform />
            <TranslateTransform/>
        </TransformGroup>
    </Image.RenderTransform>
</Image>
</Border>

Code C# associé

/// <summary>
/// Called when [border mouse wheel].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseWheelEventArgs"/> instance containing the event data.</param>
private void OnBorderMouseWheel(object sender, MouseWheelEventArgs e)
{
    var border = (Border)sender;
    var image = (Image)border.Child;

    var st = ((TransformGroup)image.RenderTransform).Children.OfType<ScaleTransform>().First();
    var tt = ((TransformGroup)image.RenderTransform).Children.OfType<TranslateTransform>().First();

    var start = e.GetPosition(image);
    var origin = new Point(tt.X, tt.Y);

            
    double zoom = e.Delta > 0 ? .2 : -.2;
    st.ScaleX += zoom;
    st.ScaleY += zoom;

    var target = e.GetPosition(image);

    var v = st.Transform(Substract(start,target));
    tt.X = origin.X - v.X;
    tt.Y = origin.Y - v.Y;

}

Point _start;
Point _origin;
bool _isMouseCaptured;

/// <summary>
/// Called when [border mouse left button down].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
private void OnBorderMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var border = (Border)sender;
    var image = (Image)border.Child;
            
    var tt = ((TransformGroup)image.RenderTransform).Children.OfType<TranslateTransform>().Single();
    _start = e.GetPosition(border);
    _origin = new Point(tt.X, tt.Y);

    _isMouseCaptured = true;
    image.CaptureMouse();
}

/// <summary>
/// Called when [border mouse left button up].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
private void OnBorderMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    var border = (Border)sender;
    var image = (Image)border.Child;
    _isMouseCaptured = false;
    image.ReleaseMouseCapture();
}

/// <summary>
/// Called when [border mouse move].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
private void OnBorderMouseMove(object sender, MouseEventArgs e)
{
    var border = (Border)sender;
    var image = (Image)border.Child;

    if (_isMouseCaptured)
    {
        var tt = (TranslateTransform)((TransformGroup)image.RenderTransform)
            .Children.First(tr => tr is TranslateTransform);

        var v = Substract(_start, e.GetPosition(border));
        tt.X = _origin.X - v.X;
        tt.Y = _origin.Y - v.Y;
    }
}

/// <summary>
/// Substracts the specified PT1.
/// </summary>
/// <param name="pt1">The PT1.</param>
/// <param name="pt2">The PT2.</param>
/// <returns></returns>
public static Point Substract(Point pt1, Point pt2)
{
    return new Point(pt1.X - pt2.X, pt1.Y - pt2.Y);
}