如何处理面向 Windows 应用商店应用程序和 WP7、WP8、WPF 的可移植类库中的图像?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12512239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 05:27:01  来源:igfitidea点击:

How do I work with images in a portable class library targeting Windows Store Apps and WP7,WP8,WPF?

wpfmicrosoft-metrowindows-runtimeportable-class-library

提问by Iris Classon

I am working on a first PCL that targets : WSA (Windows Store Application), WPF,WP7,WP8. We can say that it is a rolerdex kind of application, you have contacts , they have contact details and images. (it's not, but I can't give details about the application, so I am using a very simple example instead). Here are some of my questions :)

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

  1. Should I have the images in the PCL?
  1. 我应该在 PCL 中有图像吗?

If yes:

如果是:

  1. How do reference the image for usage in WSA?
  2. How do I best solve scaling with the scale qualifiers etc. when used across different projects?
  1. 如何在 WSA 中引用图像以供使用?
  2. 在不同项目中使用时,如何最好地解决缩放限定符等问题?

I am not using a database and the images are not downloaded from an external service- I would like to keep the images (not many really) locally, in the app or in the PCL.

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

EDIT: I just want to display images. That's it. It's a static rolerdex, you can't add new people. I just want to display 5 number of people and their image (in the PCL). How do I reference the images if it's a Windows Store Application?

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

I have a binding and the DataContext is set to a ViewModel in the PCL. The ViewModel aggregates the data to be displayed from the models. The property I've bound against is MyImage. Ignoring the other platforms, how would the Uri look like? Everything else works fine.

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

I really just want help with these three questions, although I really appreciate all the answers!!!

我真的只是想要这三个问题的帮助,虽然我真的很感激所有的答案!!!

采纳答案by David Kean

For a lot of cases, images are platform-specific. They need to cater for size and DPI of the device itself, and would need to fit in with the look and feel of the application. For these situations, I would have the View itself decide what images to show to the user, probably based on some sort of state/mode provided by the ViewModel.

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

However, these are cases where the images need to come from the ViewModel, for example, in the case of the sender thumbnails that get displayed in mail applications. In these cases, I have the ViewModel return some sort of a platform-agnostic concept of an image (such as byte[]), and then have the platform-specific projects convert that into something that their UI stack understands (in XAML, this would be a ImageSource).

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

The code would look something like this:

代码看起来像这样:

Portable project:

便携式项目

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();
            }
        }
    }
}

Note:You will need to remove or add GetTypeInfo() depending on the platforms you are targeting.

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

Here we're reading from an embedded resource (Properties -> Build Action -> Embedded Resource), but you could imagine this coming from the network, or somewhere else.

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

Windows Store app project:In the Windows Store app, you would have a value converter to convert from byte[] -> ImageSource:

Windows 应用商店应用项目:在 Windows 应用商店应用中,您将有一个值转换器从 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();
        }
    }
}

In the code behind of the View, set the DataContext:

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

DataContext = new ViewModel();

Then in the View itself binding to the ViewModel.Image property, and set the converter:

然后在 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>

回答by Stuart

Images are indeed quite problematic when you need to scale them across different platforms with different pixel densities and different screen sizes.

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

Coupled with this, there are often problems with image manipulation libraries not being portable - especially when they use hardware acceleration on each platform.

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



Assuming that your rolodex app is somehow going to allow users to capture images, and then to upload them to some shared database/service for later viewing, here's how I might approach the problem. It's not the only solution - there's no "one right way" here!

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

** Capturing and uploading images **

** 捕获和上传图像 **

  1. For capturing the picture (either from a folder or from a camera) I would have to use a native hook for each platform - so NOT PCL for this part

  2. If I then needed to do some on-device processing of the image (e.g. resizing or adding a thumbnail) then again this would probably be done differently on each platform - so NOT PCL.

  3. Once you have the photo (e.g. as a JPEG encoded in a MemoryStream) and want to upload it to a server, then I would probably do this using the asynchronous handlers HttpWebRequest (these are the old methods, nothing to do with async/await) in common PCL code

  4. What's running on the server.... well, that's almost certainly not PCL code - and I might use methods there to resize the image into device ready sizes - e.g. different size thumbnails

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

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

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

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

** Showing images **

** 显示图片 **

  1. When I need to display someone's image from the database, then I would probably get the server to return a list of available thumbnail URLs - this is server code so would be not PCL

  2. The app code that actually asks for the list of contacts or the contact detail - that would probably be PCL code - and would probably use HttpWebRequest again.

  3. The View code that takes the contact and renders it on the screen? That would probably be XAML - and I would just use the native Image control on each platform to consume and render an appropriate thumbnail for the situation.

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

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

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

