AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.4k stars 2.2k forks source link

Loading Bitmap using Relative File Path #2183

Closed jeffreye closed 3 months ago

jeffreye commented 5 years ago

Before #2104, it is able to use the relative path for Image

<Image Width="16" Height="16" Source="Images/File.png" />

(mak sure Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)); first)

, where File.png is a Content that will copy to output directory. Currently, It throws a error of :

System.ArgumentException: Relative uris for 'resm' scheme aren't supported; resm:SampleApp.MainWindow.xaml?assembly=SampleApp uses resm.

However, it still is able to load the image with relative path by

Bitmap image = new Bitmap("Images/File.png");

Gillibald commented 5 years ago

I don't think supporting this in the way you used it was intended. We need to introduce the file scheme to support loading assets from file. Currently the only way to load assets from file is in code. You could load the image in some viewmodel or in your code behind that should work.

When you load the file from your application folder why don't you embed that file into your application and load it from resources?

jeffreye commented 5 years ago

Considering the assembly has to be loaded into memory before it can be run, I think that's bad. Shipping the resource as a separate standalone file would be a better choice, especially for a large file.

kekekeks commented 5 years ago

We might add Host handling for file: scheme. So app-relative paths would look like file://appdir/Images/File.png. I think hostname in file:// is never used, so it should be fine.

nicolasr75 commented 5 years ago

We used to bind Image controls to relative paths to files. This was important for us because users wanted to be able to exchange the files with their own ones (logos etc. for "branding" the software). Therefore I strongly vote for supporting files with relative paths again!

Gillibald commented 5 years ago

I would suggest using a custom converter for this until this is implemented.

nicolasr75 commented 5 years ago

Could you provide a short sketch or a link to some resource showing how to do this?

Gillibald commented 5 years ago

Just implement IValueConverter that converts a string to Bitmap.

This ctor needs to be called within the converter: return new Bitmap(path);

Then use that converter in XAML.

nicolasr75 commented 5 years ago

Ok, thanks.

Gillibald commented 5 years ago

I would introduce static fields for the image paths and use them in a binding for the corresponding image control.

https://stackoverflow.com/questions/42159000/xaml-converter-for-static-resource

Just some idea how to solve this. A bit overhead but it should work until we can properly support file: scheme.

Not sure if that works with Avalonia. With WPF this is supported:

Source={Binding Source={StaticResource myImagePathInResources}, Converter={StaticResource myBitmapConverter} }

nicolasr75 commented 5 years ago

Ok, I ended up with a mix of property and static resource binding. As a reference, here is my working demo code. Thanks again for the quick help!

xmlns:clr="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MyNamespace"
...
<Window.Resources>
    <clr:String x:Key="logoPath">Graphics/StaticLogo.png</clr:String>
    <local:PathToBitmapConverter x:Key="pathToBitmapConverter"/>
</Window.Resources>
...
<Image Source="{Binding Source={StaticResource logoPath}, Converter={StaticResource pathToBitmapConverter}}"></Image>
<Image Source="{Binding DynamicImage, Converter={StaticResource pathToBitmapConverter}}"></Image>

And the converter class

public class PathToBitmapConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string path = (string)value;

        return new Bitmap(path);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}