I would like to know how to change RenderTransformOrigin of an element without changing its location. I've noticed that changing the RenderTransformOrigin will affect the element location.
How can I calculate the X and Y Value between the element location before and after changing the RenderTransformOrigin?
XAML
<Button
Content="Button"
Height="100"
Width="100"
RenderTransformOrigin="0, 0">
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="90"/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button
RenderTransformOrigin
MSDN says,
Gets or sets the center point of any possible render transform declared by System.Windows.UIElement.RenderTransform, relative to the bounds of the element. This is a dependency property.
so for sure there wont be any effect on location. location can be affected based on TranslateTransform
<TranslateTransform X="{Binding LX}" Y="{Binding LY}" />
<Button Content="Hello" Width="100" Height="100" RenderTransformOrigin=".5,.5" BorderThickness="2" BorderBrush="Black">
<Button.LayoutTransform>
<TransformGroup>
<ScaleTransform
ScaleX="{Binding X}"
ScaleY="{Binding Y}"/>
<RotateTransform
Angle="{Binding RotateAngle}"/>
</TransformGroup>
</Button.LayoutTransform>
</Button>
set X and Y to 1 ie: X = Y = 1; in your ViewModel
I have tried this has no effect on location changing the RenderTransformOrigin
MSDN
RenderTransformOrigin has a somewhat nonstandard use of the Point structure value, in that the Point does not represent an absolute location in a coordinate system. Instead, values between 0 and 1 are interpreted as a factor for the range of the current element in each x,y axis. For example, (0.5,0.5) will cause the render transform to be centered on the element, or (1,1) would place the render transform at the bottom right corner of the element. NaN is not an accepted value.
Values beyond 0 and 1 are also accepted, and will result in more unconventional transform effects. For instance, if you set RenderTransformOrigin to be (5,5), and then apply a RotateTransform, the rotation point will be well outside the bounds of the element itself. The transform will spin your element around in a big circle that originates beyond bottom right. The origin might be somewhere inside its parent element and could possibly be possibly out of frame or view.
Negative point values are similar, these will go beyond the top left bounds.
Render transforms do not affect layout, and are typically used to animate or apply a temporary effect to an element.
Related
Anyone know a good way to create this object from Xaml? It also has to work at .5 Opacity when layered on top of other controls.
It also has to be resizable via Horizontal or Vertical Alignment.
I'm having some difficulty. The closest I get is with 2 borders, one having a negative margin--but it doesn't work when Opacity is applied.
Code that works:
<Path Fill="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry RadiusX="5" RadiusY="5" Rect="0,0,200,100" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry RadiusX="5" RadiusY="5" Rect="105,5,90,90" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Use a GeometryGroup with an EvenOdd FillRule, or a CombinedGeometry with a GeometryCombineMode of Xor or Exclude. The geometries to combine will both be RectangleGeometry objects, with an appropriate RadiusX and RadiusY. The result will be the outer rectangle with a "hole" in it where the inner rectangle was located. (I assume this is what you want rather than a white rectangle within the black one.)
You can then assign this composite geometry to a Path as its Data property, and set the Fill and Opacity as required.
I am performing scaling, translation and rotation on an image using RenderTransform. Transformations are not done using mouse events, rather, the user will click a button and then the image will scale/translate/rotate on a fixed value.
My problem is I want to determine the new position/size of the image each time a scaling, translation or rotation is performed. So I added Changed events on the code-behind. The question is how do you get the new position/size?
Please take a look at what I've done so far:
XAML:
<Border x:Name="mainImageBorderCtrl" ClipToBounds="True">
<Grid x:Name="imageGridCtrl">
<Grid.RenderTransform>
<TranslateTransform Changed="TranslateTransform_Changed"/>
</Grid.RenderTransform>
<Image x:Name="mainImageCtrl" RenderTransformOrigin="0.5, 0.5" Source="{Binding Image}">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform Changed="ScaleTransform_Changed"/>
<RotateTransform />
</TransformGroup>
</Image.RenderTransform>
</Image>
</Grid>
</Border>
Please note that I separated TranslateTransform from ScaleTransform and RotateTransform so that the orientation won't be affected.
The following are the 2 changed events. In here, I expect to get the new position/size every time a scale or a translate has occurred. But the bounds values do not change.
Code-behind
private Rect bounds;
private void TranslateTransform_Changed(object sender, EventArgs e)
{
bounds = imageGridCtrl.TransformToAncestor(mainImageBorderCtrl).TransformBounds(new Rect(imageGridCtrl.RenderSize));
}
private void ScaleTransform_Changed(object sender, EventArgs e)
{
bounds = imageGridCtrl.TransformToAncestor(mainImageBorderCtrl).TransformBounds(new Rect(imageGridCtrl.RenderSize));
}
EDIT: My goal here is to restrict the translation(panning) of the Image inside the Border control, that's why I need to get the bounds of the Image after each transformation, whatever the transformation is, so that I can check whether the bounds of the Image exceeds the Border.
TranslateTransform is generating correct bounds. However, for ScaleTransform you are getting bounds on control imageGridCtrl but the transform is added as child of control mainImageCtrl.
So you should use mainImageCtrl in place of imageGridCtrl to get correct bounds:
bounds = mainImageCtrl.TransformToAncestor(mainImageBorderCtrl)
.TransformBounds(new Rect(mainImageCtrl.RenderSize));
The transform change events fire too early. You should hook up to LayoutUpdated event of the image control. Then try:
mainImageCtrl.TransformToAncestor(mainImageBorderCtrl)
.TransformBounds(new Rect(mainImageCtrl.RenderSize))
I'm trying to crop a circle from one image, and put it on top another image in WPF.
The Circle's center changes according to the mouse movements, and needs to be bounded dynamically.
I tried to position two images on top of each other, and use a third image that I draw in real time as an opacity mask.
Could you please provide short code to solve this problem efficiently ?
The code below describes what you can do with an OpacityMask. It's a little counterintuitive, because we expect a XAML rendering to layer elements bottom-to-top.
However, in this case you want your "background" image to layer on top of the foreground, because the OpacityMask will serve to display only that portion of the foreground described by the position and size of the VisualBrush, rendering the rest transparent. It's given as follows:
<Grid x:Name="MainGrid" MouseMove="Grid_MouseMove">
<Rectangle Fill="Red" ></Rectangle>
<Rectangle Fill="Green">
<Rectangle.OpacityMask>
<VisualBrush Stretch="None" >
<VisualBrush.Visual>
<Ellipse Width="40" Height="40" StrokeThickness="1" Fill="Black" />
</VisualBrush.Visual>
<VisualBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform x:Name="OpacityFilterTransform" X="1" Y="1"/>
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Rectangle.OpacityMask>
</Rectangle>
</Grid>
Then, this event handler code computes the position of the ellipse and applies it to the OpacityFilter's TranslateTransform object, giving you control over the position of the image.
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this);
var height = MainGrid.ActualHeight;
var width = MainGrid.ActualWidth;
// with the position values, interpolate a TranslateTransform for the opacity mask
var transX = position.X / width;
var transY = position.Y / height;
OpacityFilterTransform.X = transX - 0.5;
OpacityFilterTransform.Y = transY - 0.5;
}
This solution should work for any descendant of Visual you care to layer.
I was wondering if there was a way in WPF to create a shape (ex.: polygon, line, ...) but instead of using absolute positioning for the points of the shape, we could use something like percentage.
For example, instead of having a line with absolute positioning like this: (X1=0,Y1=50,X2=100,Y2=50), we could have a line with percentage values (0 to 1) like this (X1=0,Y1=0.5, X2=1, Y2=0.5 where 1 is equivalent to the size of the parent). So no matter what is the size of the parent of the shape, the shape would always be proportional to its parent.
That could be done with dependency properties, but I would find it much cleaner if there was a way to do it with something like I described. I hope I didn't miss something really obvious that does exactly that...
Thanks!
You could achieve a similar effect if you scale it by applying a scale transform on your geometry the size of the control.
<Path Width="100" Height="100" Stroke="Red">
<Path.Data>
<LineGeometry StartPoint="0 0" EndPoint="1 1">
<LineGeometry.Transform>
<ScaleTransform ScaleX="{Binding Path=Width, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}}"
ScaleY="{Binding Path=Height, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}}" />
</LineGeometry.Transform>
</LineGeometry>
</Path.Data>
</Path>
This should draw a red line with absolute points (0, 0) to (100, 100).
I am in the following situation: designing an interactive flow-chart GUI. I stuck with animating a scale down animation of the flow-chart. Composed elements of the flow-chart are minimized, but they keep being active.
I have something like this:
<Canvas Canvas.Left="55" Canvas.Top="720" Height="100" Width="500" Tag="stepDown">
<Line Stroke="#99CCFF" StrokeThickness="8" X1="0" X2="720" Y1="10" Y2="10">
<Polygon Stroke="Black" StrokeThickness="2" Points="0,30 40,0 40,60" Canvas.Left="-20" Canvas.Top="-20" Fill="#99CCFF"></Polygon>
<Polygon Stroke="Black" StrokeThickness="2" Points="0,0 0,60 40,30" Canvas.Left="720" Canvas.Top="-20" Fill="#99CCFF"></Polygon>
<Image Canvas.Left="-50" Canvas.Top="-70" Height="53" Name="image32" Source="img/outlet.png" Stretch="Fill" Width="30" Tag="relative" />
</Canvas>
And I would like to shift the whole canvas and its elements to the left - no problem with Translate Transform. Furthermore I would like to scale down only the Line, no problem with Scale Transform.
But (!) at the same time, I want that the Polygons stick to the two line endings of the line. When scaling down only the line, the Polygons, at least one, floats away.
I don't know how to dock these elements or define them at a relative basis. It works fine with a scale down on the whole Canvas, but this changes the Polygon and Images as well.
In order to me you have to define hanchors points, as attached properties. Even define a behavior that track these points accordingly to poligon placements. Then you can bind these property to the line start/end point. In this way line should stay gripped to the polygons. But I did'nt try, is just a design idea.