** If you were storing the images locally **

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

  1. If you were storing the images locally instead of using a central server, then you'll probably need to use non-PCL code for this too. Each platform has the same basic type of methods: LoadFile, SaveFile, etc - and each will provide mechanisms to create, enumerate, read and write folders and files, but each platform does this via a different API to the file system (e.g. System.IO in WPF, IsolatedStorage in WP7 Silverlight, etc).

  2. Once you've got your files into a common(ish) structure, then the PCL control code will be able to treat each file as just a pair of strings - the folder it's in and the file name...

  3. ... and in your UI layer (e.g. XAML) you will probably be able to reference those image files directly - probably can be done using a specific ValueConverter to generate the correct filename or filestream on each platform - e.g. in wp7 you could use the converter from Windows Phone 7 Silverlight binding image from the IsolatedStorage

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

  2. 一旦您将文件放入通用(ish)结构中,PCL 控制代码将能够将每个文件视为一对字符串 - 它所在的文件夹和文件名......

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



So, my summary is:

所以,我的总结是:

  • I'd use PCL code wherever I could
  • but in reality that PCL code is only going to be in the control and networking - not in the image collection, image processing or image rendering
  • 我会尽可能使用 PCL 代码
  • 但实际上,PCL 代码只会在控制和网络中——而不是在图像收集、图像处理或图像渲染中


Some other things that might help:

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

  • currently I find it's quite hard to mix .Net4.5 async/await code with "normal" code - so even sharing the networking code might be quite hard to do - you might actually save time if you write separate code for everything :(
  • to help share the code, I would definitely try to use some form of IoC or DI to try to inject specific implementation of platform specific code where it's needed (this is what I do a lot in MvvmCross - and it's what @dsplaisted talks about in service location in http://blogs.msdn.com/b/dsplaisted/archive/2012/08/27/how-to-make-portable-class-libraries-work-for-you.aspx
  • 目前我发现很难将 .Net4.5 异步/等待代码与“普通”代码混合使用 - 所以即使共享网络代码也可能很难做到 - 如果为所有内容编写单独的代码,实际上可能会节省时间:(
  • 为了帮助共享代码,我肯定会尝试使用某种形式的 IoC 或 DI 来尝试在需要的地方注入特定于平台的代码的特定实现(这是我在 MvvmCross 中经常做的事情 - 这就是 @dsplaisted 在服务位置在http://blogs.msdn.com/b/dsplaisted/archive/2012/08/27/how-to-make-portable-class-libraries-work-for-you.aspx

回答by ITLackey

I tend to agree that image handling should probably be platform specific. Just as a general rule anything view related should not be included in a PCL. The above answers have some great technical detail that I will not bother repeating, but wanted to post a link to a slide deck I put together to discuss principals of using PCL. http://prezi.com/ipyzhxvxjvp-/sharing-is-caring-code-reuse-in-xaml-applications/

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

Hopefully this gives some general guidance on the overall design strategies and reinforces the information in the previous answers.

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

回答by Atul Gupta

Most people have already said and the answer is also "Not possible". UI elements in PCL goes against the philosophy of PCL, which should be usable as is across platforms that you are targetting.

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

Here's what MSDN has to say on PCL - "the Portable Class Library project doesn't contain any UI componentsbecause of behavioral differences between the UIs of different devices" (from http://msdn.microsoft.com/en-us/library/gg597391.aspx)

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

hope this helps

希望这可以帮助

回答by kns98

you can put the image on the view model as type "object" and use an interface (which is injected) to create the bitmap.

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

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;
        }
    }

}

in the WPF version, you could do this

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

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

By using this method you can data bind right to the image and do not need a converter.

通过使用此方法,您可以将数据绑定到图像,而无需转换器。

回答by Daniel Plaisted

I would probably put the actual image files in each app. I don't think you get much value out of trying to embed them in the PCL.

我可能会将实际的图像文件放在每个应用程序中。我认为尝试将它们嵌入到 PCL 中不会获得太多价值。

In your portable ViewModels, you can reference the image via a Uri. Unfortunately that Uri needs to be different on each platform. For Windows Store apps it needs to be something like ms-appx:////Assets/image.pngwhere for Windows Phone I think it will just be /Assets/image.png.

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

So you'll need some code to figure out the correct Uri format. That could be a platform-specific service with a portable interface that the ViewModels would call. You might also be able to create a binding converter that would do whatever conversion you needed.

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

There are certainly other ways of doing this-- this is just what I'd picked based on the information in your question.

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

回答by Nick Berardi

My first instinct is to say no you shouldn't have images in your PCL given what you have described.

我的第一反应是说不,鉴于您所描述的内容,您的 PCL 中不应包含图像。

But if I were going to do it images can be stored via resources in the different libraries and you can store a different image for each platform in there and then do an Environment inspection to determine what image to pull out. But this approach really only should be attempted if you need scale.

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

By scale I mean you are open sourcing this library where 1000's of programs are going to use it. If this is for your own internal use for 3 or 4 apps, I would say don't bother save your self the headache. Just pass the image into the shared library or set it via a config and be done with it.

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

Or just don't manage images via the PCL. Because image manipulation changes and is different on every platform, especially the ones you mentioned. And you are bound to run into some weird bug where it just doesn't work the same as it does on the other platforms.

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