I have an interface:
public interface ICloseable
{
event EventHandler Closing;
event EventHandler Closed;
void Close();
}
which I'm implementing in my ViewModel
:
public class TestViewModel : ICloseable
{
public event EventHandler Closing;
public event EventHandler Closed;
public void Close()
{
RaiseEvent(Closing);
// do some cleaning...
RaiseEvent(Closed);
}
protected virtual void RaiseEvent(EventHandler handler)
{
handler?.Invoke(this, null);
}
}
This TestViewModel
is set as the DataContext
of the following TestView
:
public partial class TestView : Window
{
private bool _contextClosed = false; // true if DataContext has been closed
public TestView()
{
InitializeComponent();
var context = new TestViewModel();
Closing += TestView_Closing;
DataContext = context;
context.Closed += TestViewModel_Closed;
}
private void TestView_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var context = DataContext as ICloseable;
if (!_contextClosed)
{
e.Cancel = true;
context.Close();
return;
}
}
private void TestViewModel_Closed(object sender, EventArgs e)
{
_contextClosed = true;
Application.Current.Dispatcher.Invoke(() => Close());
}
private void TestButton_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
Whenever I try to close TestView
(either by clicking 'X' on the titlebar or the TestButton
), I get the following error:
System.InvalidOperationException: 'Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing.'
at the line:
Application.Current.Dispatcher.Invoke(() => Close());
in the TestViewModel_Closed
method.
I am wondering why this is happening even though I set e.Cancel = true
the first time TestView_Closing
is called.
Thanks for any help.
There is a synchronous method call chain from the TestView_Closing
event handler to the Close
call in the Disaptcher Action, which can be seen in the stack trace of the exception:
at System.Windows.Window.VerifyNotClosing()
at System.Windows.Window.InternalClose(Boolean shutdown, Boolean ignoreCancel)
at System.Windows.Window.Close()
at System.Windows.Threading.Dispatcher.Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
at System.Windows.Threading.Dispatcher.Invoke(Action callback)
at ViewModelClosing.TestView.TestViewModel_Closed(Object sender, EventArgs e) in C:\Usr\Projekte\StackOverflow\ViewModelClosing\MainWindow.xaml.cs:line 50
at ViewModelClosing.TestViewModel.RaiseEvent(EventHandler handler) in C:\Usr\Projekte\StackOverflow\ViewModelClosing\MainWindow.xaml.cs:line 80
at ViewModelClosing.TestViewModel.Close() in C:\Usr\Projekte\StackOverflow\ViewModelClosing\MainWindow.xaml.cs:line 75
at ViewModelClosing.TestView.TestView_Closing(Object sender, CancelEventArgs e) in C:\Usr\Projekte\StackOverflow\ViewModelClosing\MainWindow.xaml.cs:line 43
at System.Windows.Window.OnClosing(CancelEventArgs e)
at System.Windows.Window.WmClose()
You should replace
Application.Current.Dispatcher.Invoke(() => Close());
by the asynchronous call
Dispatcher.BeginInvoke(new Action(Close));
where Dispatcher
is a property of the Window class. There is no need to use Application.Current.Dispatcher
.