Recently I’ve started to work on a Xamarin Forms project.
One of my first tasks was to handle the icons in the system.
After getting the SVG icons from our designer, I dove in to see how I can render them,
But to my disappointment, it looked like Xamarin Forms does’t have a native support for SVG files.
Coming from the WPF world, I thought to myself,
Well, how hard can it be? I’ll just google it! Some one already handled it!
Boy! I was really disappointed!
Not a single straight to the point guide or example!
So after long digging around, and checking various packages that will handle the work for me,
I came across the fact that SkiaSharp can now handle SVG images.
BUT, with additional packages installed.
So to save you the frustration, let’s dig right into the solution.
I’ll try to keep it short and to the point.
TL;DR version
If all you want is the code and you can handle it yourself,
Feel free to check the example on my GitHub.
Preparations
Prerequisites
To understand what is going on in this guide,
You should be familiar with the basics of SkiaSharp for Xamarin Froms.
Follow the link above if you are not familiar with it yet.
The Packages
Install the following packages from NuGet:
- SkiaSharp.Views.Forms
- SkiaSharp.Svg
- SkiaSharp.Extended
Add SVG files
In your PCL project, add the SVG file you want to render.
IMPORTANT
The build action should be set to Embedded Resource.
For this demo,
I’ve chosen this cute cat image from OpenClipart.
An awesome website to get free cliparts.
Let’s Code
Create reusable control
Now, we could write everything from start each time we need an SVG .
But why would we do that if we can create a reusable control for that?
I decided to create a control that derives from the Frame class, so that our SVG icon can get a nice border built in it already.
public class Icon : Frame { private readonly SKCanvasView _canvasView = new SKCanvasView(); public Icon() { Padding = new Thickness(0); // Thanks to TheMax for pointing out that on mobile, the icon will have a shadow by default. // Also it has a white background, which we might not want. HasShadow = false; BackgroundColor = Color.Transparent; Content = _canvasView; _canvasView.PaintSurface += CanvasViewOnPaintSurface; } }
As you can see, we don’t do here much:
- Create a private member for SKCanvasView.
- Set it as the Content of our Frame.
- Register to the PaintSurface event.
Add Resource Id
Since we want our control to be reusable,
We need to add a bindable property for the resource.
And since we want to make sure we refresh the image once the property is changed,
We are going to invalidate the surface when it happens.
public static readonly BindableProperty IconFilePathProperty = BindableProperty.Create( nameof(ResourceId), typeof(string), typeof(Icon), default(string), propertyChanged: RedrawCanvas); public string ResourceId { get => (string)GetValue(IconFilePathProperty); set => SetValue(IconFilePathProperty, value); } private static void RedrawCanvas(BindableObject bindable, object oldvalue, object newvalue) { Icon svgIcon = bindable as Icon; svgIcon?._canvasView.InvalidateSurface(); }
Draw the SVG
Now we can go straight to the drawing part.
In our PaintSurface event handler: CanvasViewOnPaintSurface, we are going to draw the image.
private void CanvasViewOnPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKCanvas canvas = args.Surface.Canvas; canvas.Clear(); if (string.IsNullOrEmpty(ResourceId)) return; using (Stream stream = GetType().Assembly.GetManifestResourceStream(ResourceId)) { SKSvg svg = new SKSvg(); svg.Load(stream); canvas.DrawPicture(svg.Picture); } }
Let’s breakdown the above method:
- In any case, we want to clear the canvas first.
We get it from the event arguments and clear it. - Validate the existence of ResourceId.
- Load the resource SVG as a stream from the assembly.
- Create an SKSvg object and load it with the stream.
- Draw it as a picture on the canvas.
Simple as that!
Use the control
Now, let’s use the control in a view.
<StackLayout> <Label Text="Here is a cat" VerticalOptions="Center" HorizontalOptions="Center" FontSize="30"/> <svg:Icon ResourceId="SvgXF.Cat.svg" WidthRequest="300" HeightRequest="300" HorizontalOptions="Center" VerticalOptions="Center"/> </StackLayout>
IMPORTANT
Note the usage of the ResourceId.
To specify the resource, you need to provide the full path to it,
including the name of your assembly.
Almost There
Run your application and you should see the following:
As you can see, our cat is drawn just as it is described in the original SVG file.
Although we declared the icon to be 300×300, we see a small cat in the upper left part of the screen.
Let’s fix that!
Leverage the power of SVG
From Wikipedia:
Scalable Vector Graphics (SVG) is an XML-based vector image format for two-dimensional graphics with support for interactivity and animation. The SVG specification is an open standard developed by the World Wide Web Consortium (W3C) since 1999. SVG images and their behaviors are defined in XML text files.
What this basically means is that we can make our image as big as we want, without losing the quality of the image.
To see the full potential of an SVG, check out my next blog post about an interactive SVG image control.
Since we didn’t handle this in our code, we receive the original size of the image.
Let’s edit our CanvasViewOnPaintSurface method:
private void CanvasViewOnPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKCanvas canvas = args.Surface.Canvas; canvas.Clear(); if (string.IsNullOrEmpty(ResourceId)) return; using (Stream stream = GetType().Assembly.GetManifestResourceStream(ResourceId)) { SKSvg svg = new SKSvg(); svg.Load(stream); SKImageInfo info = args.Info; canvas.Translate(info.Width / 2f, info.Height / 2f); SKRect bounds = svg.ViewBox; float xRatio = info.Width / bounds.Width; float yRatio = info.Height / bounds.Height; float ratio = Math.Min(xRatio, yRatio); canvas.Scale(ratio); canvas.Translate(-bounds.MidX, -bounds.MidY); canvas.DrawPicture(svg.Picture); } }
The added code allows us to scale the picture generated from the SVG file to fit to the given size of our control.
The ratio is calculated based on the larger edge of the SVG view box,
So that no part of the SVG will be cropped.
Running the code now will yield the wanted result:
We are done
That’s it!
Now we have a working Xamarin Forms control that can render any given SVG.
It will scale to any given size of the control and will even react to size changes.
Feel free to have a look at the complete example on my GitHub.
https://github.com/mono/SkiaSharp/issues/2413. You have any solution for this ?
Sorry, but I don’t know how to solve this.
But if you do, feel free to share the solution for other to know.
Hola The Pshul, estoy probando tu código y funciona perfecto.
Pero no he logrado colocar un icono svg un TabbedPage especialmente en la propiedad IconImageSource, sabes como hacerlo?
Great implementation – many thanks for the blog post and sample.
One problem I found, which others have mentioned, is it isn’t able to render all of my SVGs correctly (some appeared in black instead of the correct colours – particularly those which utilise feColorMatrix or gradient fills for example).
One workaround I found is to export SVG’s from Adobe using ‘Presentation Mode’, but a better solution I found is to use another Svg library for Skia – Svg.Skia (which is an almost direct replacement for SkiaSharp.Extended.Svg).
Svk.Skia offers a more complete svg document model and rendering – solving my black SVGs. It might be helpful for others:
https://github.com/wieslawsoltes/Svg.Skia/
To use:
1. Add the Svk.Skia package via Nuget.
2. Replace the using: using SKSvg = Svg.Skia.SKSvg;
3. Replace the load function (so you can get at the document model):
var svgDocument = SvgModelExtensions.Open(stream);
if (svgDocument != null)
{
svg.FromSvgDocument(svgDocument);
….
4. Replace the ViewBox calc:
SKRect bounds = SKRect.Create(svgDocument.ViewBox.MinX, svgDocument.ViewBox.MinY,
svgDocument.ViewBox.Width, svgDocument.ViewBox.Height);
Thats it! Hope its helpful to someone and thanks once again!
Wow, that’s great Obliterator!
Thanks for helping out! 🙂
Thanks a lot for your help. Using your example I managed to troubleshoot the issue with displaying colours. (Our SVGs have tags for reuse, instead of multiple style attributes, and everything was displayed black. For some reason that is not supported maybe?)
I meant tags
OK tag notation not rendering here :). I meant style tags
I had the same problem.
See my post above for a possible fix.
Thank for the tutorial!
I just have one question. It doesn’t seem to work for me when an svg has layers / gradients. I tried with a picture today and just got a blob (bottom layer of the svg)
Any idea on how to solve this?
Thanks
Hi Daniel,
Glad you found it helpful.
I used images with this solution. I’m not sure which layers are supported or not. Can you please try your full app with another SVG? A simpler one? Just to make sure that the problem is not with the app you are developing but with the SVG itself.
Thank you for useful stuff!
Hi, I love this guide, it was simpler than the other options.
I’d like to ask if there’s a way to have dynamic sizes for the svg images, so that the image scale depending on the screen size, always taking a percentage of the screen or something. Since right now if I use the app on a bigger screen I just end up with a tiny image. Thank you.
Hi LSpark,
Thanks for taking interest in my post. 🙂
If you want to take a percentage of a screen, you will have to calculate it by yourself.
Here is the documentation about Device Display in the Xamarin.Essentials:
https://docs.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android
All you have to do is get the size of your screen in pixels and decide which percent of that you want your image to be. And then set the sizes to the SvgIcon.
Good luck! 🙂
hi , thanks a lot for this tutorial , it was helpful , i want to know how can i make a cercle border for an svg image
Hi Hau,
Sorry for the late response.
You can draw anything you’d like using SkiaSharp, including circles.
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/basics/circle
I hope you’ll find it useful.
Thank you, worked beautifully!
I’m glad you found it useful! 🙂
Hi! First thanks for the tutorial and the Git repository =)
I have implemented it exactly the same and it works very well except when the device rotates …
Have you tried it?
I share an example with my case:
https://paste.ofcode.org/atpFLQ36Qbi4ruBSDc4KZz (My code)
https://imgur.com/a/cM8GMSl (Screenshot)
– If I use a stacklayout that simulates the SVG everything looks good even if the device is rotated.
– But if I use the SVG all right until I turn the device horizontally ….
Then the SVG is minimized to a minuscule size and even its container that has a high 70 is reduced to approximately 20.
Any ideas? Thank you.
Hi Ephram,
Thank you for taking interest in my blog and reaching out to me.
I’m glad you’ve found my tips useful.
I tried to rotate the device in the past and it worked fine for me.
Just to make sure, I’ve ran the code you’ve sent me with my black cat SVG from the blog post and it rendered fine.
See the image below:
I’m not really sure what could cause the problem, but from my experience, it might as well be the content in the SVG file itself.
If it is possible, please share your SVG file with me. I will test it again and see if I can help you out.
Thanks,
Alex.
Thanks – this is awesomely useful! SkiaSharp is what I thought should be available in Xamarin but until reading this blog I had not found it. Great stuff.
I’m glad you’ve found it useful! 🙂
Thank you for your kind response,
Alex.
Hi, Thanks for the post. It is very useful.
I ran into an issue. I am trying to bind ResourceId with my data. My code is:
Now I am getting error:
No property, bindable property, or event found for ‘ResourceId’, or mismatching type between value and property.
Any ideas how to fix this?
My code was erased. Here is the idea how the code looks like:
Label Text=”{Binding Title}” …..
svg:Icon ResourceId=”{Binding Icon}” …….
Hi And,
Thank you for taking interest in my blog and sorry for the very late response.
I’m really eager to help you out, but without the full details, I can give only some guesses to what could go wrong:
My only guess is that you might have an issue with your binding context. In case your View doesn’t have the correct context, you might miss the Icon property you want to bind to.
Let me know how it goes,
Alex.
did you find a sollution for this problem? Since i’m having the same
Hey after a quick google search i found a simple sollution. Apparantly due to xamarin naming standards the BindableProperty should be named *propertyname*Property.
So I just changed
SvgFilePathProperty
to
ResourceIdProperty
and this fixed the issue
Hi Dominic Voets,
Glad you’ve found a solution to your problem.
Bindable properties can be indeed nasty. But once you understand the logic, they are not so scary. 🙂
Great work! SO much easier (and less bloat) then other options. However, it seems to only handle very simple SVGs. The majority of SVGs I’ve tried do not not render correctly. Is this a limitation of SkiaSharp, or of the particular use of it here?
Hi Barry Sohl,
Thank you for taking interest in the blog post and sorry for the very late response.
Sadly, I think that you are right. I ran into this issue as well when I was trying to show a bit more complex SVG images. I think that this is a limitation of SkiaSharp.
If you find a workaround, let me know. 🙂
I was able to determine that my main issue (colors being removed) is with the tag, which evidently Skia does not support. At least your solution still renders the icon as a single color, which I can then tint. Most of the SVG libraries won’t even render the image at all (though they do provide useful error images).
Thanks for updating me. I actually didn’t know about the tags issue.
Glad to hear that it worked out eventually. 🙂
Hey there,
Found your post and trying to implement your Icon class, but whenever I try to run it I get an InvalidCastException somewhere after the Canvas is invalidated. Unfortunately there’s no stack trace or anything, it just says “External”.
Have you seen this? Any suggestions?
Hi Eddie,
I’ve never encountered this issue myself, so it is a bit hard to estimate the cause.
Feel free provide more details (exception line + code, what did you change from the example, etc…) and I’ll do my best to help you out.
Generally, we can change color of “SVGimage” by editing code in html format/text format then it gets refelects to the SVG image
but,
How can we change the color of SVG image using a class file and bind the file to html file?
We will change colors at the given class file.
IS there any solution for this.
Any useful content will be appreciated and thanks in Advance
Wow what a great and well-needed post! Love the memes — no coders these day use memes!
Might as well add a few things I think are useful:
1) svg in the XAML is just the project assembly namespace.
xmlns:svg=”clr-namespace:MyAssembly; assembly=MyAssembly”
2) if your assembly changes at compiletime you can dynamically load the resourceid with just the file name (in xaml make resourceid the file name then add this to your canvasviewonpaint):
Assembly assembly = Assembly.GetExecutingAssembly();
var x = assembly.GetManifestResourceNames();
for (int i = 0; i < x.Length; i++)
{
if (x[i].Contains(ResourceId))
{
ResourceId = x[i];
}
}
My image is rendering as a black frame and I’ve copied you’re example exactly other than the file name of course, any ideas why this might be? Thanks!
Hi Jay,
I haven’t encountered this issue before.
Make sure that the exact SVG file is rendering fine on a browser. If it does, I guess that SkiaSharp doesn’t support the tags your SVG contains. :-\
Sorry about that.
It does so I guess not, thanks anyway!
Thanks for your contribution about SVG and Xamarin.
I am quite stuck on using SVG. I been trying your solution and the popular lib FFImageLoading. However, the image does not look like it supposed to.
See attached image, the correct image is at left side, and the one using your code on the right side. Ignore the white background. Using FFImageLoading the result is even worse. Any experience with this?
https://imgur.com/a/ydrpmzJ
Cheers
Hi Viktor,
Thanks for taking the time to share your issue.
I’m not sure what went wrong in your implementation. A code sample would probably help out a lot here.
My first guess would be that you don’t provide enough space for you icon.
Please make sure that the vertical and horizontal options are set to Fill. And if this doesn’t help, try setting the height and width manually.
If nothing helps, you are welcome to share your code and I’ll give my best shot. 🙂
Hi!
I’m using your code from GitHub and simple replace the Cat.svg file with mine, I have uploaded it here: https://ufile.io/jkxfo
I then add it as an embedded resource.
I think the nuget (lib) is having an issue with drawing linearGradients?
Compare the SVG file in while running it in your application with viewing it in a browser (Chrome for example).
Thanks
Hi Viktor,
I haven’t noticed the gradient issue at first. Thanks for pointing it out.
I think that you are right. Looks like SkiaSharp does has an issue with drawing your linear gradient.
My only advice at this point is either translate the SVG to a bitmap or use another software to generate the SVG.
Sorry, I’m sure that this is not the answer you were looking for, but it looks like there is not much I can do. ¯\_(ツ)_/¯
If you do find a way to display your SVG, please be kind to share your solution. I’ll be sure to include it in the post. With credit of course. 😉
Yeah!
Dosnt look like there is any lib thats support it today to it fullest. Fastest way forward today would be to use a BMP/PNG and then go back to SVG when its supported for those specific files.
If i found another solution I will let you know! Thanks for the help!
First of all, thank you for this genius blog post!
BUT!
I figured out to NOT use ‘ViewBox’ property of SKSvg object to determine the correct aspect ratio. Instead use ‘CanvasSize’. In fact, it’s the size of the canvas that makes the image, not the image itself within.
Here is my modificated version of the scaling:
var canvas = e.Surface.Canvas;
canvas.Clear();
var svg = LoadImage();
if (svg == null)
return;
var canvasWidth = svg.CanvasSize.Width;
var canvasHeight = svg.CanvasSize.Height;
var xRatio = e.Info.Width / canvasWidth;
var yRatio = e.Info.Height / canvasHeight;
var ratio = Math.Min(xRatio, yRatio);
canvas.Scale(ratio);
canvas.DrawPicture(svg.Picture);
Also you need to resize the control in order to display the re-scaled image. I managed this too. But I’m not quite sure if it works in all kinds of layouts. So I’m just referring to the scaling of the image.
PS: you don’t even need the ‘Translate’ calls.
Hi JunkyXL,
Thank you for taking interest in the post and thank you for your input.
ViewBox and CanvasSize are two different properties with two different functionalities and I’m glad you’ve found the one that better suits your needs. 🙂
Regarding the ‘Translate’ calls, it depends on your layout, the positioning of your svg control and the svg icon itself.
I didn’t include the ‘Translate’ calls in the initial release but then I’ve ran into some issues in a project I’m working on.
The ‘Translate’ calls are there for the corner cases.
Thank you for taking the time to comment and share with everyone your findings,
Alex.
Hi again!
I’m using your code example, https://github.com/AlexPshul/SvgXF
I’m simple replacing you Cat.svg file with my svg file and add it as an embedded resource. MY question is more about how “up-to-date” those SVG implementations are.
I have uploaded the svg file here: https://ufile.io/jkxfo
Add that to your Cat project and compare the image by looking at it in a browser (Chrome) and in the application. You will then see that the application cant draw the image as it should look. In the svg file, there is “linearGradient” that I misstake the lib is having issues with.
Thanks!
Very nice example. Any thoughts on how to manipulate the SVG image to change the colors? I was trying to load the document and change the elements.. but could use some help. Any ideas?
Hi Ray,
I would recommend you checking out w3schools tutorial on SVGs. It is very informative and explains a lot about what happens behind the scenes.
https://www.w3schools.com/graphics/svg_intro.asp
For coloring, you should check the stroke section.
Also, if you want to create your own SVG file or edit an existing one, I would recommend https://inkscape.org/en/ . It is a free, open source svg editor.
Wow, great work!
Could this be used to set a background image somehow or am I better of saving it as a huge png and scaling it?
SVG’s are vectors, so can’t be used to represent images unless they are a set of shapes.
Unless you have something like this: https://boingboing.net/2018/02/05/programmer-artist-creates-algo.html
Cheers,
Paul
The resource id is not valid for me. I have to add the target platform to the resource id.
When printing the resources in the assembly it shows up like this:
> MyApp.UWP.Images.dashboard.approach.svg
But the path I am trying to use is:
> MyApp.Images.dashboard.approach.svg
How can I convince Xamarin that this is the same resource? I don’t see this issue in your example and it confuses me :S
Hi Daniel,
Sorry to hear that you are having trouble with this.
Unfortunately, I don’t really know why the issue is happening in your project. I believe it has something to do with the way you’ve created the project.
Maybe you’ve changed the default assembly name?
Feel free to share a link to a GitHub repo that can recreate the issue and I’ll give it my best shot.
The following code should help. It ignores the namespace when selecting the resource:
private Stream GetEmbeddedResourceStream()
{
var assembly = typeof(App).GetTypeInfo().Assembly;
var resourceNames = assembly.GetManifestResourceNames();
var resourcePath = resourceNames.FirstOrDefault(x => x.EndsWith(“.” + this.SvgSource, StringComparison.CurrentCultureIgnoreCase));
if (resourcePath == null)
{
Debug.WriteLine($”SVG not found {this.SvgSource}. Make sure it is set to EmbeddedResource.”);
return null;
}
return assembly.GetManifestResourceStream(resourcePath);
}
Very interesting, I will try that as well. I solved it by manually editing the `projitems` file and added logical names like this:
MyApp.Images.dashboard.approach.svg
Seems my xml was stripped out, here’s a SO link instead:
https://stackoverflow.com/a/49033654/1068167
Hmmm, not sure why this is happening,
But I’m glad you’ve found a solution. 🙂
Great work! I am running into an issue where the xml parser complains about:
“For security reasons DTD is prohibited in this XML document. To enable DTD processing set the DtdProcessing property on XmlReaderSettings to Parse and pass the settings into XmlReader.Create method.”
Why it runs into a DTD, I do not know. Perhaps something is embedded in the svg. Is it possible to set the DTD parser to parse the svg anyway as explained here: https://stackoverflow.com/questions/13854068/dtd-prohibited-in-xml-document-exception ?
Cheers,
D
Hi D,
First of all, thank you for taking interest in my blog and reaching out.
I actually had the issue previously, and simply decided to remove the “<!DOCTYPE” part from the SVG.
However, reading the link you’ve attached, made me wonder if there is another way.
After digging around, I saw that SKSvg has an option to load the svg file using a regular XML Reader.
Should look something like this:
I must say, I didn’t test the code.
But let me know if this solved your issue or not.
Good luck,
Alex.
Worked perfectly, very nice.
Thanks for the post. I had a similar control already but the scaling stopped working properly so I stole your scaling code.
Another great source of icons is Syncfusion’s Metro Studio which is a free download although they do spam you with a lot of emails.
Cheers,
Paul
Glad you’ve found a good use of the sample code.
Thank you for sharing your source for icons, it is always good to know as many sources as possible. 🙂
Thanks for taking interest in the blog,
Alex.
Hi The Pshul, I’m trying your code but I’m facing one problem with this line :
GetType().Assembly.GetManifestResourceStream(ResourceId))
it seems that the method GetManifestResourceStream() doesn’t exist.
Thanks for you help
I found the solution, and I suggest to add this line in your icon constructor :
BackgroundColor = Color.Transparent;
HasShadow = false;
It’s hiding background and border in Android.
Nice work !
Hi TheMax,
Sorry, I didn’t see your comment on time,
Glad you’ve found a solution and glad you’ve found my post useful. 🙂
Thank you for pointing out the background and the shadow properties.
I’ll add it right away! 🙂
Nice Blog. Different. I liked the sponge bob animation.
I downloaded your sample repo, but could not get it to build in VS2015.
Will your code with work the image control or is it just for icons. I have a card game with 50+ card images, currently in png format. But would like to switch to SVG. I have tried to implement “ffimageloading” and other skiasharp based SVG code but found them to cumbersome. Yours, on the surface, seems much easier to implement.
I followed the post in Xamarin Forums to here.
Hi Ron,
Thanks for your nice feedback! 🙂
In my sample, I’m using a .net standard 2.0 PCL project,
For .net standard 2.0 to work, you need VS2017, version 15.3 at least.
That’s why you can’t build it on your machine.
Regarding the image control question, this won’t be necessary.
The idea of the SVG control is to completely remove the need for an image control. You can simply use it in your view where you need it.
So I guess there is no way to test, and see your code in action with VS2015.
My app targets Net Framework 4.5. Is it advisable to try and implement your code directly into my apps code? I can always create a new branch for testing your code and cancel the changes if I can’t get it to work.
Thanks – Ron
You are right,
AFAIK, in VS2015 the code won’t work.
I think you should be able to make it work if you implement it directly in your app. I don’t see a reason why it shouldn’t work. 🙂
I am kinda slow; I need to see a working example in order for me to be able to implement it into my app. I learn best by copying and pasting code.
I am in the process of downloading VS2017, but first i did a backup image. I’ll let you know if my migrates OK, and if i am able to get your demos up and running.
Ron
Downloaded the code and got it to work fine in VS2017. I really like your implementation, **it is very easy to use**. I added my own image by setting the image in the code behind which was very easy, just reference the the svg:Icon image by its, x:name and set the ResourceId like so:
svgCard.ResourceId = “SvgXF.dik.svg”
It is much easier to use than the other SVG implementation out there, such as “”ffinageloading”, and others I have tried.
Congratulations on a great piece of coding. You should publicize it more! I used an Optimized SVG file generated by Inkscape and it worked fine:
![](https://us.v-cdn.net/5019960/uploads/editor/s7/c1ie49uv7slq.png “”)
Thank you for your very nice feedback, I’m very glad that it worked out for you!
If you found the post helpful, you can help me publicize it more by spreading the word. 🙂