Build a WinRT Metro App to Access the Windows 8 File System

Write a C# and XAML Metro app that reads, writes, copies, and deletes Windows 8 folders

Michael Crump

April 13, 2012

13 Min Read
Build a WinRT Metro App to Access the Windows 8 File System


Windows 8 introduces a new native runtime called Windows Runtime (WinRT), which is the backbone of the new Metro user experience in Windows 8. ForWindows developers, now is the ideal time to explore the Windows 8 Developer Preview and start working with Windows 8 and WinRT. In addition, see "How to Build Windows 8 Applications" and "How to Work with Local Notifications in Windows 8-Style Apps" to get started with Windows 8 app development. Because file systemaccess is one of the first things any developer needs to understand when learning a new platform, a good way to help you get your feet wet with WinRTis to use it to build a file system–access application. I'll walk you through the process of creating a Windows 8 C# and XAML Metro application anddemonstrate how to manipulate files programmatically. After building the app, we'll explore additional file features, including reading file propertiesand adding a file to the Windows most recently used (MRU) list.

Setting Up Your Development Environment

Before getting started, you need to make sure your development environment is set up properly. Begin by downloading the following items:

  • To follow along with this article, you'll need to download the Windows 8 Consumer Preview from the Windows Dev Center. After installing the Consumer Preview, you also need to install Visual Studio 11 Beta, which is required to build Metro applications. You can either run this software on actual hardware or install it onto a virtual machine or virtual hard disk.

  • I also recommend downloading the Metro style app samples for reference. This article explains in detail some of the sample code that the Metro style app samples pack contains. The samples pack is licensed for public use, so feel free to use it in your own applications.

Note: The screenshots in this article are taken from the Visual Studio 11 Developer Preview. If you're using the most recent version of Visual Studio2011 Beta, you might see some minor variations in menu commands or colors. However, the essential functionality remains the same.

Building a Simple UI

Now that you have the necessary tools to begin building a Metro application using C# and XAML, let's begin. Launch Visual Studio 11 Beta and selectVisual C#, Windows Metro style, Application. Give the project the name WriteAFileToDocumentsFolder, as shown in Figure 1, and click OK.


Figure 1: Creating a new Metro project in Visual Studio 11

When you select the C#/XAML application template, Visual Studio 11 provides the basic application structure. This structure contains only two XAMLfiles, App.xaml and MainPage.xaml.

  • App.xaml is a file used by Metro applications to declare shared resources such as brushes and various style objects. Also, the code-behind file of App.xaml is used to handle global application level events, such as OnLaunched and OnActivated (similar to the Global.asax file for ASP.NET applications).

  • The MainPage.xaml file contains the description of what the UI will look like once the application is executed. The default Metro MainPage.xaml file consists of a UserControl and a Grid. In the sample app, we'll use a combination of a Grid with a StackPanel to display a series of buttons and text, as shown in Figure 2.

Figure 2: Using a Grid and StackPanel to display a series of buttons and text

                                                                                                                                                                                            



Note: Be sure to resolve button event handlers, if you copy and paste the code in Figure 2. Now, when you view the application from within thesimulator, you'll see the basic UI, as shown in Figure 3.


Figure 3: Basic UI for WriteAFileToDocumentsFolder app

A Bit About the Windows.Storage Namespace

Like most developers, I'd love to dive straight into the code. We'll get to the code soon, but I think it would be useful to first take a look at theWindows.Storage namespace, which our application uses.

If you take a look at theWindows.Storage documentation, you'll discover that the Windows.Storage namespace has a StorageFolder class that lets you manipulate folders and their content. By exploring theclass further, you'll find that it contains a method called StorageFolder.CreateFileAsync. This method will asynchronously create a new file in thecurrent folder.

Because Windows 8 is asynchronous in nature, I know that I need to mark my button event handlers as async. The async modifier indicates to the compilerthat a method or lambda expression will be asynchronous. Inside the async method, we typically use the await operator to suspend execution ofthe method until the task is completed. At this point, control is returned to the caller of the method, and suspension is accomplished without exitingthe async method.

For more information about the async keyword, see this Quickstart guide. In short, the async and await keywords will make your applications more responsive when waiting to return large amounts of data -- which, in this case,is the time that it takes to actually write a file to disk.

Let's Write Some Code!

Now we're ready to jump into the coding of the app. Instead of the button event handler looking like this:

