3

I'm currently using Caliburn.Micro 2.0 for my Windows Phone 8.1 project (Universal App) and I'm having problem with conditionally cancelling page close after user clicks a MessageDialog button.

It seems that Caliburn closes page after leaving CanClose() method, not waiting for the callback which is called after async MessageDialog.

public class MyViewModel: Screen
{
  public override async void CanClose(Action<bool> callback)
  {     
    MessageDialog dlg = new MessageDialog("Close?","Confirmation");
    dlg.Commands.Add(new UICommand() { Id = 0,  Label = "Yes" });
    dlg.Commands.Add(new UICommand() { Id = 1,  Label = "No"  });       

    var result = await dlg.ShowAsync();

    callback((int)result.Id == 0);  
  }
}
Tom Shane
  • 694
  • 7
  • 18

2 Answers2

2

The only solution I have at the moment is set a field with a flag indicating if the page can be closed. On the user attempt to navigate back I tell Caliburn to abort the close and I display the confirmation dialog. When I get the result I set the flag to true and navigate back manually. This causes another call to CanClose, but this time I set the callback to true and skip the dialog part.

I don't like this solution much, but it is only way I managed to solve this problem.

private bool canClose = false;

public override async void CanClose(Action<bool> callback)
{
  callback(canClose);

  if (!canClose)
  {
    MessageDialog dlg = new MessageDialog("Close?","Confirmation");
    dlg.Commands.Add(new UICommand() { Id = 0,  Label = "Yes" });
    dlg.Commands.Add(new UICommand() { Id = 1,  Label = "No"  });       

    var result = await dlg.ShowAsync();

    if ((int)result.Id == 0)
    {
      canClose = true;
      navigationService.GoBack();
    }
  }
}

PS: I don't use MessageDialog directly in my ViewModel, I'm using a dialog service interface for popups. I just used it here to demonstrate the issue.

Tom Shane
  • 694
  • 7
  • 18
0

While the enhancement CanClose isn't set, this is my approach utilize Navigating event to solve this 'problem'

If user could just GoBack() would be easy to handle, but in my case, there's many options to navigate. So, the only way that I found to solve it is described below:

public MyViewModel(INavigationService navigationService)
{
    _navigationService = navigationService;
    _navigationService.Navigating += OnGoBack;
}

private async void OnGoBack(object sender, NavigatingCancelEventArgs e)
{
    e.Cancel = true;

    var dlg = new MessageDialog("Close?", "Confirmation"); //Dialog for demo purpose only!
    dlg.Commands.Add(new UICommand() { Id = 0, Label = "Yes" });
    dlg.Commands.Add(new UICommand() { Id = 1, Label = "No" });
    var result = await dlg.ShowAsync();

    if ((int) result.Id != 0) return;
    _navigationService.Navigating -= OnGoBack;
    if (e.NavigationMode == NavigationMode.Back)
        _navigationService.GoBack();
    else
    {
        var myViewModel = Type.GetType($"YourNameSpaceViewModels.{e.SourcePageType.Name}Model");
        _navigationService.NavigateToViewModel(myViewModel);
    }
}

Explaination:

$"YourNameSpaceViewModels.{e.SourcePageType.Name}Model"
Here I get the full path to my class where user want to goto

And so, I navigate to it _navigationService.NavigateToViewModel(myViewModel);

rubStackOverflow
  • 5,615
  • 2
  • 29
  • 43