locked
Как сохранить ListView при завершении приложения? RRS feed

  • Общие обсуждения

  • На форме имеется ListView со списком, который необходимо сохранять при закрытии приложения. SuspensionManager сохраняет только простые виды данных, а как сохранить Items из ListView?
    10 февраля 2014 г. 1:41

Все ответы

  • Добрый день.

    Например, выгрузить в текстовый файл.

    10 февраля 2014 г. 6:20
    Отвечающий
  • При суспенде у Вас будет всего 5 секунд, что бы сохранить данные. Так что учтите это и лучше сохраняйте данные из ListView в тот момент, когда Вы их вообще получили.
    10 февраля 2014 г. 8:19
  • Можно хранить коллекцию локально в виде XML-документа. Для этого можно использовать механизм Сериализации данных. Соответственно для восстановления данных можно использовать механизм десериализации данных. Не могу сказать, на сколько это быстрый процесс, но если не ошибаюсь, то это самый быстрый способ сохранения данных в XML
    10 февраля 2014 г. 11:52
  • Пишете простой класс Сериализации/десериализации

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    using Windows.Storage;
    using Windows.Storage.Streams;
    using Windows.System.Threading;
    using PikabuRss.Models;
    
    namespace PikabuRss.Common
    {
        internal static class XmlSerializable
        {
            public static string Filename;
    
            private static List<object> _data = new List<object>();
    
            public static List<object> Data
            {
                get { return _data; }
                set { _data = value; }
            }
    
            public static StorageFile File { get; set; }
    
            public static async Task Save<T>()
            {
                if (!await DoesFileExistAsync(Filename))
                {
                    await
                        ApplicationData.Current.LocalFolder.CreateFileAsync(Filename,
                            CreationCollisionOption.ReplaceExisting);
                }
                await SaveAsync<T>(_data, Filename);
            }
    
            public static async Task Restore<T>()
            {
                if (await DoesFileExistAsync(Filename))
                {
                    await ThreadPool.RunAsync(sender => RestoreAsync<T>().Wait(), WorkItemPriority.Normal);
                }
                else
                {
                    File = await ApplicationData.Current.LocalFolder.CreateFileAsync(Filename);
                }
            }
    
            private static async Task<bool> DoesFileExistAsync(string fileName)
            {
                try
                {
                    await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    
            private static async Task SaveAsync<T>()
            {
                StorageFile sessionFile =
                    await
                        ApplicationData.Current.LocalFolder.GetFileAsync(Filename);
                IRandomAccessStream sessionRandomAccess = await sessionFile.OpenAsync(FileAccessMode.ReadWrite);
                IOutputStream sessionOutputStream = sessionRandomAccess.GetOutputStreamAt(0);
                var serializer = new XmlSerializer(typeof (List<object>), new[] {typeof (T)});
    
                serializer.Serialize(sessionOutputStream.AsStreamForWrite(), _data);
                sessionRandomAccess.Dispose();
                await sessionOutputStream.FlushAsync();
                sessionOutputStream.Dispose();
            }
    
            private static async Task RestoreAsync<T>()
            {
                StorageFile sessionFile =
                    await
                        ApplicationData.Current.LocalFolder.GetFileAsync(Filename);
                if (sessionFile == null)
                {
                    return;
                }
                IInputStream sessionInputStream = await sessionFile.OpenReadAsync();
    
                try
                {
                    var serializer = new XmlSerializer(typeof (List<object>), new[] {typeof (T)});
                    Data = (List<object>) serializer.Deserialize(sessionInputStream.AsStreamForRead());
                }
                catch (Exception)
                {
                }
                finally
                {
                    sessionInputStream.Dispose();
                }
            }
    
            public static List<Item> GetItemCollection()
            {
                return _data.Select(data => data as Item).ToList();
            }
        }
    }

    Соответственно публичные методы Save() и Restore() сохраняют и восстанавливают коллекцию

    Обращение к методам будет такое

    // Для сохранения
    XmlSerializable.Filename = "pikabu.xml";
    foreach (var item in pikabuCollection)
        {
            XmlSerializable.Data.Add(item);
        }
    await XmlSerializable.Save<Item>();
    
    // Для восстановления
    XmlSerializable.Filename = "pikabu.xml";
    await XmlSerializable.Restore<Item>();
    var data = XmlSerializable.GetItemCollection();

    XmlSerializable.Filename - желаемое имя файлы,

    XmlSerializable.Data = Список объектов

    10 февраля 2014 г. 12:48
  • Евгений, переделайте этот класс.

    Не храните имя и файл в статике - передавайте имя в метод. Файл вообще не храните, он Вам не нужен.

    Не храните коллекцию в статике, передавайте объект, который хотите сериализовать в метод.

    Не пишите руками Dispose, оборачивайте работы с IDisposable в using.


    • Изменено Oleg Kurzov 10 февраля 2014 г. 13:15
    10 февраля 2014 г. 13:14
  • Поправил класс для сериализации/десериализации

    internal static class XmlSerializable
        {
            /// <summary>
            ///     Сериализует полученный на входе список в XML-документ
            /// </summary>
            /// <typeparam name="T">Тип объектов списка</typeparam>
            /// <param name="data">Список для сериализации</param>
            /// <param name="fileName">Желаемое имя файла</param>
            /// <returns></returns>
            public static async Task Save<T>(List<object> data, string fileName)
            {
                StorageFile sf;
                if (!await DoesFileExistAsync(fileName))
                {
                    sf = await
                        ApplicationData.Current.LocalFolder.CreateFileAsync(fileName,
                            CreationCollisionOption.ReplaceExisting);
                }
                else
                {
                    sf = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                }
                await SaveAsync<T>(data, sf);
            }
    
            /// <summary>
            ///     Десериализует XML-документ в простой перечислитель
            /// </summary>
            /// <typeparam name="T">Тип элементов списка</typeparam>
            /// <param name="fileName">Название файла для десериализации</param>
            /// <returns></returns>
            public static async Task<IEnumerable<Object>> Restore<T>(string fileName)
            {
                if (!await DoesFileExistAsync(fileName)) return null;
                StorageFile sf = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                if (sf == null) return null;
                List<object> data = await RestoreAsync<T>(sf);
                return data;
            }
    
            private static async Task<bool> DoesFileExistAsync(string fileName)
            {
                try
                {
                    await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    
            private static async Task SaveAsync<T>(List<object> data, StorageFile storageFile)
            {
                using (IRandomAccessStream sessionRandomAccess = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    using (IOutputStream sessionOutputStream = sessionRandomAccess.GetOutputStreamAt(0))
                    {
                        var serializer = new XmlSerializer(typeof (List<object>), new[] {typeof (T)});
                        serializer.Serialize(sessionOutputStream.AsStreamForWrite(), data);
                    }
                }
            }
    
            private static async Task<List<Object>> RestoreAsync<T>(StorageFile storageFile)
            {
                List<Object> data;
                using (IInputStream sessionInputStream = await storageFile.OpenReadAsync())
                {
                    var serializer = new XmlSerializer(typeof (List<Object>), new[] {typeof (T)});
                    data = (List<Object>) serializer.Deserialize(sessionInputStream.AsStreamForRead());
                }
                return data;
            }
        }
    //Сериализация списка
    await XmlSerializable.Save<Item>(PikabuItems.ToList<object>(), "pikabu.xml");
    
    //Десериализация списка
    var col = await XmlSerializable.Restore<Item>("pikabu.xml");
    var list = col.Select(data => data as Item).ToList();


    11 февраля 2014 г. 10:21
  • Евгений, а Вы при десериализации возвращаете List<Object>, но у Вас есть generic "Т", незачем кастить к List<object>.
    11 февраля 2014 г. 11:22
  • Олег, а как это сделать? Я пока что налетаю на эксэпшн
    11 февраля 2014 г. 12:10
  • Для начала Save тоже нужно делать через "T", попробуйте вообще избавиться от List<object>. 
    11 февраля 2014 г. 12:57
  • Да, действительно. Все работает 

    internal static class XmlSerializable
        {
            /// <summary>
            ///     Сериализует полученный на входе список в XML-документ
            /// </summary>
            /// <typeparam name="T">Тип объектов списка</typeparam>
            /// <param name="data">Список для сериализации</param>
            /// <param name="fileName">Желаемое имя файла</param>
            /// <returns></returns>
            public static async Task Save<T>(List<T> data, string fileName)
            {
                StorageFile sf;
                if (!await DoesFileExistAsync(fileName))
                {
                    sf = await
                        ApplicationData.Current.LocalFolder.CreateFileAsync(fileName,
                            CreationCollisionOption.ReplaceExisting);
                }
                else
                {
                    sf = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                }
                await SaveAsync(data, sf);
            }
    
            /// <summary>
            ///     Десериализует XML-документ в список
            /// </summary>
            /// <typeparam name="T">Тип элементов списка</typeparam>
            /// <param name="fileName">Название файла для десериализации</param>
            /// <returns></returns>
            public static async Task<List<T>> Restore<T>(string fileName)
            {
                if (!await DoesFileExistAsync(fileName)) return null;
                StorageFile sf = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                if (sf == null) return null;
                List<T> data = await RestoreAsync<T>(sf);
                return data;
            }
    
            private static async Task<bool> DoesFileExistAsync(string fileName)
            {
                try
                {
                    await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    
            private static async Task SaveAsync<T>(List<T> data, StorageFile storageFile)
            {
                using (IRandomAccessStream sessionRandomAccess = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    using (IOutputStream sessionOutputStream = sessionRandomAccess.GetOutputStreamAt(0))
                    {
                        var serializer = new XmlSerializer(typeof (List<T>));
                        serializer.Serialize(sessionOutputStream.AsStreamForWrite(), data);
                    }
                }
            }
    
            private static async Task<List<T>> RestoreAsync<T>(StorageFile storageFile)
            {
                List<T> data;
                using (IInputStream sessionInputStream = await storageFile.OpenReadAsync())
                {
                    var serializer = new XmlSerializer(typeof (List<T>));
                    data = (List<T>) serializer.Deserialize(sessionInputStream.AsStreamForRead());
                }
                return data;
            }
        }

    И, соответственно, вызов методов будет такой:

    await XmlSerializable.Save(PikabuItems.ToList(), "pikabu.xml");
    var col = await XmlSerializable.Restore<Item>("pikabu.xml");



    • Изменено evgeniy.polonskiy 12 февраля 2014 г. 5:44 чуть поправил
    12 февраля 2014 г. 5:36
  • Теперь, попробуйте избавиться от List<T> и замените его на T :)
    12 февраля 2014 г. 6:25
  • Так? )

    internal static class XmlSerializable
        {
            /// <summary>
            ///     Сериализует полученный на входе список в XML-документ
            /// </summary>
            /// <typeparam name="T">Тип объектов списка</typeparam>
            /// <param name="data">Список для сериализации</param>
            /// <param name="fileName">Желаемое имя файла</param>
            /// <returns></returns>
            public static async Task Save<T>(T data, string fileName)
            {
                StorageFile sf;
                if (!await DoesFileExistAsync(fileName))
                {
                    sf = await
                        ApplicationData.Current.LocalFolder.CreateFileAsync(fileName,
                            CreationCollisionOption.ReplaceExisting);
                }
                else
                {
                    sf = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                }
                await SaveAsync(data, sf);
            }
    
            /// <summary>
            ///     Десериализует XML-документ в T
            /// </summary>
            /// <typeparam name="T">Тип элементов списка</typeparam>
            /// <param name="fileName">Название файла для десериализации</param>
            /// <returns></returns>
            public static async Task<T> Restore<T>(string fileName)
            {
                try
                {
                    StorageFile sf = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                    T data = await RestoreAsync<T>(sf);
                    return data;
                }
                catch (Exception e)
                {
                    throw new Exception(e.Message);
                }
            }
    
            private static async Task<bool> DoesFileExistAsync(string fileName)
            {
                try
                {
                    await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    
            private static async Task SaveAsync<T>(T data, StorageFile storageFile)
            {
                using (IRandomAccessStream sessionRandomAccess = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    using (IOutputStream sessionOutputStream = sessionRandomAccess.GetOutputStreamAt(0))
                    {
                        try
                        {
                            var serializer = new XmlSerializer(typeof(T));
                            serializer.Serialize(sessionOutputStream.AsStreamForWrite(), data);
                        }
                        catch (Exception e)
                        {
                            throw new Exception(e.Message);
                        }
                    }
                }
            }
    
            private static async Task<T> RestoreAsync<T>(StorageFile storageFile)
            {
                T data;
                using (IInputStream sessionInputStream = await storageFile.OpenReadAsync())
                {
                    var serializer = new XmlSerializer(typeof (T));
                    data = (T) serializer.Deserialize(sessionInputStream.AsStreamForRead());
                }
                return data;
            }
        }

    Соответственно обращение к методам

    await XmlSerializable.Save(PikabuItems.ToList(), "pikabu.xml");
    var col = await XmlSerializable.Restore<List<Item>>("pikabu.xml");

    12 февраля 2014 г. 6:50
  • Да, уже лучше.
    12 февраля 2014 г. 7:50
  • Не работает, ругается на строчку 

    var col = await XmlSerializable.Restore<List<Item>>("pikabu.xml");

    А конкретнее на <List<Item>>, если заменить ее на <ListViewItem> то показывает что ListView только для чтения.

    Как же мне сохранить и восстановить ListView, какие могут быть альтернативы? Может как-нибудь через LocalSettings? Не думал что такая задача вызовет сложности...

    20 февраля 2014 г. 19:14
  • Ну вам там явно не <ListViewItem> надо подставлять, <List<Item>> надо заменить на тип коллекции которая привязана к ListView, скорее всего ObservableCollection<"тип"> или просто List<"тип">

    А настройки приложения на то и настройки, что хранят там настройки а не данные...

    20 февраля 2014 г. 19:28
  • Ну вам там явно не <ListViewItem> надо подставлять, <List<Item>> надо заменить на тип коллекции которая привязана к ListView, скорее всего ObservableCollection<"тип"> или просто List<"тип">

    А настройки приложения на то и настройки, что хранят там настройки а не данные...

    listView1.Items = await XmlSerializable.Restore<List<ObservableCollection>>("pikabu.xml"); - "Не удалось найти имя типа или пространства имен ObservableCollection

    А если:
    listView1.Items = await XmlSerializable.Restore<List<ObservableCollection>>("pikabu.xml"); - 

    Ошибка 2 Невозможно присвоить значение свойству или индексатору "Windows.UI.Xaml.Controls.ItemsControl.Items" -- доступ только для чтения C:\Users\Александр\Documents\Visual Studio 2013\Projects\App1\App1\EditPage.xaml.cs 241 13 App1


    Ошибка 3 Неявное преобразование типа "System.Collections.Generic.List<Windows.UI.Xaml.Controls.ItemCollection>" в "Windows.UI.Xaml.Controls.ItemCollection" невозможно C:\Users\Александр\Documents\Visual Studio 2013\Projects\App1\App1\EditPage.xaml.cs 241 33 App1

    ListView простой, состоит из строчек типа:

    listView1.Items.Add("111");

    listView1.Items.Add("222");

    listView1.Items.Add("333");

    20 февраля 2014 г. 22:37
  • Насколько понял у вас ListView заполняется вручную, тогда там идет не типизированная коллекция ItemCollection. Коллекция в ListView через свойство Items доступна только для чтения, присваиванием ее заполнить не получится. Можно попробовать заполнить свойство ItemsSource, вот так:

    listView1.ItemsSource = await XmlSerializable.Restore<ItemCollection>("pikabu.xml");

    Не знаю правда что получится, можно еще попробовать сначала создать коллекцию ItemCollection и восстановить в эту коллекцию, а уже потом заполнить свойство ItemsSource, примерно так:

    ItemCollection items = await XmlSerializable.Restore<ItemCollection>("pikabu.xml"); listView1.ItemsSource = items;

    21 февраля 2014 г. 1:39
  • Ну или еще вариант:

    ItemCollection items = await XmlSerializable.Restore<ItemCollection>("pikabu.xml");
    foreach (var item in items)
    {
       listView1.Items.Add(item);
    }
    21 февраля 2014 г. 1:43
  • Не работает, ругается на строчку 

    var col = await XmlSerializable.Restore<List<Item>>("pikabu.xml");

    А конкретнее на <List<Item>>, если заменить ее на <ListViewItem> то показывает что ListView только для чтения.

    Как же мне сохранить и восстановить ListView, какие могут быть альтернативы? Может как-нибудь через LocalSettings? Не думал что такая задача вызовет сложности...

    Вы запутались в самом начале

    List<Item> это список, состоящий из элементов типа Item. С таким же успехом можно заменить Item на string, int и так далее... Item может состоять из чего угодно, например:

    public class Item
    {
         public string Title;
         public int PagesCount;
         // Ну и что-угодно
    }
    5 марта 2014 г. 5:25