Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify the color of a SVG image in .NET Maui

Tags:

xaml

maui

.NET MAUI has the ability to use SVG images which is really nice, but I haven't been able to set the color of the SVG image. The official docs state I could use the TintColor in the project file but that's not a good solution as I want to be able to use different colors depending on certain conditions. So can we somehow specify the color of a SVG image?

like image 817
Jasper Avatar asked Dec 01 '25 09:12

Jasper


2 Answers

I just figured out that the Maui Community toolkit has a IconTintColorBehavior that does just want I want.

Usage:

<Image Source="shield.png">
    <Image.Behaviors>
        <toolkit:IconTintColorBehavior TintColor="Red" />
    </Image.Behaviors>
</Image>

namespace

xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
like image 138
Jasper Avatar answered Dec 03 '25 23:12

Jasper


In my answer, I describe two techniques that allow you to use SVG vector graphics in a way that the color can change, and, that you can render at different scales:

  • .NET MAUI Path/Data
  • True Type Font

MAUI has a Path class which can be used to draw curves and complex shapes. Scaling and setting colors can be done by setting Path. When used with Aspect, Fill, Stroke, Width and Height the shapes can change size and colors.

To use Path we need to adjust your SVG. To take you thru it, I present a sample SVG and apply such conversions to it.

Consider a 32x32 SVG that has 3 solid shapes:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <!-- square -->
  <path fill="red" d="M 4 4 h 8 v 8 h -8 v -8" />
  <!-- triangle -->
  <path fill="red" d="M 24 4 l 4 8 h -8 l 4 -8" />
  <!-- circle -->
  <path fill="red" d="M 16 20 a 4 4 0 0 1 0 8 a 4 4 0 0 1 0 -8" />
</svg>

PathData.gif

To prepare it for Path Data we need to make the following changes:

  • Define a bounding geometry: M 0 0 L 32 32 L 0 0
  • Connect the square: L 4 4 h 8 v 8 h -8 v -8 L 0 0
  • Connect the triangle: L 24 4 l 4 8 h -8 l 4 -8 L 0 0
  • Connect the circle: L 16 20 a 4 4 0 0 1 0 8 a 4 4 0 0 1 0 -8 L 0 0

Apply these conversions will result into a new SVG with a single path in it:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
    <path fill="red" d="
M 0 0 L 32 32 L 0 0

L 4 4 h 8 v 8 h -8 v -8
L 0 0

L 24 4 l 4 8 h -8 l 4 -8
L 0 0

L 16 20
a 4 4 0 0 1 0 8
a 4 4 0 0 1 0 -8
L 0 0" />
</svg>

Note that the things that I did were:

  • Having only one move at the start (there can be no more than one move)
  • Requiring that I trace out the viewbox, in this case (0,0) - (32,32)
  • All subsequent geometries do not use move and they have a trailing "L 0 0" to bring it back to the start
  • I only use fill since I do not want to see the connecting lines

Now that I've made these changes, I can copy the path from the SVG straight into the Path/Data and use it in .NET MAUI XAML like this:

<Border Padding="1" HorizontalOptions="Center">
    <Path
        Aspect="Uniform"
        Data="M 0 0 L 32 32 L 0 0 L 4 4 h 8 v 8 h -8 v -8 L 0 0 L 24 4 l 4 8 h -8 l 4 -8 L 0 0 L 16 20 a 4 4 0 0 1 0 8 a 4 4 0 0 1 0 -8 L 0 0"
        Fill="Red"
        HeightRequest="100"
        WidthRequest="100" />
</Border>

Note that the answer avoided stroke outlines. To do outlines using this approach, one has to define holes in the shapes by drawing the inner polygon hole anticlockwise. Also, we must connect the outer polygon to the inner polygon with a forward and reversing connecting line. After the connecting line, we must connect all polygon parts back to the starting point (e.g. L 0 0).

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
    <path fill="red" d="
M 0 0 L 32 32 L 0 0

L 4 4 h 8 v 8 h -8 v -8
l 1 1
v 6 h 6 v -6 h -6
l -1 -1
L 0 0

L 24 4 l 4 8 h -8 l 4 -8
v 2
l -2.5 5 h 5 l -2.5 -5
v -2
L 0 0

L 16 20
a 4 4 0 0 1 0 8
a 4 4 0 0 1 0 -8
v 1
a 3 3 0 0 0 0 6
a 3 3 0 0 0 0 -6
v -1
L 0 0" />
</svg>

PathData2

Convert the above to .NET MAUI Path:

<Border Padding="1" HorizontalOptions="Center">
    <Path
        Aspect="Uniform"
        Data="M 0 0 L 32 32 L 0 0 L 4 4 h 8 v 8 h -8 v -8 l 1 1 v 6 h 6 v -6 h -6 l -1 -1 L 0 0 L 24 4 l 4 8 h -8 l 4 -8 v 2 l -2.5 5 h 5 l -2.5 -5 v -2 L 0 0 L 16 20 a 4 4 0 0 1 0 8 a 4 4 0 0 1 0 -8 v 1 a 3 3 0 0 0 0 6 a 3 3 0 0 0 0 -6 v -1 L 0 0"
        Fill="Red"
        HeightRequest="100"
        WidthRequest="100" />
</Border>

[Original answer involving true type fonts]

