'How can I support a stream image in the IconImageSource property of toolbarItems?

I have an image that converts to a stream and is added to the IconImageSource attribute of toolbar items.

** Facing issues **

Set the IconImageSource property in ToolbarItems to a stream ImageSource. In Android, It raised issues like "Cannot access a closed stream, at System.IO.MemoryStream.get_Position()".

It does not display any image in toolbarItems on the iOS and UWP platforms.

I created two imageSource properties in the sample: OpenToolItem and CloseToolItem.

How can I assign stream value to OpenToolItem and CloseToolItem Property in a my application?

Please refer the code below.

My mainpage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<local:TestingControl  xmlns:local="clr-namespace:TestStreamControl" 
                       xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       x:Class="TestStreamControl.MainPage" OpenToolItem="https://aka.ms/campus.jpg" 
                       CloseToolItem="https://image.shutterstock.com/image-photo/bright-spring-view-cameo-island-600w-1048185397.jpg">
</local:TestingControl>

My mainpage.xaml.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace TestStreamControl
{
    public partial class MainPage : TestingControl
    {
        public MainPage()
        {
            InitializeComponent();
            Assembly assembly = Assembly.GetExecutingAssembly();
            Stream openStream = assembly.GetManifestResourceStream("TestStreamControl.Images.Meow2.jpg");
            var openSource = ImageSource.FromStream(() => openStream);
            OpenToolItem = openSource;

            Stream closeStream = assembly.GetManifestResourceStream("TestStreamControl.Images.lake_water_natural.jpg");
            var closeSource = ImageSource.FromStream(() => closeStream);
            CloseToolItem = closeSource;
        }
    }
}

TestingControl.cs

namespace TestStreamControl {

public partial class TestingControl : ContentPage
{
    private ToolbarItem ToolbarItemSource;
    public TestingControl()
    {
        InitializeComponent();
        AddToolbarItem();
    }
    #region Bindable Property
    public static readonly BindableProperty OpenToolItemProperty =
         BindableProperty.Create(nameof(OpenToolItem), typeof(ImageSource), typeof(TestingControl), default(ImageSource), BindingMode.Default, null, OnPropertyChanged);

    public static readonly BindableProperty CloseToolItemProperty =
              BindableProperty.Create(nameof(CloseToolItem), typeof(ImageSource), typeof(TestingControl), default(ImageSource), BindingMode.Default, null, OnPropertyChanged);

    public static readonly BindableProperty ItemStateProperty =
        BindableProperty.Create(nameof(ItemState), typeof(bool), typeof(TestingControl), false, BindingMode.TwoWay, null, OnItemStatePropertyChanged);
    #endregion

    #region Public Properties
    public ImageSource OpenToolItem
    {
        get { return (ImageSource)GetValue(OpenToolItemProperty); }
        set { this.SetValue(OpenToolItemProperty, value); }
    }

    public ImageSource CloseToolItem
    {
        get { return (ImageSource)GetValue(CloseToolItemProperty); }
        set { this.SetValue(CloseToolItemProperty, value); }
    }
    public bool ItemState
    {
        get { return (bool)GetValue(ItemStateProperty); }
        set { this.SetValue(ItemStateProperty, value); }
    }

    #endregion

    #region Property changed events
    private static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        (bindable as TestingControl).OnSourcePropertyChanged(oldValue, newValue);
    }
    private static void OnItemStatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        (bindable as TestingControl).OnSourcePropertyChanged(oldValue, newValue);
    }
    private void OnSourcePropertyChanged(object oldValue, object newValue)
    {
        UpdateToolbar();
    }

    #endregion

    private void AddToolbarItem()
    {
        this.ToolbarItemSource = new ToolbarItem
        {
            Text = "Open",
            Priority = int.MaxValue,
            Order = ToolbarItemOrder.Primary,
            IconImageSource = this.GetToolbarItem()
        };
        this.ToolbarItems.Add(this.ToolbarItemSource);
        this.ToolbarItemSource.Clicked += OnToolbarItemClicked;
    }

    private void OnToolbarItemClicked(object sender, EventArgs e)
    {
        if (ItemState)
        {
           message.Text = "Message sent";
        }
        else
        {
           message.Text = "Message Received";
            
        }

        this.ItemState = !this.ItemState;
        UpdateToolbar();
    }

    private ImageSource GetToolbarItem()
    {

        var internalOpenIcon = OpenToolItem ?? (Device.RuntimePlatform == Device.UWP ? "https://aka.ms/campus.jpg" : "https://aka.ms/campus.jpg");
        var internalCloseIcon = CloseToolItem ?? (Device.RuntimePlatform == Device.UWP ? "https://image.shutterstock.com/image-photo/bright-spring-view-cameo-island-600w-1048185397.jpg" : "https://image.shutterstock.com/image-photo/bright-spring-view-cameo-island-600w-1048185397.jpg");
        return this.ItemState ? internalCloseIcon : internalOpenIcon;
    }
    private void UpdateToolbar()
    {

        if (this.ToolbarItemSource != null)
        {
            if (OpenToolItem != null || CloseToolItem != null)
            {
                this.ToolbarItemSource.Text = this.ItemState ? "Close" : "Open";
                this.ToolbarItemSource.IconImageSource = this.GetToolbarItem();
            }
            if (OpenToolItem == null && CloseToolItem == null)
            {
                this.ToolbarItemSource.Text = this.ItemState ? "Close" : "Open";
                this.ToolbarItemSource.IconImageSource = this.GetToolbarItem();
            }
            else
            {
                // this.ToolbarItemSource.Text = this.ItemState ? "Close" : "Open";
                this.ToolbarItemSource.IconImageSource = this.GetToolbarItem();
            }
        }
    }
 }

}

TestingControl.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TestStreamControl.TestingControl">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Welcome to Xamarin.Forms!" x:Name="message"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>


Solution 1:[1]

The problem is these lines of code:

Stream openStream = assembly.GetManifestResourceStream("TestStreamControl.Images.Meow2.jpg");
var openSource = ImageSource.FromStream(() => openStream);

The GetManifestResourceStream is before the Imagesource.FromStream() call. That opens a stream, one time. (That stream is automatically closed at the end of the surrounding method, MainPage constructor. This later causes the error "Cannot access a closed stream".)

ImageSource requires code that will open the stream each time it is needed:

var openSource = ImageSource.FromStream(() =>
    {
        Stream openStream = assembly.GetManifestResourceStream("TestStreamControl.Images.Meow2.jpg");
        return openStream;
    });

The GetManifestResourceStream call is now inside the delegate code. That code gets called each time the ToolbarItem needs the stream.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ToolmakerSteve