Windows8 Metro开发 (04) : 保存/读取本地应用程序设置

Josè Mourinho的专栏 http://blog.csdn.net/zyc13701469860

转载请注明原作者和出处。


有些时候我们需要保存应用程序的设置,如用户的系统设定。在Android中,我们可以使用sharepreference。在Metro中我们该怎么做呢?

保存/读取基本类型数据
 Metro程序会把要保存的数据写入ApplicationData.Current.LocalSettings字典中,并保存在本地。程序在开始运行的时候会从本地初始化该字典。加载之前保存的数据。这样我们就可以方便的保存/读取基本类型数据了。
我将其封装成了一个工具类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;

namespace Win8_Study.Pages
{
    class LocalDataUtil
    {
        #region 保存/读取基本类型数据
        public static void SaveData(string key, object value)
        {
            ApplicationData.Current.LocalSettings.Values[key] = value;
        }

        public static object GetData(string key)
        {
            return ApplicationData.Current.LocalSettings.Values[key];
        }


        public static void RemoveData(string key)
        {
            ApplicationData.Current.LocalSettings.Values.Remove(key);
        }
        #endregion
    }
}

下面我们来看一个示例:
Windows8 Metro开发 (04) : 保存/读取本地应用程序设置_第1张图片
默认显示的字体大小为24,我们将其字体改为28后返回到主页面,然后重新进入该页面。你会发现字体的大小变为28了。
重新启动程序进入该页面,你会发现字体的大小仍然为28。
下拉列表的选中项与字体大小是时刻对应的.

实现方法如下
1.每次进入该页面的时候,首先获取之前保存的字体大小
a.没有获取到
将字体大小设为默认值
b.获取到
将字体大小设为获取的值
2.用户改变字体大小时,保存改变后的值
    public sealed partial class LocalDataPage : Win8_Study.Common.LayoutAwarePage
    {
        private readonly string TEXT_VALUE = "国米_百度百科\n" +
            "国际米兰足球俱乐部(Football Club Internazionale Milano,简称 Inter 或 Internazionale)" +
            "是一家位于意大利北部伦巴第区米兰市的足球俱乐部。";
        private readonly double TEXT_FONT_SIZE = 24;
        private readonly string TEXT_FONT_SIZE_KEY = "LocalDataPage-TEXT_FONT_SIZE_KEY";

        public LocalDataPage()
        {
            this.InitializeComponent();
        }

        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            Init();
        }


        private void Init()
        {
            //首先读取之前保存的设置,如果为空设置成默认状态
            InitFontSize();
            leftTextBlock.Text = TEXT_VALUE;
        }

        protected override void SaveState(Dictionary<String, Object> pageState)
        {
        }

        #region 保存程序设置
        private void OnLeftComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var comboBox = sender as ComboBox;
            var item = comboBox.SelectedItem as ComboBoxItem;
            SetTextFontSize(Convert.ToDouble(item.Content));
        }

        private void SetTextFontSize(double size)
        {
            leftTextBlock.FontSize = size;
            LocalDataUtil.SaveData(TEXT_FONT_SIZE_KEY, size);
        }

        private void InitFontSize()
        {
            var obj = LocalDataUtil.GetData(TEXT_FONT_SIZE_KEY);
            double size = TEXT_FONT_SIZE;
            if (obj != null)
            {
                size = Convert.ToDouble(obj);
            }    
            foreach (var element in leftFontSizeComboBox.Items)
            {
                var item = element as ComboBoxItem;
                if (item.Content.ToString().Equals(size.ToString()))
                {
                    leftFontSizeComboBox.SelectedItem = item;
                    break;
                }
            }            
        }
        #endregion
        ...
    }
或许你会尝试着用这种方法去非基本类型的数据(比如Page,Color什么的)。嘿嘿,挂了吧。
那么我们该怎样去保存非基本类型的数据呢?比如一个包含学生信息的集合?

保存/读取非基本类型的数据--序列化/反序列化
保存程序的实时数据是十分必要的。比如你从网络上获取了一些娱乐新闻并显示给用户,你需要将这些数据保存下来,以便程序下次运行的时候使用。
下次运行程序的时候,这些数据就会变成本地的了,加载速度会非常快,因为你不需要再去从网络获取数据。
如果你不这样做的话,用户可能会在网络非常拥塞的情况下看到一个非常"干净"的屏幕。这个时候你的应用也许会被(应该是必须)。。。

举一个"稍微"复杂点的例子
现在有Student,Coder两个类,它们都继承了父类 People。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace Win8_Study.Pages
{
    [DataContract]
    public abstract class People
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public int Age { get; set; }

        public People(string name,int age)
        {
            this.Name = name;
            this.Age = age;
        }
    }

    [DataContract]
    public class Student : People
    {
        [DataMember]
        public int Score { get; set; }

        public Student(string name, int age, int score)
            : base(name, age)
        {
            this.Score = score;
        }
    }

    [DataContract]
    public class Coder : People
    {
        [DataMember]
        public int WorkYears { get; set; }

        public Coder(string name, int age, int workYears)
            : base(name, age)
        {
            this.WorkYears = workYears;
        }
    }
}
我们需要在ListView上随机显示一些学生和程序员的信息,并保存下来。然后清空ListView,读取保存的数据,看结果与之前的是否相同。

