23

我正在开发第一个针对以下目标的 PCL:WSA(Windows 商店应用程序)、WPF、WP7、WP8。我们可以说它是一种rolerdex 类型的应用程序,你有联系人,他们有联系方式和图片。(不是,但我无法提供有关应用程序的详细信息,因此我使用了一个非常简单的示例)。这是我的一些问题:)

  1. 我应该在 PCL 中有图像吗?

如果是:

  1. 如何参考图像以在 WSA 中使用?
  2. 在不同项目中使用时,如何最好地使用比例限定符等解决缩放问题?

我没有使用数据库,并且图像不是从外部服务下载的——我想将图像(实际上并不多)保存在本地、应用程序或 PCL 中。

编辑:我只想显示图像。而已。这是一个静态的rolerdex,你不能添加新的人。我只想显示 5 个人及其图像(在 PCL 中)。如果它是 Windows 应用商店应用程序,我如何引用图像?

我有一个绑定,并且 DataContext 设置为 PCL 中的 ViewModel。ViewModel 聚合模型中要显示的数据。我绑定的属性是 MyImage。忽略其他平台,Uri 会是什么样子?其他一切正常。

我真的只是想要这三个问题的帮助,尽管我真的很感谢所有的答案!!!

4

7 回答 7

23

在很多情况下,图像是特定于平台的。他们需要满足设备本身的尺寸和 DPI,并且需要适应应用程序的外观和感觉。对于这些情况,我会让 View 自己决定向用户显示哪些图像,可能基于 ViewModel 提供的某种状态/模式。

但是,在这些情况下,图像需要来自 ViewModel,例如,在邮件应用程序中显示的发件人缩略图的情况下。在这些情况下,我让 ViewModel 返回某种与平台无关的图像概念(例如 byte[]),然后让特定于平台的项目将其转换为他们的 UI 堆栈可以理解的东西(在 XAML 中,这个将是一个 ImageSource)。

代码看起来像这样:

便携式项目

using System.IO;
using System.Reflection;

namespace Portable
{
    public class ViewModel
    {
        private byte[] _image = LoadFromResource("Image.png");

        public byte[] Image
        {
            get { return _image; }
        }

        private static byte[] LoadFromResource(string name)
        {
            using (Stream stream = typeof(ViewModel).GetTypeInfo().Assembly.GetManifestResourceStream("Portable." + name))
            {
                MemoryStream buffer = new MemoryStream();
                stream.CopyTo(buffer);

                return buffer.ToArray();
            }
        }
    }
}

注意:您需要根据您的目标平台删除或添加 GetTypeInfo()。

在这里,我们从嵌入式资源(属性 -> 构建操作 -> 嵌入式资源)中读取数据,但您可以想象这来自网络或其他地方。

Windows Store 应用程序项目: 在 Windows Store 应用程序中,您将有一个值转换器从 byte[] -> ImageSource 转换:

using System;
using System.IO;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media.Imaging;

namespace App
{
    public class ByteToImageSourceValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            InMemoryRandomAccessStream s = new InMemoryRandomAccessStream();

            byte[] bytes = (byte[])value;
            Stream stream = s.AsStreamForWrite();
            stream.Write(bytes, 0, bytes.Length);
            stream.Flush();
            stream.Seek(0, SeekOrigin.Begin);


            BitmapImage source = new BitmapImage();
            source.SetSource(s);

            return source;           

        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
}

在 View 后面的代码中,设置 DataContext:

DataContext = new ViewModel();

然后在 View 本身绑定到 ViewModel.Image 属性,并设置转换器:

<Page.Resources>
    <local:ByteToImageSourceValueConverter x:Name="ImageConverter"/>
</Page.Resources>

<Grid >
    <Image HorizontalAlignment="Left" Height="242" Margin="77,10,0,0" VerticalAlignment="Top" Width="278" Source="{Binding Image, Converter={StaticResource ImageConverter}}"/>

