Caliburn-Micro / Caliburn.Micro

A small, yet powerful framework, designed for building applications across all XAML platforms. Its strong support for MV* patterns will enable you to build your solution quickly, without the need to sacrifice code quality or testability.
http://caliburnmicro.com/
MIT License
2.79k stars 775 forks source link

TryCloseAsync(false) problem? #857

Open Frenchy62620 opened 1 year ago

Frenchy62620 commented 1 year ago

I open a Dialog viewModel with WindowManager.

so inside the Dialog, when i use TryCloseAsync(true) to close the Dialog, no problem,

but when i want to Cancel with TryCloseAsync(false) i have this error:

Message=DialogResult can be set only after Window is created and shown as dialog.

i am missingsomething??

the DailogViewModel is opened from the mainViewModel normlly with WindowManager

sorry for my english, hope you understant what i want to mean....

Regards terry

KasperSK commented 1 year ago

Can you make a reproduction of this? Sounds like a bug.

Frenchy62620 commented 1 year ago

Hi KasperSK

its just the project WpfApp1

Bootstrapper:

using Caliburn.Micro;
using Ninject;
using System;
using System.Collections.Generic;
using System.Windows;
using WpfApp1.ViewModels;

namespace WpfApp1
{
    public class Bootstrapper : BootstrapperBase
    {
        private IKernel kernel;
        public Bootstrapper()
        {
            Initialize();
        }

        protected override void Configure()
        {
            kernel = new StandardKernel();
            kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
            kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();

            //var bindings0 = kernel.GetBindings(typeof(MyGridViewModel));
            //var bindings1 = kernel.GetBindings(typeof(MainViewModel));
        }
        protected override async void OnStartup(object sender, StartupEventArgs e)
        {
            await DisplayRootViewForAsync<MainViewModel>();
        }
        protected override object GetInstance(Type service, string key)
        {
            return kernel.Get(service);
        }
        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return kernel.GetAll(service);
        }
        protected override void BuildUp(object instance)
        {
            kernel.Inject(instance);
        }
    }
}

the mainViewModel

using Caliburn.Micro;
using Ninject;
using Ninject.Syntax;
using System.Dynamic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfApp1.ViewModels
{
    public class MainViewModel : Screen
    {
        private readonly IResolutionRoot resolutionRoot;
        private readonly IEventAggregator eventAggregator;
        public MainViewModel(IResolutionRoot resolutionRoot, IEventAggregator eventAggregator)
        {
            this.resolutionRoot = resolutionRoot;
            this.eventAggregator = eventAggregator;
        }
        #region first stackpanel
        public async void DoModal()
        {
            //var vm = new MyDialogViewModel();
            var vm = resolutionRoot.Get<MyModalDialogViewModel>();
            var manager = resolutionRoot.Get<IWindowManager>();

            dynamic settings = new ExpandoObject();
            settings.WindowStyle = WindowStyle.ThreeDBorderWindow;
            settings.ShowInTaskbar = true;
            settings.Title = "COUCOU";
            settings.WindowState = WindowState.Normal;
            settings.ResizeMode = ResizeMode.CanMinimize;

            var result = await manager.ShowDialogAsync(vm, null, settings);

            MessageBox.Show($"The dialog returns with the result {result}");
        }

        public async void DoClose()
        {
            await TryCloseAsync();
        }
        #endregion

    }
}

MyModalDialogViewModel

using Caliburn.Micro;
using Ninject.Syntax;

namespace WpfApp1.ViewModels
{
    public class MyModalDialogViewModel : Screen
    {
        private readonly IResolutionRoot resolutionRoot;
        private readonly IEventAggregator eventAggregator;

        public MyModalDialogViewModel(IResolutionRoot resolutionRoot, IEventAggregator eventAggregator)
        {
            this.resolutionRoot = resolutionRoot;
            this.eventAggregator = eventAggregator;
        }
        public async void DoAccept()
        {
            await TryCloseAsync(true);
        }
        public async void DoCancel()
        {
            await TryCloseAsync(false);  // <------------------  ERROR here
        }
    }
}

the dialogView

<UserControl x:Class="WpfApp1.Views.MyModalDialogView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:cal="http://www.caliburnproject.org"
             xmlns:local="clr-namespace:WpfApp1.Views"
             mc:Ignorable="d" Background="Azure"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Width="500" Height="500" Background="AntiqueWhite">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Background="Aqua">
            <Button x:Name="DoAccept" Content="Accept" Padding="20" Margin="10" IsDefault="True" />
            <Button x:Name="DoCancel" Content="Cancel" Padding="20" Margin="10" IsCancel="True" />
        </StackPanel>
    </Grid>
</UserControl>

So when i cancel, i have the error each time

Paciv commented 6 months ago

Your button is set as IsCancel="True" and have no Command bind In this case (see Button.cs#L277) the framework automatically executes a DialogCancelCommand which closes the window.

Hence you call to TryCloseAsync(false) tries to set the Window.DialogResult while the window is already considered as closed.

I believe you can remove your call to TryCloseAsync(false) as the framework command already set the DialogResult to false which triggers the window close.