(Print this page)

Using OneDrive from your .Net Windows Forms application
Published date: Saturday, March 24, 2018
On: Moer and Éric Moreau's web site

OneDrive has proven to be very useful over the years. Anybody remembers its ancestor called Live Mesh?

Even if OneDrive offers many ways to be used by users, it might be useful for your applications, including an old-legacy Windows Forms application, to access that feature.

This article will authenticate to a OneDrive library, list available folders and files, download files, and upload files. All this will be done using the Microsoft Graph Client Library for .Net.

The downloadable code

This month’s downloadable demo solution contains both VB and C#. The solution was created using Visual Studio 2017 but can be used in most older versions as well.

I strongly encourage you to download the code as not everything will be pasted here.

Issues with authentication

Apparently, the authentication mechanism in use here will not work for everybody!

For the sake of testing, I am using my OneDrive Personal account and it works like a charm.

If you have issues, have a look at Creating custom authentication provider for OneDriveClient and GraphClient.

I have been told that in some cases, the application will seem to work except that no (or very close to no) files will be returned and showing into the treeview.

Registering your application

Before being able to connect to your OneDrive, you need to register a key to which you can grant some privileges. You can also just delete the key and the application will just stop working.

To create the key, follow these steps;

  1. Sign in to the App Registration Portal. You need to login using the same account as the OneDrive you are trying to access.
  2. Click the “Add an app” button next to the “Converged applications” section.
  3. Give a name to your application and click the Create button.
  4. A registration page will show asking for a few more information.
  5. Copy the Application Id (shown in Figure 1) and save it for later.
  6. Click the “Add Platform” button and select “Native Application”.
  7. Just scroll completely at the bottom and click the “Save” button.

Figure 1: Registering the application



If your application is not registered, it will just fail when trying to authenticate.

Referencing NuGets

The Microsoft Graph library is available in the form of a NuGet package. Open the “Manage NuGet Packages for the Solution” (by right-click on your solution), search for Graph, select Microsoft.Graph from the list and click the install button. The installation will require some other packages as shown in figure 2. Click on OK and accept the license agreement (after carefully reading it of course!).

Figure 2: Referencing the required NuGets



