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

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

Downloads
142173_Windows8_Metro.zip

Windows 8 introduces a new native runtime called Windows Runtime (WinRT), which is the backbone of the new Metro user experience in Windows 8. For Windows 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 system access 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 WinRT is 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 and demonstrate how to manipulate files programmatically. After building the app, we'll explore additional file features, including reading file properties and 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 Studio 2011 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 select Visual C#, Windows Metro style, Application. Give the project the name WriteAFileToDocumentsFolder, as shown in Figure 1, and click OK.

142173_fig1_create_Metro_project-sm
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 XAML files, 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 the simulator, you'll see the basic UI, as shown in Figure 3.

142173_fig3_basic_app_UI-sm
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 the Windows.Storage namespace, which our application uses.

If you take a look at the Windows.Storage documentation, you'll discover that the Windows.Storage namespace has a StorageFolder class that lets you manipulate folders and their content. By exploring the class further, you'll find that it contains a method called StorageFolder.CreateFileAsync. This method will asynchronously create a new file in the current 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 compiler that a method or lambda expression will be asynchronous. Inside the async method, we typically use the await operator to suspend execution of the method until the task is completed. At this point, control is returned to the caller of the method, and suspension is accomplished without exiting the 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 to this 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 in Figure 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 first that 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 local folder, located in C:\Users\username\AppData\Local\Packages\package. Within the local folder you can create, modify, and delete files and folders without asking the user's permission, by using the Windows.Storage.ApplicationData class. You can also do the same in the application's installation folder located in C:\Users\username\AppxLayouts via the Windows.ApplicationModel.Package class. You can access the user's Downloads folder 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.

142173_fig5_KnownFolders_class_options-sm
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 method requires 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 named devproconnections.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.

142173_fig6_Opening_Package-appxmanifest
Figure 6: Opening Package.appxmanifest

Package.appxmanifest is simply an XML file 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, the file 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 the appropriate 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.

142173_fig7_select_document_library_access-sm
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.

142173_fig8_modifying_declarations-sm
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 has been created.

142173_fig9_app_after_modifying_declarations-sm
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:\n\n" + 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 the other pieces previously. Since the IRandomAccessStream is an interface that supports random access of data in input and output streams, we'll use it to open the file (devproconnections.txt) asynchronously. Next, we instantiate the DataWriter, which will write the data to an output stream defined in the IOutputStream interface. Finally, we write some data to the file and store it asynchronously. We then flush the data asynchronously in a sequential stream 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 that the 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 that it uses the DataReader class instead of the DataWriter class. Go ahead and run this sample, and the text located in the devproconnections.txt file will display 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 code for 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 asynchronously create 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 a file 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 snippet in 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 +
    "\nFile 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 save files, 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 "provides access 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 file
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);

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 in this 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 WinRT platform.

Michael Crump is 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:

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish