I am trying to programmatically add bezier paths to a canvas in a WPF window. This works fine if I write them out in XAML, but adding one programmatically fails
XAML
<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="320" Width="480" ContentRendered="Window_ContentRendered">
<Canvas Margin="10" Name="canvas">
<Rectangle Width="50" Height="100" Fill="LightSalmon" Margin="0,50"></Rectangle>
</Canvas>
</Window>
Code behind
private void Window_ContentRendered(object sender, EventArgs e)
{
var r = new Rectangle();
r.Width = 50;
r.Height = 50;
r.StrokeThickness = 3;
r.Fill = new SolidColorBrush(Colors.Black);
canvas.Children.Add(r);
//bezier is a System.Windows.Shapes.Path
bezier.Stroke = new SolidColorBrush(Colors.Black);
bezier.StrokeThickness = 35;
PathFigure pf = new PathFigure { StartPoint = new Point(50, 67.5) };
PolyBezierSegment pbs = new PolyBezierSegment(new Point[] { new Point(100, 67.5), new Point(100, 50), new Point(150, 50) }, false);
pf.Segments.Add(pbs);
PathFigureCollection pfc = new PathFigureCollection { pf };
PathGeometry pg = new PathGeometry();
pg.Figures = pfc;
bezier.Data = pg;
canvas.Children.Add(bezier);
canvas.Dispatcher.Invoke(() => { }, DispatcherPriority.Render);
}
Since the black rectangle is added correctly it must be something with building up the bezier path, but I can't figure out where the problem is. There are no errors or exceptions. It just doesn't show up on re-rendering.
For completeness, here the (currently commented out) XAML of the bezier, that works
<Path Stroke="Black" StrokeThickness="30" Name="blackPath">
<Path.Data>
<PathGeometry>
<PathFigureCollection>
<PathFigure StartPoint="50,67.5">
<PolyBezierSegment Points="100,67.5 100,50 150,50" />
</PathFigure>
</PathFigureCollection>
</PathGeometry>
</Path.Data>
</Path>
Problem solved
PolyBezierSegment pbs = new PolyBezierSegment(
new Point[] { new Point(100, 67.5),
new Point(100, 50),
new Point(150, 50) },
false); // <== Wrong
should be
PolyBezierSegment pbs = new PolyBezierSegment(
new Point[] { new Point(100, 67.5),
new Point(100, 50),
new Point(150, 50) },
true); // <== Set to true if the Stroke is defined separately, which is the case for me
Related
Does anyone know of an easy way to animate a movement from an Image's current location to a new location (X,Y) using WPF animation with no XAML, 100% programmatically? And with no reference to "this" (with RegisterName etc).
I am trying to make an extension class for Image to do animation stuff on it. It is easy enough to change the width and height properties through animation, but after searching for location animation of an object it suddenly becomes more advanced.
As it is an extension class I will only have a reference to the actual Image object and the X and Y I want to move it to.
public static void MoveTo(this Image targetControl, double X, double Y, double Width, double Height){
//code here
...
}
Update:
Thanks. Almost working. It seems The GetTop and GetLeft returns 'NaN' not explicitly set. Found the workaround in this post: Canvas.GetTop() returning NaN
public static void MoveTo(this Image target, double newX, double newY) {
Vector offset = VisualTreeHelper.GetOffset(target);
var top = offset.Y;
var left = offset.X;
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(0, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
trans.BeginAnimation(TranslateTransform.XProperty, anim2);
}
I had to swap two of the values (FROM) with 0. I assume that must be because in this context the upper left corner of the picture is the origin? But now it works.
Try this:
public static void MoveTo(this Image target, double newX, double newY)
{
var top = Canvas.GetTop(target);
var left = Canvas.GetLeft(target);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(top, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(left, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.XProperty,anim1);
trans.BeginAnimation(TranslateTransform.YProperty,anim2);
}
Here it is... It changes the size and moves a MediaElement under the Canvas. Just input your parameters:
Storyboard story = new Storyboard();
DoubleAnimation dbWidth = new DoubleAnimation();
dbWidth.From = mediaElement1.Width;
dbWidth.To = 600;
dbWidth.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbHeight = new DoubleAnimation();
dbHeight.From = mediaElement1.Height;
dbHeight.To = 400;
dbHeight.Duration = dbWidth.Duration;
story.Children.Add(dbWidth);
Storyboard.SetTargetName(dbWidth, mediaElement1.Name);
Storyboard.SetTargetProperty(dbWidth, new PropertyPath(MediaElement.WidthProperty));
story.Children.Add(dbHeight);
Storyboard.SetTargetName(dbHeight, mediaElement1.Name);
Storyboard.SetTargetProperty(dbHeight, new PropertyPath(MediaElement.HeightProperty));
DoubleAnimation dbCanvasX = new DoubleAnimation();
dbCanvasX.From = 0;
dbCanvasX.To = 5;
dbCanvasX.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbCanvasY = new DoubleAnimation();
dbCanvasY.From = 0;
dbCanvasY.To = 5;
dbCanvasY.Duration = dbCanvasX.Duration;
story.Children.Add(dbCanvasX);
Storyboard.SetTargetName(dbCanvasX, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasX, new PropertyPath(Canvas.LeftProperty));
story.Children.Add(dbCanvasY);
Storyboard.SetTargetName(dbCanvasY, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasY, new PropertyPath(Canvas.TopProperty));
story.Begin(this);
<Viewbox Stretch="Uniform" StretchDirection="Both" SnapsToDevicePixels="True">
<Grid Width="640" Height="480" Name="MainLayout" SnapsToDevicePixels="True" Background="Black">
<Canvas Width="640" Height="480" Name="MainCanvas" SnapsToDevicePixels="True">
<MediaElement Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" LoadedBehavior="Manual" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" />
<Button Canvas.Left="294" Canvas.Top="196" Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</Canvas>
</Grid>
</Viewbox>
UPDATE:
Instead of MediaElement use this line:
<Rectangle Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" Fill="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" />
And don't forget to put the C# code to:
private void button1_Click(object sender, RoutedEventArgs e) {}
You can use MediaElement as well but you have to define a VideoClip to see something ;)
Please find a solution that uses the Left and Top properties of Canvas for the extension method. See the following code:
public static void MoveTo(this Image target, Point newP)
{
Point oldP = new Point();
oldP.X = Canvas.GetLeft(target);
oldP.Y = Canvas.GetTop(target);
DoubleAnimation anim1 = new DoubleAnimation(oldP.X, newP.X, TimeSpan.FromSeconds(0.2));
DoubleAnimation anim2 = new DoubleAnimation(oldP.Y, newP.Y , TimeSpan.FromSeconds(0.2));
target.BeginAnimation(Canvas.LeftProperty , anim1);
target.BeginAnimation(Canvas.TopProperty, anim2);
}
This code is based on #DeanChalk's answer.
It moves an Image contained within a Canvas (RFID_Token) diagonally from the top-right to the bottom-left, positioned centrally over another Image within a Canvas (RFID_Reader).
<Canvas>
<Canvas x:Name="RFID_Reader_Canvas">
<Image x:Name="RFID_Reader" Source="RFID-Reader.png" Height="456" Width="682" Canvas.Left="37" Canvas.Top="524"/>
</Canvas>
<Canvas x:Name="RFID_Token_Canvas">
<Image x:Name="RFID_Token" Source="RFID-Token.png" Height="268" Width="343" Canvas.Left="874" Canvas.Top="70"/>
</Canvas>
</Canvas>
var StartX = Canvas.GetLeft(RFID_Token);
var StartY = Canvas.GetTop(RFID_Token);
var EndX = RFID_Reader.Width / 2 + Canvas.GetLeft(RFID_Reader) - StartX - (RFID_Token.Width / 2);
var EndY = RFID_Reader.Height / 2 + Canvas.GetTop(RFID_Reader) - StartY - (RFID_Token.Height / 2);
var AnimationX = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(1));
var AnimationY = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(1));
var Transform = new TranslateTransform();
RFID_Token_Canvas.RenderTransform = Transform;
Transform.BeginAnimation(TranslateTransform.XProperty, AnimationX);
Transform.BeginAnimation(TranslateTransform.YProperty, AnimationY);
I kept having NaN or 0 values for my nested elements, here's a modified version of Danny's answer :
public void MoveTo(Canvas canvas, FrameworkElement target, FrameworkElement destination)
{
Point oldPoint = target.TransformToAncestor(canvas).Transform(new Point(0, 0));
Point newPoint = destination.TransformToAncestor(canvas).Transform(new Point(0, 0));
var EndX = destination.Width / 2 + newPoint.X - oldPoint.X - (target.Width / 2);
var EndY = destination.Height / 2 + newPoint.Y - oldPoint.Y - (target.Height / 2);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(0.3));
DoubleAnimation anim2 = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(0.3));
trans.BeginAnimation(TranslateTransform.XProperty, anim1);
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
is there any equivalent for System.Drawing.Drawing2D.GraphicsPath.AddArc, in Xaml UWP rendering ?
I have tried the below use case in XAML UWP up-to my knowledge.
Path path = new Path();
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
ArcSegment arcSegment = new ArcSegment();
arcSegment.IsLargeArc = true;
arcSegment.Size = new Windows.Foundation.Size(100, 100);
arcSegment.Point = new Windows.Foundation.Point(100, 100);
arcSegment.RotationAngle = 180;
arcSegment.SweepDirection = SweepDirection.Clockwise;
pathFigure.StartPoint = new Windows.Foundation.Point(100, 100);
pathFigure.Segments.Add(arcSegment);
path.Data = pathGeometry;
path.Fill = new SolidColorBrush(Colors.Red);
path.StrokeThickness = 2;
pathGeometry.Figures.Add(pathFigure);
But the above is not 100% equivalent to System.Drawing.Drawing2D.GraphicsPath.AddArc(Rectangle, Single, Single)
is there any equivalent for System.Drawing.Drawing2D.GraphicsPath.AddArc, in Xaml UWP rendering ?
In classical windows form application, we have System.Drawing.Drawing2D namespace for drawing graphics. But this namespace is not supported in UWP app. UWP app has its own APIs for drawing graphics. You should be able to draw shapes by APIs under Windows.UI.Xaml.Shapes namespace. More details about how to draw shapes on uwp app please reference this article.
The method System.Drawing.Drawing2D.GraphicsPath.AddArc is actually for drawing a arc, which you already found in uwp app you could use adding ArcSegment instead.
But the above is not 100% equivalent to System.Drawing.Drawing2D.GraphicsPath.AddArc(Rectangle, Single, Single)
UWP app has its own APIs for drawing that it may not have a method that has same method name and same parameters as it in windows form. But you should be able to use other methods in uwp to implement the same effects. For example, the AddArc method has three parameters, Rectangle is for defined the x-diameter and y-diameter and the shape location, and startAngle and sweepAngle for defined the start and end angle. In uwp app, the ArcSegment can define the arc x-radius and y-radius by size property, and point for define start and end location.
For example, in a windows form, we may use the following code drawing a arc;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Create a GraphicsPath object.
GraphicsPath myPath = new GraphicsPath();
// Set up and call AddArc, and close the figure.
Rectangle rect = new Rectangle(20, 20, 50, 100);
myPath.StartFigure();
myPath.AddArc(rect, 0, 180);
myPath.CloseFigure();
// Draw the path to screen.
e.Graphics.DrawPath(new Pen(Color.Red, 3), myPath);
}
In uwp app you can do the same thing in xaml by the following code:
<Path StrokeThickness="2" Margin="40,40,0,0" Stroke="Red">
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="True">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="25,50" Point="50,0" IsLargeArc="True" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
Or code behind:
private void btnCreatepath_Click(object sender, RoutedEventArgs e)
{
Path path = new Path();
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
ArcSegment arcSegment = new ArcSegment();
arcSegment.IsLargeArc = true;
arcSegment.Size = new Windows.Foundation.Size(25, 50);
arcSegment.Point = new Windows.Foundation.Point(50, 0);
arcSegment.RotationAngle = 180;
pathFigure.IsClosed = true;
pathFigure.Segments.Add(arcSegment);
path.Data = pathGeometry;
path.Stroke = new SolidColorBrush(Colors.Red);
path.StrokeThickness = 2;
path.Margin = new Thickness(40, 40, 0, 0);
pathGeometry.Figures.Add(pathFigure);
gridroot.Children.Add(path);
}
And the result:
In a word that you should be able find APIs to draw the same thing which you done in the form.
I've found downloaded and converted into XAML some vertor picture:
<Canvas Width="0" Height="0" ClipToBounds="True">
<Path Fill="#FF000000" Stroke="#FF000000" StrokeMiterLimit="4" Name="path26">
<Path.Data>
<PathGeometry FillRule="Nonzero" Figures="M51.688,5.25C46.261,5.1091 ... 51.344,83.125z" />
</Path.Data>
</Path>
</Canvas>
and now, i'd like to draw this picture on my custom drawing:
private void Draw()
{
DrawingGroup aDrawingGroup = new DrawingGroup();
for (int DrawingStage = 0; DrawingStage < 10; DrawingStage++)
{
GeometryDrawing drw = new GeometryDrawing();
GeometryGroup gg = new GeometryGroup();
if (DrawingStage == 1)
{
drw.Brush = Brushes.Beige;
drw.Pen = new Pen(Brushes.LightGray, 0.01);
RectangleGeometry myRectGeometry = new RectangleGeometry();
myRectGeometry.Rect = new Rect(0, 0, 3, 2.3);
gg.Children.Add(myRectGeometry);
}
if (DrawingStage == 2)
{
drw.Pen = new Pen(Brushes.Black, 0.02);
for (int i = 5; i < 16; i++)
{
LineGeometry myRectGeometry = new LineGeometry(new Point(2.9, i * 0.1), new Point(0.1, i * 0.1));
gg.Children.Add(myRectGeometry);
}
}
drw.Geometry = gg;
aDrawingGroup.Children.Add(drw);
}
noteImage.Source = new DrawingImage(aDrawingGroup);
}
How could i draw and scale this an external picture?
We have an application and we need a custom shape per each signal(changeable value in time) to add it in canvas for having a reference. in future by changing the signal value shape must changing to. for example if value=1 the shape must be a rectangle and if value=2 shape must be an ellipse.
I want to save Geometry of these shapes in a list and use them when I want.
I have an Idea for this case but it dosent work.
public class DigitalShape : Shape
{
public Geometry SelectedGeometry
{
get { return (Geometry)GetValue(SelectedGeometryProperty); }
set { SetValue(SelectedGeometryProperty, value); }
}
public static readonly DependencyProperty SelectedGeometryProperty = DependencyProperty.Register("SelectedGeometry",
typeof(Geometry), typeof(DigitalShape), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
protected override Geometry DefiningGeometry
{
get
{
return SelectedGeometry;
}
}
}
Get geometries from shapes with RenderedGeometry like this:
Rectangle rect = new Rectangle();
rect.Width = 20;
rect.Height = 20;
rect.StrokeThickness = 1;
rect.Fill = Brushes.Yellow;
rect.Stroke = Brushes.Yellow;
Canvas.SetLeft(rect, 100);
Canvas.SetTop(rect, 100);
DigitalShape _symbolShape = new DigitalShape();
_symbolShape.SelectedGeometry = rect.RenderedGeometry;
please help me guys.
Update 1:
testing data property of path with this code in new project, but I didn't see the rectangle:
private void button1_Click(object sender, RoutedEventArgs e)
{
Rectangle rect = new Rectangle();
rect.Width = 20;
rect.Height = 20;
rect.StrokeThickness = 1;
rect.Fill = Brushes.Yellow;
rect.Stroke = Brushes.Yellow;
Canvas.SetLeft(rect, 100);
Canvas.SetTop(rect, 100);
Path path = new Path();
path.Data = rect.RenderedGeometry;
myCanvas.Children.Add(path);
}
Update 2:
using path from another way. when myElp is hard coded and created in xaml, rendergeometry is worked and get a right geometry with values but when I create myElp in Code behined rendergeometry give me an empty geometry.
Xaml Code:
<Grid>
<Canvas x:Name="myCanvas" Width="200" Height="200" Background="Aqua"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="62,234,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<Ellipse x:Name="myElp" Fill="#FFF4F4F5" Height="25" Margin="23,0,30,20" Stroke="Black"/>
</Grid>
Code Behined:
PathGeometry pathGeo = PathGeometry.CreateFromGeometry(myElp.RenderedGeometry);
Path path = new Path();
path.Data = pathGeo;
path.Stroke = Brushes.Yellow;
path.StrokeThickness = 1;
path.Fill = Brushes.Yellow;
Canvas.SetLeft(path, 10);
Canvas.SetTop(path, 10);
myCanvas.Children.Add(path);
I see 2 Ellipse when Run this code. 1 its own ellipse of xaml and 1 my path.
How can i use render geometry for shape who created in code behined?!
I have a c# class library that supports Silverlight4 as well.
The problem is , I need to use System.Drawing.Drawing2d.GraphicsPath in my code. for drawing a shape. but it is not there in silverlight. Can anyone suggest me an alternative?
Thanks in advance.
Regards,
James
take a look at http://www.c-sharpcorner.com/UploadFile/mahesh/PathInSL03252009005946AM/PathInSL.aspx
Snippet XAML:
<Path Stroke="Black" StrokeThickness="4"
Data="M 80,200 A 100,50 45 1 0 100,50" />
Snippet Dynamic:
public void CreateAPath()
{
// Create a blue and a black Brush
SolidColorBrush blueBrush = new SolidColorBrush();
blueBrush.Color = Colors.Blue;
SolidColorBrush blackBrush = new SolidColorBrush();
blackBrush.Color = Colors.Black;
// Create a Path with black brush and blue fill
Path bluePath = new Path();
bluePath.Stroke = blackBrush;
bluePath.StrokeThickness = 3;
bluePath.Fill = blueBrush;
// Create a line geometry
LineGeometry blackLineGeometry = new LineGeometry();
blackLineGeometry.StartPoint = new Point(20, 200);
blackLineGeometry.EndPoint = new Point(300, 200);
// Create an ellipse geometry
EllipseGeometry blackEllipseGeometry = new EllipseGeometry();
blackEllipseGeometry.Center = new Point(80, 150);
blackEllipseGeometry.RadiusX = 50;
blackEllipseGeometry.RadiusY = 50;
// Create a rectangle geometry
RectangleGeometry blackRectGeometry = new RectangleGeometry();
Rect rct = new Rect();
rct.X = 80;
rct.Y = 167;
rct.Width = 150;
rct.Height = 30;
blackRectGeometry.Rect = rct;
// Add all the geometries to a GeometryGroup.
GeometryGroup blueGeometryGroup = new GeometryGroup();
blueGeometryGroup.Children.Add(blackLineGeometry);
blueGeometryGroup.Children.Add(blackEllipseGeometry);
blueGeometryGroup.Children.Add(blackRectGeometry);
// Set Path.Data
bluePath.Data = blueGeometryGroup;
LayoutRoot.Children.Add(bluePath);
}