How to render InkStroke on Canvas using BezierSegment in UWP C# - c#

I am porting an application from javascript to UWP c# and I am struggling with the new InkCanvas. If you are familiar with the new InkCanvas in UWP, I would truly appreciate your help.
This is the code I wrote that takes renders an InkStroke onto a Canvas.
public static void Bezier(Canvas canvas, InkStroke stroke)
{
var segments = stroke.GetRenderingSegments();
PathFigure pthFigure = new PathFigure() { StartPoint = new Point(segments[0].Position.X, segments[0].Position.Y)};
for (int i = 1; i < segments.Count; i++)
{
var segment = segments[i];
var bezier = new BezierSegment();
bezier.Point1 = new Point(segment.BezierControlPoint1.X, segment.BezierControlPoint1.Y);
bezier.Point2 = new Point(segment.BezierControlPoint2.X, segment.BezierControlPoint2.Y);
bezier.Point3 = new Point(segment.Position.X, segment.Position.Y);
pthFigure.Segments.Add(bezier);
}
PathGeometry pthGeometry = new PathGeometry();
pthGeometry.Figures.Add(pthFigure);
Path path = new Path();
//path.Stroke = new SolidColorBrush(stroke.DrawingAttributes.Color);
//path.StrokeThickness = stroke.DrawingAttributes.Size.Height;
path.Stroke = new SolidColorBrush(Colors.Red);
path.StrokeThickness = 1;
path.Data = pthGeometry;
canvas.Children.Add(path);
}
Unfortunately, the control points seem to be messed up, and I do not understand why.
You can see below an image of what I get running this code.
The black stroke is the one originally rendered in the InkCanvas, and the red stroke is the one rendered on the Canvas from the code above.
The black stroke is the one originally rendered in the InkCanvas, and the red stroke is the one rendered on the Canvas from the code above
Anyone has any idea of what I am doing wrong?

I found the problem! I was setting drawingAttributes.FitToCurve = false and this caused the control points to be zero. I assumed this setting was only affecting the rendering, but now it makes sense because I was calling GetRenderingSegments. I was able to draw the shape by using GetInkPoints and then draw a polyline.

Related

Set Route Color in GMap.NET.WindowsPresentation

I am using the WPF version of Gmap.NET.
This feels like a stupid question....but I can't figure out how to change the stroke color/width of a route.
In winforms GMapRoute has property Stroke that can be set as you would expect
GMapRoute r = new GMapRoute(route.Points, "My route");
r.Stroke.Width = 2;
r.Stroke.Color = Color.TurdBrown;
The WPF version seems very different and I can't figure it out.
I could access to these properties using a casting, here is my code:
GMapRoute mRoute = new GMapRoute(route.Points);
mRoute.RegenerateShape(MainMap);
((System.Windows.Shapes.Path)mRoute.Shape).Stroke = new SolidColorBrush(Colors.Red);
((System.Windows.Shapes.Path) mRoute.Shape).StrokeThickness = 20;
Firts of all I created the GMapRoute, then I generated its shape in the map, then I modified the shape changing color and thickness.
I hope that this can help you.
I think use RegenerateShape for creating Shape is not good for performance.
It is better to setup style of line before adding route to map.
List<PointLatLng> routePath = List<PointLatLng>();
routePath.Add(new PointLatLng(Lat1,Lon1));
....
routePath.Add(new PointLatLng(LatN,LonN));
GMapRoute groute = new GMapRoute(routePath);
groute.Shape = new Path() { Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 4 };
map.Markers.Add(groute);

Clip an ellipse in WPF - positioning off

I am trying to 'cut' of part of several ellipses, in what is quite a big program. Since I had troubles making this work, I have started a new project in which I tried to solve the solution on a small scale. Again, I get some very weird results - which I expect have to do with positioning. Please see the below code for a minimal working example.
The program creates an ellipse, gives it a pretty colour and places it on the stage. It then proceeds to create what is called a 'RectangleGeometry', which we will use for the clipping. Please note that the geometry is placed at 0,0 with a width of 40 and height of 200. The result can be seen in the following screenshot;
My end goal is this: to be able to place a RectangleGeometry at any position (lets say 200,300) and have it clip the ellipse at exactly that position.
Ellipse abcEllipse = new Ellipse{
Margin = new Thickness(100, 100, 0, 0),
Fill = Brushes.HotPink,
Height = 80,
Width = 60
};
DrawCanvas.Children.Add(abcEllipse);
RectangleGeometry clipRectangle = new RectangleGeometry {
Rect = new Rect(0, 0, 40, 200)
};
GeometryGroup myGeometryGroup1 = new GeometryGroup();
myGeometryGroup1.Children.Add(clipRectangle);
Path myPath1 = new Path { Stroke = Brushes.Black, StrokeThickness = 1 };
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(255, 204, 204, 255);
myPath1.Opacity = 0.2;
myPath1.Fill = mySolidColorBrush;
myPath1.Data = myGeometryGroup1;
DrawCanvas.Children.Add(myPath1);
abcEllipse.Clip = clipRectangle;
Update: Some more clarification might indeed be required; I want to achieve the following effect - as seen in the attached image. An ellipse is placed on the stage, it is rotated and the top of the ellipse is cut off. (In such a way that it 'respects' the new rotation and thus cuts at what is now the new top)
However; this is the result I get when I apply the knowledge that "the clip geometry assigned to the shape will be relative to that shape;". As you can see, it doesn't really cut at the 'new' top of the ellipse.
Update 2: Added the code used to rotate the ellipse;
TransformGroup tg = new TransformGroup();
tg.Children.Add(new RotateTransform(40));
abcEllipse.RenderTransform = tg;

