Blaise Braye, IT Edition

Aller au contenu | Aller au menu | Aller à la recherche

mardi, 21 février 2012

Attention à éviter de copier de fichier depuis un projet silverlight vers une librairie dédiée à du desktop

Je travaille aujourd'hui en environnement visual studio 2010, avec une solution qui contient des projets destinés à différent framework (silverlight 4 et .net 4 essentiellement).

Je viens d'obtenir le message suivant, inopinément après une compilation: "Projet file must include the .NET Framework assembly 'WindowsBase, PresentationCore, PresentationFramework' in the reference list."

Dix minutes plus tôt la compilation fonctionnait. Il m'en a fallut dix de plus pour comprendre que cela venait d'une copie de fichier, dans visual studio, depuis un projet silverlight vers un projet desktop. En l'occurence, c'était une ressource et elle a été copiée avec ces attributs. L'attribut problématique était "Build Action" qui était fixé à "Page" or je le voulais en tant que ressource. De ce fait, le compilateur se plaignait que les librairies de présentation ne soient pas référencées par ma librairie desktop.

Mon intuition me dit que ce problème peut survenir systématiquement pour ce scénario quand on déplace le fichier depuis projet destiné à une target alpha vers un projet destiné à une target beta.

vendredi, 10 février 2012

Positionner et redimmensionner une image en silverlight

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);
}