Как работать с изображениями в переносимой библиотеке классов, ориентированной на приложения Windows Store и WP7, WP8, WPF?
Я работаю над первым PCL, который нацелен: WSA (приложение для хранения Windows), WPF, WP7, WP8. Мы можем сказать, что это приложение типа rolerdex, у вас есть контакты, у них есть контактные данные и изображения. (это не так, но я не могу сообщить подробности о приложении, поэтому я использую очень простой пример). Вот некоторые из моих вопросов:)
- Должен ли я иметь изображения в PCL?
Если да:
- Как ссылаться на изображение для использования в WSA?
- Как лучше всего решать масштабирование с помощью квалификаторов шкалы и т.д. при использовании в разных проектах?
Я не использую базу данных, и изображения не загружаются из внешней службы - я хотел бы сохранить изображения (не многие действительно) локально, в приложении или в PCL.
EDIT:
Я просто хочу отображать изображения. Это. Это статический rolerdex, вы не можете добавлять новых людей. Я просто хочу отобразить 5 человек и их изображение (в PCL). Как ссылаться на изображения, если это приложение для Windows Store?
У меня есть привязка, и DataContext установлен в ViewModel в PCL. ViewModel объединяет данные, которые будут отображаться на моделях. Свойство, с которым я связан, - это MyImage. Игнорируя другие платформы, как выглядел бы Ури? Все остальное отлично работает.
Я просто хочу помочь с этими тремя вопросами, хотя я действительно ценю все ответы!!!
Ответы
Ответ 1
Во многих случаях изображения зависят от платформы. Они должны удовлетворять размерам и DPI самого устройства и должны соответствовать требованиям внешнего вида приложения. В этих ситуациях я бы сам сам вид решал, какие изображения показывать пользователю, возможно, на основе какого-то состояния/режима, предоставляемого ViewModel.
Однако это случаи, когда изображения должны появляться из ViewModel, например, в случае миниатюр отправителя, которые отображаются в почтовых приложениях. В этих случаях у меня есть ViewModel, возвращающая какую-то платформо-агностическую концепцию изображения (например, byte []), а затем проекты, специфичные для платформы, преобразуют их во что-то, что понимает их стек пользовательского интерфейса (в 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() в зависимости от платформ, на которые вы нацеливаетесь.
Здесь мы читаем из встроенного ресурса (Properties → Build Action → Embedded Resource), но вы можете представить, что это происходит из сети или где-то еще.
Проект приложения Windows Store:
В приложении Windows Store у вас будет конвертер значений для преобразования из байта [] → 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();
Затем в самом представлении, привязаном к свойству 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>
Ответ 2
Изображения действительно довольно проблематичны, когда вам нужно масштабировать их на разных платформах с различной плотностью пикселей и различными размерами экрана.
В сочетании с этим часто возникают проблемы с тем, что библиотеки обработки изображений не переносятся, особенно когда они используют аппаратное ускорение на каждой платформе.
Предполагая, что ваше приложение rolodex каким-то образом позволит пользователям записывать изображения, а затем загружать их в какую-то общую базу данных/службу для последующего просмотра, вот как я могу подойти к этой проблеме. Это не единственное решение - здесь нет "одного правильного пути"!
** Захват и загрузка изображений **
-
Для захвата изображения (из папки или из камеры) мне придется использовать собственный крючок для каждой платформы - так что НЕ PCL для этой части
-
Если мне тогда понадобилось сделать некоторую обработку изображения на устройстве (например, изменение размера или добавление миниатюры), то это, вероятно, будет выполняться по-разному на каждой платформе - так что НЕ PCL.
-
Как только у вас есть фотография (например, как JPEG, закодированная в MemoryStream) и вы хотите загрузить ее на сервер, я бы, вероятно, сделал это с помощью асинхронных обработчиков HttpWebRequest (это старые методы, делать с async/await) в общем коде PCL
-
Что работает на сервере... ну, что почти наверняка не является кодом PCL, и я мог бы использовать методы для изменения размера изображения в готовые к установке устройства - например. миниатюры разного размера
** Отображение изображений **
-
Когда мне нужно отобразить какое-то изображение из базы данных, я бы, вероятно, получил сервер, чтобы вернуть список доступных эскизов URL-адресов - это код сервера, поэтому не будет PCL
-
Код приложения, который фактически запрашивает список контактов или контактную информацию, которая, вероятно, будет PCL-кодом, и, вероятно, снова будет использовать HttpWebRequest.
-
Код просмотра, который берет контакт и отображает его на экране? Вероятно, это XAML, и я бы просто использовал собственный элемент управления Image на каждой платформе, чтобы потреблять и отображать соответствующий снимок для этой ситуации.
** Если вы сохраняете изображения локально **
-
Если вы сохраняете изображения локально вместо использования центрального сервера, то вам, вероятно, также понадобится использовать для этого не-PCL-код. Каждая платформа имеет один и тот же базовый тип методов: LoadFile, SaveFile и т.д. - и каждый будет предоставлять механизмы для создания, перечисления, чтения и записи папок и файлов, но каждая платформа делает это с помощью другого API в файловой системе (например, System. IO в WPF, IsolatedStorage в WP7 Silverlight и т.д.).
-
После того, как ваши файлы будут объединены в общую структуру (ish), тогда управляющий код PCL сможет обрабатывать каждый файл как пару строк - папку и имя файла..
-
... и на вашем уровне пользовательского интерфейса (например, XAML) вы, вероятно, сможете напрямую ссылаться на эти файлы изображений - возможно, это может быть сделано с использованием определенного ValueConverter для генерации правильного имени файла или потока на каждой платформе - например, в wp7 вы можете использовать конвертер из Windows Phone 7 Silverlight, привязывающий изображение от изолированного хранилища
Итак, мое резюме:
- Я бы использовал код 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
Ответ 3
Я склонен согласиться с тем, что обработка изображений должна, вероятно, быть специфичной для платформы. Точно так же, как общее правило, все связанные взгляды не должны включаться в PCL. В приведенных выше ответах есть некоторые важные технические детали, которые я не буду повторять, но хотел опубликовать ссылку на слайд-колоду, которую я собрал, чтобы обсудить принципы использования PCL. http://prezi.com/ipyzhxvxjvp-/sharing-is-caring-code-reuse-in-xaml-applications/
Надеюсь, это дает общее руководство по общим стратегиям проектирования и усиливает информацию в предыдущих ответах.
Ответ 4
Большинство людей уже сказали, и ответ также "Невозможно". Элементы пользовательского интерфейса в PCL противоречат философии PCL, которая должна использоваться, как и на всех платформах, на которые вы нацеливаетесь.
Здесь, что MSDN говорит на PCL: "Проект Portable Class Library не содержит компонентов интерфейса из-за поведенческих различий между пользовательскими интерфейсами разных устройств" (от http://msdn.microsoft.com/en-us/library/gg597391.aspx)
надеюсь, что это поможет
Ответ 5
Мой первый инстинкт - сказать нет, у вас не должно быть изображений в вашем PCL, учитывая то, что вы описали.
Но если я собираюсь сделать это, изображения могут быть сохранены через ресурсы в разных библиотеках, и вы можете сохранить другое изображение для каждой платформы, а затем провести проверку среды, чтобы определить, какое изображение вытащить. Но этот подход действительно должен быть предпринят только в том случае, если вам нужен масштаб.
По шкале я имею в виду, что вы открываете источник этой библиотеки, где 1000 программ будут ее использовать. Если это для вашего собственного внутреннего использования для 3 или 4 приложений, я бы сказал, не беспокойтесь, сохраняя свою собственную головную боль. Просто передайте изображение в общую библиотеку или установите его через конфигурацию и сделайте с ней.
Или просто не управляйте изображениями через PCL. Поскольку манипуляции с изображениями меняются и различаются на каждой платформе, особенно те, которые вы упомянули. И вы обязательно столкнетесь с какой-то странной ошибкой, где она просто не работает так же, как на других платформах.
Ответ 6
Я бы, вероятно, поместил фактические файлы изображений в каждое приложение. Я не думаю, что вы получаете большую пользу от попыток внедрить их в PCL.
В ваших портативных ViewModels вы можете ссылаться на изображение через Uri. К сожалению, ури должны быть разными на каждой платформе. Для приложений Windows Store это должно быть что-то вроде ms-appx:////Assets/image.png
, где для Windows Phone я думаю, что это будет просто /Assets/image.png
.
Итак, вам понадобится код для определения правильного формата Uri. Это может быть специфичный для платформы сервис с портативным интерфейсом, который вызовет ViewModels. Вы также можете создать конвертер привязки, который будет делать любое преобразование, в котором вы нуждаетесь.
Есть, конечно, другие способы сделать это - это именно то, что я выбрал на основе информации в вашем вопросе.
Ответ 7
вы можете поместить изображение на модель представления в качестве типа "объект" и использовать интерфейс (который вводится) для создания растрового изображения.
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));
}
}
Используя этот метод, вы можете привязать данные к изображению и не нуждаться в конвертере.