</Grid>
于 2012-09-20T18:31:36.350 回答
14

当您需要在具有不同像素密度和不同屏幕尺寸的不同平台上缩放图像时,图像确实存在很大问题。

再加上这一点,图像处理库通常存在不可移植的问题——尤其是当它们在每个平台上使用硬件加速时。


假设您的 rolodex 应用程序以某种方式允许用户捕获图像,然后将它们上传到某些共享数据库/服务以供以后查看,这就是我可能解决问题的方法。这不是唯一的解决方案——这里没有“一个正确的方法”!

** 捕获和上传图像 **

  1. 为了捕获图片(从文件夹或相机),我必须为每个平台使用本机挂钩 - 所以这部分不是 PCL

  2. 如果我需要在设备上对图像进行一些处理(例如调整大小或添加缩略图),那么这可能会在每个平台上以不同的方式完成 - 所以不是 PCL。

  3. 一旦您有了照片(例如,在 MemoryStream 中编码为 JPEG)并想要将其上传到服务器,那么我可能会使用异步处理程序 HttpWebRequest 来执行此操作(这些是旧方法,与 async/await 无关)在通用 PCL 代码中

  4. 服务器上正在运行什么......好吧,那几乎肯定不是 PCL 代码 - 我可能会使用那里的方法将图像调整为设备就绪大小 - 例如不同大小的缩略图

** 显示图像 **

  1. 当我需要从数据库中显示某人的图像时,我可能会让服务器返回可用缩略图 URL 的列表 - 这是服务器代码,因此不是 PCL

  2. 实际请求联系人列表或联系人详细信息的应用程序代码(可能是 PCL 代码)可能会再次使用 HttpWebRequest。

  3. 获取联系人并将其呈现在屏幕上的视图代码?那可能是 XAML - 我只会使用每个平台上的本机 Image 控件来使用和呈现适合情况的缩略图。

** 如果您在本地存储图像 **

  1. 如果您在本地存储图像而不是使用中央服务器,那么您可能也需要为此使用非 PCL 代码。每个平台都有相同的基本类型的方法:LoadFile、SaveFile 等 - 每个平台都将提供创建、枚举、读取和写入文件夹和文件的机制,但是每个平台通过不同的文件系统 API(例如 System. WPF 中的 IO、WP7 Silverlight 中的独立存储等)。

  2. 一旦你把你的文件变成了一个通用的(ish)结构,那么 PCL 控制代码将能够将每个文件视为一对字符串 - 它所在的文件夹和文件名......

  3. ...并且在您的 UI 层(例如 XAML)中,您可能能够直接引用这些图像文件 - 可能可以使用特定的 ValueConverter 在每个平台上生成正确的文件名或文件流来完成 - 例如在 wp7 中,您可以使用来自 Windows Phone 7 Silverlight 的转换器来自IsolatedStorage 的绑定图像


所以,我的总结是:

  • 我会尽可能使用 PCL 代码
  • 但实际上,PCL 代码只会在控制和网络中——而不是在图像收集、图像处理或图像渲染中