If you are looking for a cross-platform vector file format that .NET MAUI natively supports and you are willing to pre-convert, then, consider converting your SVG icons assets to a TTF (true-type font). TTF will keep your icons in vector format and they can be resized and recolored at runtime.

  1. Convert your SVGs into a single TTF
  2. Add the TTF to your .NET MAUI application's resource
  3. Add it to the MauiProgram.cs ConfigureFonts
  4. Use the font directly in XAML Label or FontImageSource

Here are detailed steps on how to do the above.

1. Convert your SVGs into a single TTF

Install Node.JS / NPM package https://www.npmjs.com/package/fantasticon :

sudo apt update
sudo apt install npm
sudo npm install -g fantasticon

Prepare a conversion workspace:

maui-svg-ttf/scripts/svg/compass.svg
maui-svg-ttf/scripts/svg/globe.svg
maui-svg-ttf/scripts/svg/locator.svg
maui-svg-ttf/scripts/svg/shield.svg
maui-svg-ttf/scripts/makefonts.sh
maui-svg-ttf/scripts/my-icons.js

Here's makefonts.sh:

mkdir -p /tmp/maui-svg-ttf
fantasticon --config my-icons.js
cp /tmp/maui-svg-ttf/my-icons.ttf ../Resources/fonts/my-icons.ttf

Here's my-icons.js:

module.exports = {
    name: 'my-icons',
    fontHeight: 500,
    normalize: true,
    inputDir: 'svg',
    outputDir: '/tmp/maui-svg-ttf',
    fontTypes: ['ttf'],
    assetTypes: ['css', 'json', 'html'],
    formatOptions: {
        json: {
            indent: 2
        }
    },
    codepoints: {
        'globe'   : 0xe000, // globe.svg   at 0xe000 codepoint
        'compass' : 0x0001, // compass.svg at 0x0001 codepoint
        'locator' : 0x0002, // locator.svg at 0x0002 codepoint
        'shield'  : 0xe003, // shield.svg  at 0xe003 codepoint
    },
    getIconId: ({
        basename, // `string` - Example: 'foo';
        relativeDirPath, // `string` - Example: 'sub/dir/foo.svg'
        absoluteFilePath, // `string` - Example: '/var/icons/sub/dir/foo.svg'
        relativeFilePath, // `string` - Example: 'foo.svg'
        index // `number` - Example: `0`
    }) => {
        return basename;
    }
};

2. Add the TTF to your .NET MAUI application's resource

Make sure your new font my-icons.ttf gets copied to your Resources/fonts/ folder.

3. Add it to the MauiProgram.cs ConfigureFonts

// MauiProgram.cs ...
var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    .ConfigureFonts(fonts =>
    {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
        fonts.AddFont("my-icons.ttf", "my-icons");
    });
builder.Services.AddTransient<MainViewModel>();
builder.Services.AddTransient<MainPage>();

4. Use the font directly in XAML Label or FontImageSource

<VerticalStackLayout>
    <Label Text="&#xe000;" FontFamily="my-icons" FontSize="{Binding IconSize}" TextColor="{Binding Color}" Rotation="{Binding Angle}" HorizontalOptions="Center"/>
    <HorizontalStackLayout HorizontalOptions="Center">
        <Image HeightRequest="{Binding IconSize}" WidthRequest="{Binding IconSize}" Rotation="{Binding Angle}">
            <Image.Source>
                <FontImageSource Glyph="&#xe001;" FontFamily="my-icons" Color="{Binding Color}" Size="{Binding IconSize}"  />
            </Image.Source>
        </Image>
        <Image HeightRequest="{Binding IconSize}" WidthRequest="{Binding IconSize}" Rotation="{Binding Angle}">
            <Image.Source>
                <FontImageSource Glyph="&#xe002;" FontFamily="my-icons" Color="{Binding Color}" Size="{Binding IconSize}"  />
            </Image.Source>
        </Image>
        <Image HeightRequest="{Binding IconSize}" WidthRequest="{Binding IconSize}" Rotation="{Binding Angle}">
            <Image.Source>
                <FontImageSource Glyph="&#xe003;" FontFamily="my-icons" Color="{Binding Color}" Size="{Binding IconSize}" />
            </Image.Source>
        </Image>
    </HorizontalStackLayout>
    <Slider Minimum="0" Maximum="255" Value="{Binding RedValue, Mode=TwoWay}"/>
    <Slider Minimum="0" Maximum="255" Value="{Binding GreenValue, Mode=TwoWay}"/>
    <Slider Minimum="0" Maximum="255" Value="{Binding BlueValue, Mode=TwoWay}"/>
    <Slider Minimum="32" Maximum="80" Value="{Binding IconSize, Mode=TwoWay}"/>
    <Slider Minimum="0" Maximum="360" Value="{Binding Angle, Mode=TwoWay}"/>
</VerticalStackLayout>
// MainPage.xaml.cs ...
public partial class MainPage : ContentPage
{
    public MainPage(MainViewModel VM)
    {
        InitializeComponent();
        BindingContext = VM;
    }
}
// MainViewModel.cs ...
public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Color))]
    int _redValue = 128;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Color))]
    int _greenValue = 0;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Color))]
    int _blueValue = 0;

    [ObservableProperty]
    int _iconSize = 32;

    [ObservableProperty]
    int _angle = 0;

    public Color Color => Color.FromRgb(RedValue, GreenValue, BlueValue);
}

SVGtoTTF-New.gif

Full source code for the above: https://github.com/stephenquan/maui-svg-ttf

like image 22
Stephen Quan Avatar answered Dec 03 '25 23:12

Stephen Quan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!