3

Individually, all code works perfectly. The snippet for saving the file, the snippet for picking a directory to save it to and also the message dialog works great.

But when I tie it all together, I get an access denied. I am not using the DocumentsLibrary capability since it is not required of me to do so in this case, however, enabling this capability after running into issues confirmed that it is not the issue.

Scenario: User wants to create a new document after entering text in the text box. A MessageDialog appears, asking them if they want to save changes to the existing file first - the user clicks Yes (save file).

Now, here is where you handle the event that was raised by the MessageDialog.

Inside the IUICommand command event handler, you test for which button was clicked, and act accordingly.

I did this with a switch statement:

switch(command.Label) {
   case "Yes":
   SaveFile(); // extension method containing save file code that works on its own
   break;
   case "No":
   ClearDocument();
   break;
   default:
   break;
}

Now, each case works great except for the Yes button. When you click yes, an e tension method is called which has code that saves to a file

It is when you click yes button that you get the ACCESS DENIED exception. Details of the exception didn't reveal anything.

I think that it has something to do with how I am using the MesaageDialog. But after searching for hours I have yet to find a sample on how to save a file with the FileSavePicker when a MesaageDialog button is pressed.

Any ideas in how this should be done?

Update w/ Code

When the user clicks the New document button on the AppBar, this method fires:

async private void New_Click(object sender, RoutedEventArgs e)
{
    if (NoteHasChanged)
    {
        // Prompt to save changed before closing the file and creating a new one.
        if (!HasEverBeenSaved)
        {

            MessageDialog dialog = new MessageDialog("Do you want to save this file before creating a new one?",
                "Confirmation");
            dialog.Commands.Add(new UICommand("Yes", new UICommandInvokedHandler(this.CommandInvokedHandler)));
            dialog.Commands.Add(new UICommand("No", new UICommandInvokedHandler(this.CommandInvokedHandler)));
            dialog.Commands.Add(new UICommand("Cancel", new UICommandInvokedHandler(this.CommandInvokedHandler)));

            dialog.DefaultCommandIndex = 0;
            dialog.CancelCommandIndex = 2;

            // Show it.
            await dialog.ShowAsync();
        }
        else { }
    }
    else
    {
        // Discard changes and create a new file.
        RESET();
    }
}

And the FileSavePicker stuff:

private void CommandInvokedHandler(IUICommand command)
{
    // Display message showing the label of the command that was invoked
    switch (command.Label)
    {
        case "Yes":

            MainPage rootPage = this;
            if (rootPage.EnsureUnsnapped())
            {
                // Yes was chosen. Save the file.
                SaveNewFileAs();
            }
            break;
        case "No":
            RESET(); // Done.
            break;
        default:
            // Not sure what to do, here.
            break;
    }
}

async public void SaveNewFileAs()
{
    try
    {
        FileSavePicker saver = new FileSavePicker();
        saver.SuggestedStartLocation = PickerLocationId.Desktop;
        saver.CommitButtonText = "Save";
        saver.DefaultFileExtension = ".txt";
        saver.FileTypeChoices.Add("Plain Text", new List<String>() { ".txt" });

        saver.SuggestedFileName = noteTitle.Text;

        StorageFile file = await saver.PickSaveFileAsync();
        thisFile = file;

        if (file != null)
        {
            CachedFileManager.DeferUpdates(thisFile);

            await FileIO.WriteTextAsync(thisFile, theNote.Text);

            FileUpdateStatus fus = await CachedFileManager.CompleteUpdatesAsync(thisFile);
            //if (fus == FileUpdateStatus.Complete)
            //    value = true;
            //else
            //    value = false;

        }
        else
        {
            // Operation cancelled.
        }

    }
    catch (Exception exception)
    {
        Debug.WriteLine(exception.InnerException);
    }
}
Arrow
  • 2,784
  • 8
  • 38
  • 61

2 Answers2

0

Any progress on this issue? I currently have the same problem. I have also found that the same problem occurs if a second MessageDialog is shown in the IUICommand event.

My solution is to cancel the first operation (that shows the first message dialog). Here some code I’m using (it’s accessible in a global object):

    private IAsyncInfo mActiveDialogOperation = null;
    private object mOperationMutex = new object();

    private void ClearActiveOperation(IAsyncInfo operation)
    {
        lock (mOperationMutex)
        {
            if (mActiveDialogOperation == operation)
                mActiveDialogOperation = null;
        }
    }

    private void SetActiveOperation(IAsyncInfo operation)
    {
        lock (mOperationMutex)
        {
            if (mActiveDialogOperation != null)
            {
                mActiveDialogOperation.Cancel();
            }

            mActiveDialogOperation = operation;
        }
    }

    public void StopActiveOperations()
    {
        SetActiveOperation(null);
    }

    public async void ShowDialog(MessageDialog dialog)
    {
        StopActiveOperations();

        try
        {
            IAsyncOperation<IUICommand> newOperation = dialog.ShowAsync();
            SetActiveOperation(newOperation);
            await newOperation;
            ClearActiveOperation(newOperation);
        }
        catch (System.Threading.Tasks.TaskCanceledException e)
        {
            System.Diagnostics.Debug.WriteLine(e.Message);
        }
    }

So every time I want to show a MessageDialog I call ShowDialog. This will cancel the current dialog if any (then a TaskCanceledException occurs).

In the case when I will use a FileSavePicker, I call StopActiveOperations before PickSaveFileAsync is called.

This works but I can’t say I like it. It feels like I’m doing something wrong.

PEK
  • 3,688
  • 2
  • 31
  • 49
0

OK, now I have figured it out :-). The documentation says explicit that you shouldn’t show new popups/file pickers in the UICommand:

http://msdn.microsoft.com/en-US/library/windows/apps/windows.ui.popups.messagedialog.showasync

This is an example of a bad way to do it:

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        MessageDialog dialog = new MessageDialog("Press ok to show new dialog (the application will crash).");
        dialog.Commands.Add(new UICommand("OK", new UICommandInvokedHandler(OnDialogOkTest1)));
        dialog.Commands.Add(new UICommand("Cancel"));

        await dialog.ShowAsync();
    }

    private async void OnDialogOkTest1(IUICommand command)
    {
        MessageDialog secondDialog = new MessageDialog("This is the second dialog");
        secondDialog.Commands.Add(new UICommand("OK"));

        await secondDialog.ShowAsync();
    }

This is the correct way to do it:

    private async void Button_Click_1(object sender, RoutedEventArgs e)
    {
        MessageDialog dialog = new MessageDialog("Press ok to show new dialog");
        UICommand okCommand = new UICommand("OK");
        UICommand cancelCommand = new UICommand("Cancel");

        dialog.Commands.Add(okCommand);
        dialog.Commands.Add(cancelCommand);

        IUICommand response = await dialog.ShowAsync();

        if( response == okCommand )
        {
            MessageDialog secondDialog = new MessageDialog("This is the second dialog");
            secondDialog.Commands.Add(new UICommand("OK"));

            await secondDialog.ShowAsync();
        }
    }

Quite simple actually, I should have get this earlier...

PEK
  • 3,688
  • 2
  • 31
  • 49