Specify the color of a SVG image in .NET Maui - xaml

.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?

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>

Maybe you could use skiasharp. I give some examples.
First add Nuget, such as SkiaSharp.Views.Maui.Controls, SkiaSharp.Svg.
In the xaml, define a SKCanvasView. The PaintSurface event handler is where you do all your drawing.
<StackLayout>
<skiact:SKCanvasView WidthRequest="500" HeightRequest="500" x:Name="mycanvasview" PaintSurface="mycanvasview_PaintSurface">
</skiact:SKCanvasView>
</StackLayout>
In the .cs file, implement mycanvasview_PaintSurface method. Add some code like this:
private void mycanvasview_PaintSurface(object sender, SkiaSharp.Views.Maui.SKPaintSurfaceEventArgs e)
{
SKImageInfo info = e.Info;
SKSurface surface = e.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
Stream stream = LoadStream(typeof(MainPage),"myfile.svg");
SKSvg svg = new SKSvg();
svg.Load(stream);
using (var paint = new SKPaint())
{
paint.ColorFilter = SKColorFilter.CreateBlendMode(
SKColors.Yellow,
SKBlendMode.SrcIn);
canvas.DrawPicture(svg.Picture ,paint);
}
}
private static Stream LoadStream(Type type, string v)
{
Assembly assembly = type.GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream(v);
return stream;
}
You svg file will change the color.
I hope my answer could help you.

Related

Xamarin.Forms loading Xaml with LoadFromXaml is not creating contained objects for a StackLayout

I'm trying to add to an existing xamarin.forms page, chunks of xaml that will be generated dynamically.
I'm starting my research using the following example from the official docs.
Everything works fine there, but when I try to change the string with the button def for a string with the stacklayout with the button inside, only the stacklayout is inflated, with no children at all.
That's weird, because you can inflate a full page in the example, but it looks like I'm missing something here.
Any advice about how to use LoadFromXaml for partial composite objects?
// MainPage.xaml.cs
void OnLoadButtonClicked(object sender, EventArgs e)
{
string navigationButtonXAML = "<StackLayout><Button Text=\"Navigate\" /></StackLayout>";
var sl = new StackLayout().LoadFromXaml(navigationButtonXAML);
_stackLayout.Children.Add(sl);
}
From the official doc, it's only using LoadFromXaml for single view or a complete contentPage, I also tried LoadFromXaml for layout, and it's loading the layout without its children. For loading layout with children, I'm try debugging with source code, will update later, and as a workaround, you can use layout with contentview like this:
string navigationStackLayoutXAML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ContentView xmlns=\"http://xamarin.com/schemas/2014/forms\" xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\" xmlns:d=\"http://xamarin.com/schemas/2014/forms/design\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"d\" x:Class=\"LoadRuntimeXAML.CustomerViewDemo\"> <ContentView.Content> <StackLayout > <Label Text=\"Hello Xamarin.Forms!\" /> <Button Text=\"SECONDB\"/> </StackLayout> </ContentView.Content> </ContentView>";
ContentView contentView = new ContentView().LoadFromXaml(navigationStackLayoutXAML);
_stackLayout.Children.Add(contentView);
Although it's xaml is a little bit more complex, but it can use predefined attributes just as in .xaml, also, elements inside the contentView will be accessible through:
Button secondB = contentView.FindByName<Button>("secondB");

View a specific image from the complete gallery path without browsing

I'm trying:
<Image Source="/storage/emulated/0/Pictures/Test/IMG_20200408_085036.jpg"/>
No effect.
File imageSpc = new File('/storage/emulated/0/Pictures/Test/IMG_20200408_085036.jpg');
var imageSpc = new File('/storage/emulated/0/Pictures/Test/IMG_20200408_085036.jpg');
Error, cannot create static variable for type File or var.
<Image x:Name="fotka" Aspect="AspectFit" HeightRequest = "50"
WidthRequest = "60"/>
fotka.Source = ImageSource.FromFile("/storage/emulated/0/Pictures/Test/IMG_20200408_085036.jpg");
No effect
Is possible to show specific image from gallery in XAML or at least from the code
I did enable ADB notification option in device.
In The moment image to be displayed compiler show error:
How to fix this Error?
According to your description, you want to display image using gallery path, if yes, please take a look the following code:
Firstly, you need to get image path from gallery in Android or ios.
Create interface IFileSystem in PCL(Forms)
public interface IFileSystem
{
string GetGalleryImage();
}
Then implementing this interface in platform, the example is in Android.
[assembly: Dependency(typeof(FileSystemImplementation))]
namespace demo3.Droid
{
public class FileSystemImplementation : IFileSystem
{
public string GetGalleryImage()
{
var filePath = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures);
var path = System.IO.Path.Combine(filePath.AbsolutePath, "Test/image1.jpg");
return path;
}
}
}
Add one Image control in PCL(Forms), name image1, using DependencyService to get image path.
<Button
x:Name="btn1"
Clicked="Btn1_Clicked"
Text="load image" />
<Image
x:Name="image1"
HeightRequest="100"
WidthRequest="100" />
private void Btn1_Clicked(object sender, EventArgs e)
{
var path = DependencyService.Get<IFileSystem>().GetGalleryImage();
image1.Source = ImageSource.FromFile(path);
}
This is article about DependencyService, you can take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/introduction
Update:
I found, each image before show in activity must be resized to max size 4096 x 4096 px otherwise image not shown, without any errors message.

