2010-08-06 21 views
8

Je voudrais redimensionner un cercle sur ma toile à l'aide d'un curseur. Ce cercle peut être déplacé sur le canevas par un glisser & drop stuff que j'ai fait dans le code, donc sa position n'est pas corrigée.WPF: Redimensionnement d'un cercle, en conservant le point central au lieu de TopLeft?

J'ai lié la valeur du curseur à la hauteur et la largeur d'une ellipse. Malheureusement, lorsque j'utilise le curseur, le cercle est redimensionné avec son point en haut à gauche (en fait le point en haut à gauche du rectangle dans lequel il est assis) restant le même pendant l'opération.

Je voudrais le redimensionner avec son centre point étant constant pendant le fonctionnement. Existe-t-il un moyen facile de le faire en XAML? BTW, j'ai déjà essayé ScaleTransform, mais il n'a pas fait ce que je voulais.

Merci beaucoup! :-)

Jan

<Canvas x:Name="MyCanvas"> 

    <!-- this is needed for some adorner stuff I do in code behind --> 
    <AdornerDecorator Canvas.Left="10" 
         Canvas.Top="10"> 
     <Ellipse x:Name="myEllipse" 
      Height="{Binding Path=Value, ElementName=mySlider}" 
      Width="{Binding Path=Value, ElementName=mySlider}" 
      Stroke="Aquamarine" 
      Fill="AliceBlue" 
      RenderTransformOrigin="0.5 0.5"> 
      <Ellipse.RenderTransform> 
       <RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}" /> 
      </Ellipse.RenderTransform> 
     </Ellipse> 
    </AdornerDecorator> 

    <Slider x:Name="mySlider" 
      Maximum="100" 
      Minimum="0" 
      Width="100" 
      Value="10" 
      Canvas.Left="150" 
      Canvas.Top="10" /> 
    <Slider x:Name="myRotationSlider" 
      Maximum="360" 
      Minimum="0" 
      Width="100" 
      Value="0" 
      Canvas.Left="150" 
      Canvas.Top="50" /> 
</Canvas> 

Répondre

4

Vous pouvez lier votre Canvas.Left et Canvas.Top à votre hauteur et la largeur par un ValueConverter.

Spécifiquement (modifier):
Créez une propriété pour Canvas.Left et Canvas.Top, puis liez-les.
Stocke les anciennes valeurs pour Width et Heigth ou l'ancienne valeur du curseur.
Chaque fois que le curseur est modifié, obtenez le changement incrémental "dx" en soustrayant la valeur stockée.
(N'oubliez pas de mettre à jour la valeur stockée ...)
Ajoutez dx à la propriété Width et Height.
Et, comme Will l'a dit, ajouter dx/2 * -1 aux propriétés Canvas.Left et Canvas.Top.

Est-ce que cela a du sens?

+0

Merci pour votre réponse! Il m'a montré que ma question était incomplète ;-) La position du cercle sur la toile n'est pas fixe, c'est pourquoi je n'ai pas trouvé le moyen d'utiliser un ValueConverter ... – Jan

+0

Attendez, cela fonctionnerait. Vous devriez avoir un convertisseur de valeur qui fait 'value/2 * -1' – Will

+0

Salut Martin! Merci - Je vais tester cela mercredi et rendre compte :) – Jan

2

Le problème est que vous utilisez le SLIDER pour ajuster la largeur et la hauteur. La largeur et la hauteur ne sont pas calculées autour de RenderTransformOrigin; seulement RenderTransforms utilise cette valeur.

est ici une version corrigée (BRB, kaxaml):

<Canvas x:Name="MyCanvas"> 
<!-- this is needed for some adorner stuff I do in code behind --> 
    <AdornerDecorator Canvas.Left="50" Canvas.Top="50"> 
     <Ellipse 
      x:Name="myEllipse" 
      Width="10" 
      Height="10" 
      Fill="AliceBlue" 
      RenderTransformOrigin="0.5 0.5" 
      Stroke="Aquamarine"> 
      <Ellipse.RenderTransform> 
       <TransformGroup> 
        <RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}"/> 
        <ScaleTransform 
         CenterX=".5" 
         CenterY=".5" 
         ScaleX="{Binding Path=Value, ElementName=mySlider}" 
         ScaleY="{Binding Path=Value, ElementName=mySlider}"/> 
       </TransformGroup> 
      </Ellipse.RenderTransform> 
     </Ellipse> 
    </AdornerDecorator> 
    <Slider 
     x:Name="mySlider" 
     Width="100" 
     Canvas.Left="150" 
     Canvas.Top="10" 
     Maximum="10" 
     Minimum="0" 
     SmallChange=".01" 
     Value="1"/> 
    <Slider 
     x:Name="myRotationSlider" 
     Width="100" 
     Canvas.Left="150" 
     Canvas.Top="50" 
     Maximum="360" 
     Minimum="0" 
     Value="0"/> 
</Canvas> 

Bien sûr, cela ne fonctionnera probablement pas pour vous. Pourquoi? Eh bien, le ScaleTransform que j'ai utilisé zoom non seulement le cercle mais aussi la bordure; à mesure que le cercle grossit, la frontière le fait aussi. J'espère que vous ne vous en soucierez pas.

En outre, réaliser en combinant des transformations (échelle puis tourner dans ce cas) qu'ils sont appliqués dans l'ordre, et on peut affecter comment un autre est fait. Dans votre cas, vous ne le remarquerez pas. Mais si, disons, vous faisiez une rotation et traduisiez, l'ordre serait pertinent.


