2010-08-06 12 views
8

Chciałbym zmienić rozmiar koła na moim płótnie za pomocą suwaka. Ten okrąg może być przenoszony na płótnie przez jakiś przeciągnięty kod, który usunąłem z kodu, więc jego pozycja nie jest ustalona.WPF: Zmieniając rozmiar koła, zachowując punkt środkowy zamiast TopLeft?

Związałem wartość suwaka z wysokością i szerokością elipsy. Niestety, kiedy używam suwaka, kółko zmienia rozmiar z lewym górnym punktem (właściwie lewy górny punkt prostokąta, w którym siedzi) pozostając tym samym podczas operacji.

Chciałbym zmienić rozmiar jego punktu centrum punkt jest stały podczas operacji. Czy jest to prosty sposób na XAML? BTW, już wypróbowałem ScaleTransform, ale nie całkiem zrobiłem to, co chciałem.

Wielkie dzięki! :-)

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> 

Odpowiedz

4

Można związać swoją Canvas.Left i Canvas.Top do wysokości i szerokości poprzez ValueConverter.

W szczególności (edycja):
Utwórz właściwość dla każdego elementu Canvas.Left i Canvas.Top i połącz je.
Zapisz stare wartości Szerokość i Wysokość lub starą wartość suwaka.
Gdy suwak zostanie zmieniony, uzyskaj przyrostową zmianę "dx", odejmując zapisaną wartość.
(Nie zapomnij zaktualizować zapisanej wartości ...)
Dodaj dx do właściwości Szerokość i Wysokość.
I, jak powiedział Will, dodaj dx/2 * -1 do właściwości Canvas.Left i Canvas.Top.

Czy to ma sens?

+0

Dzięki za odpowiedź! Okazało się, że moje pytanie było niepełne ;-) Położenie koła na płótnie nie jest stałe, dlatego nie znalazłem sposobu na użycie ValueConvertera ... – Jan

+0

Czekaj, to by działało. Musiałbyś mieć konwerter wartości, który ma wartość "value/2 * -1' – Will

+0

Hi Martin! Dzięki - mam zamiar przetestować to w środę i złożyć raport :) – Jan

2

Problem polega na tym, że używasz SLIDERA do regulacji szerokości i wysokości. Szerokość i wysokość nie są obliczane wokół RenderTransformOrigin; tylko RenderTransforms używa tej wartości.

Oto wersja poprawiona (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> 

Oczywiście, to prawdopodobnie nie będzie pracować dla Ciebie. Czemu? Cóż, użyty ScaleTransform, którego użyłem, powiększa nie tylko okrąg, ale także obramowanie; w miarę jak krąg powiększa się, granica również się poprawia. Mam nadzieję, że nie będziesz się tym przejmował.

Wykonuj również kombinację transformacji (skalowanie, a następnie obracanie w tym przypadku), aby były one stosowane w kolejności i można wpływać na sposób wykonania innej. W twoim przypadku tego nie zauważysz. Ale jeśli, powiedzmy, robisz rotację i tłumaczysz, kolejność będzie trafna.


Ah, o czym myślałem? Po prostu przyklej elipsę do siatki (najprostsze rozwiązanie, ale działają inne pojemniki). Siatka automatycznie dba o centrowanie elipsy podczas zmiany jej rozmiaru. Nie potrzeba żadnych konwerterów wartości! Oto kod:

<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

Dzięki Will! Niestety, nie dbam o Granicę ;-) Tak, już wypróbowałem ScaleTransform, ale to również zepsuło moje Adornerskie (które zostawiłem tutaj dla zwięzłości) ... Jakieś inne pomysły? :-) – Jan

+0

@Jan Trudna sytuacja. Prawdopodobnie będziesz musiał stworzyć niestandardową kontrolę w takim przypadku. Powodem jest to, że nie można powiązać z Canvas.Left i Canvas.Top. Jeśli możesz, możesz użyć konwertera na wiązaniu, aby przyjąć wartość (powiedzmy wartość suwaka 10), zaneguj ją i podziel ją przez 2. To przesunie początek koła, aby dopasować jego wzrost szerokości i wysokości. Ponieważ nie możesz tego zrobić, możesz utworzyć niestandardowy formant, który wykona te obliczenia i odpowiednio zaktualizuje elipsę. – Will

+0

Trzymaj się, myliłem się. Popełniłem błąd w moim teście. Możesz powiązać Canvas.Left/Top i użyć przelicznika wartości, aby tłumaczyć wzrost rozmiaru na zmianę położenia płótna. – Will

2

Ponieważ używasz płótna, lokalizacja, w której znajduje się element, jest lokalizacją. Jeśli chcesz zmienić górną, lewą pozycję, musisz to zrobić samodzielnie.Jeśli używasz innego typu Panelu, np. Siatki, możesz zmienić wyrównanie swojej Elipsy, aby umieścić ją w tej samej względnej lokalizacji, bez względu na rozmiar. Możesz uzyskać ten efekt, dodając siatkę w swoim AdornerDecorator i centrując elipsę, ale musisz także ustawić AdornerDecorator lub Grid na stały rozmiar, ponieważ nie rozciągną się w płótnie.

Najlepszym rozwiązaniem, z którego można skorzystać, jest ScaleTransform zastosowany do właściwości RenderTransform za pomocą RenderTransformOrigin w wersji 0.5,9.5. Mówiłeś, że masz problemy z ScaleTransform, ale nie masz problemu.

+0

ScaleTransform w zasadzie wyskalował wszystko, co miałem, więc moi adoratorzy (który zostawiłem tu z kodu dla zwięzłości) większe jak skalowałem, a także grubość obrysu (rozmiar granicy) wzrosła. Chcę tylko zmienić rozmiar samego okręgu, z grubością obrysu i rozmiarem Adornera pozostawionym na stałym poziomie ... – Jan

+1

Tak więc z twoich odpowiedzi brzmi to tak, jakbyś chciał ustawić zmienny punkt środkowy dla swojej Elipsy, a nie Górny, Lewy. Możesz uzyskać ten efekt poprzez dodanie wiązania z konwerterem na margines na AdornerDecorator, aby ustawić Margines na (-ActualWidth/2, -ActualHeight/2,0,0). Spowoduje to przesunięcie elementów w górę i pozostawienie ich do środka względem punktu ustawionego jako Góra, Lewo. –

+0

Cześć John! Brzmi interesująco - dziękuję za twoje rozwiązanie :) To może być bardzo pomocne, jeśli rozwiązanie Martina przy użyciu Wiązania z Canvas.Top/.Left nie działa dla mnie ... – Jan

1

Owiń swoją elipsę w siatkę o maksymalnym rozmiarze. Tak długo, jak to jest mniejsze, Elipsa będzie w środku siatki:

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

może być konieczne dostosowanie logiki przeciągając uchwyt przeciągania siatki zamiast samego elipsy.

Powiązane problemy