How can I scale a UIElement in C#? - c#

I have defined a canvas in XAML that I add a lot of figures to in C#. I've got some pinch going on but need some help applying the scale to the UIElement.
I've created my ScaleTransform like this:
ScaleTransform scaleTrans = new ScaleTransform();
scaleTrans.ScaleX = scaleFactor;
scaleTrans.ScaleY = scaleFactor;
But how can I apply it to my UIElement (the only children of my canvas)?

You need to use the UIElement.RenderTransform property :
yourElement.RenderTransform = scaleTrans;

Related

XAML/UWP add drop shadow to shapes on canvas

So I am rendering a variety of shapes on a Canvas, think PowerPoint. The user can CRUD shapes on the Canvas.
I would now like to add a DropShadow to a Shape.
Only, I have no idea how to actually do that. Community Toolkit does not seem to allow adding a dropshadow in Code-Behind (at least there is no docu). The other few solutions found online seem to apply ways that are either not UWP-compatible, or deprecated...
Any help?
The DropShadow class provides means of creating a configurable shadow that can be applied to a SpriteVisual or LayerVisual (subtree of Visuals). You can try to use it to add drop shadow to your shapes. First create a new DropShadow and associate it to your visual. For example:
Compositor compositor = ElementCompositionPreview.GetElementVisual(YourShape).Compositor;
var shadowVisual = compositor.CreateSpriteVisual();
Vector2 newSize = new Vector2(0, 0);
if (YourShape is FrameworkElement element)
{
newSize = new Vector2((float)element.ActualWidth, (float)element.ActualHeight);
}
shadowVisual.Size = newSize;
var dropShadow = compositor.CreateDropShadow();
dropShadow.BlurRadius = 10;
dropShadow.Opacity = 0.3f;
dropShadow.Offset = new Vector3(10,10,10);
dropShadow.Color = Windows.UI.Colors.Black;
shadowVisual.Shadow = dropShadow;
ElementCompositionPreview.SetElementChildVisual(YourShape, shadowVisual);
For more details about DropShadow, you can refer to this document.

How Can I Convert a shape like rectangle or circle to stylus point collection in WPF?

My question has three parts:
I Can draw shapes like line, Circle, Rectangle and .. on WPF Canvas. I want to use InkCanvas features like erasing and moving strokes.
Is it possible to convert this shape to a collection of stylus
points and add this collection to InkCanvas?
If it's possible how can I do that?
Is there a better approach to this situation?
Guide me please.
first of all, the answer is Yes. you can convert paths to stroke collection and then add them to the InkCanvas.
For the second part of your question, the answer should be something like this:
Point mypoint;
Point tg;
var pointCollection = new List<Point>();
for (var i = 0; i < 500; i++)
{
SomePath.Data.GetFlattenedGeometryPath()
.GetPointAtFractionLength(i / 500f, out mypoint, out tg);
pointCollection.Add(p);
}
For stylus point and stylus point collection:
StylusPointCollection StPoints = new StylusPointCollection();
add stylus points during converting path to collection of points by:
StPoints.Add(new StylusPoint(p.X, P.Y));
And after this step calling Stroke method to create a collection of strokes from your stylus collection:
Stroke st = null;
st = new Stroke(StPoints);
Update
Yes! there is a better ways for adding shapes to inkCanvas.
You can define this stylus points shape directly and add them using MouseDown, MouseMove.. for example for drawing a Rectangle:
pts.Add(new StylusPoint(mouseLeftDownPoint.X, mouseLeftDownPoint.Y));
pts.Add(new StylusPoint(mouseLeftDownPoint.X, currentPoint.Y));
pts.Add(new StylusPoint(currentPoint.X, currentPoint.Y));
pts.Add(new StylusPoint(currentPoint.X, mouseLeftDownPoint.Y));
pts.Add(new StylusPoint(mouseLeftDownPoint.X, mouseLeftDownPoint.Y));
Or Override DrawCore method of Stroke Class and define a new stroke type.
Custom Rendering Ink (MSDN)

Extract Clip from WPF Canvas

