Windows App开发之文件与数据
读取文件和目录名
这一节開始我们将陆续看到Windows App是如何操作文件的。
在Windows上读取文件名称、目录名
首先我们在XAML中定义一个Button和TextBlock,将读取文件/目录名的过程写在前者的click事件中。后者则用来显示文件信息。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal">
<Button Name="btnGetName" Width="200" Height="100" Content="读取文件名称" Click="btnGetName_Click"/>
<TextBlock Name="textBlockFileName" Width="500" Height="300" FontSize="30" Margin="12"/>
</StackPanel>
</Grid>
以下这段代码,首先通过StorageFolder类读取图片库。然后使用异步的方式将图片库的文件和目录信息载入对应的List中。
新建一个StringBuilder用以保存这些文件的信息。在这里仅仅是使用了文件/目录的Name属性,但属性还有非常多。比方Path属性。最后再将这些获取到的信息赋值给TextBlock就可以。
private async void btnGetName_Click(object sender, RoutedEventArgs e)
{
StorageFolder pictureFolder = KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> pictureFileList = await pictureFolder.GetFilesAsync();
IReadOnlyList<StorageFolder> pictureFolderList = await pictureFolder.GetFoldersAsync();
StringBuilder picutreFolderInfo = new StringBuilder();
foreach(StorageFile f in pictureFileList)
{
picutreFolderInfo.Append(f.Name+"\n");
}
foreach(StorageFolder f in pictureFolderList)
{
picutreFolderInfo.Append(f.Name+"\n");
}
textBlockFileName.Text = picutreFolderInfo.ToString();
}
注意要在方法名前面加上async哦。还有要在清单文件里声明我们的应用要使用图片库哦,一会在Windows Phone中也一样。
在Windows Phone上读取文件名称、目录名
后台代码不用做不论什么改动。仅仅需把XAML代码改动改动以适应屏幕就可以~
<Grid>
<StackPanel Orientation="Vertical">
<Button Name="btnGetName" Width="150" Height="70" HorizontalAlignment="Center"
Content="读取文件名称" Click="btnGetName_Click"/>
<TextBlock Name="textBlockFileName" Width="300" Height="300" FontSize="30" Margin="12" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
读取文件名称的其它方法
private async void btnGetName_Click(object sender, RoutedEventArgs e)
{
StorageFolder picutureFolder = KnownFolders.PicturesLibrary;
StringBuilder pictureFolderInfo = new StringBuilder();
IReadOnlyList<IStorageItem> pictureFileItem = await picutureFolder.GetItemsAsync();
foreach(var i in pictureFileItem)
{
if (i is StorageFolder)
pictureFolderInfo.Append(i.Name + "\n");
else
pictureFolderInfo.Append(i.Name + "\n");
}
textBlockFileName.Text = pictureFolderInfo.ToString();
}
文件选取器
使用文件选取器保存文件
就我个人而言。还是非常喜欢使用文件选取器的。由于能够用自己的代码来调用系统的各种弹框。
在这个演示样例中。首先在XAML中加入一个Button和一个TextBlock,分别命名为btnSaveFile和tBlockSaveInfo。
对于这个保存文件这个操作在后台的Click事件中就能够轻易完毕了。
private async void btnSaveFile_Click(object sender, RoutedEventArgs e)
{
FileSavePicker saveFile = new FileSavePicker();
saveFile.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// 显示在下拉列表的文件类型
saveFile.FileTypeChoices.Add("批处理文件", new List<string>() { ".bat" });
// 默认的文件名称
saveFile.SuggestedFileName = "SaveFile";
StorageFile file = await saveFile.PickSaveFileAsync();
if(file!=null)
{
// 在用户完毕更改并调用CompleteUpdatesAsync之前,阻止对文件的更新
CachedFileManager.DeferUpdates(file);
string fileContent = "@echo off \n dir/s \n pause";
await FileIO.WriteTextAsync(file, fileContent);
// 当完毕更改时,其它应用程序才干够对该文件进行更改。
FileUpdateStatus updateStatus = await CachedFileManager.CompleteUpdatesAsync(file);
if(updateStatus==FileUpdateStatus.Complete)
{
tBlockSaveInfo.Text = file.Name + " 已经保存好了。
";
}
else
{
tBlockSaveInfo.Text = file.Name + " 保存失败了。";
}
}
else
{
tBlockSaveInfo.Text = "保存操作被取消。
";
}
}
代码中的下拉列表的文件类型就是例如以下所看到的这个样子哟。
大部分的内容我都已经通过凝视的方式加入到代码中了,至于fileContent的那段代码究竟是什么意思,大家试试就知道了,我感觉蛮有意思的。3行代码列出硬盘上全部文件及目录
假设大家试过打开这个bat文件,有没有认为有趣呢?
更厉害的是。我们刚才所写的代码能够在Windows Phone上不经改动而直接使用。我的Lumia 638已经刷上了Windows 10预览版。大家能够瞧瞧,全新的资源管理器。
使用文件选取器打开文件
和用文件选取器保存文件相相似,打开文件的逻辑都差点儿相同。
这个演示样例中相同在XAML中定义一个名为btnOpenFile的Button和一个名为tBlockOpenInfo的TextBlock。
private async void btnOpenFile_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker openFile = new FileOpenPicker();
openFile.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openFile.ViewMode = PickerViewMode.List;
openFile.FileTypeFilter.Add(".txt");
openFile.FileTypeFilter.Add(".docx");
openFile.FileTypeFilter.Add(".pptx");
// 选取单个文件
StorageFile file = await openFile.PickSingleFileAsync();
if (file != null)
{
tBlockOpenInfo.Text = "你所选择的文件是: " + file.Name;
}
else
{
tBlockOpenInfo.Text = "打开文件操作被取消。";
}
// 选择多个文件
//IReadOnlyList<StorageFile> fileList = await openFile.PickMultipleFilesAsync();
//StringBuilder fileOpenInfo = new StringBuilder();
//if(fileList!=null)
//{
// foreach( StorageFile f in fileList)
// {
// fileOpenInfo.Append(f.Name + "\n");
// }
// tBlockOpenInfo.Text = "你所选择的文件是: "+"\n"+ fileOpenInfo.ToString();
//}
//else
//{
// tBlockOpenInfo.Text = "打开文件操作被取消。
";
//}
}
我已经将选取多个文件的代码也列了出来,仅仅须要取消凝视就可以。像ViewMode和FileTypeFilter这样的属性。看看名字应该都知道了吧。重在实践。
在手机上也是通用的。刚才我试过了。成功进入了资源管理器,只是没能打开文件。应该是由于预览版的原因,这个预览版连Office都被移除了。预计会在下一版中加入通用版的Office应用。
写入和读取
准备工作
在XAML中加入一个TextBlock用于显示相关信息,加入一个Button来使用它的Click事件。当然了。最后分别创建2个。
创建文件和读取文件
1.实例化StorageFolder类
我们的文件不可能让其任意保存在计算机/手机中的不论什么一个地方,应该先确定它的目录,对吧?
在新的Windows 8中。微软开启了Windows上的App时代,下载的软件再也不能任意安装到不论什么地方了。而是由操作系统统一放到一块叫做“独立存储”的地方。这也是出于安全的考虑。用过Windows Phone 8的朋友应该更加清楚了。
那么以下这行代码的LocalFolder究竟在哪里呢?
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
下图中的文件,就是我当前所写的App。
(补充一条哦,一開始我装了Win8后,下载了一个游戏。模拟类的,有金币呀什么的,后来我找到这个App的文件,将数据改了之后金币就哗哗的啦。
当然了,对于其它单机而言这个全然不值一提,但App的数据,相信还有非常多人没有改过吧。)
那么这张图中的红方框的目录就是LocalFolder啦,以下另一个存储漫游文件的目录。
不论是读取文件还是写入文件。都得先确定一个目录哦。
2.实例化StorageFile
确定了目录,就得确定文件咯。对于创建文件而言。执行以下代码。既然用到了异步,在函数上加上async是不可缺少的咯。这一点我们在前面讲到过。
后面的ReplaceExisting属性是指的,假设该文件(名)已经存在了,则替换它。
StorageFile file =
await folder.CreateFileAsync("New Document.txt", CreationCollisionOption.ReplaceExisting);
那么对于读取文件呢。就直接读取好啦。
StorageFile file = await folder.GetFileAsync("sample.txt");
3.创建和读取文件
将文本写入文件依照例如以下代码。将文件名称和文本内容(字符串)。
await FileIO.WriteTextAsync(file, "Write text to file.");
读取文件也是相似的。
string text = await FileIO.ReadTextAsync(file);
我们还能够将这个读取的字符串传递给前面定义的TextBlock来加以调试。以下是完整的代码。
// 创建文件
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("New Document.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, "Write text to file.");
// 2 从文本读取文件
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file = await folder.GetFileAsync("sample.txt");
string text = await Windows.Storage.FileIO.ReadTextAsync(file);
tBlockReadInfo.Text = text;
使用缓冲区将字节写入到文件或从文件读取字节
1.实例化StorageFolder类
同上。
2.实例化StorageFile
同上。
3.将字节写入到文件
a.建立缓冲区
var buffer = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary("There's buffer ...... ", Windows.Security.Cryptography.BinaryStringEncoding.Utf8);
b.将缓冲区中的字节写入到文件
await Windows.Storage.FileIO.WriteBufferAsync(file, buffer);
4.从文件读取字节
a.将文件载入到缓冲区
var buffer = await Windows.Storage.FileIO.ReadBufferAsync(file);
b.实例化DataReader。读取缓冲区
DataReader dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer);
c.从DataReader对象中读取字符串
string text = dataReader.ReadString(buffer.Length);
使用流将文本写入文件或从文件读取文本
1.实例化StorageFolder类
同上。
2.实例化StorageFile
同上。
3.新建流,并异步地将file打开。使用可读写的方式
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
4.将文本写入到文件
a.使用using
using (var writeStream= stream.GetOutputStreamAt(0))
{
......
}
b.(在using语句的花括号内)创建DataWriter对象。并调用DataWriter.WriteString方法,将文本写入到writeStream中
DataWriter dataWriter = new DataWriter(writeStream);
dataWriter.WriteString("Stream is a good thing.");
c.将文本保存到文件里。并通过StoreAsync和FlushAsync方法存储和关闭流
await dataWriter.StoreAsync();
await writeStream.FlushAsync();
5.从文件读取文本
a.获取该流的size
var size = stream.Size;
b.使用using
using (var readStream = stream.GetOutputStreamAt(0))
{
......
}
c.(在using语句的花括号内)创建DataWriter对象,并调用LoadAsync方法,最后调用ReadString就可以。最后还能够将信息输出到TextBlock中。
DataReader dataReader = new DataReader(readStream);
uint uintBytes = await dataReader.LoadAsync((uint)size);
string text = dataReader.ReadString(uintBytes);
tBlockReadInfo.Text = text;
获取文件属性
这一节来看看获取文件属性吧,能够获取到文件名称、类型、近期訪问时间等等属性。
创建Button和TextBlock
以下这段代码呢,都非常easy。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Width="200" Height="70" Name="btnGetProp" Content="获取文件属性" Click="btnGetProp_Click"/>
<TextBlock Name="tBlockProp" Margin="12" Width="480" FontSize="30"/>
</StackPanel>
</Grid>
</Page>
在Click事件中,先获取到图片库,当然了。你能够获取其它地方。我电脑上的库文件里,就仅仅有文档库和图片库有文件了。
然后创建一个文件查询,最后将这些文件都赋给files。
这里的var可谓是非常的强大啊。实例化一个StringBuilder对象来辅助输出信息那真是太完美只是了。我们在前面也经常使用到它。
var folder = KnownFolders.PicturesLibrary;
var fileQuery = folder.CreateFileQuery();
var files = await fileQuery.GetFilesAsync();
StringBuilder fileProperties = new StringBuilder();
for (int i = 0; i < files.Count; i++)
{
StorageFile file = files[i];
fileProperties.AppendLine("File name: " + file.Name);
fileProperties.AppendLine("File type: " + file.FileType);
BasicProperties basicProperties = await file.GetBasicPropertiesAsync();
string fileSize = string.Format("{0:n0}", basicProperties.Size);
fileProperties.AppendLine("File size: " + fileSize + " bytes");
fileProperties.AppendLine("Date modified: " + basicProperties.DateModified);
fileProperties.AppendLine(" ");
}
tBlockProp.Text = fileProperties.ToString();
这样一来就完毕对Name、FileType、Size和DateModified属性的获取,但另一类属性,则比較难以获取,它们就是“扩展属性”。
List<string> propertiesName = new List<string>();
propertiesName.Add("System.DateAccessed");
propertiesName.Add("System.FileOwner");
IDictionary<string, object> extraProperties = await file.Properties.RetrievePropertiesAsync(propertiesName);
var propValue = extraProperties[dateAccessedProperty];
if (propValue != null)
fileProperties.AppendLine("Date accessed: " + propValue);
propValue = extraProperties[fileOwnerProperty];
if (propValue != null)
fileProperties.AppendLine("File owner: " + propValue);
最后将fileProperties传递给TextBlock就可以。
tBlockProp.Text = fileProperties.ToString();
最后调试App,就会像下图一样了。
可是如你所见,文件名称太长了却无法自己主动换行,并且数据也显示不完整。改改XAML中的TextBlock就可以。TextWrapping属性设置为Wrap,则能够换行;将TextBlock加入到ScrollViewer内则会显示出一个滚动栏。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Width="200" Height="70" Name="btnGetProp" Content="获取文件属性" Click="btnGetProp_Click"/>
<ScrollViewer>
<TextBlock Name="tBlockProp" Margin="12" Width="480" FontSize="30" TextWrapping="Wrap"/>
</ScrollViewer>
</StackPanel>
</Grid>
保存、读取、删除应用数据
在前面的几节中,都是关于数据的。这方面的内容事实上还有非常多非常多。省略掉一部分后,也还是有非常多。这一节是非常重要的一部分。它关于如何保存和读取数据。
先来看看一个大概的背景吧,我这里写的非常easy啦。
保存的内容就是这四个框框里填写的数据咯。先上XAML代码。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Vertical">
<Rectangle Name="recRed" Width="200" Height="200" Fill="Red"/>
<Rectangle Name="recGreen" Width="100" Height="400" Fill="Green"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Text="红色矩形的长" FontSize="25" Margin="12" Width="150" Height="50" />
<TextBlock Text="红色矩形的宽" FontSize="25" Margin="12" Width="150" Height="50" />
<TextBlock Text="绿色矩形的长" FontSize="25" Margin="12" Width="150" Height="50" />
<TextBlock Text="绿色矩形的宽" FontSize="25" Margin="12" Width="150" Height="50" />
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBox Name="tBoxRedHeight" FontSize="25" Width="150" Height="50" Margin="12"/>
<TextBox Name="tBoxRedWidth" FontSize="25" Width="150" Height="50" Margin="12"/>
<TextBox Name="tBoxGreenHeight" FontSize="25" Width="150" Height="50" Margin="12" />
<TextBox Name="tBoxGreenWidth" FontSize="25" Width="150" Height="50" Margin="12" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Width="150" Height="70" Name="btnSaveAppData" Click="btnSaveAppData_Click" Content="保存应用数据"/>
<Button Width="150" Height="70" Name="btnReadAppData" Click="btnReadAppData_Click" Content="读取应用数据"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>
单个设置
先来看看单个设置呗,以下就是代码咯。
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
public MainPage()
{
this.InitializeComponent();
}
private void btnSaveAppData_Click(object sender, RoutedEventArgs e)
{
localSettings.Values["RectangleRedHeight"] = tBoxRedHeight.Text;
localSettings.Values["RectangleRedWidth"] = tBoxRedWidth.Text;
localSettings.Values["RectangleGreenHeight"] = tBoxGreenHeight.Text;
localSettings.Values["RectangleGreenWidth"] = tBoxGreenWidth.Text;
}
private void btnReadAppData_Click(object sender, RoutedEventArgs e)
{
Object objRectangleRedHeight = localSettings.Values["RectangleRedHeight"];
Object objRectangleRedWidth = localSettings.Values["RectangleRedWidth"];
Object objRectangleGreenHeight = localSettings.Values["RectangleGreenHeight"];
Object objRectangleGreenWidth = localSettings.Values["RectangleGreenWidth"];
recRed.Height = double.Parse(objRectangleRedHeight.ToString());
recRed.Width = double.Parse(objRectangleRedWidth.ToString());
recGreen.Height = double.Parse(objRectangleGreenHeight.ToString());
recGreen.Width = double.Parse(objRectangleGreenWidth.ToString());
}
首先定义了两个全局变量,假设看过前面几篇文章,这个应该就非常清楚了。
顾名思义,第一个是用来保存本地设置的,第二个则是用来訪问本地目录的。
这里是单个设置地进行保存的,后面还有2种方式。那么就来调试吧,注意在点击了保存数据按钮之后把App关掉哦,关掉之后再载入,这样才算是保存了应用数据嘛。你说对不正确呢?
以下就是我的測试结果了。
复合设置
我们的设计都不用变,后台代码改动例如以下。
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
public MainPage()
{
this.InitializeComponent();
}
private void btnSaveAppData_Click(object sender, RoutedEventArgs e)
{
Windows.Storage.ApplicationDataCompositeValue compositeSettings = new ApplicationDataCompositeValue();
compositeSettings["RectangleRedHeight"] = tBoxRedHeight.Text;
compositeSettings["RectangleRedWidth"] = tBoxRedWidth.Text;
compositeSettings["RectangleGreenHeight"] = tBoxGreenHeight.Text;
compositeSettings["RectangleGreenWidth"] = tBoxGreenWidth.Text;
localSettings.Values["RectangleSettings"] = compositeSettings;
}
private async void btnReadAppData_Click(object sender, RoutedEventArgs e)
{
Windows.Storage.ApplicationDataCompositeValue compositeSettings =
(Windows.Storage.ApplicationDataCompositeValue)localSettings.Values["RectangleSettings"];
if (compositeSettings == null)
{
Windows.UI.Popups.MessageDialog messageDialog =
new Windows.UI.Popups.MessageDialog("你好像没有保存不论什么应用数据哦!");
await messageDialog.ShowAsync();
}
else
{
recRed.Height = double.Parse(compositeSettings["RectangleRedHeight"].ToString());
recRed.Width = double.Parse(compositeSettings["RectangleRedWidth"].ToString());
recGreen.Height = double.Parse(compositeSettings["RectangleGreenHeight"].ToString());
recGreen.Width = double.Parse(compositeSettings["RectangleGreenWidth"].ToString());
}
}
使用ApplicationDataCompositeValue 会创建一个复合设置,通过代码所看到的方式就可以加入数据,最后会将其加入到localSettings中。
读取数据的时候。相同是先在localSettings中通过键值对的方式来取出这个复合设置。假设该设置为空。就会调用MessageDialog控件弹窗通知没有保存数据。
对于这个控件。能够訪问这里:【万里征程——Windows App开发】控件大集合2。假设复合设置存在则将他们分别进行类型转换后复制给对应的矩形的属性。
在容器中存放数据
在容器存放数据事实上也就这么回事啦,无非就是先创建一个容器,然后假设创建成功了。就在当中加入对应的数据就可以。
至于载入数据。在这里我使用了一个bool变量来检查容器是不是已经创建好了,假设创建好了就能够将对应的数据取出然后赋值了,假设没有的话则一样挑出弹窗。
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
public MainPage()
{
this.InitializeComponent();
}
private void btnSaveAppData_Click(object sender, RoutedEventArgs e)
{
Windows.Storage.ApplicationDataContainer containerSettings =
localSettings.CreateContainer("RecSettingsContainer", Windows.Storage.ApplicationDataCreateDisposition.Always);
if (localSettings.Containers.ContainsKey("RecSettingsContainer"))
{
localSettings.Containers["RecSettingsContainer"].Values["RectangleRedHeight"] = tBoxRedHeight.Text;
localSettings.Containers["RecSettingsContainer"].Values["RectangleRedWidth"] = tBoxRedWidth.Text;
localSettings.Containers["RecSettingsContainer"].Values["RectangleGreenHeight"] = tBoxGreenHeight.Text;
localSettings.Containers["RecSettingsContainer"].Values["RectangleGreenWidth"] = tBoxGreenWidth.Text;
}
}
private async void btnReadAppData_Click(object sender, RoutedEventArgs e)
{
bool hasContainerSettings = localSettings.Containers.ContainsKey("RecSettingsContainer");
if(hasContainerSettings)
{
recRed.Height = double.Parse(localSettings.Containers["RecSettingsContainer"].Values["RectangleRedHeight"].ToString());
recRed.Width = double.Parse(localSettings.Containers["RecSettingsContainer"].Values["RectangleRedWidth"].ToString());
recGreen.Height = double.Parse(localSettings.Containers["RecSettingsContainer"].Values["RectangleGreenHeight"].ToString());
recGreen.Width = double.Parse(localSettings.Containers["RecSettingsContainer"].Values["RectangleGreenWidth"].ToString());
}
else
{
Windows.UI.Popups.MessageDialog messageDialog =
new Windows.UI.Popups.MessageDialog("你好像没有保存不论什么应用数据哦!");
await messageDialog.ShowAsync();
}
}
接下来就来个执行的截图咯。还有弹框的截图^_^
删除数据
1.对于单个设置和复合设置
localSettings.Values.Remove("compositeSettings");
2.对于复合数据
localSettings.DeleteContainer("containerSettings");
删除并不难,或者说,这一节都不难。有了这些,我们在做游戏的时候,就能够将用户对游戏的设置都保存下来啦。