Can we do erase to transparency in Windows phone 8?

Actually i am wondering to erase an image to transparency. like i have an image on page background and another image above that. Now i want that if i erase above image by finger then lower image should be appear, simply means to say image will become transparent.i'm doing something like this but its not meet my requirements.
Your suggestions are Welcome :)
private void Canvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
currentPoint = e.GetPosition(this.canvas);
//Initialize line according to currentpoint position.
Line line = new Line()
{
X1 = currentPoint.X,
Y1 = currentPoint.Y,
X2 = oldPoint.X,
Y2 =oldPoint.Y
};
line.StrokeDashCap = PenLineCap.Round;
line.StrokeEndLineCap = PenLineCap.Round;
line.StrokeLineJoin = PenLineJoin.Round;
line.StrokeThickness = 20;
line.Stroke = new SolidColorBrush(Colors.Black) ;
////////////////////////////////
//Set color & thickness of line.
//Line add in canvas children to draw image & assign oldpoint.
this.canvas.Children.Add(line);
oldPoint = currentPoint;
}
You can do it with 3 different ways:
Using opaque overlay and UIElement.Clip property. But you need to deal with Geometry. And I'm afraid it will be very CPU cost.
Using WriteableBitmap and changing an alpha channel of image. You can do it using WriteableBitmapEx.
Using opaque overlay and UIElement.OpacityMask property. I think it's the best way to accomplish that, as you're not limited to use bitmap (so you can place any XAML control below overlay) as in the second way.

Custom tile generates a System.OutOfMemoryException exception in wp8

