Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Image can disappear when going back to the page. #14471

Open
ziomek64 opened this issue Apr 8, 2023 · 64 comments
Open

[Android] Image can disappear when going back to the page. #14471

ziomek64 opened this issue Apr 8, 2023 · 64 comments
Labels
area-controls-image Image control area-controls-shell Shell Navigation, Routes, Tabs, Flyout migration-compatibility Xamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convert platform/android 🤖 potential-regression This issue described a possible regression on a currently supported version., verification pending s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Milestone

Comments

@ziomek64
Copy link

ziomek64 commented Apr 8, 2023

Description

First things first please rename the title of this issue to the more correct one as I didn't know how to title it.

I'm experiencing a problem, might be xaml or shell related. The images dissapear when moving between tabs. If I have Image with its source set on the page on the moment the app is building and debugging it will stay there when changing tabs but when Image is loaded on runtime like when I pull data from API (being binded), (Or add image url in xaml and hot reload) it will dissapear when changing tabs.

When I switched .net run time for all platforms from .net 7 to .net 6 it seems to fix it so the issue might be related to that somehow? That's the only discovery I've made so far. Also this issue doesn't seem to persist on other platforms at least it doesn't on Windows. I don't have any apple devices.

Video shows that when adding an image before hand works fine when switching tabs. But there is a bug when adding an image in runtime. This doesn't happen on .net 6.

2023-03-24_09-20-38.mp4

Steps to Reproduce

  1. Make fresh project
  2. Try to add picture on runtime and switch tabs.

Link to public reproduction project repository

https://github.com/ziomek64/TabBarPhotoTest

Version with bug

7.0 (current)

Last version that worked well

6.0.424

Affected platforms

Android

Affected platform versions

Android

Did you find any workaround?

Use maui on .net 6

Relevant log output

No response

@ziomek64 ziomek64 added the t/bug Something isn't working label Apr 8, 2023
@jsuarezruiz jsuarezruiz added the area-controls-image Image control label Apr 10, 2023
@ghost ghost added the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label Apr 10, 2023
@jsuarezruiz jsuarezruiz added platform/android 🤖 area-controls-shell Shell Navigation, Routes, Tabs, Flyout and removed legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor labels Apr 10, 2023
@jsuarezruiz jsuarezruiz added this to the Backlog milestone Apr 10, 2023
@ghost
Copy link

ghost commented Apr 10, 2023

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

@janseris
Copy link

Isn't this XAML Hot Reload issue?

@ziomek64
Copy link
Author

Isn't this XAML Hot Reload issue?

I don't think so, because this happens when I have image source binded. When I pull data it works fine, but If I go from this page and come back the image is not there. But when a new image with new source is loaded it again works until I leave the page

@hartez hartez changed the title [Android] Image can dissapear when going back to the page. [Android] Image can disappear when going back to the page. Apr 10, 2023
@kirakosyan
Copy link

DevEx claims that the bug in their control is related to this. Information in the discussion can be useful
https://supportcenter.devexpress.com/ticket/details/t1155623/dxcollectionview-does-not-show-images-in-its-items-when-its-parent-tab-is-switched

@mtmgoliath
Copy link

I have managed to find a work-around.

I seems that when setting the URL to just a string property in the VM and binding the Image Source to it then the image disappears.

Instead set/bind to a property inside an object in the VM.

   //Create separate model
  public class Banner
  {
      public string Image { get; set; }
      public string Text { get; set; }
  }
  
  //In the VM create property for object
  public Banner HeadlineBanner { get; set; }
  
  
  //Then in the VM set properties on page appearing event
  [RelayCommand]
  public async void PageAppearing()
  {
      try
      {
          await Task.Run(async () =>
          {
              //Get details from api service
              var headlineBanner = new HeadlineBanner();
              headlineBanner.Image = _apiService.ImageUrl;
              headlineBanner.Text = _apiService.Text;
            
              //Set the object
              HeadlineBanner = headlineBanner;
          });

      }
      catch (Exception ex)
      {
          Crashes.TrackError(ex);
      }
  }