Prevent system FontSize change from affecting the size in the Xamarin Application

So I noticed that an App I made will look messed up if the Font size in the users phone is changed to above medium, I googled a bit found many unanswered or answered but not to the point of the same question.
I want to be able to do that in the PCL class if possible , if not possible then the most interesting platform for me is android so a fix specific for android would do.
Here is a sample of my Xaml code so you can get a reference:
<Label Text="STORE" FontSize="23" HeightRequest="40" WidthRequest="212" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"></Label>
So to be clear the question is how do I prevent the system from overriding my fontsize which is 23 in this case?
Thanks
SImply add this in your MainActivity after OnCreate.
#region Font size change Prevent
public override Resources Resources
{
get
{
var config = new Configuration();
config.SetToDefaults();
return CreateConfigurationContext(config).Resources;
}
}
#endregion Font size change Prevent
For those who still struggle how to disable accessibility font scaling on Android.
You need to create custom renderer for label, button and common input controls like this:
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Label), typeof(MyApp.Droid.Renderers.LabelRendererDroid))]
namespace MyApp.Droid.Renderers
{
class LabelRendererDroid : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null) return;
Control.SetTextSize(Android.Util.ComplexUnitType.Dip, (float)e.NewElement.FontSize);
}
}
}
For Xamarin picker controls there is no FontSize property, so we can add it to App class:
public static double NormalFontSize => Device.GetNamedSize(NamedSize.Medium, typeof(Picker));
and then utilize it in picker renderer:
Control.SetTextSize(Android.Util.ComplexUnitType.Dip, (float)App.NormalFontSize);
Also by changing this NormalFontSize property we can set whatever desired font size for picker, as it is not available without renderer.
Have you seen the items in chapter 5 of this book?
https://developer.xamarin.com/guides/xamarin-forms/creating-mobile-apps-xamarin-forms/
Although not an exact answer, depending on what you are attempting to do you might be able to use the example at the end of the chapter modified to allow for a "Max" font size. All from within the PCL.
It's worth noting however that the inability to scale the font to a big size could be an indication of accessibility problem.

The given System.Uri cannot be converted into a Windows.Foundation.Uri

I'm trying to programmatically load a BitmapImage in a XAML Metro app. Here's my code:
var uri = new Uri("/Images/800x600/BackgroundTile.bmp", UriKind.RelativeOrAbsolute);
var imageSource = new BitmapImage(uri);
The second line crashes with a System.ArgumentException:
The given System.Uri cannot be converted into a Windows.Foundation.Uri. Please see http://go.microsoft.com/fwlink/?LinkID=215849 for details.
The link just goes to the MSDN home page, so it's no use.
I've also tried removing the leading /, in case WinRT has different expectations about relative URIs, but I still get the same exception.
Why am I getting this exception for what seems to be a perfectly valid URI?
In the Consumer Preview, the correct URL format has apparently changed to ms-appx:/Images/800x600/BackgroundTile.bmp
Judging from the documentation for Windows.Foundation.Uri, it looks like WinRT doesn't support relative URIs. I tried a pack:// URI, but that gave me a UriFormatException, so apparently that's not the way to do it in WinRT either.
I found the answer on this thread: MS invented yet another URI format for WinRT resources. This works:
new Uri("ms-resource://MyAssembly/Images/800x600/BackgroundTile.bmp")
Note that you don't add your actual assembly name -- the MyAssembly part is literal text.
You will need to use the page's BaseUri property or the image control's BaseUri property like this:
//using the page's BaseUri property
BitmapImage bitmapImage = new BitmapImage(this.BaseUri,"/Images/800x600/BackgroundTile.bmp");
image.Source = bitmapImage;
or
//using the image control's BaseUri property
image.Source = new BitmapImage(image.BaseUri,"/Images/800x600/BackgroundTile.bmp");
you can see the reason and solution here
In case you're still having issues or are being asked to find an app to open the link, are you trying to use a WebView? If so, try using ms-appx-web instead of ms-appx.
An example:
this.webBrowser.Navigate(new Uri("ms-appx-web:///level/level/level/index.html"));
Also note the lack of the URIKind parameter--evidently not needed at all in these instances.
(I believe you may need to vary the leading forward slashes depending on your reference)
This would work in XAML but would not work in code... so each control or page has a BaseUri property which you can use to build the proper uri for assets... here is an example:
imageIcon.Source = new BitmapImage(new Uri(this.BaseUri, "Assets/file.gif"));
// Or use the base uri from the imageIcon, same thing
imageIcon.Source = new BitmapImage(new Uri(imageIcon.BaseUri, "Assets/file.gif"));
also you would need to set the build action to "Content" rather than Embedded Resource... otherwise you need to use the ms-resource:// protocol.
I know this is old but I hope this helps. I wrote this in XAML and it worked for me. The Ide I'm using is vs-2015 with win-10.
<Window>
<Grid>
<Grid.Background>
<ImageBrush ImageSource="NameOfYourImage.JPG or any Image type"/>
</Grid.Background>
<GroupBox x:Name="groupBox" Header="GroupBox" HorizontalAlignment="Left" Height="248" Margin="58,33,0,0" VerticalAlignment="Top" Width="411">
<GroupBox.Background>
<ImageBrush ImageSource="NameOfYourImage.JPG or any Image type"/>
</GroupBox.Background>
</GroupBox>
</Grid>
</Window>
MVVM ;)
public static ImageSource SetImageSource(string uriPath)//.com/image or some path
{
// this method very good for mvvm ;)
try
{
//In Model - public ImageSource AccountPhoto { get; set; }
//AccountPhoto = SetImageSource("some path");
return return new BitmapImage()
{
CreateOptions = BitmapCreateOptions.IgnoreImageCache,
UriSource = new Uri(uriPath)
};
}
catch
{
Debug.WriteLine("Bad uri, set default image.");
return return new BitmapImage()
{
CreateOptions = BitmapCreateOptions.IgnoreImageCache,
UriSource = new Uri("ms-appx:///Avatar/Account Icon.png")
};
}
}

