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
Related
I have the following xaml code:
<UserControl.Resources>
<Style TargetType="GroupBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0" BorderThickness="1 1 0 0" BorderBrush="#25A0DA" CornerRadius="2 0 0 0" Background="Gray">
<Label Foreground="White">
<ContentPresenter Margin="0" ContentSource="Header" RecognizesAccessKey="True" />
</Label>
</Border>
<Rectangle Grid.Row="0" Grid.Column="1" Fill="Gray" IsHitTestVisible="False"/>
<Border Grid.Row="0" Grid.Column="1" BorderThickness="1 0 0 1" BorderBrush="#25A0DA" CornerRadius="0 0 0 70" Background="Green"/>
<Border Grid.Row="1" Grid.ColumnSpan="2" BorderThickness="1 0 0 0" BorderBrush="#25A0DA">
<ContentPresenter Margin="4" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<GroupBox Grid.Row="0" Foreground="White" Header="This is some header" Content="This is some content"/>
As you can see, my header consists of two borders and a rectangle.
I currently use the rectangle to color the space that the rounded corner of the second border leaves blank.
The space that is currently green should actually be completely transparent however, if I set the background color of the second border to transparent the rest of the rectangle I use to fill the other space becomes visible of course.
How can I make the green part completely transparent (like the content area for example)
There is a funny thing called Path geometry. So instead of making the custom header using a combination of Retangles and Borders with round corners, you can draw more complex shapes easily, with some basic knowledge on Geometry.
So replace them with the following code
Edit:Some explanation on the code, the figure is compose of four segments, three of them are line segments, which you just define the end point of the line. And the interesting and more complex one is the ArcSegment, which you also need to define the size and direction of the arc, besides the end point of the arc segment. Play with the value of ArcSegment's Size, and see how it changes your shape.
<Path Stroke="Black" StrokeThickness="1" Fill="Gray">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="0, 0">
<PathFigure.Segments>
<LineSegment Point="100, 0" />
<ArcSegment
Size="50,50"
SweepDirection="Counterclockwise"
Point="150,50" />
<LineSegment Point="0, 50" />
<LineSegment Point="0, 0" />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
And you have an outcome like this
<Path Fill="Red" Data="M10 10 C15 20 30 20 30 20 H10"/>
Is this what you want ?
I have a TextBox in which I want to add an image as background. The first try is this:
<TextBox Text="{Binding Titulo}" Height="Auto" HorizontalAlignment="Stretch" Margin="5,5,5,5" Name="txtTitulo" VerticalAlignment="Stretch"
TextAlignment="Center"
FontSize="30"
Padding="0,20,0,0"
FontWeight="Heavy"
BorderBrush="red"
BorderThickness="5">
<TextBox.Background>
<ImageBrush ImageSource="../Imagenes/Logo (233x251) - fondo transparente (test).png" AlignmentX="Center" Stretch="None" AlignmentY="Center">
</ImageBrush>
</TextBox.Background>
</TextBox>
The result in this case is this:
If I use this code, that uses transform:
<TextBox Text="{Binding Titulo}" Height="Auto" HorizontalAlignment="Stretch" Margin="5,5,5,5" Name="txtTitulo" VerticalAlignment="Stretch"
TextAlignment="Center"
FontSize="30"
Padding="0,20,0,0"
FontWeight="Heavy"
BorderBrush="red"
BorderThickness="5">
<TextBox.Background>
<ImageBrush ImageSource="../Imagenes/Logo (233x251) - fondo transparente (test).png" AlignmentX="Center" Stretch="None" AlignmentY="Center">
<ImageBrush.Transform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5" CenterX="0" CenterY="0" />
</ImageBrush.Transform>
</ImageBrush>
</TextBox.Background>
</TextBox>
The result is this:
The image that I want to scale is this:
It seems that when it scales, reduce the size, but it crops the image to the size of the TextBox.
What I want to do it is scale the image to see the complete image, but I am not be able to get it.
You need to use Uniform as value for the Stretch property. Like this:
Stretch="Uniform"
So, your code would look like this:
<TextBox Text="{Binding Titulo}" Height="Auto" HorizontalAlignment="Stretch" Margin="5,5,5,5" Name="txtTitulo" VerticalAlignment="Stretch"
TextAlignment="Center"
FontSize="30"
Padding="0,20,0,0"
FontWeight="Heavy"
BorderBrush="red"
BorderThickness="5">
<TextBox.Background>
<ImageBrush ImageSource="../Imagenes/Logo (233x251) - fondo transparente (test).png" AlignmentX="Center" Stretch="Uniform" AlignmentY="Center"/>
</TextBox.Background>
</TextBox>
The Uniform value makes the image fill all the available space while keeping its original scale.
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;
}
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>
I know I can create a dashed border with a rectangle or a border with different stroke thickness for different sides:
<StackPanel Orientation="Horizontal">
<Rectangle Stroke="Green" StrokeThickness="2" StrokeDashArray="4 2" Fill="LightGreen" Height="64" Width="32" Margin="5"/>
<Border BorderBrush="Green" BorderThickness="2,2,2,0" Background="LightGreen" Height="64" Width="32" Margin="5" />
</StackPanel>
Is there anyway I can achieve both:
?
UPDATE: This needs to fill the space in it's parent (unlike my example with fixed sizes), e.g. a Grid - so a DrawingGeometry which has fixed sizes and my own Pen cannot be used to achieve this.. can it?
Try this:
<Border BorderThickness="4,4,4,0" Background="LightGreen">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle
Stroke="Green" Fill="LightGreen"
StrokeDashArray="4 2"
StrokeThickness="4"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"/>
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
</Border>
It's border, so when put inside of grid it will use the available space and you can set different width for every side, it uses rectangle for visual brush, so you can easily set the borders to dashed.
A hacky solution but it works is to cover the side of the dashed Rectangle you want hidden:
<Grid Width="100" Height="100">
<Rectangle Stroke="Green" StrokeThickness="4" StrokeDashArray="4 2" Fill="LightGreen" Margin="10"/>
<Rectangle StrokeThickness="0" Height="4" Margin="10" VerticalAlignment="Bottom" Fill="LightGreen"/>
</Grid>