27

I am trying to implement this MVVM pattern for the WPF form closing which is also explained in this blog and I am getting System.InvalidOperationException with error message "DialogResult can be set only after Window is created and shown as dialog." when I am trying to set the Dialog Result on Close button command:

DialogResult = true;

Here is my ViewModel:

class MainWindowViewModel:INotifyPropertyChanged
{
    private bool? dialogResult;
    public bool? DialogResult
    {
        get { return dialogResult; }
        set
        {
            if (value != this.dialogResult)
            {
                this.dialogResult = value;
                OnPropertyChanged("DialogResult");
            }
        }
    }

    public string Text
    {
        get { return "Hello!"; }
    }

    void CloseCommandExecute()
    {
        this.DialogResult = true;
    } 

and Here is the XAML View:

<Window x:Class="WpfApplication.Mvvm.Windowclosing.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication.Mvvm.Windowclosing"
        local:DialogCloser.DialogResult="{Binding DialogResult}"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="100"/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding Text}" Grid.Row="0"/>
        <Button Grid.Row="1" Command="{Binding CloseCommand}">Close Me</Button>
    </Grid>
</Window>

What am I doing wrong here?

Community
  • 1
  • 1
luqi
  • 2,779
  • 2
  • 18
  • 14

11 Answers11

32

Setting a dialog result only works when you open your form with ShowDialog(). You get this error when you try to set the dialog result on a form opened with Show().

Trann
  • 3,539
  • 1
  • 19
  • 15
  • 2
    @Sonhja i think that IsCancel or IsDefault property causes this issue. – Ievgen Oct 01 '13 at 15:27
  • 21
    In my case, I was settings `DialogResult = false;` as initialization, and after testing logic, I set `DialogResult = true;`. That was the source of the issue. you **cannot** set `DialogResult` twice! furthermore, `DialogResult=true; DialogResult=true;` raises the exception as well :-) – itsho Jul 24 '15 at 13:03
  • Inadvertently setting DialogResult twice was the problem for me too. – Emperor Eto Oct 15 '19 at 16:47
17

I came across this problem when I created a window, which was called through ShowDialog(). In the window, I had an Ok_Clicked which included a bunch of statements. In order to 'guarantee' the dialog returned false if there was anything wrong I first initialized the DialogResult to false. If everything was right, I then set DialogResult to true and closed the window. I kept getting the same exception.

I learned that if the DialogResult was not set to true, ShowDialog would always return false. When I removed DialogResult = false in the beginning of the Ok_Clicked, I no longer got the exception.

Zelxin
  • 23
  • 4
Unplug
  • 709
  • 10
  • 27
9

I came across an alternate answer that may help others. I ended up calling Close() on the window before setting the DialogResult. Make sure you don't do that--it will cause this error.

Jarvis
  • 681
  • 8
  • 33
6

For those who use ShowDialog you can remove properties from a button:

IsCancel=true and IsDefault=true

Ievgen
  • 4,261
  • 7
  • 75
  • 124
  • 6
    But then the Escape and Enter keys won't activate the cancel or default button like your users would probably expect them to. – Simes May 25 '16 at 09:03
3

I know this thread is already pretty old but today I ran into the same problem. What I was doing wrong is that I opened a new Window with ShowDialog() than I closed the new Window with Close() and tried to set the DialogResult to True.

But its already enough if you only set the DialogResult to True and after that the Window will close itself automatically and there is no need for calling the Close() method.

C.User
  • 153
  • 18
  • In my case, correct. Search not only for setting `DialogResult` multiple times, but also calling `Close()` before setting the result value. – Denis G. Labrecque Apr 06 '22 at 13:29
  • Wow~ In winform setting DialogResult has nothing to do with Close(). And now they are combined as one. – cheny Apr 14 '22 at 06:22
1

I was getting this error and solved it by setting the DialogResult before any other code in the click event handler. When it was at the end, just before Close, I would get the error. But with it at the beginning, it works. Hope this helps someone, it drove me nuts before I finally got it working.