How to dynamically load internal xaml in Silverlight 3 at runtime?

I have a few internal Canvas xaml files which I want to load dynamically at runtime. For example, I want to display the Canvas in a page. However, I cannot seem to get it to work. I've tried using XamlReader, Application.LoadComponent, and using an XDocument; all to no avail. I cannot seem to find the best practice for this on-line either.
The Canvases are stored like this: MyApp/Resources/Logos/Logo1.xaml. I'm not sure if Logo1.xaml should have a build action of "Component" or "Resource". In any case, using the URI of "MyApp;components/Resources/Logos/Logo1.xaml" seems to be correct, but Application.LoadComponent gets an XamlParseException at Line 0 Position 0.
Here's a pseudo-example of Logo1.xaml:
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="286.233" Height="143.425">
<Canvas>
<Path />
<Path />
<Path />
<Path />
</Canvas>
</Canvas>
The data for the path elements has been omitted for brevity's sake.
Any ideas?
I got this to work using build action "Resource" and the XamlLoader with a ContentControl:
var resourceName = string.Format("MyApp;component/Resources/Logos/{0}.xaml", logoName);
var uri = new Uri(resourceName, UriKind.Relative);
var streamResourceInfo = Application.GetResourceStream(uri);
string xaml = null;
using (var resourceStream = streamResourceInfo.Stream)
{
using (var streamReader = new StreamReader(resourceStream))
{
xaml = streamReader.ReadToEnd();
}
}
Canvas canvas = XamlReader.Load(xaml) as Canvas;
this.contentControl.Content = canvas;
The accepted answer is not intuitive to me, but may suit some people.
I generally feel more comfortable with System.IO.File.WriteAllText and System.IO.File.ReadAllText. I borrowed a code piece from MSDN. For a test, just create a new Canvas at runtime:
Canvas newCanvas = new Canvas()
Then, just write it out, with File.WriteAllText, simple too, avoiding all the URI stuff, which as a beginner I find confusing. Lastly, just read it back in as above. Done. This proves you can simply just read in some valid code for a canvas from a text file, and it can be loaded dynamically.
private void Button2_Click(object sender, RoutedEventArgs e)
{
Canvas newCanvas = new Canvas();
newCanvas.Name = "newCanvas";
string savedCanvas = XamlWriter.Save(newCanvas);
File.WriteAllText("savedCanvas.txt", savedCanvas);
savedCanvas = File.ReadAllText("savedCanvas.txt");
// Load the canvas
StringReader stringReader2 = new StringReader(savedCanvas);
XmlReader xmlReader2 = XmlReader.Create(stringReader2);
Canvas newCreatedCanvas = (Canvas)XamlReader.Load(xmlReader2);
this.Content = newCreatedCanvas;
}
The ideas are from MSDN as below:
XamlReader.Load Method (XmlReader)
.NET Framework 4
Reads the XAML input in the specified XmlReader and returns an object that is the root of the corresponding object tree.
Namespace: System.Windows.Markup
Assembly: PresentationFramework (in PresentationFramework.dll)