Has anyone got any idea why I'm getting an OutOfMemoryException when I'm creating my custom tile?
I'm trying to create custom images for my primary tile on a windows phone 8 app from a ScheduledAgent. The error doesn't occur until my very last line of code is executed which is the NotifyComplete().
Here is the code (Not the cleanest but ok for prototyping I guess). This code only handles the wide tile and it tries to load an image an image downloaded from a website and then it tries to render a logo and a description over this image.
Here is the code:
private void CreateTiles()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
for (int i = 0; i < 2; i++)
{
var bmp = new WriteableBitmap(691, 336);
var articleImg = new BitmapImage(new Uri(articles[i].ImageFilename, UriKind.Relative));
var articleImage = new Image { Source = articleImg };
articleImage.Stretch = Stretch.UniformToFill;
articleImg.CreateOptions = BitmapCreateOptions.None; // Force the bitmapimage to load it's properties so the transform will work
var bmpLogo = new WriteableBitmap(100, 100);
var logoImg = new BitmapImage(new Uri("/Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative));
var logoImage = new Image { Source = logoImg };
logoImage.Opacity = 1.0;
logoImg.CreateOptions = BitmapCreateOptions.None; // Force the bitmapimage to load it's properties so the transform will work
var articleBannerGrid = new Grid();
articleBannerGrid.Background = ColorExtensions.ToSolidColorBrush("#000F558E");
articleBannerGrid.Opacity = .5;
articleBannerGrid.Height = 100;
articleBannerGrid.VerticalAlignment = VerticalAlignment.Bottom;
articleBannerGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100) });
articleBannerGrid.ColumnDefinitions.Add(new ColumnDefinition());
articleBannerGrid.Children.Add(logoImage);
Grid.SetColumn(logoImage, 0);
var textBlock = new TextBlock();
textBlock.Text = articles[i].Description;
textBlock.FontWeight = FontWeights.Bold;
textBlock.Margin = new Thickness(10, 5, 30, 5);
textBlock.TextWrapping = TextWrapping.Wrap;
textBlock.Foreground = new SolidColorBrush(Colors.White); //color of the text on the Tile
textBlock.FontSize = 30;
textBlock.Opacity = 1.0;
var articleTextGrid = new Grid();
articleTextGrid.Height = 100;
articleTextGrid.VerticalAlignment = VerticalAlignment.Bottom;
articleTextGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100) });
articleTextGrid.ColumnDefinitions.Add(new ColumnDefinition());
articleTextGrid.Children.Add(textBlock);
Grid.SetColumn(textBlock, 1);
var canvas = new Grid();
canvas.Width = articleImg.PixelWidth;
canvas.Height = articleImg.PixelHeight;
canvas.Children.Add(articleImage);
canvas.Children.Add(articleBannerGrid);
canvas.Children.Add(articleTextGrid);
bmp.Render(canvas, null);
bmp.Invalidate(); //Draw bitmap
FileStream fs = new FileStream(articles[i].ImageFilename, FileMode.Create);
bmp.SaveJpeg(fs, bmp.PixelWidth, bmp.PixelHeight, 0, 100);
fs.Close();
fs.Dispose();
articleImage = null;
articleImg = null;
}
//GC.Collect();
//GC.WaitForPendingFinalizers();
ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault();
if (TileToFind != null)
{
string title = articles[0].Tag;
string backTitle = articles[1].Tag;
string content = articles[0].Description;
string backContent = articles[1].Description;
FlipTileData tileData = new FlipTileData()
{
Title = title,
BackTitle = backTitle,
BackContent = backContent,
WideBackContent = backContent,
BackgroundImage = new Uri(articles[0].ImageFilename, UriKind.Relative),
BackBackgroundImage = new Uri(articles[1].ImageFilename, UriKind.Relative),
WideBackgroundImage = new Uri(articles[0].ImageFilename, UriKind.Relative),
WideBackBackgroundImage = new Uri(articles[1].ImageFilename, UriKind.Relative),
};
TileToFind.Update(tileData);
}
NotifyComplete();
});
}
I assume that it is the correct place to generate the custom tile i.e. ScheduledAgent.
I found an article Dynamic Live Tile issue WP8 [WriteableBitmap] on the Nokia website with the same problem but no solution there either.
I'll continue debugging this tomorrow by removing everything and adding each bit individually step by step to see if I can spot anything but if anyone has got any suggestion or solution, I'd appreciate if you could help.
If I'm going about it the wrong way, please let me know what's the best method to update tiles in the scheduled agent.
Thanks.
WP BackgroundAgents have a small memory cap. 20MB for WP8 without update 3 and 25 for WP8 with Update 3.
I would suggest that you create a another project and add Coding4Fun toolkit and MemoryCounter in to your page and then add the code in your BackgroudAgent to the sample page and try to create the tiles. Then look at how much memory it uses. I think amount of memory usage is higher than the 20/25MB cap. if so you have to find a way to reduce it.
As usual, Microsoft is omitting critical information from its documentation.
After reducing the size of my tile from 691x336 to 336x165, it worked, so it got me thinking and I thought that the size recommended by Microsoft in this article Flip Tile template for Windows Phone 8 for wp8 and wp8.1 seemed excessive, so I did a bit more research and this is when I found this excellent explanation in an article in stackoverflow i.e.
Windows Phone 8 Startscreen Tile sizes and margins
This clearly states the size of the tiles but not based on OS versions but based on screen resolutions.
In my test environment, I was using the default 'Emulator WVGA 512MB' and again by default the screen size is 480x800, so taking into account the information provided in the answer of this article, my tile size should have been 430x210.
I resized my (already reduced) tile back up to 430x210, and it still worked.
While there is a memory cap of 20/25Mb depending on OS and patch being used, which probably causes many problems from the various articles I've read on the web, in my instance I believe it was more likely to have been affected by tile size being incorrect, and therefore the additional memory, or lack thereof.
I will update this article once I get to using 8.1 in my IDE with a larger resolution but for now, I have to assume that it was definitely related to the size of my tile.
I'll definitely make sure to add code to create tile based on os, patch and resolution being used. Bit of a pain to best honest!
Hope this helps.

WPF printing/xps issue

