четверг, 7 июня 2012 г.

Работа с файлами в Windows 8 приложениях

Итак, продолжим говорить про разработку Metro Style приложений для Windows 8.
Перед тем, как перейти к примерам, пара слов про осбенности работы Metro Style приложений. Они работают очень похоже на Web-приложения, причем наиболее близкий родственник видимо Silverlight. Есть некая "песочница" в которой все крутится. Причем при запуске Metro Style приложения в Windows 8 оно должно сказать к чему из весьма ограниченного перечня ресурсов операционной системы оно хочет получить доступ. Небольшую дискуссию на эту тему можно почитать на форуме MSDN. Я же сегодня постараюсь показать, как настроить приложение, чтобы оно получило доступ к папке с документами пользователя, ну и покажу, как с этими файлами поработать.


 Итак, небольшая предварительная подготовка. Я поместил в папку Мои документа 9 файлов: 3 txt, 3 jpg (два с расширением jpеg и один с jpg) и 3 zip архива:
Все, подготовка завершена. Создаем Metro Style приложение. Т.к. мы будем работать с файлами, то мы должны эту возможность у операционной системы запросить. Для этого в Solution Explorer ищем файл Package.appxmanifest:
Кликаем на нем два раз, чтобы открылась страница настройки. Сразу переходим на закладку Capabilities и ставим галку Document Library Access:
Как видите, рядом с заголовком вкладки Capabilities появился предупреждающий символ. Т.к. нам мало того, что надо запросить доступ к папке, но еще и указать к каким типам файлов мы его хотим получить. Для этого переходим на закладку Declarations, в выпадающем списке File Type Association, добавляем и настраиваем для расширений txt и jpeg:
Все, настройку закончили, теперь переходим к примерам на работу с файлами.
Пример первый, получение списка файлов. Вполне логично, что перед тем, как работать с файлами, нам было бы неплохо узнать а что там вообще есть. В Metro Style приложениях есть специальные диалоговые окна для выбора файлов, но с ними мы познакомимся как-нибудь в другой раз, сейчас же, как настоящие суровые программисты, все будем делать руками. Для начала, давайте разместим на нашей главной странице StackPanel куда мы будем кидать кнопки и ListBox (для показа списка файлов):


<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="1*" />
    Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal">StackPanel>
    <ListBox Grid.Row="1" x:Name="lbFiles" >ListBox>
Grid>

Все, пишем метод выбирающий список файлов и вызываем его из обработчика загрузки перехода на нашу страницу:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    LoadFileList();
} 
async void LoadFileList()
{
    // Получаем объект связанный с нашей папкой 
    StorageFolder myDocuments = KnownFolders.DocumentsLibrary;
    // Получаем список файлов и показываем его в ListBox
    lbFiles.ItemsSource = await myDocuments.GetFilesAsync(CommonFileQuery.OrderByName);
    lbFiles.DisplayMemberPath = "Name";
}
Запускам и видим вот такой список:

Если сравним с картинокй приведенной чуть выше из проводника, то увидим два существенных отличия:
1. Из файлов которые были в папке мы видим только те, расширения которых совпадают с расширениями запрошенными в манифесте (обратите внимание, что 03.jpg не выбрался).
2. Есть какие-то файлы, которых мы в проводнике не видели. Но с ними тоже достаточно просто,  в моих документах была еще папка с проектами Visual Studio. Ну а метод GetFilesAsunc любезно пробежался по всем вложенным папкам и нашел там все то, с чем приложение может работать.
Во втором примере, давайте посмотрим переименование/копирование и удаление файлов. На самом деле, нам уже даже делать то особо ничего не надо. Метод GetFilesAsync возвращает IReadOnlyList, т.е. в списке у нас сейчас эти самые StorageFile. Ну а у этого класса есть замечательные методы: CopyAsync, CopyAndReplaceAsync, DeleteAsync ну и так далее. На мой взгляд, все эти возможности будут в Metro Style приложениях мало востребованы, т.к. они особо не предназначены для работы с файлами, только вот если Delete, которое надо будет вызывать, если приложение будет создавать временные файлы. Вот давайте для него и напишем небольшой пример.
На StackPanel добавим кнопку с таким вот обработчиком:
private async void btDeleteFile_Click(object sender, RoutedEventArgs e)
{
    if (lbFiles.SelectedItem != null)
    {
        StorageFile deletedFile = lbFiles.SelectedItem as StorageFile;
        if (deletedFile != null)
        {
            await deletedFile.DeleteAsync();
        }
    }
    LoadFileList();
}
Запускаем, выбираем файл 01.jpeg и жмем кнопку удалить:

На изменение цвета фона не обращайте внимание, это просто ListBox так отрабатывает всем фоном наличие в нем выбранного элемента.
Теперь, в третьем примере, посмотрим чтение из файла. Я покажу как читать текст, но есть соответствующие методы для чтения в виде Stream-ов. Итак, пусть пользователь выберет у нас в списке текстовый файл, а мы его покажем в TextBlock-е, который добавим на форму. Для этой демонстрации добавим в наш Grid еще одну строку, в нее добавим TextBlock с именем tbTextFromFile и вот такой обработчик для события изменения выбранного элемента в ListBox:

private async void lbFiles_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
    if (lbFiles.SelectedItem != null && lbFiles.SelectedItem is StorageFile)
    {
        StorageFile source = (StorageFile)lbFiles.SelectedItem;
        if (source.FileType == ".txt")
        {
            tbTextFromFile.Text = await FileIO.ReadTextAsync(source, UnicodeEncoding.Utf8);
        }
        else
        {
            tbTextFromFile.Text = "";
        }
    }
}

Выбираем файл и смотрим что там в нем есть:
Ну и последний, четвертый пример на запись в файл. Два предыдущих примера, были на то, как работать с файлами любезно предоставленными GetFilesAsync, здесь же нам необходимо создать новый файл. Для этого мы воспользуемся методом класса StorageFolder. Добавим еще одну кнопку в наш StackPanel и запишем в ее обработчике Click следующий код:

private async void btWriteToFile_Click_1(object sender, RoutedEventArgs e)
{
    StorageFolder myDocuments = KnownFolders.DocumentsLibrary;
    StorageFile createdFile = await myDocuments.CreateFileAsync("00.txt");
    await Windows.Storage.FileIO.WriteTextAsync(createdFile, "Эта строка записана из программы!");
    LoadFileList();
}
Запускаем, нажимаем на кнопку "Создать файл", выбираем свежесозданный файл:

Хотя, вы знаете, похоже я вас обманул. Я тут посмотрел, запись в файл через поток, будет отличатся, от записи через WriteTextAsync. Поэтому, пятый пример, как записать в файл через Stream. Я просто добавляю еще одну кнопку вот с таким обрабочиком на Click:

private async void btWriteToFileByStream_Click_1(object sender, RoutedEventArgs e)
{
    StorageFolder myDocuments = KnownFolders.DocumentsLibrary;
    StorageFile createdFile = await myDocuments.CreateFileAsync("00s.txt");
    // Создаем поток для записи
    IRandomAccessStream fileStream = await createdFile.OpenAsync(FileAccessMode.ReadWrite);
    // Создаем поток, который будем писать в файл
    DataWriter dataWriter = new DataWriter(fileStream);
    dataWriter.WriteString("Эта строка записана из программы!");
    // Записываем из потока днных в поток файл
    await dataWriter.StoreAsync();
    // Записываем на диск
    await fileStream.FlushAsync();
    LoadFileList();
}
Все, картинку приводить не буду, поверьте мне на слово, все работает.

Комментариев нет:

Отправить комментарий