创建学生和程序员信息的方法很简单,在这里创建5-10条信息,每条信息的内容随机显示:
private List<People> GetPeopleDatas()
        {
            List<People> peoples = new List<People>();
            Random ran = new Random(DateTime.Now.Millisecond);
            int count = ran.Next(5) + 5;//5 - 10
            for (int i = 0; i < count; ++i)
            {
                int type = ran.Next(2);
                if (type == 0)
                {
                    peoples.Add(new Student("学生" + (i + 1), ran.Next(12) + 6, 60 + ran.Next(41)));
                }
                else
                {
                    peoples.Add(new Coder("程序员" + (i + 1), ran.Next(10) + 22, ran.Next(5)));
                }
            }
            return peoples;
        }
根据类别创建不同ListView项
private void OnRightRandomAddDataButtonClicked(object sender, RoutedEventArgs e)
        {
            _peoples = GetPeopleDatas();
            SetListViewData(_peoples);
        }
        private void SetListViewData(List<People> peoples)
        {
            itemListView.Items.Clear();
            foreach (People p in peoples)
            {
                ListViewItem item = new ListViewItem();
                item.FontSize = 20;
                if (p is Student)
                {
                    Student s = p as Student;
                    item.Content = string.Format("{0} 年龄:{1} 成绩: {2}", s.Name, s.Age, s.Score);
                }
                else
                {
                    Coder c = p as Coder;
                    item.Content = string.Format("{0} 年龄:{1} 工作时间: {2}年", c.Name, c.Age, c.WorkYears);
                }
                itemListView.Items.Add(item);
            }
        }

保存数据
        private async void OnRightSaveDataButtonClicked(object sender, RoutedEventArgs e)
        {
            await SerializerUtil.XMLSerialize(_peoples,typeof(List<People>));
            await PopupUtil.ShowMessageDialog(string.Format("保存数据成功! item数量{0}",_peoples.Count), "提示");
        }
注意到People,Student,Coder中的序列化标志的吗?不添加的话是无法序列化的。
其中 SerializerUtil.XMLSerialize是我封装的序列化代码的方法,其实现方式如下:
        public static async Task XMLSerialize(object instance, Type type)
        {
            //取得当前程序存放数据的目录
            StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
            //定义文件名
            string fileName = "LocalDataPage-list_data.xml";
            //创建文件,如果文件存在就覆盖
            StorageFile newFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting)
            //将内容序列化至文件
            Stream newFileStream = await newFile.OpenStreamForWriteAsync();
            DataContractSerializer ser = new DataContractSerializer(type, GetTypes());
            ser.WriteObject(newFileStream, instance);
            newFileStream.Dispose();
        }
注意GetTypes()方法,在序列化的时候需要指定序列化对象的类型集合。在这里需要序列化的数据类型有3个:People,Student,Coder。所以我们应该这样设定:
private static ObservableCollection<Type> GetTypes()
        {
            //添加要序列化的类型
            if (_Types == null)
            {
                _Types = new ObservableCollection<Type>();
                _Types.Add(typeof(People));
                _Types.Add(typeof(Student));
                _Types.Add(typeof(Coder));
            }
            return _Types;
        }
其中_Types是全局对象ObservableCollection<Type>。
至此,数据就保存好了。

读取数据
读取数据也就是进行反序列化,我们可以得到之前保存的对象集合,其数据类型是List<People>,然后我们将该对象集合交给ListView显示即可。
private async void OnRightLoadDataButtonClicked(object sender, RoutedEventArgs e)
        {
            try
            {
                var obj = await SerializerUtil.XMLDeserialize(typeof(List<People>));
                _peoples = obj as List<People>;
                SetListViewData(_peoples);
                await PopupUtil.ShowMessageDialog(string.Format("读取数据成功! item数量{0}", _peoples.Count),
                    "提示");
                return;
            }
            catch (FileNotFoundException)
            {
                
            }
            await PopupUtil.ShowMessageDialog("你还没有保存数据。", "提示");
        }
反序列化
 public static async Task<object> XMLDeserialize(Type type)
        {
            StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
            string fileName = "LocalDataPage-list_data.xml";
            StorageFile newFile = await folder.GetFileAsync(fileName);
            Stream newFileStream = await newFile.OpenStreamForReadAsync();
            //进行反序列化
            DataContractSerializer ser = new DataContractSerializer(type, GetTypes());
            object instance = ser.ReadObject(newFileStream);
            newFileStream.Dispose();
            return instance;
        }
可以看到读取的数据与之前保存的相同.我们的目的达到了。

程序运行的效果如下:
1.随机显示学生和程序员信息:
Windows8 Metro开发 (04) : 保存/读取本地应用程序设置_第2张图片
2.保存数据
保存文件的内容
<ArrayOfPeople xmlns="http://schemas.datacontract.org/2004/07/Win8_Study.Pages" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><People i:type="Student"><Age>14</Age><Name>学生1</Name><Score>66</Score></People><People i:type="Coder"><Age>24</Age><Name>程序员2</Name><WorkYears>4</WorkYears></People><People i:type="Student"><Age>7</Age><Name>学生3</Name><Score>86</Score></People><People i:type="Coder"><Age>23</Age><Name>程序员4</Name><WorkYears>1</WorkYears></People><People i:type="Coder"><Age>25</Age><Name>程序员5</Name><WorkYears>2</WorkYears></People></ArrayOfPeople>
3.清除数据并读取之前保存的数据
显示效果同第一张图.

专栏网址:http://blog.csdn.net/zyc13701469860/article/details/8194090






你可能感兴趣的:(C#,C#,metro,metro,windows8,windows8,本地应用程序设置)