I have written the following chunk of code that prints my ListBox perfectly when being sent to a physical printer, however when trying to send it to the XPS printer driver or using the XpsDocumentWriter class (I assume they use the same code under the hood) I receive the following exception:
System.ArgumentException was unhandled by user code
Message=Width and Height must be non-negative.
Source=ReachFramework
StackTrace:
at System.Windows.Xps.Serialization.VisualSerializer.WriteTileBrush(String element, TileBrush brush, Rect bounds)
The exception obviously points to an item not having a correct width/height however I have debugged the code when sending it to the different printers (physical and XPS driver) and I haven't been able to find any differences.
Below is how I create the visual to send to the printer:
private ScrollViewer GeneratePrintableView()
{
ScrollViewer scrollView = new ScrollViewer();
Grid grid = new Grid { Background = Brushes.White, Width = this.myListBox.ActualWidth, Height = this.myListBox.ActualHeight };
grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions[0].Height = new GridLength(0, GridUnitType.Auto);
grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions[1].Height = new GridLength(0, GridUnitType.Auto);
// Add the title and icon to the top
VisualBrush titleClone = new VisualBrush(this.TitleBar);
var titleRectangle = new Rectangle { Fill = titleClone, Width = this.TitleBar.ActualWidth, Height = this.TitleBar.ActualHeight };
grid.Children.Add(titleRectangle);
Grid.SetRow(titleRectangle, 0);
this.myListBox.Width = this.myListBox.ActualWidth;
this.myListBox.Height = this.myListBox.ActualHeight;
VisualBrush clone = new VisualBrush(this.myListBox) { Stretch = Stretch.None, AutoLayoutContent = true };
var rectangle = new Rectangle { Fill = clone, Width = this.myListBox.ActualWidth, Height = this.myListBox.ActualHeight };
Border border = new Border { Background = Brushes.White, Width = this.myListBox.ActualWidth, Height = this.myListBox.ActualHeight };
border.Child = rectangle;
grid.Children.Add(border);
Grid.SetRow(border, 1);
scrollView.Width = this.myListBox.ActualWidth;
scrollView.Height = this.myListBox.ActualHeight;
scrollView.Content = grid;
scrollView.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
return scrollView;
}
Here is the GetPage override in my DocumentPaginator implementation:
public override DocumentPage GetPage(int pageNumber)
{
Page page = new Page();
double z = 0.0;
this.grid = new Grid();
this.grid.RowDefinitions.Add(new RowDefinition());
this.grid.RowDefinitions[0].Height = new GridLength(0, GridUnitType.Auto);
this.grid.Children.Add(this.printViewer);
Grid.SetRow(this.printViewer, 0);
//Adjusting the vertical scroll offset depending on the page number
if (pageNumber + 1 == 1) //if First Page
{
this.printViewer.ScrollToVerticalOffset(0);
this.printViewer.UpdateLayout();
}
else if (pageNumber + 1 == _verticalPageCount) //if Last Page
{
if (this.printViewer.ScrollableHeight == 0) //If printing only single page and the contents fits only on one page
{
this.printViewer.ScrollToVerticalOffset(0);
this.printViewer.UpdateLayout();
}
else if (this.printViewer.ScrollableHeight <= this.printViewer.Height) //If scrollconentheight is less or equal than scrollheight
{
this.printViewer.Height = this.printViewer.ScrollableHeight;
this.printViewer.ScrollToEnd();
this.printViewer.UpdateLayout();
}
else //if the scrollcontentheight is greater than scrollheight then set the scrollviewer height to be the remainder between scrollcontentheight and scrollheight
{
this.printViewer.Height = (this.printViewer.ScrollableHeight % this.printViewer.Height) + 5;
this.printViewer.ScrollToEnd();
this.printViewer.UpdateLayout();
}
}
else //Other Pages
{
z = z + this.printViewer.Height;
this.printViewer.ScrollToVerticalOffset(z);
this.printViewer.UpdateLayout();
}
page.Content = this.grid; //put the grid into the page
page.Arrange(new Rect(this.originalMargin.Left, this.originalMargin.Top, this.ContentSize.Width, this.ContentSize.Height));
page.UpdateLayout();
return new DocumentPage(page);
}
Interestingly if I change the Fill of rectangle to a Brush instead of clone then I do not receive the exception and the outputted file is the correct size.
I have spent over a day trying to debug why this isn't working and I am hoping that someone out there has either seen a similar issue or is able to point out any mistakes I am making.
Thanks for any responses.
I had to give up finding a solution with VisualBrush. If there is a GroupBox in the Visual for the brush I could never get it to produce a XPS file. It always fails with
System.ArgumentException was unhandled by user code Message=Width and Height must be non-negative. Source=ReachFramework StackTrace: at System.Windows.Xps.Serialization.VisualSerializer.WriteTileBrush(String element, TileBrush brush, Rect bounds)
The workaround was to clone the content that should go in the VisualBrush (Is there an easy/built-in way to get an exact copy (clone) of a XAML element?) and use that directly in a Grid instead of an VisualBrush
Have you checked the value of ActualWidth and ActualHeight of myListBox when the VisualBrush is being created? I don't know from where myListBox comes, but if it is not rendered by the time you are generating your xps document you may run into problems. You can try to manually force the control to render and see if it makes any difference.
I was unable to rectify the problem however using this link Paginated printing of WPF visuals I was able to find a suitable solution to allow printing of complicated visuals within my WPF application.
It's 2016 now and it's still not fixed. The problem is using TileBrush or any descendant type (VisualBrush in your case). If you use absolute mapping, it works, it's the relative mapping that causes the problem. Calculate the final size yourself and set Viewport to this size, ViewportUnits to Absolute. Also make sure you don't use Stretch.

Categories