Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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 :)

  1. Should I have the images in the 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?

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.

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?

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.

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

like image 319
Iris Classon Avatar asked Sep 20 '12 12:09

Iris Classon


People also ask

Which of the following can be used to target portable class library?

Windows Phone 7 and higher (selected by default) Windows Phone 7.5 and higher Windows Phone 8. Above this platform support Portable Class Library, but note if want to work on window phone platform for that you need to first install the Windows Phone SDK for the Portable Class Library.

How do I create a portable class library in Visual Studio 2019?

Select the Visual C# → Windows template in the left pane and select Class Library (Portable) in the middle pane. Enter StringLibrary in the name field and click OK to create this project.

What is PCL project?

PCL projects target specific profiles that support a known set of BCL classes/features. However, the down side to PCL is that they often require extra architectural effort to separate profile specific code into their own libraries.


2 Answers

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.

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).

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.

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:

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:

DataContext = new ViewModel(); 

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

<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> 
like image 142
David Kean Avatar answered Oct 21 '22 05:10

David Kean


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!

** 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

** 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.

** 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


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

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
like image 30
Stuart Avatar answered Oct 21 '22 04:10

Stuart