Доступ к картинкам в изолированном хранилище

    Привет хабраюзеры!

    Выполнив несколько проектов на wp7, я столкнулся с часто возникающей задачей: хранения изображений в изолированном хранилище и последующей отрисовкой этих изображений. При просмотре тематических блогов и статей я не обнаружил ничего кроме самого прямого и простого ресурсоемкого способа это сделать. Как оказывается — так делают все! Итак, давайте поговорим об этом способе и о моем небольшом отркытии.

    Для хранения данных на диске используется Isolated Storage. Этот класс позволяет разработчику получить доступ к изолированному каталогу на диске. К этому каталогу может получить доступ только одно приложение и в нем можно создавать иерархическую структуру для хранения данных. Это одновременно и хорошо и плохо — ведь не получится расшарить данные между приложениями. Но задача с изображениями остается все такой же важной. С другой стороны, если эта задача необходима для кеширования изображения для работы офлайн — стоит подумать: ведь система кеширует изображения самостоятельно, возможно в вашем приложении и нет необходимости реализовывать кеширование вручную. Итак:

    Способ №1 — самый «простой»


    Сохраняем картинки в хранилище любым удобным вам способом. Теперь для отображения изображения необходимо использовать изолированное хранилище. Из хранилища в поток читается изображение, после чего изображение из потока попадает в BitmapImage и этот объект присваивается свойству Source.
    Упрощенный конвертор который проделывает вышеописанные действия:

    public class IsoImageConverter : IValueConverter  
    {  
        //Convert Data to Image when Loading Data  
        public object Convert(object value, Type targetType, object parameter,  
            System.Globalization.CultureInfo culture)  
        {  
            var bitmap = new BitmapImage();  
            try  
            {  
                var path = (string)value;  
                if (!String.IsNullOrEmpty(path))
                {  
                    using (var file = LoadFile(path))  
                    {  
                        bitmap.SetSource(file);  
                    }  
                }  
            }  
            catch  
            {  
            }  
            return bitmap;  
        }  
      
        private Stream LoadFile(string file)  
        {  
            using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())  
            {  
                return isoStore.OpenFile(file, FileMode.Open, FileAccess.Read);  
            }  
        }  
          
        public object ConvertBack(object value, Type targetType, object parameter,  
            System.Globalization.CultureInfo culture)  
        {  
            throw new NotImplementedException();  
        }  
    }
    

    Ну, и XAML:

    <Image Source="{Binding ImagePath, Converter={StaticResource IsoImageCoverter}}"/>
    

    Этот способ описывается везде как способ для отображения изображения из IS

    Теперь способ №2 — самый нетривиальный


    Однажды мне была дана задача: исследовать возможность построения offline карт с помощью существующего компонента карт. Я накачал изображений в IS я решил попробовать применить способ номер 1. Ничего не вышло. Поломав голову несколько часов, я вспомнил как когда-то читал где-то про то, что эмулятор, который установлен на виндовой машине можно расковырять и получить путь к приложению и файлам в хранилище. Что ж, если есть путь и доступ по этому пути возможен только из приложения — наверное, можно будет вставить ссылку на картинку в Image и она отобразиться. Итак, что было сделано:

    Для начала в дебагере была отловлена ссылка на изображение при сохранении.



    Путь: \Applications\Data\AD105BA4-EC12-49E0-9077-B5D95DBA2FEE\Data\IsolatedStore\test\SplashScreenImage.jpg

    Теперь имея путь к картинке можно использовать его как Source у изображения. Но, как оказалось, необходимо использовать только абсолютные пути, потому необходимо добавить префикс file:///

    Убеждаемся что картинка там и что она не повреждена:



    и вставляем такой код в XAML:

    <Image Height="200" Width="200" Source="file:///Applications/Data/AD105BA4-EC12-49E0-9077-B5D95DBA2FEE/Data/IsolatedStore/test/SplashScreenImage.jpg"/>
    


    И в результате:



    С мап компонентом ничего не вышло, но ценный опыт для себя вынес. В итоге был найден более простой способ отображения изображений из IsolatedStorage.

    P.S: Картинки из папки Shared таким способом вытянуть не получилось.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 7

      0
      Скажу вам больше. Так можно генерить XAML и грузить UserControl прямо из IsolatedStorage
        0
        Генерировать в рантайме XAML (только если ваше приложение не редактор), по-моему, неверный подход. Правильнее сразу создавать необходимую структуру из экземпляров классов, в таком случае не будет тратиться время на генерацию и парсинг разметки.
        Интересно, в какой задаче Вам это понадобилось?
          0
          Менять дизайн после выхода в маркет :)
            0
            Один раз сгенерить XAML, динамически. И грузить его. UserControl грузит свой замл и парсит его нативным методом, что явно быстрее. Вот polhovskiy предположил ниже
              0
              *выше
          0
          Если речь идет об изображениях-данных (а не о скинах кнопочек и т.д.), то ИМХО удобнее пользоваться бд движком, а не файловой системой. Главное не держать блобы вместе с нормальными данными. Даже SQLite должен с задачей справляться.
          Самый большой плюс такого подхода — переносимость на другие платформы.

          PS Тут лучше подойдут NoSQL решения, но я пока не знаю простых универсальных библиотек под них для мобильных платформ.
            0
            Способ на самом деле интересный, я даже написал компоненту который значительно упрощает использование такого подхода, не стал его публиковать и отказался от его использования из за нескольких ньюансов:

            1. Способ не документированный и при очередном обновлении платформы может сломаться.
            2. Идентификатор AppId меняется после публикации и соответственно что бы этот способ нормально работал надо как минимум парсить манифест.

            По поводу способа №1 способ рабочий но лучше все таки избегать использования конверторов. Это сильно бьет по производительности, тем более если работаете с картами. Лучше или из кода привязывать картинку или же можно привязываться к полю типа BitmapImage без конвертора.

            По поводу того что первый способ у вас не сработал:
            А разве вы не освобождаете ресурсы в методе до того как вернуть ссылку на поток?

            private Stream LoadFile(string file)
            {
            using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
            {
            return isoStore.OpenFile(file, FileMode.Open, FileAccess.Read);
            }
            }

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.