Ah, à quoi je pensais? Il suffit de coller l'ellipse dans une grille (solution la plus simple, mais d'autres conteneurs fonctionneraient). La grille prend automatiquement soin de centrer l'ellipse lorsqu'elle est redimensionnée. Pas besoin de convertisseurs de valeur! Voici le code:

<Canvas x:Name="MyCanvas"> 
<!-- this is needed for some adorner stuff I do in code behind --> 
    <Grid Width="100" Height="100"> 
     <AdornerDecorator> 
      <Ellipse 
       x:Name="myEllipse" 
       Width="{Binding Path=Value, ElementName=mySlider}" 
       Height="{Binding Path=Value, ElementName=mySlider}" 
       Fill="AliceBlue" 
       RenderTransformOrigin="0.5 0.5" 
       Stroke="Aquamarine"> 
       <Ellipse.RenderTransform> 
        <RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}"/> 
       </Ellipse.RenderTransform> 
      </Ellipse> 
     </AdornerDecorator> 
    </Grid> 
    <Slider 
     x:Name="mySlider" 
     Width="100" 
     Canvas.Left="150" 
     Canvas.Top="10" 
     Maximum="100" 
     Minimum="0" 
     Value="10"/> 
    <Slider 
     x:Name="myRotationSlider" 
     Width="100" 
     Canvas.Left="150" 
     Canvas.Top="50" 
     Maximum="360" 
     Minimum="0" 
     Value="0"/> 
</Canvas> 
+0

Merci Will! Malheureusement, je me soucie de la Border ;-) Alors, oui, j'ai déjà essayé ScaleTransform, mais ça a aussi chamboulé mes Adorners (que j'ai omis ici par souci de concision) ... D'autres idées? :-) – Jan

+0

@Jan Situation difficile. Vous allez probablement devoir créer un contrôle personnalisé dans ce cas. La raison en est que vous ne pouvez pas lier à Canvas.Left et Canvas.Top. Si vous le pouvez, vous pouvez utiliser un convertisseur sur la liaison pour prendre la valeur (disons la valeur du curseur 10), l'annuler et la diviser par 2. Cela déplace l'origine du cercle pour qu'il corresponde à sa largeur et sa hauteur. Puisque vous ne pouvez pas faire cela, vous pouvez créer un contrôle personnalisé qui effectue ce calcul et met à jour l'ellipse en conséquence. – Will

+0

Attends, j'ai tort. Fait une erreur dans mon test. Vous pouvez lier à Canvas.Left/Top et utiliser un convertisseur de valeur pour traduire une augmentation de la taille en une modification de la position du canevas. – Will

2

Étant donné que vous utilisez un canevas, l'emplacement d'un élément est l'emplacement. Si vous voulez que le haut, la position gauche change, vous devez le faire vous-même.Si vous utilisiez un autre type de panneau, comme une grille, vous pouvez modifier l'alignement de votre ellipse pour la placer dans le même emplacement relatif, quelle que soit sa taille. Vous pouvez obtenir cet effet en ajoutant une grille à l'intérieur de votre AdornerDecorator et en centrant l'Ellipse, mais vous devrez également définir l'AdornerDecorator ou la Grid à une taille fixe car ils ne s'étireront pas dans un Canvas.

La meilleure solution que vous pourriez utiliser serait une ScaleTransform appliquée à la propriété RenderTransform avec un RenderTransformOrigin de 0.5,0.5. Vous avez dit que vous aviez des problèmes avec ScaleTransform mais pas quel était le problème. Enveloppez votre ellipse dans une grille de la taille maximale.

+0

ScaleTransform a grossi tout ce que j'avais, donc mes Adorners (que j'ai omis du code ici pour la brièveté) plus grand que j'ai redimensionné, et aussi l'épaisseur de la course (taille de la bordure) a augmenté. Je veux seulement redimensionner le cercle lui-même, avec l'épaisseur du trait et la taille de l'Adorner à gauche ... – Jan

+1

Donc, à partir de vos réponses, il semble que vous vouliez définir un point central variable pour votre Ellipse plutôt que Top, Left. Vous pouvez obtenir cet effet en ajoutant une liaison avec un convertisseur à la marge sur AdornerDecorator pour définir la marge sur (-ActualWidth/2, -ActualHeight/2,0,0). Cela va essentiellement déplacer les éléments vers le haut et les centrer sur le point que vous avez défini comme Top, Left. –

+0

Salut John! Cela semble intéressant - merci pour votre solution :) Cela pourrait être très utile si la solution de Martin utilisant Bindings avec Canvas.Top/.Left ne fonctionne pas pour moi ... – Jan

1

Tant qu'il est plus petit, l'Ellipse sera centrée sur la grille:

<Grid 
     Canvas.Left="10" 
     Canvas.Top="10" 
     Width="100" 
     Height="100"> 
     <AdornerDecorator> 
      <Ellipse x:Name="myEllipse" 
      Height="{Binding Path=Value, ElementName=mySlider}" 
      Width="{Binding Path=Value, ElementName=mySlider}" 
      Stroke="Aquamarine" 
      Fill="AliceBlue" 
      RenderTransformOrigin="0.5 0.5"> 
       <Ellipse.RenderTransform> 
        <RotateTransform Angle="{Binding Path=Value, ElementName=myRotationSlider}" /> 
       </Ellipse.RenderTransform> 
      </Ellipse> 
     </AdornerDecorator> 
    </Grid> 

Vous devrez peut-être ajuster votre logique glisser pour gérer en faisant glisser la grille au lieu de l'Ellipse lui-même.