Then in the view bind to object's properties

<Image Source="{Binding HeadlineBanner.Image}" />
<Label Text="{Binding HeadlineBanner.Text}" />

I also found that I was having trouble if binding to a ContentView, instead of directly writing out the layout in the View.

Hopefully this helps get your app out the door!

@ziomek64
Copy link
Author

ziomek64 commented May 4, 2023

I have managed to find a work-around.

I seems that when setting the URL to just a string property in the VM and binding the Image Source to it then the image disappears.

Instead set/bind to a property inside an object in the VM.

   //Create separate model
  public class Banner
  {
      public string Image { get; set; }
      public string Text { get; set; }
  }
  
  //In the VM create property for object
  public Banner HeadlineBanner { get; set; }
  
  
  //Then in the VM set properties on page appearing event
  [RelayCommand]
  public async void PageAppearing()
  {
      try
      {
          await Task.Run(async () =>
          {
              //Get details from api service
              var headlineBanner = new HeadlineBanner();
              headlineBanner.Image = _apiService.ImageUrl;
              headlineBanner.Text = _apiService.Text;
            
              //Set the object
              HeadlineBanner = headlineBanner;
          });

      }
      catch (Exception ex)
      {
          Crashes.TrackError(ex);
      }
  }

Then in the view bind to object's properties

<Image Source="{Binding HeadlineBanner.Image}" />
<Label Text="{Binding HeadlineBanner.Text}" />

I also found that I was having trouble if binding to a ContentView, instead of directly writing out the layout in the View.

Hopefully this helps get your app out the door!

I honestly would prefer to stay at .net 6 and wait for the fix as I have too much to change. But somebody will use it for sure. I don't need it as I didn't use any new features from 7

@ziomek64
Copy link
Author

ziomek64 commented May 4, 2023

I have managed to find a work-around.

I seems that when setting the URL to just a string property in the VM and binding the Image Source to it then the image disappears.

Instead set/bind to a property inside an object in the VM.

   //Create separate model
  public class Banner
  {
      public string Image { get; set; }
      public string Text { get; set; }
  }
  
  //In the VM create property for object
  public Banner HeadlineBanner { get; set; }
  
  
  //Then in the VM set properties on page appearing event
  [RelayCommand]
  public async void PageAppearing()
  {
      try
      {
          await Task.Run(async () =>
          {
              //Get details from api service
              var headlineBanner = new HeadlineBanner();
              headlineBanner.Image = _apiService.ImageUrl;
              headlineBanner.Text = _apiService.Text;
            
              //Set the object
              HeadlineBanner = headlineBanner;
          });

      }
      catch (Exception ex)
      {
          Crashes.TrackError(ex);
      }
  }

Then in the view bind to object's properties

<Image Source="{Binding HeadlineBanner.Image}" />
<Label Text="{Binding HeadlineBanner.Text}" />

I also found that I was having trouble if binding to a ContentView, instead of directly writing out the layout in the View.

Hopefully this helps get your app out the door!

I honestly don't know how do they expect people to update to newer version when bugs like that make it impossible to use maui

@jonathanpeppers jonathanpeppers added the partner/hot-reload-xaml Issues impacting XAML Hot Reload experiences label May 5, 2023
@drasticactions drasticactions removed the partner/hot-reload-xaml Issues impacting XAML Hot Reload experiences label May 6, 2023
@drasticactions
Copy link
Contributor

I removed the Hot-Reload-Xaml tag, #14471 (comment)

This is not a Hot Reload bug.

@drasticactions
Copy link
Contributor

@ziomek64 I've tried reproducing this with your sample project, and in the MAUI repo, in code and with XAML Hot Reload, and it works every time. If I set it so the image loads dynamically, or through a binding, it stays loaded when switching tabs.