Terry Tyson
  • 629
  • 8
  • 18
1

I had the

IsDefault="True"

property defined on more than one button.

IngoB
  • 2,552
  • 1
  • 20
  • 35
1

You're trying to set Window.DialogResult too early due to the binding. Presumably your DialogCloser.DialogResult (which you haven't shown us) implementation also sets DialogResult on Window. Try changing the binding mode to OneWayToSource so that changes only propagate to your VM and not the other way around:

local:DialogCloser.DialogResult="{Binding DialogResult, Mode=OneWayToSource}"

Or maybe it's just a matter of changing your behavior to only set Window.DialogResult if the Window has been shown as a dialog. Hard to say without seeing everything.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • 1
    Nope! I tried that but didn't work here. This error only occurs when I click the close button. – luqi Nov 30 '11 at 12:48
  • Was your window actually shown as a dialog? ie. `ShowDialog()` not `Show()` – Kent Boogaart Nov 30 '11 at 12:51
  • I didn't show the implementation of DialogCloser.DialogResult because its mentioned in this blog link which I mentioned above too: http://blog.excastle.com/2010/07/25/mvvm-and-dialogresult-with-no-code-behind/ – luqi Nov 30 '11 at 12:52
  • Boogart: I am calling the form from the App.xaml StatupUri – luqi Nov 30 '11 at 12:54
  • Kent gave me the hint of ShowDialog/Show difference which help me fix the issue. I changed the implementation of the DialogCloser to now use window.close(); and not set the DialogResult. Seems like StartupUri of application calls Show() and you can't set DialogResult if Show() is called. – luqi Nov 30 '11 at 13:19
  • If you fix the answer I can accept your answer Kent! Thanks anyway – luqi Nov 30 '11 at 13:23
0

I ran in to the same issue, using ShowDialog() and setting the result before calling close.

What I did do however was hide the dialog before setting the result. So, when setting DialogResult the Visibility has to be set to Visible, both Hidden and Collapsed will give you the misleading error that "DialogResult can be set only after Window is created and shown as dialog.".

RavenLiquid
  • 131
  • 1
  • 1
  • 3
0

For me, like Jarvis, this error was caused by setting DialogResult after calling Close() for a window that was shown via ShowDialog().

In my case, I think it was because I was porting code from WinForms to WPF. This ordering worked fine in WinForms, but WPF doesn't like it.

I tried other fixes, like removing the isDefault and isCancel attributes from my buttons, but those had no effect.

K. Hrdina
  • 1
  • 1
0

My approach was to pass the dialog as a command parameter from the Close Me button. This approach is done with the help of Galasoft MVVM Light Toolkit.

First of all, you should define your window name by adding this piece of code

x:Name="mainWindow"

Then set the "mainWindow" as the command parameter of the Close Me button command.

<Window x:Class="WpfApplication.Mvvm.Windowclosing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication.Mvvm.Windowclosing"
    x:Name="mainWindow"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MainWindowViewModel />
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="100"/>
        <RowDefinition Height="40"/>
    </Grid.RowDefinitions>
    <TextBlock Text="{Binding Text}" Grid.Row="0"/>
    <Button Grid.Row="1" 
            Command="{Binding CloseCommand}"
            CommandParameter="{Binding ElementName=mainWindow}">Close Me</Button>
</Grid>

In the view model, update CloseCommandExecute() to include Window as its parameter and set the value of DialogResult based on that parameter

void CloseCommandExecute(Window window)
{
    window.DialogResult = true;
}

The command property will look like this.

private ICommand _btnCloseMeCmd;
public ICommand CloseMeCmd
    {
        get
        {
            if (_btnCloseMeCmd== null)
            {
                _btnCloseMeCmd= new RelayCommand<Window>(CloseCommandExecute, null);
            }
            return _btnCloseMeCmd;
        }
    }

Don't forget to add using GalaSoft.MvvmLight.Command; in your view model.