将存储在Windows Phone的隔离存储中的图像绑定到图像控件

4

是否可能通过xaml将隔离存储中存在的图像绑定到图像控件?我发现一些实现方式,例如通过属性获取图像并将其绑定到xaml控件。但这不是我正在寻找的实现方式。我的问题是,编写一个附加属性和助手方法从隔离存储中获取内容。我在Windows Phone 7中使用的LowProfileImage类中找到了类似的实现方式。但我认为它现在已经过时了。如果有人尝试过类似的实现方式,请帮助我实现相同的功能。此外,如果实现方式有任何性能损耗,请也提供相关信息。

2个回答

10

是的,可以在应用程序UI中使用来自隔离存储的图像。需要将文件中的图像加载到 BitmapImage 中,然后将您控件的 ImageSource 绑定到该 BitmapImage。 我正在使用以下方法:

首先,有一种异步加载图像的方法:

private Task<Stream> LoadImageAsync(string filename)
    {
        return Task.Factory.StartNew<Stream>(() =>
        {
            if (filename == null)
            {
                throw new ArgumentException("one of parameters is null");
            }

            Stream stream = null;

            using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isoStore.FileExists(filename))
                {
                    stream = isoStore.OpenFile(filename, System.IO.FileMode.Open, FileAccess.Read);                               
                }
            }
            return stream;
        });
    }

那么它可以像这样使用:

public async Task<BitmapSource> FetchImage()
    {
        BitmapImage image = null;
        using (var imageStream = await LoadImageAsync(doc.ImagePath))
        {
            if (imageStream != null)
            {
                image = new BitmapImage();
                image.SetSource(imageStream);
            }
        }
        return image;
    }

最后,你只需将 FetchImage() 方法的返回值分配给某个绑定到 UI 元素的视图模型属性即可。当然,为了使这种方法可靠地工作,你的视图模型应正确实现 INotifyPropertyChanged 接口。


如果您想使用附加属性方法,下面是操作步骤:

public class IsoStoreImageSource : DependencyObject
{
    public static void SetIsoStoreFileName(UIElement element, string value)
    {
        element.SetValue(IsoStoreFileNameProperty, value);
    }
    public static string GetIsoStoreFileName(UIElement element)
    {
        return (string)element.GetValue(IsoStoreFileNameProperty);
    }

    // Using a DependencyProperty as the backing store for IsoStoreFileName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsoStoreFileNameProperty =
        DependencyProperty.RegisterAttached("IsoStoreFileName", typeof(string), typeof(IsoStoreImageSource), new PropertyMetadata("", Changed));

    private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Image img = d as Image;

        if (img != null)
        {
            var path = e.NewValue as string;
            SynchronizationContext uiThread = SynchronizationContext.Current;

            Task.Factory.StartNew(() =>
            {
                using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (isoStore.FileExists(path))
                    {
                        var stream = isoStore.OpenFile(path, System.IO.FileMode.Open, FileAccess.Read);
                        uiThread.Post(_ =>
                        {
                            var _img = new BitmapImage();
                            _img.SetSource(stream);
                            img.Source = _img;
                        }, null);
                    }
                }
            });               
        }
    }
}

然后在XAML中:

<Image local:IsoStoreImageSource.IsoStoreFileName="{Binding Path}" />

这种方法存在一些限制:

  • 它只能在Image控件上使用,尽管您可以将其更改为任何类型。 它只是不是很通用。
  • 就性能而言,每次更改图像源时,它将使用一个线程池线程。这是目前在Windows Phone 8上从隔离存储异步读取的唯一方法。 您绝对不希望同步执行此操作。

但它有一个重要的优点:

  • 它可行!:)

Haspemulator,我可以再问一个问题吗?“Deloyment.Current.Dispatcher.BeginInvole() && uiThread.Post()”有什么区别? - Stephan Ronald
3
使用Deployment.Current.Dispatcher.BeginInvoke()也可以。主要的区别在于代码的可移植性。System.Threading.SynchronizationContext是所有.NET环境的通用概念。这意味着,在Windows Store和Windows Phone 8应用程序上它可以同样地工作,而Deployment.Current.Dispatcher仅适用于Windows Phone。当然,IsolatedStorageFile也是Windows Phone特定的,因此在这种情况下这两种方法之间的差异并不那么重要。但是,还是有必要牢记这些差异,以备不时之需。 - Haspemulator
@Haspemulator:看起来你的答案真的很有前途。但是我在一个大约有300个项目的列表框中尝试了同样的方法。但是在滚动时,我从隔离存储对象中得到了未经授权的异常。我认为如果你只有一个图像控件要绑定,那么这种方法没有任何优势。如果它可以与列表框一起使用,那就太好了。期待你的回复。 - Nitha Paul
@Haspemulator:如果我使用默认库图像尝试相同的操作,那么它将正常工作。但是,如果我尝试使用相机拍摄的图像,则应用程序将崩溃,“System.Windows.ni.dll中发生了类型为'System.OutOfMemoryException'的异常,但未在用户代码中处理”。此崩溃发生在您设置源的代码中(即在_img.SetSource(stream)中)。 - Nitha Paul
@Haspemulator:我将此问题作为单独的问题添加了。请查看此链接https://dev59.com/13HYa4cB1Zd3GeqPJT4K - Nitha Paul
显示剩余3条评论

1
我喜欢上述方法,但如果你感兴趣,还有一种更简单的方法可以实现。您可以进入XAML并将图像源绑定到一个字符串属性,然后动态地将文件路径放入该属性中。
<!-- XAML CODE -->
 <Image Source="{Binding imagePath}"/>


//Behind property
public String imagePath { get; set; }

将您的路径加载到图像路径中,然后将图像源绑定到图像路径字符串。您可能需要执行INotifyPropertyChanged,但是该方法应该能够通过适当的绑定工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接