Can you create an example, in code, where this happens?

@drasticactions drasticactions added the s/needs-info Issue needs more info from the author label May 8, 2023
@ghost
Copy link

ghost commented May 8, 2023

Hi @ziomek64. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@ziomek64

This comment was marked as outdated.

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels May 8, 2023
@ziomek64
Copy link
Author

ziomek64 commented May 9, 2023

I updated repro repo https://github.com/ziomek64/TabBarPhotoTest to contain a real world sceneario. On the first page just pull down to refresh to change avatar and banner, then go to second page and come back. Both images are gone. Same on debug and release.

2023-05-09.17-35-46.mp4

@sellotape
Copy link

A slightly easier - though inspired by - workaround to @ziomek64's above:

It seems triggering a property-changed event on the property that the image Source is bound to is sufficient. Add this to your view model (or to a base of it to use it more widely):

#if ANDROID
public void Github14471Hack(string propertyName) => OnPropertyChanged(propertyName);
#endif

(I'm assuming you're using CommunityToolkit.Mvvm here; if not, fire the INotifyPropertyChanged.PropertyChanged event however you do currently).

In your page code-behind, initialize a _viewModel field in the constructor and override OnAppearing in your page:

#if ANDROID
protected override void OnAppearing()
{
    _viewModel.Github14471Hack(nameof(_viewModel.ImageUrl)); // Or whatever "ImageUrl" is for you.
    base.OnAppearing();
}
#endif

If you have a large image with controls overlaying it you will potentially see a flicker, but at the moment it's less of an evil than the image disappearing.

@oscore-john
Copy link

I am experiencing this too. I have tried both of the above workarounds, no joy.

