DialogResult using Coroutines

Topics: Actions & Coroutines
Jan 25, 2012 at 10:01 PM
Edited Jan 25, 2012 at 10:02 PM

Hello,

I am using following code to show a dialog when clicking a button in another view. I want the dialog to return result when user clicks the Delete button. The Delete button on the dialog view is bound to Delete method on VM. However even when I close the dialog, I am not getting the result back. Infact I am not even hitting break point at line after yield statement.  Is there something missing in my code?

Thanks

 

Apurva

 

//code for launching the dialog

 

 public IEnumerable Delete()
        {
            var openDialog = ShowDialog.Of();
            yield return openDialog;
            var result = ((DeleteStandViewModel)openDialog.Dialog).TheDialogResult;
            if (result.ToString().ToLower() == "delete")
            {
//do something
}
}

//Show Dialog class borrowed from an earlier post
public class ShowDialog:IResult
    {
        //private Type _screenType;
        readonly Type screenType;

        [SetterProperty]
        public IWindowManager WindowManager { get; set; }

        public ShowDialog(Type screenType)
        {
            this.screenType = screenType;
        }

        public void Execute(ActionExecutionContext context)
        {
            var screen = IoC.GetInstance(screenType, null);

			Dialog = screen;
            
            WindowManager.ShowDialog(screen);
         
            var deactivated = screen as IDeactivate;
            if (deactivated == null)
			Completed(this, new ResultCompletionEventArgs());
		else {
            deactivated.Deactivated += (o, e) =>
            {
                //if (e.WasClosed)
                //{
                    Completed(this, new ResultCompletionEventArgs());
                //}
            };
		}

            }

        public object Dialog { get; private set; }
        public event EventHandler Completed = delegate { };

        public static ShowDialog Of()
        {
            return new ShowDialog(typeof(T));
        }
    }

//Delete stand view model
 public class DeleteStandViewModel : Screen
    {
        public string TheDialogResult { get; set; }
        private View _view;
        public void Delete()
        {
            TheDialogResult = "Delete";
            TryClose(true);
        }

        public void Cancel()
        {
            TheDialogResult = "Cancel";
        }

        public DeleteStandViewModel()
        {
            this.DisplayName = "Delete Stand";
        }

        protected override void OnViewLoaded(object view)
        {
            base.OnViewLoaded(view);
            
        }
    }
Coordinator
Jan 25, 2012 at 10:19 PM

Try changing your method signature to this:

public IEnumerable<IResult> Delete();
Jan 25, 2012 at 10:23 PM

I miscopied the method ...my delete method on the code to launch dialog is-

 

//code for launching the dialog

  public IEnumerable<IResult> Delete()
        {
            var openDialog = ShowDialog.Of<DeleteStandViewModel>();
            yield return openDialog;
            var result = ((DeleteStandViewModel)openDialog.Dialog).TheDialogResult;
            if (result.ToString().ToLower() == "delete")
            {

               //do something
            }
        }

I got it to work by changing Execute on Dialog class to following. Is this the right way to do it?

   public void Execute(ActionExecutionContext context)
        {
            var screen = IoC.GetInstance(screenType, null);

			Dialog = screen;
            
            WindowManager.ShowDialog(screen);
         
            var deactivated = screen as IDeactivate;
            if (deactivated == null)
			Completed(this, new ResultCompletionEventArgs());
		else {
            Completed(this, new ResultCompletionEventArgs());

            //deactivated.Deactivated += (o, e) =>
            //{
            //    if (e.WasClosed)
            //    {
            //        Completed(this, new ResultCompletionEventArgs());
            //    }
            //};
		}

            }

Jan 26, 2012 at 5:43 PM

I would think that the control is returning to your VM straight away now. The deactivated code you have commented out in the ShowDialog result watches the dialog and reacts when it's closed. Now it's reacting straight away which is why you'll see the breakpoint in the VM being hit but probably with the dialog still open at the time.

Feb 17, 2012 at 2:08 PM

I've run into the same issues as #apurvagoyal.