I'm having issues trying to not only clip, but to "extract" a portion of a WPF Canvas. So basically I would like the "Clip" to expand to the full size of the window, or convert the clipped item to separate UI Element for exporting to PNG. I write pseudocode because the real code comes from an Autocad model.
double oPrintWidth=1169;
Canvas c = new Canvas();
c.Width = oPrintWidth * 2.54;
c.Height = c.Width * ratio;
// Define the path to clip
string thisPathData = "M12233 M222333 M3443" // fake
c.Clip = Geometry.Parse(thisPathData);
At this point I have the same size canvas but everything other than my path is now black. And the path is still in the original position. I need to now make the clip the entire canvas.
I have played with RenderTransform but I'm lost as what to do next, I'm not so good with matrix calculations.
Original Canvas (Collection of UI Elements)
AFTER CLIP
DESIRED RESULT
Ultimately this would be printed but would prefer to keep it in WPF until last minute to retain VECTOR properties for translating to SVG/XPS/ETC
To make a Clip of the entire Canvas and then apply that Clip to the Canvas I recommend you let WPF do it for you be setting the ClipToBounds property:
Canvas c = new Canvas();
c.ClipToBounds = true;
If that doesn't suit your needs, I would look at the Margin, ActualWidth, and ActualHeight properties to determine the clip region. Then create a RectangleGeometry that matches the size of your Canvas.
EDIT in response to your comments.
Well, I've had some time to work at it some more. What I have been able to do is create a clip region, then I transformed the canvas so that the clip region filled the canvas as much as possible. I think this is what you are after...
First of all I needed to measure the clipped region:
Rect bounds = canvas.Clip.Bounds;
double scaleX = c.Width / (bounds.Right - bounds.Left);
double scaleY = c.Height / (bounds.Bottom - bounds.Top);
This scaling information is used to make the clipped region fit exactly to the size of the canvas.
Now, we need to apply transformations to the canvas:
TransformGroup group = new TransformGroup();
TranslateTransform move = new TranslateTransform(-bounds.Left, -bounds.Top);
ScaleTransform scale = new ScaleTransform(scaleX, scaleY);
group.Children.Add(move);
group.Children.Add(scale);
canvas.RenderTransform = group;
So what is happening here? First of all, the objective is to apply a couple transformations. We need to center the clipped region (translation) and we need to make the clipped region larger (scale). Now, when I say clipped region, I mean the contents of that region. In actuality, we are moving the canvas's rendered output. Moving the region bounds is not what we want to do.
To do this in WPF, we need to add each transformation we want to a child of a TransformGroup.
In this case, we are translating the canvas's output so that its top-left corner is (0, 0) This is necessary because afterwards we will scale the rendered output. So, now, we need to scale the canvas's output so that the image fits as large as it can. To do this, we need to create a ratio that compares the canvas size to the clipped region size.
Here is the formula for scaling the output:
ratio = canvasSize / clippedSize
scaledSize = clippsedSize * ratio
Now, scaling the canvas's output will allow the clipped region to appear as large as possible.
Take a look at the results. Here are images demonstrating the canvas's output before and after the transformations are applied:
Before
After
I figure I might as well give you all the code I used:
Canvas c = new Canvas();
double oPrintWidth=100;
double ratio = .89;
c.Width = oPrintWidth * 2.54;
c.Height = c.Width * ratio;
c.Background = new ImageBrush((ImageSource)FindResource("TestImage")) { Stretch = Stretch.UniformToFill };
// Define the path to clip
string newPath = "M 64,64 L 64,128 128,128, 128,64 Z";
c.Clip = Geometry.Parse(newPath);
Rect bounds = c.Clip.Bounds;
double scaleX = c.Width / (bounds.Right - bounds.Left);
double scaleY = c.Height / (bounds.Bottom - bounds.Top);
TransformGroup group = new TransformGroup();
TranslateTransform move = new TranslateTransform(-bounds.Left, -bounds.Top);
ScaleTransform scale = new ScaleTransform(scaleX, scaleY);
group.Children.Add(move);
group.Children.Add(scale);
c.RenderTransform = group;
MyBorder.Child = c;
And the XAML:
<Window.Resources>
<BitmapImage UriSource="uvtest.jpg" x:Key="TestImage"/>
</Window.Resources>
<Grid Background="Gray">
<Border x:Name="MyBorder" Background="White" BorderBrush="Black" BorderThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

C# WPF - Adorner ZIndex