private void btnCreate_Click(object sender, RoutedEventArgs e)   { //TODO }

I changed it to look like this:

async void btnCreate_Click(object sender, RoutedEventArgs e) { //TODO }

Notice that I added the async keyword. You can leave the access modifier in place, if you want. Next, I'll add the Windows.Storage namespace tothis project using the following line:

using Windows.Storage;

Now create a file in your My Documents folder by inserting the code in Figure 4 within the btnCreate_Click Event Handler.

Figure 4: Creating a file in the My Documents folder

        private async void btnCreate_Click(object sender, RoutedEventArgs e)        {            StorageFolder storageFolder = KnownFolders.DocumentsLibrary;            StorageFile sampleFile = await storageFolder.CreateFileAsync("devproconnections.txt");            this.txtResults.Text = "The file " + sampleFile.FileName + " was created.";        }



The short code sample inFigure 4 sets the storageFolder variable to your DocumentsLibrary and uses the CreateFileAsync method to create a file named devproconnections.txt.Note the use of the KnownFolders class, which provides access to the documents library on the local computer.

You might wonder whether you can save the file somewhere else besides the My Documents folder. To answer this question, you need to understand firstthat the file system access for WinRT apps is heavily restricted. This means that the only fully unrestricted access you have is to the app's localfolder, located in C:UsersusernameAppDataLocalPackagespackage. Within the local folder you can create, modify, and delete files andfolders without asking the user's permission, by using the Windows.Storage.ApplicationData class. You can also do the same in the application'sinstallation folder located in C:UsersusernameAppxLayouts via the Windows.ApplicationModel.Package class. You can access the user's Downloadsfolder without specifying anything in the manifest, but this folder has write-only access.

You have other local folder options besides My Documents. The KnownFolders class provides several additional folder-access and library-access options,which are listed in Figure 5.


Figure 5: KnownFolders class options

In the sample app, I opted to use the currently logged-in user's documents library. I could have targeted the Music,Pictures, or Video libraries just as easily.

An alternative method to the one used in our example is to use WinRT's file and folder pickers classes to read/write files. However, this methodrequires user interaction, which I wanted to avoid in this solution.

Running the Application

When you run the application and click the Create button, you'll quickly find out that nothing happens. You would have expected a file nameddevproconnections.txt to appear in the documents library. The source code compiled correctly and Visual Studio 11 didn't generate an error message.What do you do now?

Enter Package.appxmanifest. Inside your solution, you'll find a Package.appxmanifest file, as shown in Figure 6.


Figure 6: Opening Package.appxmanifest

Package.appxmanifest is simply an XMLfile and is the source manifest file created in the app package build process. When you double-click the file from within the Visual Studio 11 IDE, thefile will open in designer mode, with four tabbed panels named Application UI, Capabilities, Declarations, and Packaging.

Click the Capabilities tab, and you'll see a page containing definitions for system features or devices your application can use. To add theappropriate capability so that your application can access the documents library folder, you'll need to select the Document Library Access check box,as shown in Figure 7.


Figure 7: Selecting Document Library Access in Package.appxmanifest Capabilities

Don't Forget About Declarations

We'll also need to make a minor adjustment to the Declarations in Package.appxmanifest. Click the Declarations tab; under Available Declarations,select File Type Associations and click Add. You'll see the screen shown in Figure 8, with exclamation marks indicating required fields.


Figure 8: Modifying declarations in Package.appxmanifest

In the Name field, enter "text" and enter the FileType as .txt. This information will allow the application to save the file devproconnections.txt.

Now run the application again. Click the Create button, and you'll see a screen like that in Figure 9. The message on screen shows that the file hasbeen created.


Figure 9: Results of running the application after modifying declarations

The Write and Read Buttons

At this point, you're ready to add Write and Read buttons to the app. Figure 10 contains the code that creates these buttons.

Figure 10: Code for Read and Write buttons

        async void btnWrite_Click(object sender, RoutedEventArgs e)        {            StorageFolder storageFolder = KnownFolders.DocumentsLibrary;            try            {                StorageFile sampleFile = await storageFolder.GetFileAsync("devproconnections.txt");                IRandomAccessStream writeStream = await sampleFile.OpenAsync(FileAccessMode.ReadWrite);                IOutputStream outputStream = writeStream.GetOutputStreamAt(0);                DataWriter dataWriter = new DataWriter(outputStream);                dataWriter.WriteString("DevProConnections is a great Magazine!");                await dataWriter.StoreAsync();                outputStream.FlushAsync().Start();                this.txtResults.Text = "DevProConnections is a great Magazine! was written to " + sampleFile.FileName;            }            catch (FileNotFoundException)            {                this.txtResults.Text = "The file devproconnections.txt does not exist.";            }        }        async void btnRead_Click(object sender, RoutedEventArgs e)        {            StorageFolder storageFolder = KnownFolders.DocumentsLibrary;            try            {                StorageFile sampleFile = await storageFolder.GetFileAsync("devproconnections.txt");                IRandomAccessStream readStream = await sampleFile.OpenAsync(FileAccessMode.Read);                IInputStream inputStream = readStream.GetInputStreamAt(0);                DataReader dataReader = new DataReader(inputStream);                uint numBytesLoaded = await dataReader.LoadAsync((uint)readStream.Size);                this.txtResults.Text = "The following text was read from devproconnections.txt:" + dataReader.ReadString(numBytesLoaded);            }            catch (FileNotFoundException)            {                this.txtResults.Text = "The file devproconnections.txt does not exist.";            }        }


The code supporting the Write button is the most complex of all, so hang with me. I'll begin with the writeStream variable, because I've covered theother pieces previously. Since the IRandomAccessStream is an interface that supports random access of data in input and output streams, we'll use it toopen the file (devproconnections.txt) asynchronously. Next, we instantiate the DataWriter, which will write the data to an output stream defined in theIOutputStream interface. Finally, we write some data to the file and store it asynchronously. We then flush the data asynchronously in a sequentialstream by calling the FlushAync().Start() method.

When you run the application again and click the Write button, you'll see that the application was able to read and write to the file. To verify thatthe file write worked properly, go to your My Documents folder and double-click the devproconnections.txt file.

The Read button is almost identical except it doesn't use the IOutputStream. Instead, it uses the IInputStream interface. The other difference is thatit uses the DataReader class instead of the DataWriter class. Go ahead and run this sample, and the text located in the devproconnections.txt file willdisplay in the results box.

The Copy and Delete Buttons

Now that we've explored creating, writing, and reading a file, the next step is to write code to copy and delete a file. Figure 11 contains the codefor the Copy and Delete buttons.

Figure 11: Code for Copy and Delete buttons

        async void btnCopy_Click(object sender, RoutedEventArgs e)        {             StorageFolder storageFolder = KnownFolders.DocumentsLibrary;            try            {                StorageFile sampleFile = await storageFolder.GetFileAsync("devproconnections.txt");                StorageFile copyFile = await sampleFile.CopyAsync(storageFolder, "devproconnections-new.txt", NameCollisionOption.GenerateUniqueName);                this.txtResults.Text = "File " + copyFile.FileName + " is copied from " + sampleFile.FileName;            }            catch (FileNotFoundException)            {                this.txtResults.Text = "The file devproconnections.txt does not exist.";            }        }        async void btnDelete_Click(object sender, RoutedEventArgs e)        {            StorageFolder storageFolder = KnownFolders.DocumentsLibrary;            try            {                StorageFile sampleFile = await storageFolder.GetFileAsync("devproconnections.txt");                await sampleFile.DeleteAsync();                this.txtResults.Text = sampleFile.FileName + " is deleted";            }            catch (FileNotFoundException)            {                this.txtResults.Text = "The file devproconnections.txt does not exist.";            }        }


To copy a file, we'll need to create the StorageFile object with the file we want to copy. Then we'll call the .CopyAsync method to asynchronouslycreate a copy of the storage file, give it a filename, and store it into the specified storage folder. This method also specifies what to do when afile with the same name already exists in the specified folder. To delete a file, we'll simply call .DeleteAsync() on the StorageFile.

File Attributes

You can also easily obtain additional file information by combining the StorageFile object and the BasicProperties class, as shown in the code snippetin Figure 12. This will give us information such as the FileName, Size, and much more.

Figure 12: Obtaining additional file information

StorageFolder storageFolder = KnownFolders.DocumentsLibrary;StorageFile sampleFile = await storageFolder.GetFileAsync("devproconnections.txt");BasicProperties basicProperties = await sampleFile.Properties.GetBasicPropertiesAsync();this.txtResults.Text = "Filename: " + sampleFile.FileName +    "File size: " + basicProperties.Size + " bytes";



MRU List

Imagine that you want to create a Metro-style text editor similar to Microsoft Word. You could use WinRT's file and folder pickers to open and savefiles, but what about reopening the last file saved? Fortunately, Microsoft thought of this and provided the StorageApplicationPermissions class. Per Microsoft's Metro documentation, this class "providesaccess to lists that an app can use to track recently accessed files and/or locations or to store files and/or locations to access in the future."

First, define a public string called token that can be accessed by all methods:

string token = string.Empty;

Then add the following three lines of code, and your file will be added to the MRU:

StorageFolder storageFolder = KnownFolders.DocumentsLibrary;
StorageFile sampleFile = await storageFolder.GetFileAsync("devproconnections.txt");
token = StorageApplicationPermissions.MostRecentlyUsedList.Add(sampleFile);

You can retrieve the file by using the code in Figure 13.

Figure 13: Reopening the last file saved

StorageFile sampleFile = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(token);//read the fileIRandomAccessStream readStream = await sampleFile.OpenAsync(FileAccessMode.Read);IInputStream inputStream = readStream.GetInputStreamAt(0);DataReader dataReader = new DataReader(inputStream);uint numBytesLoaded = await dataReader.LoadAsync((uint)readStream.Size);


Catch the Metro

Getting a good grasp of Windows 8 Metro application development is the key to ensuring your future as a Windows developer. The information presented inthis article, coupled with the resources listed in this article and in the Learning Path, will give you a jump-start in building apps on the WinRTplatform.

Michael Crumpis a Microsoft MVP, speaker, and book author. He works at Telerik, spreading all the wonderful news of XAML.

To learn more About Windows 8, WinRT, and Metro:

Read more about:

Microsoft
Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like