IMHO the problem stems from using a ViewModel that inherits from Screen with the ShowDialog class. Screen implements IDeactivate and the code that #apurvagoyal has commented out is called in those cases where the ViewModel inherits from Screen. Somehow this prevents the dialog from returning as expected from a modal dialog.

In my case this code failed:

        public IEnumerable<IResult> InsertPractitioners()
        {
            var dlg = ShowDialog.Of<PractitionersOptionsViewModel>();
            yield return dlg;
            var debug = dlg.Dialog; // this line is never called
        }

My reason for inheriting from Screen was to be able to implement the action for when the user clicks the Ok-button in my dialog, e.g. call TryClose(true);

What I ended up with was this

        public IEnumerable<IResult> InsertPractitioners()
        {
            var dlg = new PractitionersOptionsViewModel();
            if (_windowManager.ShowDialog(dlg).GetValueOrDefault())
            {
                if (dlg.RealLifeSelected)
                {
                    //
                }
                else {
                    var pvm = new InsertFictitiousPractitionersViewModel();
                    yield return pvm;
                }
            }
        }

This allowed me to inherit from Screen and at the same time get the modal dialog behaviour I expected.

Don't know if this helps, but it solved the issue for me.

/norgie

Mar 28, 2013 at 10:53 AM
Edited Mar 28, 2013 at 11:31 AM
Help needed!

Hi, I have a similar implementation and it works fine except that when showing and closing one dialog after another the second dialog doesn't respond anymore and only closes on clicking the x-Button with the result that the parent window remains disabled. Here's my implementation:
public class ShowDialog : IResult
   {
      readonly Type screenType;
      readonly Screen _screen;
      readonly string name;

      [Import]
      public IWindowManager WindowManager { get; set; }

      public ShowDialog( string name )
      {
         this.name = name;
      }

      public ShowDialog( Type screenType )
      {
         this.screenType = screenType;
      }

      public ShowDialog( Screen screen )
      {
         _screen = screen;
      }

      public void Execute( ActionExecutionContext context )
      {
         var screen = _screen != null ? _screen :
                     ( !string.IsNullOrEmpty( name )
                        ? IoC.Get<object>( name )
                        : IoC.GetInstance( screenType, null ) );

         Dialog = screen;
         WindowManager.ShowDialog( screen );

         var deactivated = screen as IDeactivate;
         if ( deactivated == null )
            Completed( this, new ResultCompletionEventArgs() );
         else
         {
            deactivated.Deactivated += ( o, e ) =>
            {
               if ( e.WasClosed )
               {
                  Completed( this, new ResultCompletionEventArgs() );
               }
            };
         }
      }

      public object Dialog { get; private set; }
      public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };

      public static ShowDialog Of<T>()
      {
         return new ShowDialog( typeof( T ) );
      }

      public static ShowDialog Init( Screen screen )
      {
         return new ShowDialog( screen );
      }
   }
Thanks!

Edit:
I'm trying to show child windows like this (IMessageBox implements IScreen and is a MEF singleton):
         MessageBox.Init( MessageBoxEnums.MessageBoxTypes.YesNo, "Test 1", "Test 1" );
         var msgBox1 = ShowDialog.Of<IMessageBox>();
         yield return msgBox1;
         MessageBox.Init( MessageBoxEnums.MessageBoxTypes.YesNo, "Test 2", "Test 2" );
         var msgBox2 = ShowDialog.Of<IMessageBox>();
         yield return msgBox2;
         MessageBox.Init( MessageBoxEnums.MessageBoxTypes.YesNo, "Test 3", "Test 3" );
         var msgBox3 = ShowDialog.Of<IMessageBox>();
         yield return msgBox3;
Here's the code in Caliburn Micros Screen.cs, which seems to fail, i.e. after invoking the second child window and trying to close it, Views.Values has no elements anymore. One more note:
System.Action GetViewCloseAction(bool? dialogResult) {
            var conductor = Parent as IConductor;
            if (conductor != null) {
                return () => conductor.CloseItem(this);
            }

            foreach(var contextualView in Views.Values) {
                var viewType = contextualView.GetType();
               ....