I have a Grid with a Adorner to provide some drawn pattern. See img: http://imgur.com/D649W
My problem is that this Adorner(dots on the Grid) is layered on top of everything. The white square are draggable but now when the Adorner are on top, I can't drag. I would like the layer to be behind every component added to the Grid. Any suggestions on how I can set the ZIndex?
Thanks.
Code below:
MyAdorner ad = new MyAdorner(grid);
AdornerLayer adLayer = AdornerLayer.GetAdornerLayer(grid);
adLayer.Add(ad);
I push my Button and this is adding the MyAdorner to the grid. MyAdorner looks like this:
public MyAdorner(Grid adornedGrid)
: base(adornedGrid) {
Height = adornedGrid.Height;
Width = adornedGrid.Width;
brush = new VisualBrush();
brush.Stretch = Stretch.Fill;
brush.TileMode = TileMode.Tile;
brush.Viewport = new Rect(0, 0, SnapDistance, SnapDistance);
brush.ViewportUnits = BrushMappingMode.Absolute;
brush.Viewbox = new Rect(0, 0, SnapDistance, SnapDistance);
brush.ViewboxUnits = BrushMappingMode.Absolute;
ellipse = new Ellipse() { Fill = new SolidColorBrush(Colors.Blue), Width = 2, Height = 2 };
brush.Visual = ellipse;
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) {
Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 0);
drawingContext.DrawRectangle(brush, renderPen, new Rect(new Point(0, 0), AdornedElement.DesiredSize));
}
If your problem is that the adorner is covering the elements you want to manipulate so that they become un-draggable etc, set .IsHitTestVisible = False on the adorner.
You can also set the adorner's opacity to some semi-transparent value to see the background through it if that is desirable.
Is this what you're looking for?
Panel.SetZIndex(ad, 20)
Attached properties of the framework are usually asignable from static methods of the UIElement that holds it.
EDIT:
Possible alternative: - make your own Panel
Easy and dirty way to make sure that your wanted elements are ALWAYS on top:
Declare a static in a Util library:
public static int ZIndexCount;
Then when you want an element on top you simply do:
SetZIndex(_viewbox, Util.ZIndexCount++);
Of course, if your application runs 5 years without being interrupted the ZIndexCount will go back to 0...
It works like a charm in my applications.
I know this is quite old but I thought I try anyway:
You can add a new AdornerDecorator to you visual tree hierarchy to render the controls at the right level. By default the root of the tree provides the AdornerDecorator but you can add as many as you want and your the components you add will be rendered in them. For more information - see here
<Grid>
<AdornerDecorator>
...your Adorners render here
</AdornerDecorator>
</Grid>
https://wangmo.wordpress.com/2008/10/19/relations-between-adorner-adornerlayer-and-adornerdecorator/

WPF TranslateTransform

Im trying to use the TranslateTransform class to move a image on a Grid on Y axis. I need this movment to be smooth so i can not use SetMargin or SetCanvas. I try this in code behind:
public void MoveTo(Image target, double oldY, double newY)
{
var trans = new TranslateTransform();
var anim2 = new DoubleAnimation(0, newY, TimeSpan.FromSeconds(2))
{EasingFunction = new SineEase()};
target.RenderTransform = trans;
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
The object i want to use (a Image control) is placed on a Grid.
For the first time everything works fine.
The problems comes when i try to move the object again using the same function.
The object (a Image control) first move to the start position (initial Y coordinate) then the animation begins.
Is it not suposed for the TranslateTransform to change the coordinates (in my case the Margin property) too?
Thank you.
The transform does not change the original values.they are your point of origin. If you want a new point of origin each time you move you can handle the animation completed event. Or from your transform you can get your current offset and make that your new start point for the animation.
In other words your start values would always be your last move to values
The TranslateTransform is a specific kind of render transformation. Rather that changing properties of the control (such as the Margin property), it simply affects how the control is displayed on the screen.
You have to use the By property of DoubleAnimation.
Try that:
//everytime you execute this anmation your object will be moved 2.0 further
double offset = 2.0
var anim2 = new DoubleAnimation(newY, TimeSpan.FromSeconds(2));
anim2.To = null;
anim2.By = offset;
You've explicitly told the animation to start from 0. It's doing what you've told it.
Just remove the explicit zero fromvalue and everything will work.
var anim2 = new DoubleAnimation(newY, TimeSpan.FromSeconds(2))
{ EasingFunction = new SineEase() };

Categories

Resources