2010-08-06 16 views
8

Me gustaría cambiar el tamaño de un círculo en mi lienzo con la ayuda de un control deslizante. Este círculo se puede mover por el lienzo arrastrando algunas cosas & que hice en el código detrás, por lo que su posición no es fija.WPF: cambiar el tamaño de un círculo, manteniendo el punto central en lugar de TopLeft?

He atado el valor del control deslizante a la altura y el ancho de una elipse. Lamentablemente, cuando utilizo el control deslizante, el círculo cambia de tamaño con su punto superior izquierdo (en realidad, el punto superior izquierdo del rectángulo en el que se encuentra) permaneciendo igual durante la operación.

Me gustaría cambiar el tamaño con su centro punto constante durante la operación. ¿Hay alguna manera fácil de hacer esto en XAML? Por cierto, ya probé ScaleTransform, pero no hizo exactamente lo que quería.

¡Muchas gracias! :-)

Ene

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

Respuesta

4

Usted puede vincular a su Canvas.Left y Canvas.Top a su altura y anchura a través de un ValueConverter.

Específicamente (editar):
Cree una propiedad para el Canvas.Left y el Canvas.Top y únase a estos.
Almacene los valores anteriores para Ancho y alto o el antiguo valor del control deslizante.
Cada vez que se cambie el control deslizante, obtenga el cambio incremental "dx" restando el valor almacenado.
(No olvide actualizar el valor almacenado ...)
Agregue dx a la propiedad Anchura y Altura.
Y, como dijo Will, agregue dx/2 * -1 a las propiedades Canvas.Left y Canvas.Top.

¿Tiene sentido?

+0

¡Gracias por su respuesta! Me mostró que mi pregunta era incompleta ;-) La posición del círculo en el lienzo no es fija, es por eso que no encontré la manera de usar un ValueConverter ... – Jan

+0

Espera, esto funcionaría. Tendría que tener un convertidor de valor que haga 'value/2 * -1' – Will

+0

¡Hola Martin! Gracias - Voy a probar esto el miércoles e informar :) – Jan

2

El problema es que está utilizando el DESLIZADOR para ajustar el ancho y el alto. El ancho y la altura no se calculan en RenderTransformOrigin; solo RenderTransforms usa ese valor.

Aquí hay una versión corregida (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> 

Por supuesto, esto probablemente no va a funcionar para usted. ¿Por qué? Bueno, ScaleTransform usé zooms no solo del círculo sino también del borde; a medida que el círculo se hace más grande, el borde también lo hace. Espero que no te importe esto.

Además, tenga en cuenta al combinar transformadas (escala entonces girar en este caso) que se aplican en orden, y una puede afectar cómo se hace otra. En tu caso, no notarías esto. Pero si, por ejemplo, hiciera una rotación y traduzca, el orden sería relevante.


Ah, ¿qué estaba pensando? Simplemente coloque la elipse en una rejilla (la solución más simple pero otros recipientes funcionarían). La cuadrícula se ocupa automáticamente de centrar la elipse a medida que se redimensiona. ¡Sin necesidad de ningún convertidor de valor! Aquí está el código:

<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

¡Gracias Will! Desafortunadamente, me importa la frontera ;-) Así que sí, ya he probado ScaleTransform, pero eso también estropeó mis Adorners (que dejé aquí para abreviar) ... ¿Alguna otra idea? :-) – Jan

+0

@Jan Situación difícil. Probablemente va a tener que crear un control personalizado en ese caso. La razón es que no se puede unir a Canvas.Left y Canvas.Top. Si pudiera, podría usar un convertidor en la encuadernación para tomar el valor (digamos el valor del control deslizante 10), negarlo y dividirlo entre 2. Eso movería el origen del círculo para que coincida con su aumento de ancho y alto. Como no puede hacer esto, puede crear un control personalizado que realice este cálculo y actualice la elipse en consecuencia. – Will

+0

Espera, estoy equivocado. Cometí un error en mi prueba Puede enlazar a Canvas.Left/Top y usar un convertidor de valor para traducir un aumento de tamaño a un cambio en la posición del lienzo. – Will

2

Dado que está utilizando un lienzo, la ubicación de un elemento es la ubicación. Si desea que la posición Superior e Izquierda cambie, debe hacerlo usted mismo.Si estuviera usando otro tipo de Panel, como una Rejilla, podría cambiar la alineación de su Elipse para colocarla en la misma ubicación relativa sin importar el tamaño. Puede obtener ese efecto agregando una cuadrícula dentro de AdornerDecorator y centrando la Elipse, pero también deberá configurar el AdornerDecorator o la Cuadrícula en un tamaño fijo, ya que no se estirarán en un lienzo.

La mejor solución que podría usar sería una ScaleTransform aplicada a la propiedad RenderTransform con una RenderTransformOrigin de 0.5,0.5. Dijiste que tenías problemas con ScaleTransform pero no cuál era el problema.

+0

ScaleTransform básicamente amplió todo lo que tenía, por lo que mis Adorners (que dejé aquí fuera del código para abreviar) obtuve más grande a medida que aumentaba la escala, y también aumentaba el grosor del trazo (tamaño del borde). Solo quiero cambiar el tamaño del círculo, sin embargo, con el grosor del trazo y el tamaño de Adorner permanecen constantes ... – Jan

+1

Por lo que parece que quieres establecer un punto central variable para tu Elipse en lugar de Superior, Izquierda. Puede obtener este efecto agregando un Enlace con un Conversor a Margen en AdornerDecorator para establecer el Margen en (-ActualWidth/2, -ActualHeight/2,0,0). Esto esencialmente moverá los elementos hacia arriba y hacia la izquierda sobre el punto que establezca como Superior, Izquierda. –

+0

¡Hola John! Suena interesante, gracias por su solución :) Esto podría ser muy útil si la solución de Martin usando Bindings con Canvas.Top/.Left no funciona para mí ... – Jan

1

Envuelva su Elipse en una cuadrícula del tamaño máximo. Mientras que es más pequeño, la elipse se centrará en la cuadrícula:

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

Es posible que tenga que ajustar su lógica para manejar arrastrando arrastrando la cuadrícula en lugar de la propia elipse.

Cuestiones relacionadas