【问题标题】:Wpf Image Control blocks the fileWpf Image Control 阻止文件
【发布时间】:2015-04-14 10:48:46
【问题描述】:

我有一个简单的Window 按钮,当我点击Button 时,第二个Window 被打开。第二个Window 有一个Image 控件,它显示一个.png 文件。因此,如果我将FileObject 属性用于Binding,一切正常,我可以从File Explorer 中删除文件。但是,如果我将FileName 属性用于Binding,我无法从File Explorer 中删除文件,我会遇到操作系统异常。即使我关闭第二个窗口,我也无法执行此操作,即使我明确调用 GCFileName 属性有什么问题?有任何想法吗?

Win 7,网络 4.0

窗口1

<Grid>
    <Button Content="Ok"
            Width="100" 
            VerticalAlignment="Center" 
            HorizontalAlignment="Center" 
            Click="Click" 
            Padding="0,2,0,2" 
            IsDefault="True" 
            Name="_btnOk"/>
</Grid> 



public partial class Window : Window
{

    public Window()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void Click(Object sender, RoutedEventArgs e)
    {
        var window = new Window3();
        window.ShowDialog();
    }
}

窗口2

<Grid>
    <Image Source="{Binding FileObject}"></Image>
</Grid>

public partial class Window2 : Window
{
    public Window2()
    {
        InitializeComponent();
        DataContext = this;
        FileName = "D:/pdf/myfile.png";

        Closing += Window2_Closing;
    }

    public String FileName { get; set; }

    public Object FileObject
    {
        get
        {
            if (String.IsNullOrEmpty(FileName))
                return null;

            if (!File.Exists(FileName))
                return null;

            var ms = new MemoryStream();
            var bi = new BitmapImage();

            using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
            {
                fs.CopyTo(ms);
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.EndInit();
            }
            return bi;
        }
    }

    void Window2_Closing(Object sender, System.ComponentModel.CancelEventArgs e)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

【问题讨论】:

  • 你为什么要这样绑定? Here's how to do it.
  • 仅供参考,FileObject 属性 getter 中不需要 MemoryStream。只需设置bi.StreamSource = fs;bi.CacheOption = BitmapCacheOption.OnLoad;。该属性还应返回ImageSourceBitmapSource 而不是object

标签: c# wpf image


【解决方案1】:

当您将 Image.Source 属性绑定到 Uri(或字符串,从内部创建 Uri)时,WPF 使用内置类型转换器从 Uri 创建 BitmapFrame

如果 Uri 包含本地文件的路径,只要文件存在,BitmapFrame 就会保持文件打开。这可能比它在您的应用程序中实际使用的时间长,因为它可能被 WPF 缓存。

当您需要能够删除从中加载图像的文件时,您应该始终使用FileObject 方法,但它应该如下所示:

public ImageSource Image
{
    get
    {
        ...
        var bi = new BitmapImage();
        using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
        {
            bi.BeginInit();
            bi.CacheOption = BitmapCacheOption.OnLoad;
            bi.StreamSource = fs;
            bi.EndInit();
        }
        return bi;
    }
}

或者像这样:

public ImageSource Image
{
    get
    {
        using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
        {
            return BitmapFrame.Create(
                fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
        }
    }
}

或者您使用一个绑定转换器绑定到FileName 属性,该转换器创建一个BitmapImage 或BitmapFrame,如上所示。

【讨论】:

  • 知道了,谢谢。但是为什么你不喜欢MemoryStream 而建议BitmapCacheOption.OnLoad 呢?利润是多少?在您的第二种方法中(使用BitmapFrame)。 BitmapFrame 不会被 WPF 缓存?
  • 好处是你保存了fs.CopyTo(ms)操作,并且你可以立即关闭流(由于BitmapCacheOption.OnLoad)。您让内存流处于打开状态,这实际上不是问题,但仍应避免。在这两种情况下,都没有缓存图像。缓存仅使用 Uri 作为键来完成,您在此处没有。
【解决方案2】:

使用这段代码,我稍后会解释问题所在。

var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(FilePath);
image.EndInit();
return image;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    相关资源
    最近更新 更多