LayoutTransform on certain elements in canvas? - c#

I want a layout transform on a canvas, but I only want certain items to be affected. For instance, if I have a canvas with an image and some other control used for a particular function, I only want the image to be transformed when the values change and the other control to remain the same.
<Canvas x:Name="canvas1">
<Canvas.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform1" ScaleX="{Binding ElementName=slider1, Path=Value}" ScaleY="{Binding ElementName=slider1, Path=Value}" CenterX="0" CenterY="0"/>
</TransformGroup>
</Canvas.LayoutTransform>
<Border x:Name="border1" Cursor="SizeAll" Panel.ZIndex="1000" Visibility="Hidden" Background="Transparent" BorderThickness="1"/>
<Image x:Name="image1" RenderOptions.BitmapScalingMode="HighQuality" RenderTransformOrigin="0.5, 0.5" Stretch="Fill"/>
</Canvas>
<Slider x:Name="zoomSlider" ValueChanged="zoomSlider_ValueChanged" Value="1" Minimum="0.05" Maximum="5" IsSnapToTickEnabled="True" LargeChange=".05" TickFrequency="0.05" SmallChange="0.05"/>
And then we have:
private void zoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
TransformGroup g = new TransformGroup();
g.Children.Add(new ScaleTransform(this.zoomSlider.Value, this.zoomSlider.Value));
g.Children.Add(new TranslateTransform(0, 0));
this.canvas1.LayoutTransform = g;
}
Obviously all elements are going to be affected, but I was wondering if there's a way to specify which ones are not to be? It's absolutely important that this control remain in the canvas (as its purpose requires this).

You need to move LayoutTransform from Canvas to Image and also you don't need to handle ValueChanged for slider. Just fix binding to use proper ElementName. Change from slider1 to zoomSlider and binding will handle rest
<Canvas x:Name="canvas1">
<Border x:Name="border1" Cursor="SizeAll" Panel.ZIndex="1000" Visibility="Hidden" Background="Transparent" BorderThickness="1"/>
<Image x:Name="image1" RenderOptions.BitmapScalingMode="HighQuality" RenderTransformOrigin="0.5, 0.5" Stretch="Fill">
<Image.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=zoomSlider, Path=Value}" ScaleY="{Binding ElementName=zoomSlider, Path=Value}" CenterX="0" CenterY="0"/>
</Image.LayoutTransform>
</Image>
</Canvas>
<Slider x:Name="zoomSlider" Value="1" Minimum="0.05" Maximum="5" IsSnapToTickEnabled="True" LargeChange=".05" TickFrequency="0.05" SmallChange="0.05"/>
You can also make it to automatically apply to all Image controls in your Canvas by creating implicit Style
<Canvas x:Name="canvas1">
<Canvas.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="{Binding ElementName=zoomSlider, Path=Value}" ScaleY="{Binding ElementName=zoomSlider, Path=Value}"/>
</Setter.Value>
</Setter>
</Style>
</Canvas.Resources>
<Border x:Name="border1" Cursor="SizeAll" Panel.ZIndex="1000" Visibility="Hidden" Background="Transparent" BorderThickness="1"/>
<Image x:Name="image1" RenderOptions.BitmapScalingMode="HighQuality" RenderTransformOrigin="0.5, 0.5" Stretch="Fill"/>
</Canvas>
<Slider x:Name="zoomSlider" Value="1" Minimum="0.05" Maximum="5" IsSnapToTickEnabled="True" LargeChange=".05" TickFrequency="0.05" SmallChange="0.05"/>

Please try this code.It works.I hope this is what you want
Here is code:
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="canvas1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform1" ScaleX="{Binding ElementName=slider1, Path=Value}" ScaleY="{Binding ElementName=slider1, Path=Value}" CenterX="0" CenterY="0"/>
</TransformGroup>
</Grid.RenderTransform>
<Image Source="super.jpg" x:Name="image1" RenderTransformOrigin="0.5, 0.5" Stretch="None"/>
</Grid>
<Slider Grid.Row="1" VerticalAlignment="Center" Margin="0,20,0,0" Width="200" x:Name="zoomSlider" ValueChanged="zoomSlider_ValueChanged" Value="1" Minimum="0.05" Maximum="5" IsSnapToTickEnabled="True" LargeChange=".05" TickFrequency="0.05" SmallChange="0.05"/>
</Grid>
</Grid>
Codebehind:
private void zoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
TransformGroup g = new TransformGroup();
g.Children.Add(new ScaleTransform(this.zoomSlider.Value, this.zoomSlider.Value));
g.Children.Add(new TranslateTransform(0, 0));
this.canvas1.LayoutTransform = g;
}