其他一些可能有帮助的事情:

  • 目前我发现将.Net4.5 async/await 代码与“普通”代码混合是相当困难的——所以即使共享网络代码也可能很难做到——如果你为所有东西编写单独的代码,你实际上可能会节省时间:(
  • 为了帮助共享代码,我肯定会尝试使用某种形式的 IoC 或 DI 来尝试在需要的地方注入特定于平台的代码的特定实现(这是我在 MvvmCross 中经常做的事情——这就是 @dsplaisted 在http://blogs.msdn.com/b/dsplaisted/archive/2012/08/27/how-to-make-portable-class-libraries-work-for-you.aspx中的服务位置
于 2012-09-20T12:27:21.513 回答
2

我倾向于同意图像处理应该是特定于平台的。作为一般规则,任何与视图相关的内容都不应包含在 PCL 中。上面的答案有一些很棒的技术细节,我不想重复,但想发布一个链接,指向我放在一起讨论使用 PCL 的原则的幻灯片。http://prezi.com/ipyzhxvxjvp-/sharing-is-caring-code-reuse-in-xaml-applications/

希望这可以为整体设计策略提供一些一般性指导,并加强先前答案中的信息。

于 2012-09-20T12:57:29.640 回答
2

大多数人已经说过,答案也是“不可能”。PCL 中的 UI 元素违背了 PCL 的理念,PCL 应该可以跨您所针对的平台使用。

这是 MSDN 在 PCL 上所说的——“由于不同设备的 UI 之间的行为差​​异,可移植类库项目不包含任何 UI 组件”(来自http://msdn.microsoft.com/en-us/library /gg597391.aspx )

希望这可以帮助

于 2012-09-21T03:26:09.730 回答
0

您可以将图像作为“对象”类型放在视图模型上,并使用接口(注入)来创建位图。

public interface IBitMapCreator
{
    object Create(string path);
}

public class MyViewModel
{
    private bool _triedToSetThumb;
    private readonly IBitMapCreator _bc;

    public MyViewModel(IBitMapCreator bc, string path)
    {
        _bc = bc;
        SetThumb(path);
    }

    public object Thumb { get; private set; }

    private void SetThumb(string path)
    {
        try
        {
            if (!_triedToSetThumb)
            {
                string ext = Path.GetExtension(path).ToUpper();

                if (
                    ext == ".JPG" ||
                    ext == ".JPEG" ||
                    ext == ".PNG" ||
                    ext == ".GIF" ||
                    ext == ".BMP" ||
                    false
                    )
                {
                    Thumb = _bc.Create(path);
                    OnPropertyChanged(new PropertyChangedEventArgs("Thumb"));
                }
            }
        }
        finally
        {
            _triedToSetThumb = true;
        }
    }

}

在 WPF 版本中,你可以这样做

class BitMapCreator : IBitMapCreator
{
    public object Create(string path)
    {
        return BitmapFrame.Create(new Uri(path));
    }
} 

通过使用这种方法,您可以直接对图像进行数据绑定,并且不需要转换器。

于 2014-07-08T15:51:48.740 回答
0

我的第一直觉是说不,鉴于您所描述的,您的 PCL 中不应该有图像。

但是,如果我打算这样做,图像可以通过不同库中的资源存储,您可以在其中为每个平台存储不同的图像,然后进行环境检查以确定要提取的图像。但这种方法真的只有在需要规模时才应该尝试。

通过规模,我的意思是你正在开源这个库,有 1000 个程序将使用它。如果这是供您自己在内部使用 3 或 4 个应用程序,我会说不要费心让自己头疼。只需将图像传递到共享库或通过配置进行设置即可完成。

或者只是不通过 PCL 管理图像。因为图像处理在每个平台上都会发生变化并且不同,尤其是您提到的那些。而且您一定会遇到一些奇怪的错误,它与在其他平台上的工作方式不同。

于 2012-09-20T12:46:37.703 回答
0

我可能会将实际的图像文件放在每个应用程序中。我认为尝试将它们嵌入 PCL 并没有什么价值。

在便携式 ViewModel 中,您可以通过 Uri 引用图像。不幸的是,每个平台上的 Uri 都需要不同。对于 Windows 应用商店应用程序,它需要类似于ms-appx:////Assets/image.png我认为 Windows Phone 的位置/Assets/image.png

所以你需要一些代码来找出正确的 Uri 格式。这可能是一个特定于平台的服务,具有 ViewModel 将调用的可移植接口。您也许还可以创建一个绑定转换器来执行您需要的任何转换。

当然还有其他方法可以做到这一点——这正是我根据你的问题中的信息选择的。

于 2012-09-20T15:39:00.993 回答