Zooming WPF canvas relative to the center rather than top left - c#

I am writing a label-making program. In this program, I have a canvas with a white rectangle positioned in the center, with multiple objects that the user can resize, drag, etc on top of it. I also have an option for the user to zoom the canvas, which I accomplish using ScaleTransform via LayoutTransform. I want to have it so that, when the user zooms in, the canvas zooms in on the center, rather than relative to the top right.
Here's a demonstration:
Currently, the canvas zooms like this:
I need it to zoom like this:
How can I accomplish this task, without re-positioning the elements in the canvas when it is zoomed?
Note: I'm using LayoutTransform, since I must embed this in a ScrollViewer. RenderTransform accomplishes this, but won't let the user scroll when canvas elements exceed the visible canvas bounds.

You should be able to accomplish the same thing as RenderTransformOrigin, by using a transform that combines two translations around the scaling. E.g. using TransformGroup, or combining matrices for a MatrixTransform.
Specifically: translate the center point (X,Y) to (0,0) by using translation offsets of -X and -Y, perform the scale transform, and then translate (0,0) back to the original point (X, Y) using translation offsets of X and Y.
It's hard for me to know for sure without having a specific code example, but I have a vague sense that it ought to be possible to use RenderTransform, perhaps applied differently than you'd prefer (e.g. adding a new container to the hierarchy and applying it to that). But assuming RenderTransform simply won't work, the above should.

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="600">
<Grid>
<ScrollViewer>
<Border>
<Border.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleX="2.0" ScaleY="2.0"/>
</TransformGroup>
</Border.LayoutTransform>
<Canvas Background="Red" Width="500" Height="500" VerticalAlignment="Center" HorizontalAlignment="Center">
<Canvas.RenderTransform>
<TransformGroup>
<TranslateTransform X="0" Y="-100"/>
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle Canvas.Left="200" Canvas.Top="200" Width="100" Height="100" Fill="Yellow"/>
</Canvas>
</Border>
</ScrollViewer>
</Grid>
</Window>

Related

How to scale down UIElement in WPF and see what's originaly outside the window?

I got an UIElement who's bigger than the place it need to take. But when i scale down it using a renderTransform, the part of the element originaly hide , stay hide.
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Rectangle Fill="Orange" Stroke="Black" StrokeThickness="5" Width="600" Height="500" Margin="20" HorizontalAlignment="Left">
<Rectangle.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
How can I achieve the scale and gets all the UIElement ?
An example to illustrate the solution you recieved in comment would be :
If you have a coloured rectangle with a text inside and you do a 90° rotation:
With LayoutTransform, you would get the rectangle rotated and the text inside would look normal, since the transformation is applied before calculating the content
With RenderTransform, you would get the rectangle rotated and the text inside rotated as well, since the rectangle AND the text would be generated before doing the rotation.
For your example it gives:
With LayoutTransform, you scale the area, and then calculate its possible content
With RenderTransform, you calculate the content, and then scale both area AND its content.

wpf progressbar.RenderTransform outside of window in designer

when I apply the 180 degree transform to the progress bar it goes outside of the window. here is what it looks like in the designer.
http://i.imgur.com/mZfJHXL.jpg
how can I get it to move inside the window.
here is my code:
<Window x:Class="game.Player2Screen"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Player2Screen" Height="298" Width="310">
<ProgressBar Name="ProgressBarTimer" Orientation="Vertical" Foreground="Blue" Background="Black" MouseLeftButtonDown="ProgressBarTimer_MouseLeftButtonDown"
Value="0" HorizontalAlignment="Center" Maximum="100" VerticalAlignment="Top" Height="250" Width="265" >
<ProgressBar.RenderTransform>
<RotateTransform Angle="180"/>
</ProgressBar.RenderTransform>
</ProgressBar>
</Window>
You need to specify the render transform origin.
See this MSDN article
Perhaps you want to use a LayoutTransform instead of RenderTransform.
The way WPF handles drawing components is:
LayoutTransform
Measure
Arrange
RenderTransform
Render
It sounds like you want to do your transformation before measuring and arranging the component, not after.

Border around a rotating image

Basically I'm trying to do something whereby a WPF image is inside a WPF border, and periodically I rotate the image by changing the RotateTransform Angle property.
The problem is, when I rotate the image, the border doesn't rotate, or attempt to change to fit the new shape of the picture. I've tried setting it's Alignment properties to stretch, and even binding the height/width of the border to that of the image, but no luck. I suspect the problem is that, when I rotate the image, it doesn't actually change the height or width of the Image object, so of course the border doesn't know what to do.
Is there a better way to rotate the image that would allow the border to resize, or if not, how do I get the border to resize correctly, given that I'm changing the RotateTransform Angle.
Thanks!
You can use the LayoutTransform instead of RenderTransform for this. If you try changing the angle of rotation you'll see the border changes size to accommodate it. (Think this is what you're asking? If you actually want the border to rotate then you can just rotate that instead of the image)
<Window x:Class="rotate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Border VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="1">
<Grid Background="Blue" Width="80" Height="80">
<Grid.LayoutTransform>
<RotateTransform Angle="10"/>
</Grid.LayoutTransform>
</Grid>
</Border>
</Grid>
</Window>
Use LayoutTransform instead of RenderTranform.
RenderTransform only does a visual transformation of the control and is applied after measuring and arranging Controls. Therefore it doesn't affect the size seen by other controls.
LayoutTransform really affects the layout of the object. It's applied before measuring and arranging control, so the other control see a change in the size.
Caution: LayoutTransform is much slower and won't usually give a smooth animation.
<Border BorderThickness="5" BorderBrush="Red" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Width="50" Height="50">
<Image.LayoutTransform>
<RotateTransform Angle="45" />
</Image.LayoutTransform>
</Image>
</Border>

How to rotate a WPF Window?

Is it possible to rotate a WPF Window by 45 degree, using xaml?
First question: Why do you want to rotate the whole window?
If you really need it:
You can't rotate the normal WPF window. See: Rotate Window
You will have to create a borderless window and provide a UI to it. See: WPF Non-Client Area Design Techniques For Custom Window Frames
For rotated window look:
Set:
AllowTransparency property to
true.
WindowStyle to None to
remove window chrome
Background
to Transparent
Include a border (or anything meaningful like rectangle, circle, ellipse, etc.) as content of the window and following properties of border:
white background (or any non-transparent color)
rotate transformation, and
smaller size (so as to fit when rotated within the window).
Border will provide the UI to your window.
Be aware of cavaets of creating own borderless window, as it requires you to provide the window interface like minimise, maximise, close buttons; and may require some unmanaged code.
Also, in sample code below, the border when rotated has to be kept within the bounds of the window, otherwise it (and your custom window) will be trimmed.
Sample code
<Window x:Class="CustomWindowStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True" WindowStyle="None" Background="Transparent"
Title="MainWindow" Height="600" Width="600">
<Border BorderBrush="Green" BorderThickness="2" Background="White" Width="360" Height="360">
<Border.RenderTransform>
<RotateTransform Angle="-45" CenterX="180" CenterY="180"/>
</Border.RenderTransform>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="23" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="X" Height="23" Width="23" Name="button1" HorizontalAlignment="Right" VerticalAlignment="Top" Click="button1_Click"/>
<Grid Grid.Row="1">
<!--Main window content goes here-->
<TextBlock Text="Main window content goes here" HorizontalAlignment="Center" />
</Grid>
</Grid>
</Border>
</Window>
As far as I know you can't rotate an entire window, but you could put everything inside the window into a custom control and apply apply a RenderTransform object to the custom control.
Example (somewhat simple):
http://www.codeproject.com/KB/WPF/TransformationsIntro.aspx
-- Dan

Plane projection and scale causing bluring in silverlight

Ok,
So I've tried to make an application which relies on images being scaled by an individual factor. These images are then able to be turned over, but the use of an animation working on the ProjectionPlane rotation.
The problem comes around when an image is both scaled and rotated. For some reason it starts bluring, where a non scaled image doesn't blur.
Also, if you look at the example image below (top is scaled and rotated, bottom is rotated) the projection of the top one doesn't even seem right. Its too horizontal.
http://img43.imageshack.us/img43/5923/testimages.png http://img43.imageshack.us/img43/5923/testimages.png
This this the code for the test app:
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Canvas x:Name="LayoutRoot" Background="White">
<Border Canvas.Top="25" Canvas.Left="50">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="3" ScaleY="3" />
</TransformGroup>
</Border.RenderTransform>
<Border.Projection>
<PlaneProjection RotationY="45"/>
</Border.Projection>
<Image Source="bw-test-pattern.jpg" Width="50" Height="40"/>
</Border>
<Border Canvas.Top="150" Canvas.Left="50">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup>
</Border.RenderTransform>
<Border.Projection>
<PlaneProjection RotationY="45"/>
</Border.Projection>
<Image Source="bw-test-pattern.jpg" Width="150" Height="120"/>
</Border>
</Canvas>
</UserControl>
So if anyone could possible shed any light on why this may be happening, I'd very much appreciate it. Suggestions also welcome! :)
** Update **
Just to clarify, if the projection plane rotation is 0, the image becomes un-blurred, so its only during the rotation that the image is blurred.
The top image's width is set to 50 and the height to 40. So it is downscaled. Afterwards you scale it up to the right size 150, 120. I guess Silverlight scales the image down and doesn't store the original size due to performance optmization. Leave the Scale out and set the right width and height for the first image.
It looks like the top image is being filtered as it is being drawn. From your code you have:
<Image Source="bw-test-pattern.jpg" Width="50" Height="40"/>
for the top image and
<Image Source="bw-test-pattern.jpg" Width="150" Height="120"/>
for the bottom one. You have different image sizes so the top one might be being upscaled and therefore blurred as it interpolates the missing pixels.
I'm not familiar with silverlight so I don't know how you'd control the filtering options, but setting the top line above to the same as the bottom one might fix it.

Categories