Still in the NuGets installer, search for “Microsoft.Identity.Client” (you might need to check the “Include prerelease” checkbox in order to see it. Install it too.

Authentication

I have found a useful class named AuthenticationHelper on the web that takes care of the authentication. You can read the code of this class from the downloadable solution.

Remember the application ID I asked you to copy and set aside, it is now the time to use it. At the top of this class, there is a member named _clientId. Paste your own application ID in its value.

When running the demo application and clicking the “Sign In” button, you will find out that the standard dialogs to connect to Office 365/Azure will ask you for your account and your password. Once you have entered the correct information, another dialog will show up (see figure 3) asking you if you allow the access to your application. You need to answer yes otherwise your application will be denied access.

Figure 3: Allowing access to your application



Listing files

Once you have logged in and allowed access, the screen will load the folders you have in your OneDrive into the TreeView control (I have reused code from my March 2006 article.

You can now click the folders and/or files to select/expand/collapse items. Notice that when an item is selected, its properties will be shown in the PropertyGrid control on the right of the screen.

Figure 4: The demo application in action



If you look at the code, you will find out the Microsoft Graph library is used to query asynchronously the OneDrive service in order to get data. In this case, the GetAsync method from GraphClient.Drive.Root is queried because we want all the objects at the root of the OneDrive. We don’t want to start looping as you might have a very large OneDrive and it might take way too long to just display it.

Each object retrieved from OneDrive is used to create nodes in the TreeView. The object itself is stored in the Tag property of each node for later reference.

Here is the most interesting part:

Private Async Function TVW_FillBase() As Task
    ShowWork(True)
    tvwOneDrive.Nodes.Clear()
    Try
        Dim folder As DriveItem = Await GraphClient.Drive.Root.Request().Expand("thumbnails,children($expand=thumbnails)").GetAsync()
        If folder IsNot Nothing Then
            If folder.Folder IsNot Nothing AndAlso folder.Children IsNot Nothing AndAlso folder.Children.CurrentPage IsNot Nothing Then
                For Each obj In folder.Children.CurrentPage
                    Dim node As TreeNode = tvwOneDrive.Nodes.Add(obj.Name)
                    node.Tag = obj
                    node.Nodes.Add("DUMMY ENTRY")
                Next
            End If
        End If
    Catch exception As Exception
        PresentServiceException(exception)
    End Try

    ShowWork(False)
End Function
private async Task TVW_FillBase()
{
    ShowWork(true);
    tvwOneDrive.Nodes.Clear();

    try
    {
        DriveItem folder = await GraphClient.Drive.Root.Request().Expand("thumbnails,children($expand=thumbnails)").GetAsync();
        if (folder != null)
        {
            if (folder.Folder != null && folder.Children != null && folder.Children.CurrentPage != null)
            {
                foreach (var obj in folder.Children.CurrentPage)
                {
                    TreeNode node = tvwOneDrive.Nodes.Add(obj.Name);
                    node.Tag = obj;  
                    node.Nodes.Add("DUMMY ENTRY");
                }
            }
        }
    }
    catch (Exception exception)
    {
        PresentServiceException(exception);
    }

    ShowWork(false);
}

Deleting a file

Deleting an object is not difficult. It resumes to call the DeleteAsync method from the GraphClient.Drive.Items collection.

Here is the most interesting part:

Private Async Sub btnDelete_Click(sender As Object, e As EventArgs) Handles btnDelete.Click
    If SelectedItem Is Nothing Then
        MessageBox.Show("Select an item first")
        Return
    End If

    ShowWork(True)
    Dim result = MessageBox.Show("Are you sure you want to delete " & SelectedItem.Name & "?", "Confirm Delete", MessageBoxButtons.YesNo)
    If result = DialogResult.Yes Then
        Try
            Await GraphClient.Drive.Items(SelectedItem.Id).Request().DeleteAsync()
            RefreshNode()
            MessageBox.Show("Item was deleted successfully")
        Catch exception As Exception
            PresentServiceException(exception)
        End Try
    End If

    ShowWork(False)
End Sub
private async void btnDelete_Click(object sender, EventArgs e)
{
    if (SelectedItem == null)
    {
        MessageBox.Show("Select an item first");
        return;
    }
    ShowWork(true);
    var result = MessageBox.Show("Are you sure you want to delete " + SelectedItem.Name + "?", "Confirm Delete", MessageBoxButtons.YesNo);
    if (result == DialogResult.Yes)
    {
        try
        {
            await GraphClient.Drive.Items[SelectedItem.Id].Request().DeleteAsync();

            RefreshNode();
            MessageBox.Show("Item was deleted successfully");
        }
        catch (Exception exception)
        {
            PresentServiceException(exception);
        }
    }
    
    ShowWork(false);
}

Downloading a file

Downloading a file is not much more difficult. Here I am using the SaveFileDialog to let the user select a place where to save the file before calling the GetAsync method (again) on the Content object of the GraphClient.Drive.Items collection. This call returns a stream that you can then save into the selected file.

Here is the most interesting part:

Private Async Sub btnDownload_Click(sender As Object, e As EventArgs) Handles btnDownload.Click
    If SelectedItem Is Nothing Then
        MessageBox.Show("Select an item first")
        Return
    End If

    ShowWork(True)
    Dim item = SelectedItem
    If item Is Nothing Then
        MessageBox.Show("Nothing selected.")
        Return
    End If

    Dim dialog = New SaveFileDialog With {.FileName = item.Name, .Filter = "All Files (*.*)|*.*"}
    If dialog.ShowDialog() <> DialogResult.OK Then Return
    Using stream = Await GraphClient.Drive.Items(item.Id).Content.Request().GetAsync()
        Using outputStream = New IO.FileStream(dialog.FileName, IO.FileMode.Create)
            Await stream.CopyToAsync(outputStream)
        End Using
    End Using

    ShowWork(False)
End Sub
private async void btnDownload_Click(object sender, EventArgs e)
{
    if (SelectedItem == null)
    {
        MessageBox.Show("Select an item first");
        return;
    }
    ShowWork(true);
    var item = SelectedItem;
    if (null == item)
    {
        MessageBox.Show("Nothing selected.");
        return;
    }

    var dialog = new SaveFileDialog
    {
        FileName = item.Name,
        Filter = "All Files (*.*)|*.*"
    };
    if (dialog.ShowDialog() != DialogResult.OK)
        return;

    using (var stream = await GraphClient.Drive.Items[item.Id].Content.Request().GetAsync())
    using (var outputStream = new System.IO.FileStream(dialog.FileName, System.IO.FileMode.Create))
    {
        await stream.CopyToAsync(outputStream);
    }
    ShowWork(false);
}

Uploading a file

Uploading a file goes the opposite way. The GetFileStreamForUpload method is used to ask the user for a file to upload. This file is then opened in a stream.

With that stream object in hands, we are now ready to call the PutAsync method of the GraphClient.Drive.Root.ItemWithPath object. The file will be uploaded in the selected folder of the TreeView.

Here is the most interesting part:

TODO show GetFileStreamForUpload & btnUpload_Click

Private Function GetFileStreamForUpload(ByVal pTargetFolderName As String, ByRef pOriginalFilename As String) As IO.Stream
    Dim dialog As OpenFileDialog = New OpenFileDialog With {.Title = "Upload to " & pTargetFolderName, .Filter = "All Files (*.*)|*.*", .CheckFileExists = True}
    Dim response = dialog.ShowDialog()
    If response <> DialogResult.OK Then
        pOriginalFilename = Nothing
        Return Nothing
    End If

    Try
        pOriginalFilename = IO.Path.GetFileName(dialog.FileName)
        Return New IO.FileStream(dialog.FileName, IO.FileMode.Open)
    Catch ex As Exception
        MessageBox.Show("Error uploading file: " & ex.Message)
        pOriginalFilename = Nothing
        Return Nothing
    End Try
End Function

Private Async Sub btnUpload_Click(sender As Object, e As EventArgs) Handles btnUpload.Click
    If CurrentFolder Is Nothing Then
        MessageBox.Show("Select a folder first")
        Return
    End If

    ShowWork(True)
    Dim targetFolder = CurrentFolder
    Dim filename As String
    Using stream = GetFileStreamForUpload(targetFolder.Name, filename)
        If stream IsNot Nothing Then
            Dim folderPath As String = If(targetFolder.ParentReference Is Nothing, "", targetFolder.ParentReference.Path.Remove(0, 12) & "/" + Uri.EscapeUriString(targetFolder.Name))
            Dim uploadPath = folderPath & "/" + Uri.EscapeUriString(IO.Path.GetFileName(filename))
            Try
                Dim uploadedItem = Await GraphClient.Drive.Root.ItemWithPath(uploadPath).Content.Request().PutAsync(Of DriveItem)(stream)
                RefreshNode()
                MessageBox.Show("Uploaded with ID: " & uploadedItem.Id)
            Catch exception As Exception
                PresentServiceException(exception)
            End Try
        End If
    End Using

    ShowWork(False)
End Sub
private System.IO.Stream GetFileStreamForUpload(string pTargetFolderName, out string pOriginalFilename)
{
    OpenFileDialog dialog = new OpenFileDialog
    {
        Title = "Upload to " + pTargetFolderName,
        Filter = "All Files (*.*)|*.*",
        CheckFileExists = true
    };
    var response = dialog.ShowDialog();
    if (response != DialogResult.OK)
    {
        pOriginalFilename = null;
        return null;
    }

    try
    {
        pOriginalFilename = System.IO.Path.GetFileName(dialog.FileName);
        return new System.IO.FileStream(dialog.FileName, System.IO.FileMode.Open);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error uploading file: " + ex.Message);
        pOriginalFilename = null;
        return null;
    }
}

private async void btnUpload_Click(object sender, EventArgs e)
{
    if (CurrentFolder == null)
    {
        MessageBox.Show("Select a folder first");
        return;
    }
    ShowWork(true);
    var targetFolder = CurrentFolder;

    string filename;
    using (var stream = GetFileStreamForUpload(targetFolder.Name, out filename))
    {
        if (stream != null)
        {
            // Since the ItemWithPath method is available only at Drive.Root, we need to strip
            // /drive/root: (12 characters) from the parent path string.
            string folderPath = targetFolder.ParentReference == null
                ? ""
                : targetFolder.ParentReference.Path.Remove(0, 12) + "/" + Uri.EscapeUriString(targetFolder.Name);
            var uploadPath = folderPath + "/" + Uri.EscapeUriString(System.IO.Path.GetFileName(filename));

            try
            {
                var uploadedItem = await GraphClient.Drive.Root.ItemWithPath(uploadPath).Content.Request().PutAsync<DriveItem>(stream);
                RefreshNode();
                MessageBox.Show("Uploaded with ID: " + uploadedItem.Id);
            }
            catch (Exception exception)
            {
                PresentServiceException(exception);
            }
        }
    }
    
    ShowWork(false);
}

Conclusion

It might not be a perfect solution just yet. Don’t forget that some libraries used here are still in pre-release.

Of course, there are other (easier) ways. For example, if you sync your OneDrive to your local hard drive, anything written or deleted from there will automatically be reflected to your cloud. It is much easier to interact with a local folder than it is with the Cloud.

But many people with small ultra-books don’t have the luxury of having a local copy of their OneDrive locally (disk space is often limited).

I also encourage you to visit the OneDrive Dev Center.


(Print this page)