I notice that it happens only with jpg but not with png. (actually, don't want to give bum steer. better to say: if happens consistently with jpg files loaded from an AWS s3 bucket, but not with this png: https://icons.veryicon.com/png/Movie%20%26%20TV/Danger%20Mouse/Danger%20Mouse%20In%20A%20Pickle.png

@oscore-john
Copy link

I found a workaround which did work for me.
image

@ziomek64
Copy link
Author

ziomek64 commented Feb 17, 2024

I face the same problem. It seems to happen even if the source is a local file.

Yes, i also got the problem with local images.

Same men , they even removed needs attention that xdd. What a joke

@zafrkaya
Copy link

any update on this very serious problem?

@ziomek64
Copy link
Author

any update on this very serious problem?

Still not fixed in new update? What a joke from Microsoft

@RobTF
Copy link

RobTF commented Feb 19, 2024

We sidestepped this by writing our own Image component using Skia. Not ideal but it has allowed us to release.

Something like

/// <summary>
/// Class which implements an image which can be loaded from a byte array.
/// </summary>
public sealed class DynamicImage : SKCanvasView
{
    /// <summary>
    /// The definition of the "Source" property.
    /// </summary>
    public static readonly BindableProperty SourceProperty =
        BindableProperty.Create(
            nameof(Source),
            typeof(byte[]),
            typeof(DynamicImage),
            null,
            propertyChanged: OnPropertyChanged);

    /// <summary>
    /// The definition of the "HorizontalImageAlignment" property.
    /// </summary>
    public static readonly BindableProperty HorizontalImageAlignmentProperty =
        BindableProperty.Create(
            nameof(HorizontalImageAlignment),
            typeof(HorizontalAlignment),
            typeof(DynamicImage),
            HorizontalAlignment.Left,
            propertyChanged: OnPropertyChanged);

    /// <summary>
    /// The definition of the "VerticalImageAlignment" property.
    /// </summary>
    public static readonly BindableProperty VerticalImageAlignmentProperty =
        BindableProperty.Create(
            nameof(VerticalImageAlignment),
            typeof(VerticalAlignment),
            typeof(DynamicImage),
            VerticalAlignment.Top,
            propertyChanged: OnPropertyChanged);

    /// <summary>
    /// The definition of the "Aspect" property.
    /// </summary>
    public static readonly BindableProperty AspectProperty =
        BindableProperty.Create(
            nameof(AspectProperty),
            typeof(Aspect),
            typeof(DynamicImage),
            Aspect.AspectFit,
            propertyChanged: OnPropertyChanged);

    private SKBitmap? _image;

    /// <summary>
    /// Gets or sets the path to the SVG image.
    /// </summary>
    public byte[] Source
    {
        get => (byte[])GetValue(SourceProperty);
        set => SetValue(SourceProperty, value);
    }

    /// <summary>
    /// Gets or sets the horizontal image alignment.
    /// </summary>
    public HorizontalAlignment HorizontalImageAlignment
    {
        get => (HorizontalAlignment)GetValue(HorizontalImageAlignmentProperty);
        set => SetValue(HorizontalImageAlignmentProperty, value);
    }

    /// <summary>
    /// Gets or sets the vertical image alignment.
    /// </summary>
    public VerticalAlignment VerticalImageAlignment
    {
        get => (VerticalAlignment)GetValue(VerticalImageAlignmentProperty);
        set => SetValue(VerticalImageAlignmentProperty, value);
    }

    /// <summary>
    /// Gets or sets the image aspect ratio.
    /// </summary>
    public Aspect Aspect
    {
        get => (Aspect)GetValue(AspectProperty);
        set => SetValue(AspectProperty, value);
    }

    /// <inheritdoc/>
    protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
    {
        var canvas = e.Surface.Canvas;
        canvas.Clear();

        if (_image == null)
        {
            // no graphics loaded yet...
            return;
        }

        var info = e.Info;

        var destRect = _image.Info.Rect;

        if (Aspect == Aspect.AspectFit)
        {
            destRect = info.Rect.AspectFit(_image.Info.Size);
        }
        else if (Aspect == Aspect.AspectFill)
        {
            destRect = info.Rect.AspectFill(_image.Info.Size);
        }
        else if (Aspect == Aspect.Fill)
        {
            destRect = info.Rect;
        }

        canvas.DrawBitmap(_image, destRect);
    }

    private static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is not DynamicImage @this)
        {
            return;
        }

        @this.InvalidateGraphics();
    }

    /// <summary>
    /// Forces a reload of all graphics and invalidates the canvas.
    /// </summary>
    private void InvalidateGraphics()
    {
        LoadGraphics();
        InvalidateSurface();
    }

    private void LoadGraphics()
    {
        // load and swap out
        try
        {
            if (Source is null)
            {
                _image?.Dispose();
                _image = null;
                return;
            }

            var image = SKBitmap.Decode(Source);
            _image?.Dispose();
            _image = image;
        }
        catch
        {
            _image = null;
            return;
        }
    }
}

@edelveyn
Copy link

edelveyn commented Feb 22, 2024

Errors like this are very disappointing =(
Especially considering that Xamarin support is ending in May 2024 (with the recommendation to migrate your Xamarin projects to MAUI)

Maybe it will be useful for someone:

I found a workaround which did work for me. изображение

A quick solution if you have a collection of objects and you are binding to a property of an element of that collection.
It is enough to write in the ViewModel class something like: Items = new ObservableCollection() in the place where the data is received/updated and fill in the data (using a cache or a variable to store the collection so as not to receive the data again if it does not change).

@vitorelli
Copy link

Did you try to use C# Markup instead of XAML?

I made some tests with it and it is working fine. Moreover, C# Markup will improve your overall code and performance experience.

https://dev.to/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f
https://dev.to/smartmanapps/creating-maui-uis-in-c-1adf

@ziomek64
Copy link
Author

Did you try to use C# Markup instead of XAML?

I made some tests with it and it is working fine. Moreover, C# Markup will improve your overall code and performance experience.

https://dev.to/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f https://dev.to/smartmanapps/creating-maui-uis-in-c-1adf

What if I want XAML? It's there for a reason

@RobTF
Copy link

RobTF commented Feb 25, 2024

Did you try to use C# Markup instead of XAML?

I made some tests with it and it is working fine. Moreover, C# Markup will improve your overall code and performance experience.

https://dev.to/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f https://dev.to/smartmanapps/creating-maui-uis-in-c-1adf

It shouldn't be any more performant as doesn't Xaml get transpiled into C# at build time?

As for using C# markup. I have never, ever seen hot reload for C# work in a production project of ours. At best it triggers a debugger exception suggesting it at least tried, at worst it just does nothing.

Naturally, the hello world MAUI template works for some changes but the moment you add nuget packages and go beyond the basics in my teams experience it stops functioning very quickly. We have never relied on it.

Don't get me wrong, the XAML hot reload isn't great either and controls have to behave themselves for it to work at all due to how state is managed but generally twiddling with margins etc. seems to work about 80% of the time.

@vitorelli
Copy link

vitorelli commented Feb 27, 2024

Did you try to use C# Markup instead of XAML?

I made some tests with it and it is working fine. Moreover, C# Markup will improve your overall code and performance experience.

https://dev.to/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f https://dev.to/smartmanapps/creating-maui-uis-in-c-1adf

It shouldn't be any more performant as doesn't Xaml get transpiled into C# at build time?

As for using C# markup. I have never, ever seen hot reload for C# work in a production project of ours. At best it triggers a debugger exception suggesting it at least tried, at worst it just does nothing.

Naturally, the hello world MAUI template works for some changes but the moment you add nuget packages and go beyond the basics in my teams experience it stops functioning very quickly. We have never relied on it.

Don't get me wrong, the XAML hot reload isn't great either and controls have to behave themselves for it to work at all due to how state is managed but generally twiddling with margins etc. seems to work about 80% of the time.

Please take note that XAML elements are only compiled to IL in release not in debug. Therefore, you would be benefit from avoiding transpile time and your debug experience will be enhanced by working with faster code.

https://learn.microsoft.com/en-us/dotnet/maui/xaml/xamlc?view=net-maui-8.0

Could you please provide any code that is not working for you? I have tested HotReload with advanced layout structure and it works fine. I would be glad to help you.

@ziomek64
Copy link
Author

Did you try to use C# Markup instead of XAML?

I made some tests with it and it is working fine. Moreover, C# Markup will improve your overall code and performance experience.

https://dev.to/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f https://dev.to/smartmanapps/creating-maui-uis-in-c-1adf

It shouldn't be any more performant as doesn't Xaml get transpiled into C# at build time?

As for using C# markup. I have never, ever seen hot reload for C# work in a production project of ours. At best it triggers a debugger exception suggesting it at least tried, at worst it just does nothing.

Naturally, the hello world MAUI template works for some changes but the moment you add nuget packages and go beyond the basics in my teams experience it stops functioning very quickly. We have never relied on it.

Don't get me wrong, the XAML hot reload isn't great either and controls have to behave themselves for it to work at all due to how state is managed but generally twiddling with margins etc. seems to work about 80% of the time.

Please take note that XAML elements are only compiled to IL in release not in debug. Therefore, you would be benefit from avoiding transpile time and your debug experience will be enhanced by working with faster code.

https://learn.microsoft.com/en-us/dotnet/maui/xaml/xamlc?view=net-maui-8.0

Could you please provide any code that is not working for you? I have tested HotReload with advanced layout structure and it works fine. I would be glad to help you.

I think I provided video with real world example with project GitHub, gotta update it to .net 8 though

@u-bheki
Copy link

u-bheki commented Feb 27, 2024

Here's another example of this issue from my angle where the source is an image stream: https://github.com/u-bheki/ImageIssue14471

ImageIssue14471.webm

@ziomek64
Copy link
Author

Here's another example of this issue from my angle where the source is an image stream: https://github.com/u-bheki/ImageIssue14471

ImageIssue14471.webm

I have same exact issue in some places in my app. Can you show xaml for this collection view?

@u-bheki
Copy link

u-bheki commented Feb 27, 2024

Here's another example of this issue from my angle where the source is an image stream: https://github.com/u-bheki/ImageIssue14471

ImageIssue14471.webm

I have same exact issue in some places in my app. Can you show xaml for this collection view?

It's on the repo I linked to above: https://github.com/u-bheki/ImageIssue14471/blob/master/ImageIssue14471/Page1.xaml

@ziomek64
Copy link
Author

ziomek64 commented Mar 13, 2024

Still not fixed, good job microsoft, almost 1 year anniversary <3

@u-bheki
Copy link

u-bheki commented Mar 18, 2024

We sidestepped this by writing our own Image component using Skia. Not ideal but it has allowed us to release.

Something like

/// <summary>
/// Class which implements an image which can be loaded from a byte array.
/// </summary>
public sealed class DynamicImage : SKCanvasView
{
    /// <summary>
    /// The definition of the "Source" property.
    /// </summary>
    public static readonly BindableProperty SourceProperty =
        BindableProperty.Create(
            nameof(Source),
            typeof(byte[]),
            typeof(DynamicImage),
            null,
            propertyChanged: OnPropertyChanged);

    /// <summary>
    /// The definition of the "HorizontalImageAlignment" property.
    /// </summary>
    public static readonly BindableProperty HorizontalImageAlignmentProperty =
        BindableProperty.Create(
            nameof(HorizontalImageAlignment),
            typeof(HorizontalAlignment),
            typeof(DynamicImage),
            HorizontalAlignment.Left,
            propertyChanged: OnPropertyChanged);

    /// <summary>
    /// The definition of the "VerticalImageAlignment" property.
    /// </summary>
    public static readonly BindableProperty VerticalImageAlignmentProperty =
        BindableProperty.Create(
            nameof(VerticalImageAlignment),
            typeof(VerticalAlignment),
            typeof(DynamicImage),
            VerticalAlignment.Top,
            propertyChanged: OnPropertyChanged);

    /// <summary>
    /// The definition of the "Aspect" property.
    /// </summary>
    public static readonly BindableProperty AspectProperty =
        BindableProperty.Create(
            nameof(AspectProperty),
            typeof(Aspect),
            typeof(DynamicImage),
            Aspect.AspectFit,
            propertyChanged: OnPropertyChanged);

    private SKBitmap? _image;

    /// <summary>
    /// Gets or sets the path to the SVG image.
    /// </summary>
    public byte[] Source
    {
        get => (byte[])GetValue(SourceProperty);
        set => SetValue(SourceProperty, value);
    }

    /// <summary>
    /// Gets or sets the horizontal image alignment.
    /// </summary>
    public HorizontalAlignment HorizontalImageAlignment
    {
        get => (HorizontalAlignment)GetValue(HorizontalImageAlignmentProperty);
        set => SetValue(HorizontalImageAlignmentProperty, value);
    }

    /// <summary>
    /// Gets or sets the vertical image alignment.
    /// </summary>
    public VerticalAlignment VerticalImageAlignment
    {
        get => (VerticalAlignment)GetValue(VerticalImageAlignmentProperty);
        set => SetValue(VerticalImageAlignmentProperty, value);
    }

    /// <summary>
    /// Gets or sets the image aspect ratio.
    /// </summary>
    public Aspect Aspect
    {
        get => (Aspect)GetValue(AspectProperty);
        set => SetValue(AspectProperty, value);
    }

    /// <inheritdoc/>
    protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
    {
        var canvas = e.Surface.Canvas;
        canvas.Clear();

        if (_image == null)
        {
            // no graphics loaded yet...
            return;
        }

        var info = e.Info;

        var destRect = _image.Info.Rect;

        if (Aspect == Aspect.AspectFit)
        {
            destRect = info.Rect.AspectFit(_image.Info.Size);
        }
        else if (Aspect == Aspect.AspectFill)
        {
            destRect = info.Rect.AspectFill(_image.Info.Size);
        }
        else if (Aspect == Aspect.Fill)
        {
            destRect = info.Rect;
        }

        canvas.DrawBitmap(_image, destRect);
    }

    private static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is not DynamicImage @this)
        {
            return;
        }

        @this.InvalidateGraphics();
    }

    /// <summary>
    /// Forces a reload of all graphics and invalidates the canvas.
    /// </summary>
    private void InvalidateGraphics()
    {
        LoadGraphics();
        InvalidateSurface();
    }

    private void LoadGraphics()
    {
        // load and swap out
        try
        {
            if (Source is null)
            {
                _image?.Dispose();
                _image = null;
                return;
            }

            var image = SKBitmap.Decode(Source);
            _image?.Dispose();
            _image = image;
        }
        catch
        {
            _image = null;
            return;
        }
    }
}

I had planned to look into this a bit more and now after doing so, switching to Skia does indeed resolve the issue I was experiencing.

My switch was simpler that your, just switched to using the SKBitmapImageSource as opposed to the standard StreamImageSource.

From:

MemoryStream imageDecodeStream = new(imageBytes);
return ImageSource.FromStream(() => imageDecodeStream);

To

return new SKBitmapImageSource
{
    Bitmap = SKBitmap.Decode(imageBytes)
};
skia.mp4

@RobTF
Copy link

RobTF commented Mar 18, 2024

Nice one! Yes we used the byte array source as it fit our use case better, but as you suggest there are a lot of ways to get the image data into the control.

In any case, this sort of fix should not be required, especially for a framework which is three major versions in. 🙄

@s3ttingQ
Copy link

Yes, @u-bheki saved me a lot of pain. This works great !

If somebody need to cast from ImageSource it like I did :

private SKBitmapImageSource? CastToSkBitmapImageSource(ImageSource imgSource)
{
    StreamImageSource? streamImageSource = imgSource as StreamImageSource;

//you can change it a little bit with 'using stream' to dispose the stream and await the task
    var streamTask = streamImageSource.Stream?.Invoke(CancellationToken.None);
    var stream = streamTask.GetAwaiter().GetResult();

    SKBitmap skBitmap = SKBitmap.Decode(stream);
    stream.Dispose();

     SKBitmapImageSource skBitmapImageSource = new() { Bitmap = skBitmap };

    return skBitmapImageSource;
}

or if you want to get it from image resource location instead of the stream like @u-bheki

            SKBitmapImageSource sk = new();
            Assembly assembly = GetType().GetTypeInfo().Assembly;
            using (Stream stream = assembly.GetManifestResourceStream("Path.To.Image.png"))
            {
                sk = new() { Bitmap = SKBitmap.Decode(stream) };
            }

I am new to programming and to GIT so feel free to judge me :D
Hope this helps

@vitorelli
Copy link

vitorelli commented Mar 24, 2024

Local images works with Image control. However, it looks like to be still required to use FFImageLoading to address external images.

// TODO: CACHING https://github.com/dotnet/runtime/issues/52332

@Redth and @jonathanpeppers , It looks like the implementation existed on Xamarin:

https://github.com/xamarin/Xamarin.Forms/blob/bbcb5dbf5171a8e40b392d738f227868dccd398a/Xamarin.Forms.Core/UriImageSource.cs#L129

jonathanpeppers/glidex#106 (comment)

@vitorelli
Copy link

vitorelli commented Mar 24, 2024

@ziomek64 please try to use FFImageLoading instead of Image.

@zippo227
Copy link

zippo227 commented Mar 25, 2024

I have issues with images going missing in Android. As a work around, I am saving a cache of the image or font image as they have issues.