Related

Rotating image inside a border

Hi,
I have a image inside a Border which I would provide an angle for. Currently I am just hard coding it to 45.
My problem is that the image is displayed at the left of the border. I want it to be in the centre and fit inside the border.
When I provide an angle I want it to rotate inside the border.
<Grid Background="gray" HorizontalAlignment="Right" Width="36" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Line Grid.Column="0" X1="0" X2="0" Y1="0" Y2="500" Fill="Black" Stroke="Black" StrokeThickness="7" StrokeDashArray="0.5 0.5"
/>
<Border Grid.Column="1" Background="DarkGreen" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Image Source="pack://application:,,,/WpfApplication3;component/Resources/MyImage.png">
<Image.RenderTransform >
<RotateTransform Angle="45" />
</Image.RenderTransform>
</Image >
</Border>
<Line Grid.Column="2" X1="0" X2="0" Y1="0" Y2="5000" Fill="Black" Stroke="Black" StrokeThickness="7" StrokeDashArray="0.5 0.5"
/>
</Grid>
Please help
Set the RenderTransformOrigin property to the center of the image. RenderTransformOrigin uses relative coordinates, hence the center is at 0.5,0.5:
<Border ClipToBounds="True">
<Image ... RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform Angle="45"/>
</Image.RenderTransform>
</Image>
</Border>
In order to fit the rotated Image in the Border control, you could set its LayoutTransform:
<Border>
<Image ...>
<Image.LayoutTransform>
<RotateTransform Angle="45"/>
</Image.LayoutTransform>
</Image>
</Border>
You can use CenterX and CenterY properties of RotateTransform to set the center of rotation.
If you need the image to expand the border size when rotating you need to use LayoutTransform.
For instance it could be done as:
<Border>
<Image Source="pack://application:,,,/WpfApplication3;component/Resources/MyImage.png" x:Name="image">
<Image.RenderTransform >
<RotateTransform
Angle="45"
CenterX="{Binding ElementName=image, Path=ActualWidth, Converter={StaticResource ResourceKey=DivideBy2Converter}}"
CenterY="{Binding ElementName=image, Path=ActualHeight, Converter={StaticResource ResourceKey=DivideBy2Converter}}"
/>
</Image.RenderTransform>
</Image >
</Border>
Edit:
Take ActualSize of image, not border
I wanted to make an image similar to a clock arm. Given the angle it would just display it. The problem was that the image was not stretching at 120 or 160 and the arm would become shorter.
I finally achieved by making a rectangle and calculating its margins and CenterY
<Rectangle
Stroke="yellow" Fill="Yellow">
<Rectangle.Margin>
<MultiBinding Converter="{StaticResource MarginConverter}">
<MultiBinding.Bindings>
<Binding ElementName="border" Path="ActualWidth"/>
<Binding ElementName="border" Path="ActualHeight"/>
</MultiBinding.Bindings>
</MultiBinding>
</Rectangle.Margin>
<Rectangle.RenderTransform>
<RotateTransform CenterX="0"
CenterY="{Binding ElementName=border, Path=ActualHeight, Converter={StaticResource CalculateRectCenterY}}" Angle="120" />
</Rectangle.RenderTransform>
</Rectangle>
Michał Żochowski's post gave me the idea although his answer did not solve me problem exactly

Rotate sliders WPF

I would like to rotate 4 sliders in WPF to create a custom control.
Here is my code :
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Slider Name="Slider_Top_Left" Minimum="0" Maximum="100" Value="75" RenderTransformOrigin="0,0">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="-135"/>
</Slider.LayoutTransform>
</Slider>
<Slider Name="Slider_Top_Right" Grid.Column="1" Minimum="0" Maximum="100" Value="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="-45"/>
</Slider.LayoutTransform>
</Slider>
<Slider Name="Slider_Bottom_Right" Grid.Column="1" Grid.Row="1" Minimum="0" Maximum="100" Value="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="45"/>
</Slider.LayoutTransform>
</Slider>
<Slider Name="Slider_Bottom_Left" Grid.Column="0" Grid.Row="1" Minimum="0" Maximum="100" Value="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="-10" CenterY="-10" Angle="135"/>
</Slider.LayoutTransform>
</Slider>
</Grid>
The result :
What I want :
I've tried without the grid definitions, with different centers (it didn't change anything).
I've followed the online help for layout transform but I can't make it work.
Thank you for your help.
Put Sliders in Grid properly and Rotate the Grid. If you decide later to add more elements to custom control, where will be no need to calculate any rotation angles/centers for them
<Grid Grid.Row="3">
<Grid.LayoutTransform>
<RotateTransform Angle="45"/>
</Grid.LayoutTransform>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--top right-->
<Slider Orientation="Vertical" Grid.ColumnSpan="2" HorizontalAlignment="Center">
<Slider.LayoutTransform>
<ScaleTransform ScaleY="-1"/>
</Slider.LayoutTransform>
</Slider>
<!--bottom left-->
<Slider Orientation="Vertical" Grid.Row="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Center"/>
<!--top left-->
<Slider Grid.RowSpan="2" VerticalAlignment="Center"/>
<!--bottom right-->
<Slider Grid.Column="1" Grid.RowSpan="2" VerticalAlignment="Center">
<Slider.LayoutTransform>
<ScaleTransform ScaleX="-1"/>
</Slider.LayoutTransform>
</Slider>
</Grid>
This is how I did it:
<Grid>
<Slider x:Name="slider" HorizontalAlignment="Center" Margin="-50,50,0,0" VerticalAlignment="Center" Width="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="-45"/>
</Slider.LayoutTransform>
</Slider>
<Slider x:Name="slider1" HorizontalAlignment="Center" Margin="50,50,0,0" VerticalAlignment="Center" Width="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="-135"/>
</Slider.LayoutTransform>
</Slider>
<Slider x:Name="slider5" HorizontalAlignment="Center" Margin="50,-50,0,0" VerticalAlignment="Center" Width="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="135"/>
</Slider.LayoutTransform>
</Slider>
<Slider x:Name="slider3" HorizontalAlignment="Center" Margin="-50,-50,0,0" VerticalAlignment="Center" Width="75">
<Slider.LayoutTransform>
<RotateTransform CenterX="0" CenterY="0" Angle="45"/>
</Slider.LayoutTransform>
</Slider>
</Grid>
I am not really sure what you want to achieve but still on based of image you have provided I am posting my code :
<Slider VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Slider.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45"/>
</TransformGroup>
</Slider.RenderTransform>
</Slider>
Use angles of 45,-45,135,-135 to layout them as per your posted design.
A small tip : Next time you find any such issue, Go to properties of that control and look for all available properties.
Happy coding !!

WPF ScrollViewer scroll-bars not changing size

I am not a WPF expert so please excuse my inappropriate use of terms. I have a ScrollViewer where I am displaying a captured image. And I have a slider with which I am zooming the image in and out. Zooming works fine, but the scrollbars are not changing their size. Hence when the image goes beyond the boundaries, I cannot scroll and view it. As if the scrollbars become useless because they haven't changed their size. Here is my XAML:
The Slider:
<Slider DockPanel.Dock="Right" Width="100" VerticalAlignment="Center" Minimum="0.2" Maximum="5"
Interval="1" Value="{Binding ScaleFactor}"/>
The rest of XAML:
<Border BorderThickness="1" BorderBrush="Black" Grid.Row="1">
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
CanContentScroll="True">
<ItemsControl ItemsSource="{Binding ItemCollection}" Margin="0"
Width="{Binding Root.Boundary.Width}" Height="{Binding Root.Boundary.Height}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas>
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ScaleFactor}"
ScaleY="{Binding ScaleFactor}"CenterX="0" CenterY="0"/>
</Canvas.LayoutTransform>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Image x:Name="capturedImage"
Source="{Binding Image}"
Width="{Binding Boundary.Width}"
Height="{Binding Boundary.Height}"/>
<Path x:Name="captureContour"
Data="{Binding Outline}"
Stroke="Black" StrokeThickness="4" Opacity="0.5"
StrokeLineJoin="Round">
<Path.LayoutTransform>
<ScaleTransform ScaleX="{Binding OutlineScale.X}"
ScaleY="{Binding OutlineScale.Y}" CenterX="0"CenterY="0"/>
</Path.LayoutTransform>
</Path>
</Grid>
<DataTemplate.Triggers>
<Trigger SourceName="capturedImage" Property="IsMouseOver"
Value="True">
<Setter TargetName="captureContour" Property="Stroke"
Value="Blue"/>
<Setter TargetName="captureContour" Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect/>
</Setter.Value>
</Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Border>
The issue is due to the Canvas used as ItemsPanel. as Canvas does not expand or collapse with the size of it's children so ScrollViewer does not detect the change.
As a quick solution change the ItemsPanel to Grid. Since your example does not seems to be using Canvas properties i.e. Canvas.Left or Canvas.Top, this change may not make any difference in the appearance.
example
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ScaleFactor}"
ScaleY="{Binding ScaleFactor}"
CenterX="0"
CenterY="0" />
</Grid.LayoutTransform>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
make sure to have HorizontalAlignment="Left" VerticalAlignment="Top" in grid otherwise it may appear weird when you zoom.
give it a try and see if this is what you are looking for.
OK guys, here is what needs to be done in order to make it work:
<ItemsControl ItemsSource="{Binding ItemCollection}" Margin="0" Width="{Binding CanvasWidth}"
Height="{Binding CanvasHeight}"/>

How to rotate image about its centre rather than about the canvas it is on in WPF, C#

I'm still learning how to make apps in WPF, and im struggling a little.
Im trying to rotate individual rectangles about its centre, but they are being rotated about the canvas that it is placed on. I read about RenderTransformOrigin, and I set it to 0.5,0.5 but its still rotating relative to the canvas.
<ItemsControl Name="canvasDataBinding"
HorizontalAlignment="Left"
Height="256"
Margin="294,35,0,0"
VerticalAlignment="Top"
Width="256"
ClipToBounds="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#FFBBBBBB">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle HorizontalAlignment="Left"
Height="{Binding Height}"
Width="{Binding Width}"
Stroke="Black"
VerticalAlignment="Top"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding OffsetX}" Y="{Binding OffsetY}"/>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="{Binding Angle}"/>
</TransformGroup>
</Rectangle.RenderTransform>
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding Name}">
</ImageBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is what it is currently doing:
Edit:
Here it is when I apply the rotation with ImageBrush.RelativeTransform
I tried setting ClipToBounds = "False" on the rectangle but im getting the same result.
Just set RelativeTransform to on the image rather than the rectangle,
<Image ImageSource="{Binding Name}">
<ImageBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform X="{Binding OffsetX}" Y="{Binding OffsetY}"/>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="{Binding Angle}"/>
</TransformGroup>
</ImageBrush.RelativeTransform>
</ImageBrush>

UserControl with Bounding Box Scaling Point boxes

I want to make a UserControl like this image;
There are 9 boxes, top one is for rotating, while other one is for scaling purposes. I tried to make this with grid and background image, 2 rows, and small boxes with StackPanel, but have to provide lots of negative and positive margins to achieve this result. Is there any easy way to do so need help.
Thanks.
Something like this?
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="Rectangle">
<Setter Property="Stroke" Value="Black"/>
<Setter Property="StrokeThickness" Value="1"/>
<Setter Property="Height" Value="11"/>
<Setter Property="Width" Value="11"/>
<!-- For hittestvisibility -->
<Setter Property="Fill" Value="Transparent"/>
</Style>
</Grid.Resources>
<Line X1="0" Y1="1" X2="0" Y2="0" Stretch="Fill" Stroke="Black" />
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Height="200" Width="200" Margin="-5">
<Rectangle Margin="5" Height="Auto" Width="Auto" Fill="{x:Null}"/>
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Rectangle VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
<!-- The Content -->
<Rectangle Width="Auto" Height="Auto" Margin="20" Fill="Green"/>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
Text="Content"/>
</Grid>
</Grid>
The terminology is Bounding Box

Categories