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")
};
}
}
Related
.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.
When I try to use the method to inflate my XAML:
MenuBarItem item = new().LoadFromXaml("<MenuBarItem Text=\"Session\"><MenuFlyoutItem Text=\"New\"/><MenuFlyoutItem Text=\"Save\"/><MenuFlyoutItem Text=\"Load\"/></MenuBarItem>");
the MenuBarItem is created and Text properly assigned but all the MenuFlyoutItems are ommited and not added to the menu.
After reading Load XAML at runtime documentation and particularly the "The LoadFromXaml method can be used to inflate any XAML" and the examples given, I assumed that I can throw any valid XAML into it - from a single button, to a DataTemplate of a ListView, a MenuBarItem for a menu, to a whole ContentPage and it should work. But it's not working in this case - I get Microsoft.Maui.Controls.Xaml.XamlParseException and System.Reflection.TargetInvocationException.
Is this behavior a bug or is documentation missing some details about loading XAMLs?
When I enclose the MenuBarItem in a ContentPage's MenuBarItems like this:
new ContentPage().LoadFromXaml("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ContentPage\r\n\txmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\r\n\tx:Class=\"LoadRuntimeXAML.CatalogItemsPage\"\r\n\tTitle=\"Catalog Items\">\r\n\t<ContentPage.MenuBarItems>\r\n\t\t<MenuBarItem Text=\"Session\">\r\n\t\t\t<MenuFlyoutItem\r\n\t\t\t\tText=\"New\"/>\r\n\t\t\t<MenuFlyoutItem\r\n\t\t\t\tText=\"Save\"/>\r\n\t\t\t<MenuFlyoutItem\r\n\t\t\t\tText=\"Load\"/>\r\n\t\t</MenuBarItem>\r\n\t</ContentPage.MenuBarItems>\r\n</ContentPage>");
it inflates without error and then when I assign elements from the inflated ContentPage to the MainPage's MenuBarItems they display well. But this is an ugly workaround because I don't need a whole ContentPage, just the MenuBarItem.
Your XAML is not complete, thus cannot be parsed.
What the ContentPage has, that your XAML lacks, is the various xmlns lines, that specify the XML elements used in the XAML.
I have not tested, but try replacing <MenuBarItem with
<MenuBarItem\r\n\txmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\r\n\txmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\r\n
Adapt as needed. Any whitespace can be used anywhere \r\n is shown.
If it doesn't work, also prefix with:
<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n
But I believe that is optional.
As an aside, anything that can be done in XAML, can instead be done in C#. C# markup.
C#, being a complete computational language, can often create dynamic UI more easily than XAML, if you are building a UI that depends on different conditions.
A convenient approach in C#, is to define "helper" methods, that take whatever parameters you want, and creates a specific element. That you add to a given parent element, either via C# markup, or methods of a parent layout class.
Its easy to write helper methods that call other helper methods, to build up a whole layout to your specs, controlled at each step by the parameters that matter to you.
At the top level, you might end up with code like this:
// use custom helper methods and methods of "Grid" class.
Grid grid = MyCreateGrid();
grid.Children.Add(MyCreateRowLabel(text), 1, 0);
grid.Children.Add(
// OR use C# markup
new StackLayout
{
Children =
{
new Label().Text("Code:"),
...
}
},
1, 1
);
...
From the official document, it's only using LoadFromXaml for single view or a complete contentPage. I also tried LoadFromXaml for <MenuBarItem Text=\"Session\"><MenuFlyoutItem Text=\"New\"/><MenuFlyoutItem Text=\"Save\"/><MenuFlyoutItem Text=\"Load\"/></MenuBarItem>, and just you said that:
the MenuBarItem is created and Text properly assigned but all the MenuFlyoutItems are ommited and not added to the menu.
But you can achieve it by doing this:
MainPage.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp_loadXaml.MainPage"
x:Name="contentPage">
<Button Text="click" Clicked="Button_Clicked" HeightRequest="50"/>
</ContentPage>
MainPage.xaml.cs:
private void Button_Clicked(object sender, EventArgs e)
{
var xaml = "<MenuBarItem Text=\"Session\"></MenuBarItem>";
var xaml1 = "<MenuFlyoutItem Text=\"New\"/>";
var xaml2 = "<MenuFlyoutItem Text=\"Save\"/>";
var xaml3 = "<MenuFlyoutItem Text=\"Load\"/>";
MenuFlyoutItem menuFlyoutItem_1 = new MenuFlyoutItem().LoadFromXaml(xaml1);
MenuFlyoutItem menuFlyoutItem_2 = new MenuFlyoutItem().LoadFromXaml(xaml2);
MenuFlyoutItem menuFlyoutItem_3 = new MenuFlyoutItem().LoadFromXaml(xaml3);
MenuBarItem item = new MenuBarItem();
item.LoadFromXaml(xaml);
item.Add(menuFlyoutItem_1);
item.Add(menuFlyoutItem_2);
item.Add(menuFlyoutItem_3);
contentPage.MenuBarItems.Add(item);
}
It works well.
I have a control that consists of a button and a textbox.
I wanted to set the input scope of the textbox, so I introduced a new dependency property:
public InputScope InputScope
{
get { return (InputScope)GetValue(InputScopeProperty); }
set { SetValue(InputScopeProperty, value); } // Notify prop change
}
public static readonly DependencyProperty InputScopeProperty =
DependencyProperty.Register(nameof(InputScope), typeof(InputScope), typeof(SearchControl), new PropertyMetadata(DependencyProperty.UnsetValue));
In XAML:
<controls:SearchControl InputScope="Number" /> <!-- etc... -->
(Obviously assigning it to the InputScope property of the textbox in the style of this custom control.)
My problem: While this works, the numeric keyboard gets shown when focused, but I have blue underline in the XAML, and also an error message: The TypeConverter for "InputScope" does not support converting from a string.
Is there a way to fix it without a dirty hack?
Is there a way to fix it without a dirty hack?
You could implement a type converter. Please refer to Tim Heuer's blog post for more information and an example:
Implementing a type converter in UWP XAML: http://timheuer.com/blog/archive/2017/02/15/implement-type-converter-uwp-winrt-windows-10-xaml.aspx
You may also want to read this:
WinRT Replacement for System.ComponentModel.TypeConverter
I got some strange black magic going on with my app.
I have defined an ImageBrush in a style dictionary:
<classes:MultiResImageChooser x:Key="MultiResImageChooser"/>
<ImageBrush x:Name="SplashScreenImageBrush"
ImageSource="{Binding SplashScreenResolutionImage, Source={StaticResource MultiResImageChooser}}"
Stretch="Fill" />`
The MultiResImageChooser class has a one simple property:
public class MultiResImageChooser
{
public BitmapImage SplashScreenResolutionImage
{
get
{
switch (ResolutionHelper.CurrentResolution)
{
case Resolutions.HD720p:
return new BitmapImage(new Uri("/Images/SplashScreenImage.Screen-720p.jpg", UriKind.Relative));
case Resolutions.WXGA:
return new BitmapImage(new Uri("/Images/SplashScreenImage.Screen-WXGA.jpg", UriKind.Relative));
case Resolutions.WVGA:
return new BitmapImage(new Uri("/Images/SplashScreenImage.Screen-WVGA.jpg", UriKind.Relative));
default:
throw new InvalidOperationException("Unknown resolution type");
}
}
}
}
SplashScreenImageBrush is binded to the background property of a Border element:
<Border x:Name="SplashScreen"
Background="{StaticResource SplashScreenImageBrush}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
So, the problem is that when I debug the application on an WP8 emulator or WP8 device everything works fine.
When launching the app without debugging, the Border background property is rendered White.
The image files are included in the project and have the Build Action set to Content.
Also, if I set the ImageSource directly to an image path, everything works.
So, the problem seems to be the MultiResImageChooser, but I do not know what could be wrong with it.
Any kind of help or hints will be greatly appreciated.
BTW, this issue does not get reproduced on w WP7.1 device and emulator.
My bet: ResolutionHelper.CurrentResolution doesn't work properly for some reason (timing issue?), so the "default" branch of your switch is executed. Your binding therefore fails, the brush doesn't get initialized, and you get a white color instead. From there, I'd start by confirming the execution of the "default" branch, for instance by putting a specific image instead of throwing an exception. Then, if my theory is right, look into the ResolutionHelper to understand what's going on.
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)