// You'll want to clear out the cache using mImgStream?.Dispose() if you change the source.
private Stream mImgStream;

private async Task RefreshImageSource()
{
	if(mImgStream != null)
	{
		mImgStream.Position = 0;

		MemoryStream sourceCopy = new MemoryStream();
		await mImgStream.CopyToAsync(sourceCopy);

		mImgStream.Position = 0;
		sourceCopy.Position = 0;
		Source = ImageSource.FromStream(() => sourceCopy);
	}
}

@coop-tim
Copy link

coop-tim commented May 2, 2024

This seems to not happen if the image is within a subcomponent of a BindableLayout

@vitorelli
Copy link

@malsabi
Copy link

malsabi commented May 17, 2024

I'm still facing this on Android even when using UraniumUI navigating to page2 and coming back to page1 icons are disappeared, I even tried to create my own custom ImageButton the icons disappear for no reason when navigating back to the page.

@malsabi
Copy link

malsabi commented May 20, 2024

Yes, @u-bheki saved me a lot of pain. This works great !

If somebody need to cast from ImageSource it like I did :

private SKBitmapImageSource? CastToSkBitmapImageSource(ImageSource imgSource)
{
    StreamImageSource? streamImageSource = imgSource as StreamImageSource;

//you can change it a little bit with 'using stream' to dispose the stream and await the task
    var streamTask = streamImageSource.Stream?.Invoke(CancellationToken.None);
    var stream = streamTask.GetAwaiter().GetResult();

    SKBitmap skBitmap = SKBitmap.Decode(stream);
    stream.Dispose();

     SKBitmapImageSource skBitmapImageSource = new() { Bitmap = skBitmap };

    return skBitmapImageSource;
}

or if you want to get it from image resource location instead of the stream like @u-bheki

            SKBitmapImageSource sk = new();
            Assembly assembly = GetType().GetTypeInfo().Assembly;
            using (Stream stream = assembly.GetManifestResourceStream("Path.To.Image.png"))
            {
                sk = new() { Bitmap = SKBitmap.Decode(stream) };
            }

I am new to programming and to GIT so feel free to judge me :D Hope this helps

Is there a way for FontImageSource ?

@devhls
Copy link

devhls commented May 23, 2024

I can reproduce the same issue with local fontawesome icons:
I use BoolToObjectConverter from MAUI Community Toolkit to switch icons on click. If you click and navigate forward and back the icon disappears. Click again and the next icon appears again.

 <Image Source="{FontImageSource FontFamily=fontawesome, Glyph={Binding Source={x:Reference ToolkitCommunityExpander}, Path=IsExpanded, Converter={StaticResource ExpanderIconConverter}}}">
 </Image>

@malsabi
Copy link

malsabi commented May 23, 2024

I can reproduce the same issue with local fontawesome icons: I use BoolToObjectConverter from MAUI Community Toolkit to switch icons on click. If you click and navigate forward and back the icon disappears. Click again and the next icon appears again.

 <Image Source="{FontImageSource FontFamily=fontawesome, Glyph={Binding Source={x:Reference ToolkitCommunityExpander}, Path=IsExpanded, Converter={StaticResource ExpanderIconConverter}}}">
 </Image>

I Found a workaround for this:

When the component or the control your hosting the Image on is Unloaded (event) you set the Image.Source to null and then re set it back to its original value. So when you navigate back it will be loaded and force to trigger property changed for the Image.Source

@devhls
Copy link

devhls commented Jun 6, 2024

@malsabi thanks for the advice. BTW I used another workaround - I replaced those Images with Labels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-image Image control area-controls-shell Shell Navigation, Routes, Tabs, Flyout migration-compatibility Xamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convert platform/android 🤖 potential-regression This issue described a possible regression on a currently supported version., verification pending s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests