You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms移除再添加Image控件后丢失或抛异常如何解决?

问题描述

我创建了一个简单的Xamarin.Forms示例项目,其中添加了Image控件,通过ImageSource.FromStream()方法以流形式加载图片资源,再将该控件从父布局移除后重新添加时出现异常,各平台表现如下:

  • Xamarin.Forms UWP:图片直接消失
  • Xamarin.Forms Android:抛出无法访问已关闭的流异常
  • Xamarin.Forms iOS:暂未测试

相关代码

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="ImageException.MainPage">

    <ContentPage.Content>
        <Grid x:Name="MainGrid">
            <Image x:Name="image" />
            <Button Text="Click_Me"
                    Clicked="Button_Clicked"
                    VerticalOptions="End" />
        </Grid>
    </ContentPage.Content>
</ContentPage>

C#:

namespace ImageException
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            var assembly = typeof(MainPage).Assembly;
            string path = "ImageException";

            var stream = assembly.GetManifestResourceStream($"{path}.Image.Image.png");
            image.Source = ImageSource.FromStream(() => stream);
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            MainGrid.Children.Remove(image);
            MainGrid.Children.Add(image);
        }
    }
}

请问如何实现Image控件从布局移除再添加后仍然可以正常显示?


解决方案

根因说明

异常的核心原因是Stream对象为单次消费资源:你提前创建了唯一的流实例传给ImageSource.FromStream(),首次加载图片时流会被读取,使用完成后会被Xamarin框架自动关闭释放。当你把Image控件从布局移除再重新添加时,控件会尝试重新从绑定的ImageSource中读取流数据,此时流已经处于关闭状态,就会出现对应平台的异常表现。

可选用的修复方案

方案1:委托内每次返回新的流(改动手最少)

不要提前创建流实例,在FromStream的委托中每次调用时都重新获取嵌入资源流,保证每次需要加载图片时都有可用的新流:

public MainPage()
{
    InitializeComponent();
    var assembly = typeof(MainPage).Assembly;
    string path = "ImageException";
    // 流实例放在委托内生成,每次调用都会返回新的可用流
    image.Source = ImageSource.FromStream(() => 
    {
        return assembly.GetManifestResourceStream($"{path}.Image.Image.png");
    });
}

方案2:缓存字节数组减少重复读资源(性能更优)

如果担心多次读取嵌入资源影响性能,可以先把流读成字节数组缓存起来,每次返回包装了字节数组的新MemoryStream即可:

private byte[] _imageBuffer;
public MainPage()
{
    InitializeComponent();
    var assembly = typeof(MainPage).Assembly;
    string path = "ImageException";
    // 提前把资源读成字节数组缓存,仅读取一次资源
    using var stream = assembly.GetManifestResourceStream($"{path}.Image.Image.png");
    using var ms = new MemoryStream();
    stream.CopyTo(ms);
    _imageBuffer = ms.ToArray();
    
    image.Source = ImageSource.FromStream(() => new MemoryStream(_imageBuffer));
}

方案3:直接使用嵌入资源专用加载API

对于嵌入资源的场景,你可以直接用ImageSource.FromResource()方法,不需要自己处理流的生命周期,Xamarin.Forms内部已经做好了流的管理逻辑,代码最简洁:

public MainPage()
{
    InitializeComponent();
    image.Source = ImageSource.FromResource("ImageException.Image.Image.png", typeof(MainPage).Assembly);
}

以上三种方案都可以保证Image控件移除再重新添加后正常加载图片。


内容的提问来源于stack exchange,提问作者Vignesh Ramesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 07:36:02